SDL audio callback stops after two iterations - c++

Given the following code:
#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <iostream>
#include <chrono>
#include <thread>
static void SDLCallback(void *userData, Uint8 *data, int bytes) {
std::cerr << "SDLCallback: " << bytes / sizeof(float) << "\n";
}
int main() {
using namespace std::literals;
SDL_Init(SDL_INIT_AUDIO);
SDL_AudioDeviceID m_deviceId{};
SDL_AudioSpec m_desired, m_obtained;
m_desired.freq = 48000;
m_desired.format = AUDIO_F32SYS;
m_desired.channels = 2;
m_desired.samples = 1024;
m_desired.callback = SDLCallback;
m_desired.userdata = nullptr;
m_deviceId = SDL_OpenAudioDevice(nullptr, 0, &m_desired, &m_obtained, 0);
std::cerr << "SDL device: " << m_deviceId << std::endl;
if (m_deviceId < 2) {
std::cerr << "SDL: Couldn't open audio: " << SDL_GetError() << std::endl;
exit(1);
}
std::cerr << "rate: " << m_obtained.freq << "\n";
std::cerr << "samples: " << m_obtained.samples << "\n";
std::cerr << "bytes: " << m_obtained.size << "\n";
std::cerr << "channels: " << (int)m_obtained.channels << "\n";
SDL_PauseAudioDevice(m_deviceId, 0);
for (int i = 0; i < 10; i++) {
std::this_thread::sleep_for(100ms);
std::cerr << (SDL_GetAudioDeviceStatus(m_deviceId) == SDL_AUDIO_PLAYING)
<< std::endl;
}
SDL_CloseAudioDevice(m_deviceId);
SDL_Quit();
}
I am seeing the following output:
$ ./a.out
SDL device: 2
rate: 48000
samples: 1024
bytes: 8192
channels:
SDLCallback: 2048
SDLCallback: 2048
1
1
1
1
1
1
1
1
1
1
<program gets stuck on SDL_CloseAudioDevice>
That is, the SDLCallback function is called only twice at the beginning then not anymore, yet the audio is still marked as running. I am using PulseAudio.
What can I do to prevent that ? Where is my program wrong ? It works if I kill PulseAudio, but PulseAudio works fine for all my other software.
And if it is not wrong, how can I make sure that my users will never encounter that issue ? How can I recover without getting the program stuck on SDL_CloseAudioDevice ? As PulseAudio is very common among Linux users.

Related

No sound generated by SDL Audio Callback

I am trying to get a simple sinewave sound generation example working using SDL 2.0.12 on Windows 10, but no sound is being output.
I have no idea if it is a problem with the code or with the output device or the audio drivers.
I'd really appreciate suggestions of how I can debug the problem further.
#include <iostream>
#include "SDL.h"
float sine_freq = 200.0f;
float audio_volume = 4000.0f;
float audio_frequency;
void SineAudioCallback(void* userdata, Uint8* stream, int len) {
float* buf = (float*)stream;
for (int i = 0; i < len / 4; ++i) {
buf[i] = (float)(audio_volume * sin(2 * M_PI * i * audio_frequency));
}
return;
}
int main(int argc, char* argv[])
{
if (SDL_Init(SDL_INIT_AUDIO)) {
return 1;
}
std::cout << "[SDL] Audio driver: " << SDL_GetCurrentAudioDriver() << std::endl;
SDL_AudioSpec want, have;
SDL_zero(want);
want.freq = 5000;
want.format = AUDIO_F32;
want.channels = 2;
want.samples = 4096;
want.callback = SineAudioCallback;
std::cout <<"[SDL] Desired - frequency: " << want.freq
<< ", format: f " << SDL_AUDIO_ISFLOAT(want.format) << " s " << SDL_AUDIO_ISSIGNED(want.format) << " be " << SDL_AUDIO_ISBIGENDIAN(want.format) << " sz " << SDL_AUDIO_BITSIZE(want.format)
<< ", channels: " << (int)want.channels << ", samples: " << want.samples << std::endl;
SDL_AudioDeviceID dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
if (!dev) {
SDL_Quit();
return 1;
}
std::cout << "[SDL] Desired - frequency: " << have.freq
<< ", format: f " << SDL_AUDIO_ISFLOAT(have.format) << " s " << SDL_AUDIO_ISSIGNED(have.format) << " be " << SDL_AUDIO_ISBIGENDIAN(have.format) << " sz " << SDL_AUDIO_BITSIZE(have.format)
<< ", channels: " << (int)have.channels << ", samples: " << have.samples << std::endl;
audio_frequency = sine_freq / have.freq;
SDL_PauseAudioDevice(dev, 0);
SDL_Delay(10000);
SDL_CloseAudioDevice(dev);
SDL_Quit();
return 0;
}
The output I get is
[SDL] Audio driver: wasapi
[SDL] Desired - frequency: 5000, format: f 256 s 32768 be 0 sz 32, channels: 2, samples: 4096
[SDL] Desired - frequency: 5000, format: f 256 s 32768 be 0 sz 32, channels: 2, samples: 118
So there is a difference between SDL_AudioSpec I want and have, in the number of samples being reduced.
I also get an error message relating to the dlls although I'm not sure if it is important.
avcore\audiocore\client\audioclient\audioclientcore.cpp(1839)\AUDIOSES.DLL!00007FFC48E00F8E: (caller: 0000000070D6BE39) ReturnHr(1) tid(46dc) 80070057 The parameter is incorrect.
It turns out there were two things I needed to do to solve this problem.
For a float type the sound wave can only take values from -1 to 1, whilst I was using a much greater volume boost. I needed to change
float audio_volume = 1.0f;
For Windows, the direct sound and winmm drivers give better sample lengths than wasapi. For SDL2 on Windows, you need SDL_AUDIODRIVER=directsound or SDL_AUDIODRIVER=winmm set as an environment variable. More details here

