Search code examples
c++audiosdl

trying to assign callback variable with SDL for audio


When I try to assign the callback variable in the AudioSpec structure, the compiler doesn't like it when I'm trying to assign a type "void function" to a type SDL_AudioCallback.

void mainEngineCW4::myAudioCallback(void* userdata, Uint8* stream, int Glen) {

AudioData* audio = (AudioData*)userdata;

if (audio->length == 0)
    return;

Uint32 length = (Uint32)len;
length = (length > audio->length ? audio->length : length); // if length is more than the audio length, then set length to be the audio.length, if not, set it to be the length passed to the function

SDL_memcpy(stream, audio->position, length);

audio->position += length;
audio->length -= length; }

void mainEngineCW4::playAudio() // this function is for loading an audio device, and playing the audio through that device {

AudioData audio;
audio.position = wavStart;
audio.length = wavLength;

wavSpec.callback = mainEngineCW4::myAudioCallback;
wavSpec.userdata = &audio;

audioDevice = SDL_OpenAudioDevice(NULL, 0, &wavSpec, NULL, SDL_AUDIO_ALLOW_ANY_CHANGE);
if (audioDevice == 0)
{
    std::cerr << SDL_GetError() << std::endl;
    return;
}

SDL_PauseAudioDevice(audioDevice, 0); // mildly confused by why they decided to call the function for starting to play audio for "PauseAudioDevice" but yeah. this plays audio. }

I've split the responsibilities of the controlling audio into three functions, loadAudio, startAudio and endAudio. I've assigned the variables needed for the audio in the .h file, so it is accessible for all the functions in the class.

this is the error


Solution

  • The SDL callback signature is a stand-alone function of type void (*)(void* userdata, Uint8* stream, int len).

    Your callback signature is close but not quite a match: void (mainEngineCW4::*)(void* userdata, Uint8* stream, int len).

    The main difference is that it is a member function, which is part of its type. In short, that type -- member function -- implies that you must call it with an instance of a class, which becomes the this pointer, e.g., myEngine->myAudioCallback( ... ). Stand-alone functions do not have a this pointer and so would be called like standAloneAudioCallback( ... ).

    One way to resolve this is to make myAudioCallback a static member function. Another way is to make is a non-member (aka, stand-alone) function.

    In either case, if you need to access (non-static) member data for the mainEngineCW4 class that it is currently a part of, you will need to get access to it, usually by a static or global variable or by using the userdata param to store the class instance. I'd need to see a little more of your program to demonstrate it precisely.


    Update responding to your comments:

    There are a couple ways you could do this. Probably what you want is to set the userdata in your audio spec to your instance of the engine class and pass in a static (or standalone) callback that uses that pointer to call the member function:

    class mainEngineCW4 
    {
        struct AudioData { /* ... */ };
    
        // Private static member function, but could be a stand-alone function
        static void myStaticAudioCallback( void* const userData, Uint8* const stream, const int len )
        {
            const auto myEngine = reinterpret_cast<mainEngineCW4*>( userData );
            myEngine->myAudioCallback( stream, len );
        }
    
        void myAudioCallback( const Uint8* const stream, const int len )
        {
            // ... Process stream using AudioData struct or whatever
        }
    
    public:
        void playAudio()
        {
            auto audioSpec = SDL_AudioSpec{};
            // ... set the freq and format and what not in the audio spec
            audioSpec.callback = &myStaticAudioCallback;
            audioSpec.userdata = this;
    
            const auto audioDevice = SDL_OpenAudioDevice( NULL, 0, &audioSpec, NULL, SDL_AUDIO_ALLOW_ANY_CHANGE);
            // ...
        }
    };
    

    I've written it here all in the class definition for concision, but you can split it out to the .h/.cpp if you want. I've also added some const's, which are good practice, and I followed "Almost Always Auto" style.