skip to Main Content

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


  1. Chosen as BEST ANSWER

    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.


  2. The second example’s error message tells you what’s wrong:

    ../testcallback.cpp:37:51: error: invalid use of member ‘AudioCallbackTest::m_audioCallback’ in static member function
       37 |                 ((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
          |                                                   ^~~~~~~~~~~~~~~
    

    m_audioCallback is an instance variable so you can’t simply

    ((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
    

    You need to tell the compiler whose m_audioCallback to use:

    ((AudioCallbackTest*)_instance)->*((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);
    

    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.

    #include <iostream>
    #include <functional>
    using Uint8 = uint8_t;
    class AudioCallbackTest
    {
    public:
        AudioCallbackTest()
        {
        }
    
        static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
        {
            AudioCallbackTest* instance = (AudioCallbackTest*)_instance;  // to simplify following
            std::invoke(instance->m_audioCallback, *instance, _stream, _length);
            //                    ^ Is an instance variable. Needs to be accessed through an instance
            //          ^ and here's the instance
    
            //dunno what I have wrong in the syntax here.
            // void (AudioCallbackTest::*)(uint8_t *, int) sure looks like a method pointer to me.
            //instance->*(instance->m_audioCallback)(_stream, _length); 
    
    
        }
        void audioCallback(Uint8* , int )
        {
            std::cout << "audio callback" << std::endl;
        }
        void primeAudioCallback(Uint8* , int )
        {
            std::cout << "prime callback" << std::endl;
            m_audioCallback = &AudioCallbackTest::audioCallback;
        }
    
    private:
        void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
    };
    
    int main()
    {
        AudioCallbackTest test;
        Uint8 stream = 1;
        AudioCallbackTest::audioCallbackWrap (&test, &stream, 2);
        AudioCallbackTest::audioCallbackWrap (&test, &stream, 2);
    }
    

    Outputs the expected

    prime callback
    audio callback
    

    Since std::invoke‘s job is to simplify this sort of syntax, use it.

    Login or Signup to reply.
  3. I tried to simplify your code and according to this answer, I tried pointer conversion but it didn’t work.

    class A
    {
      void (A::* m_func_ptr)(int len) = &A::foo2;
      static void foo(void* obj, int k) {
        //want call foo1
        A* p = static_cast<A*>(obj);
        p->m_func_ptr(k);
      }
      void foo1(int len) { cout << "foo1" << endl; }
      void foo2(int len) {
        cout << "foo2" << endl;
        m_func_ptr = &A::foo1;
      }
    };
    

    Maybe you can try singleton in the same answer above, declaring a static pointer in the class:

    class A
    {
    public:
      static A* myInstance;
      //static A* getInstance() {
      //  if (!myInstance) {
      //    myInstance = new A;
      //  }
      //  return myInstance;
      //}
      static void foo(void* obj, int k) {
        //call foo1
        A* p = static_cast<A*>(obj);
        p->foo1(k);
      }
      void foo1(int len) { cout << "foo1" << endl; }
      void foo2(int len) {
        cout << "foo2" << endl;
      }
    };
    

    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.

    Login or Signup to reply.
  4. 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:

    static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
    {
        auto *inst = (AudioCallbackTest*) _instance;
        auto call_me = inst->m_audioCallback;
        (inst->*call_me) (_stream, _length);
    }
    

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search