termios settings on USB modem serial get ignored

I'm on Ubuntu 18.04 and try to read AT commands from a modem. I need to do some changes to the serial interface to properly communicate these commands. However, if I try setting the baudrate, it stays the same. I use the following code:
#include <iostream>
#include <exception>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#define DEVICE "/dev/ttyUSB2"
using namespace std;
class ATException : public exception {
public:
ATException(string msg) : msg(msg) { }
virtual const char* what() const throw() {
return msg.c_str();
}
private:
string msg;
};
void set_speeds(int fd, speed_t ispeed, speed_t ospeed) {
struct termios term_args;
int result = tcgetattr(fd, &term_args);
if (result != 0) throw ATException ("unable to tcgetattr device");
term_args.c_cflag = CS8 | CREAD | CLOCAL;
if (cfsetispeed(&term_args, ispeed) != 0) throw ATException("error setting i speed");
if (cfsetospeed(&term_args, ospeed) != 0) throw ATException("error setting o speed");
if (tcsetattr(fd, TCSANOW, &term_args) != 0) throw ATException("unable to set device attr");
}
void get_speeds(int fd, speed_t& ispeed, speed_t& ospeed) {
struct termios term_args;
memset(&term_args, 0, sizeof term_args);
int result = tcgetattr(fd, &term_args);
if (result != 0) throw ATException ("unable to tcgetattr device");
ispeed = cfgetispeed(&term_args);
ospeed = cfgetospeed(&term_args);
}
int main (int argc, char** argv) {
speed_t ispeed;
speed_t ospeed;
struct termios term_args;
int device_fd = open(DEVICE, O_RDWR);
if (device_fd == -1) {
cerr << "unable to open device " << DEVICE << endl;
exit(1);
}
try {
cout << " B9600 speed: " << B9600 << endl;
cout << " B38400 speed: " << B38400 << endl;
cout << "B115200 speed: " << B115200 << endl;
cout << "B230400 speed: " << B230400 << endl << endl;
get_speeds(device_fd, ispeed, ospeed);
cout << "current i speed: " << ispeed << endl;
cout << "current o speed: " << ospeed << endl;
speed_t test_speeds [] = {B38400, B115200, B230400};
for (int i = 0; i < (sizeof(test_speeds) / sizeof(speed_t)); i++) {
cout << "setting speeds to " << test_speeds[i] << endl;
set_speeds(device_fd, test_speeds[i], test_speeds[i]);
get_speeds(device_fd, ispeed, ospeed);
cout << "current i speed: " << ispeed << endl;
cout << "current o speed: " << ospeed << endl;
}
int chars_written;
// writing/reading
chars_written = write(device_fd, "AT\r", 3);
cout << "written " << chars_written << " chars" << endl;
char read_buf [16];
int chars_read = read(device_fd, read_buf, sizeof(read_buf));
cout << "read (" << chars_read << "): " << read_buf << endl;
usleep(50000);
chars_read = read(device_fd, read_buf, sizeof(read_buf));
cout << "read (" << chars_read << "): " << read_buf << endl;
} catch (ATException& e) {
cerr << "ERROR: " << e.what() << DEVICE << endl;
}
close(device_fd);
}
If I execute this, I get the following output:
B9600 speed: 13
B38400 speed: 15
B115200 speed: 4098
B230400 speed: 4099
current i speed: 13
current o speed: 13
setting speeds to 15
current i speed: 13
current o speed: 13
setting speeds to 4098
current i speed: 13
current o speed: 13
setting speeds to 4099
current i speed: 13
current o speed: 13
written 3 chars
...
It seems like the baudrate hasn't changed. Why is this? Did I miss something? Thanks for help. Let me know if there's something which needs to be clarified, please!
If you consider down rating my question, please also leave a comment why, so I can improve my question or learn for the future. Thanks.

