cocoggu
cocoggu

Reputation: 391

How to send a pointer on a callback function which is encapsulated in a class

I'm actually coding a program in C++ using the library PortAudio. This library is using a callback function tu manage audio input and output. In C++, I implemented this callback function in my class "Audio" and I can't send it to Pa_OpenDefaultStream(). The compiler says "this argument is incompatible with parameter of type PaStreamCallback*" with this line :

Pa_OpenDefaultStream(&this->_stream, 1, 2, paFloat32, this->_sampleRate, this->_framesPerBuffer, callbackFunction, NULL);

When I use C, sending my callbackFunction like this works well. How can I send my callback function to this OpenDefaultStream function ?

Upvotes: 2

Views: 1800

Answers (3)

DoctorMoisha
DoctorMoisha

Reputation: 1643

I know it's been a while, but I encountered same problem and found solution in C++11 style.

So we have

 int AudioHandler::CallBackFunction(const void *inputBuffer, void *outputBuffer,
                               unsigned long framesPerBuffer,
                               const PaStreamCallbackTimeInfo* timeInfo,
                               PaStreamCallbackFlags statusFlags,
                               void *userData);

Then, where in AudioHander.cpp do global variable:

 AudioHandler* veryDangerousHandler;

And in the end:

PaStreamCallback* callbackus = [](const void *inputBuffer, void *outputBuffer,
        unsigned long framesPerBuffer,
        const PaStreamCallbackTimeInfo* timeInfo,
        PaStreamCallbackFlags statusFlags,
        void *userData) -> int
{
    return veryDangerousHandler->CallBackFunction(inputBuffer, outputBuffer,
                            framesPerBuffer,
                            timeInfo,
                            statusFlags,
                            userData);
};

Upvotes: 0

rholmes
rholmes

Reputation: 4174

Erik Olson's answer works great when you have the opportunity to pass in "user data" or context. If this isn't possible, you'll probably need an ugly hack to get ahold of your this pointer from within the callback. I've seen the class with the callback also have a static copy of this pointer, which would work only if you've got only one instance of the class. Ugggggly but it works.

This ugliness is an argument for all APIs to have an elegant way of dealing with this problem, ideally without breaking type safety using the reinterpret cast.

Upvotes: 0

Erik Olson
Erik Olson

Reputation: 1164

You need to implement a plain global function with the same signature as PaStreamCallback.

It looks like PaStreamCallback takes an argument, void* userData. This is what's usually called a context argument. Without knowledge of PortAudio I guess that you can use this to represent your class instance. When you call Pa_OpenDefaultStream, pass "this" for the userData pointer, where you wrote NULL in your example.

Here is a sample implememtation of the function wrapper you need:

int MyPaStreamCallback (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
     MyClass *myClass = reinterpret_cast<MyClass*>(userData);
     return myClass->callbackFunction(input, output, frameCount, timeInfo, statusFlags);
}

Then replace your original code with:

Pa_OpenDefaultStream(&this->_stream, 1, 2, paFloat32, this->_sampleRate, this->_framesPerBuffer, MyPaStreamCallback , this); 

I assumed you would make your callbackFunction to take all the other args.

Upvotes: 3

Related Questions