I’m writing a program that uses SDL_Audio and dynamically produces sound, but I’m finding that my implementation isn’t very responsive with latency varying from .3 to .5 seconds.
My suspicion is that I produce sound long before the sound card calls for data. My solution is to trigger sound production with the first request for audio. To do this, I added a function pointer which is initially set to a prime function that
- triggers sound production
- resets the function pointer to the audioCallback method
- calls the audioCallback method directly
So in essence I have a static member function calling a member function through a member function pointer and I’m getting compilation issues.
Looking for the correct syntax for calling the member function through a function pointer.
I have the following test program that reproduces my problem.
/*
* testcallback.cpp
*
* Created on: Apr 20, 2023
* Author: phil
*/
#include <SDL2/SDL.h>
#include <fstream>
#include <iostream>
class AudioCallbackTest
{
public:
AudioCallbackTest()
{
SDL_Init(SDL_INIT_AUDIO);
SDL_AudioSpec desiredSpec = {0};
desiredSpec.format = AUDIO_S16SYS;
desiredSpec.channels = 1;
desiredSpec.freq = 44100;
desiredSpec.samples = 256;
desiredSpec.callback = audioCallbackWrap;
desiredSpec.userdata = this;
m_hwDevice = SDL_OpenAudioDevice(nullptr, false, &desiredSpec, NULL, 0);
if (m_hwDevice == 0)
{
std::cout << "SDL_OpenAudioDevice failed: SDL error [" << SDL_GetError() << "]" << std::endl;
return;
}
}
static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
{
// ((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);
((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
}
void audioCallback(Uint8* stream, int len)
{
std::cout << "audio callback" << std::endl;
}
void primeAudioCallback(Uint8* stream, int len)
{
std::cout << "prime callback" << std::endl;
m_audioCallback = &AudioCallbackTest::audioCallback;
}
private:
SDL_AudioDeviceID m_hwDevice;
void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
};
Notice the commented line in the static function. In the first form I get the error
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o testcallback.o ../testcallback.cpp
../testcallback.cpp: In static member function ‘static void AudioCallbackTest::audioCallbackWrap(void*, Uint8*, int)’:
../testcallback.cpp:35:65: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘((AudioCallbackTest*)_instance)->AudioCallbackTest::m_audioCallback (...)’, e.g. ‘(... ->* ((AudioCallbackTest*)_instance)->AudioCallbackTest::m_audioCallback) (...)’
35 | ((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
With the second form I get the following error
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o testcallback.o ../testcallback.cpp
../testcallback.cpp: In static member function ‘static void AudioCallbackTest::audioCallbackWrap(void*, Uint8*, int)’:
../testcallback.cpp:37:51: error: invalid use of member ‘AudioCallbackTest::m_audioCallback’ in static member function
37 | ((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
| ^~~~~~~~~~~~~~~
../testcallback.cpp:51:35: note: declared here
51 | void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
| ^~~~~~~~~~~~~~~
compiler version is GNU C++17 (Ubuntu 11.3.0-1ubuntu1~22.04) version 11.3.0 (x86_64-linux-gnu)
IDE is Version: 2023-03 (4.27.0)
CDT is Version: 11.1.0.202212091724
I’ve tried various syntax’s
This->m_audioCallback(_stream, _length);
This->*m_audioCallback(_stream, _length);
(This->*m_audioCallback)(_stream, _length);
This->*m_audioCallback(_stream, _length);
(*This).*m_audioCallback(_stream, _length);
I’ve googled the error message.
I’ve googled for "static member calling member through member function pointer" and got hits on how to call member functions and lots of examples of function pointers, but nothing that hit the mark.
I assume there is a proper syntax for this.
4
Answers
Never used std::invoke before so I tried that first just to understand it and it works fine.
Also tried the second option (instance->*(instance->m_audioCallback))(_stream, _length); and that also works.
Thanks so much for the help.
But in the end, I still have 200ms of latency.
The second example’s error message tells you what’s wrong:
m_audioCallback
is an instance variable so you can’t simplyYou need to tell the compiler whose
m_audioCallback
to use:Weird thing is I can’t get that to work, either. clang reports called object type ‘void (AudioCallbackTest::)(uint8_t , int)’ is not a function or function pointer, but
void (AudioCallbackTest::*)(uint8_t *, int)
sure as hell looks like a function pointer to me. g++ complains error: must use ‘.‘ or ‘->‘ to call pointer-to-member function, which I’m pretty sure I’m doing.But
std::invoke
isn’t making whatever mistake I’m making.Outputs the expected
Since
std::invoke
‘s job is to simplify this sort of syntax, use it.I tried to simplify your code and according to this answer, I tried pointer conversion but it didn’t work.
Maybe you can try singleton in the same answer above, declaring a static pointer in the class:
I don’t know how you will call the function, so I have only written part of it without actually running it.
Hope this helps.
As an alternative to @user4581301’s answer (although I think, actually, I prefer his method), here’s the magic sauce to call the function direct:
Note the extra set of brackets and the need to use, in effect,
_instance
twice, once to retrieve the member function pointer and once to call it.