OpenCL Child Kernel Error

Aloha,
I'm struggling with OpenCL child kernel feature.
Kernel SRC (Minimal example):
kernel void launcher()
{
ndrange_t ndrange = ndrange_1D(1);
enqueue_kernel(get_default_queue(), CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange,
^{
size_t id = get_global_id(0);
}
);
}
stdafx.h:
#pragma once
#define __CL_ENABLE_EXCEPTIONS
#define CL_HPP_ENABLE_EXCEPTIONS
#define CL_HPP_TARGET_OPENCL_VERSION 200
#include "targetver.h"
#include <CL/cl2.hpp>
#include <iostream>
#include <string>
Full SRC (Minimal):
#include "stdafx.h"
std::string kernel2_source(
"kernel void launcher() ""\n"
"{ ""\n"
" ndrange_t ndrange = ndrange_1D(1);""\n"
" enqueue_kernel(get_default_queue(), CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange,""\n"
" ^{""\n"
" size_t id = get_global_id(0);""\n"
" }""\n"
" );""\n"
"}""\n");
//Number of Input Elements
constexpr int numTriangles = 10;
cl_int errorcode = CL_BUILD_ERROR; //Has to be set to build error, because errorcode isn't set when exception occurs
//Move variable definitions out of main for test purposes;
//Numerous definitions
cl::Program program;
std::vector<cl::Device> devices;
std::vector<cl::Platform> platforms;
cl::CommandQueue queue;
cl::Program::Sources source{ kernel2_source };
int main() {
try {
// Query for platforms
cl::Platform::get(&platforms);
std::cout << "Num Platforms: " << platforms.size() << std::endl;
// Get a list of devices on this platform
platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices);
std::cout << "Using platform: " << platforms[0].getInfo<CL_PLATFORM_NAME>() << std::endl;
std::cout << "Num Devices: " << devices.size() << std::endl;
// Create a context for the devices
std::cout << "Using device: " << devices[0].getInfo<CL_DEVICE_NAME>() << std::endl;
//Create a context for the first device
//cl::Context context({ devices[0]});
cl::Context context({ devices[0] });
// Create a command−queue for the first device
queue = cl::CommandQueue(context, devices[0]);
cl::DeviceCommandQueue deviceQueue;
deviceQueue = cl::DeviceCommandQueue(context, devices[0]);
// Create the program from the source code
program = cl::Program(context, source);
std::cout << "Building Program" << std::endl;
// Build the program for the devices
errorcode = program.build("-cl-std=CL2.0 -g");
std::cout << "Success!" << std::endl;
cl::Kernel kernel = cl::Kernel(program, "launcher");
cl::NDRange global = numTriangles;
cl::NDRange local = 1;
queue.enqueueNDRangeKernel(kernel, cl::NullRange, global, local);
std::cout << "finished" << std::endl;
std::cin.get();
}
catch (cl::Error error)
{
std::cout << "Error!" << std::endl;
std::cout << error.what() << "(" << error.err() << ")" << std::endl;
std::cout << "Errorcode: " << errorcode << std::endl;
if (errorcode != CL_SUCCESS) { //...
std::cout << "Build Status: " << program.getBuildInfo<CL_PROGRAM_BUILD_STATUS>(devices[0]) << std::endl;
//std::cout << "Build Status: " << program.getBuildInfo<CL_PROGRAM_BUILD_STATUS>(devices[1]) << std::endl;
std::cout << "Build Options:" << program.getBuildInfo<CL_PROGRAM_BUILD_OPTIONS>(devices[0]) << std::endl;
//std::cout << "Build Options:" << program.getBuildInfo<CL_PROGRAM_BUILD_OPTIONS>(devices[1]) << std::endl;
std::cout << "Build Log:" << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(devices[0]) << std::endl;
//std::cout << "Build Log:" << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(devices[1]) << std::endl;
}
}
std::cin.get();
return 0;
}
Output:
Num Platforms: 1
Using platform: AMD Accelerated Parallel Processing
Num Devices: 2
Using device: Hawaii
Building Program
=> Exception.
There appears an uncaught exception which is strange, because all build error should be caught.
The ndrange_1D(1) is just for testing purposes (and to produce an acceptable amount of dummy output).
The device (AMD R9 390X) is OpenCL 2.0 capable.
Any ideas how to fix this?
EDIT:
Even not using exceptions and using errorcodes throws this an exception!

Clicking in ALSA tone generator

I'm learning to use ALSA on a Raspberry Pi 2. I've written a small test program in C++ to generate a 440 Hz test tone. It makes the tone, but there is a clicking sound about twice per second in the tone.
Does anyone have an idea why this might be happening? The code is below.
#include <cmath>
#include <climits>
#include <iostream>
#include <alsa/asoundlib.h>
#include "definitions.hpp"
using namespace std;
int main() {
int ret;
snd_pcm_t* pcm_handle; // device handle
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
snd_pcm_hw_params_t* hwparams; // hardware information
char* pcm_name = strdup("plughw:0,0"); // on-board audio jack
int rate = 48000;
const uint16 freq = 440;
long unsigned int bufferSize = 8192*4;
const uint32 len = bufferSize*100;
const float32 arg = 2 * 3.141592 * freq / rate;
sint16 vals[len];
for(int i = 0; i < len; i = i + 2) {
vals[i] = SHRT_MAX * cos(arg * i / 2);
}
snd_pcm_hw_params_alloca(&hwparams);
ret = snd_pcm_open(&pcm_handle, pcm_name, stream, 0);
cout << "Opening: " << snd_strerror(ret) << endl;
ret = snd_pcm_hw_params_any(pcm_handle, hwparams);
cout << "Initializing hwparams structure: " << snd_strerror(ret) << endl;
ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED);
cout << "Setting access: " << snd_strerror(ret) << endl;
ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams,
SND_PCM_FORMAT_S16_LE);
cout << "Setting format: " << snd_strerror(ret) << endl;
ret = snd_pcm_hw_params_set_rate(pcm_handle, hwparams,
rate, (int)0);
cout << "Setting rate: " << snd_strerror(ret) << endl;
ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2);
cout << "Setting channels: " << snd_strerror(ret) << endl;
ret = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, 2, 0);
cout << "Setting periods: " << snd_strerror(ret) << endl;
ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams,
&bufferSize);
cout << "Setting buffer size: " << snd_strerror(ret) << endl;
ret = snd_pcm_hw_params(pcm_handle, hwparams);
cout << "Applying parameters: " << snd_strerror(ret) << endl;
cout << endl << endl;
const void* ptr = (const void*)&vals;
int err;
do {
ptr += bufferSize;
ret = snd_pcm_writei(pcm_handle,
ptr, len);
if(ret < 0) {
err = snd_pcm_prepare(pcm_handle);
cout << "Preparing: " << snd_strerror(err)
<< endl;
}
} while(ret < 0);
cout << "Writing data: " << ret << ", " << snd_strerror(ret)
<< endl;
}
When you run it, you get this terminal output. Of course, there's no write error, just the number of bits written.
pi#raspberrypi:~/radio $ ./bin/alsatest
Opening: Success
Initializing hwparams structure: Success
Setting access: Success
Setting format: Success
Setting rate: Success
Setting channels: Success
Setting periods: Success
Setting buffer size: Success
Applying parameters: Success
Writing data: 344110, Unknown error 344110
UPDATE - NEXT DAY
OK. I've hooked the output up to my handy-dandy oscilloscope and saw the following waveform. There seems to be a discontinuity in the signal every time there's a click. I added a few lines to count how many nearly-zero values were next to each other in my sinusoid array, and there were none. Oddly enough, the ALSA sample program /test/pcm.c makes a perfect wave. Perhaps I need to write in really small chunks? There doesn't seem to be much difference between my code and the example.

Error using libavcodec with linux in C/C++

The problem: AVFormatContext::nb_streams has too larve value.
My C++ code (error handling, includes etc. omited to reduce the listing):
void printA(AVFormatContext* _a)
{
std::cout << "duration " << (unsigned long)_a->duration << "\n";
std::cout << "streams " << _a->nb_streams << "\n";
std::cout << "format name " << _a->iformat->name << "\n";
std::cout << "bit_rate " << _a->bit_rate << "\n";
std::cout << "long name " << _a->iformat->long_name << "\n";
}
int main(int argc, char **argv)
{
if ( argc < 2 )
{
std::cout << "Usage: " << argv[0] << " <file>\n";
return 1;
}
av_register_all();
AVFormatContext *pFormatCtx = avformat_alloc_context();
avformat_open_input (&pFormatCtx, argv[1], NULL, NULL);
avformat_find_stream_info(pFormatCtx, NULL);
printA( pFormatCtx );
return 0;
}
Running:
xx#xx /tmp/avlib $ ./avlibtest /ar/video/Nauka.Sna.2006.HDRip.AVC.mkv
[matroska,webm # 0x804c040] max_analyze_duration reached
[matroska,webm # 0x804c040] Estimating duration from bitrate, this may be inaccurate
duration 134741408
streams 134531840 <---- !!! :-O
format name matroska,webm
bit_rate 0
long name Matroska/WebM file format
But the "avplay" program works well.