0

I am currently having some problems with passing my member-function callback function, to a non-member function defined by the library portaudio.

This is how my class is defined:

class record {
public:
    record();
    void start_record();
    int recordCallback(const void *inputBuffer, void *outputBuffer,
                       unsigned long framesPerBuffer,
                       const PaStreamCallbackTimeInfo* timeInfo,
                       PaStreamCallbackFlags statusFlags, void *userData);
private:
    PaStreamParameters  inputParameters,
                        outputParameters;
    PaStream*           stream;
    PaError             err = paNoError;
    paTestData          data;
    int                 totalFrames;
    int                 numSamples;
    int                 numBytes;
    SAMPLE              max, val;
    double              average;
};

Within start_record() I pass the member function to a non-member function.. Meaning I pass a member-function of the class record (the callback), to a non-member function Pa_OpenStream().

err = Pa_OpenStream(
          &this->stream,
          &this->inputParameters,
          NULL,                  /* &outputParameters, */
          SAMPLE_RATE,
          FRAMES_PER_BUFFER,
          paClipOff,      /* we won't output out of range samples so don't bother clipping them */
          this->recordCallback,
          &data );
if( err != paNoError )
{
    std::cout << "Something wrong  - open_stream check" << std::endl;
    exit(1);
}

portaudio expects a function pointer of type

int (*)(const void*, void*, long unsigned int, const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void*)
and not

and not

int (record::)(const void*, void*, long unsigned int, const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void*) 

A quick solution would be to define the function outside the class scope, and make the variables which it uses and defined within the class global, but I would like to avoid that. So how do I handle this?

user207421
  • 305,947
  • 44
  • 307
  • 483
Lamda
  • 914
  • 3
  • 13
  • 39

2 Answers2

2

The duplicate of your previous question lists some ideas but lacks concrete code, so here's a sample.

First, modify class record by adding in a static callback function:

static int myCallback(const void *inputBuffer, void outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData);

Note that this has the same signature as your existing callback, except that it is a static function, so you can pass it to Pa_OpenStream.

Define that function to call your member function callback:

int record::myCallback(const void *inputBuffer, void outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
    record *pThis = (record *)userData;
    return pThis->recordCallback(inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags, nullptr);
}

We're passing nullptr as the user data pointer here since all the user data we need is held within the class. Since this callback is only used internally, you can just delete that last parameter from both here and in the function.

When you register the callback, pass in the new callback, with this as the user data parameter.

err = Pa_OpenStream(
      &this->stream,
      &this->inputParameters,
      NULL,                  /* &outputParameters, */
      SAMPLE_RATE,
      FRAMES_PER_BUFFER,
      paClipOff,      /* we won't output out of range samples so don't bother clipping them */
      &this->myCallback,
      this);

Then in recordCallback, you can access all the members of your class as you normally would.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
2

The way this is usually done is by passing the 'this' pointer in the data context, and extract it later on in your callback. You can declare the callback a simple private static stub.

Example:

class record
{
    // ...

     void start_record();
     {
         err = Pa_OpenStream(
                   &this->stream,
                   &this->inputParameters,
                   NULL,                  /* &outputParameters, */
                   SAMPLE_RATE,
                   FRAMES_PER_BUFFER,
                   paClipOff,      /* we won't output out of range samples so don't bother clipping them */
                   &record::recordCallbackStub,  // call back our static stub
                   this );   // 'this' is the data
     }

private:
    // our member function called back, can be virtual
    int recordCallback(
        const void *inputBuffer, void *outputBuffer,
        unsigned long framesPerBuffer,
        const PaStreamCallbackTimeInfo* timeInfo,
        PaStreamCallbackFlags statusFlags)  // no 'userData' param, since it's 'this'
    {
        // do your thing within the record context
    }

    // the stub to keep C happy, can be declared as extern "C" or whatever call 
    // convention is required from the library, WINAPI comes to mind.
    static int recordCallbackStub(
        const void *inputBuffer, 
        void *outputBuffer,
        unsigned long framesPerBuffer,
        const PaStreamCallbackTimeInfo* timeInfo,
        PaStreamCallbackFlags statusFlags
        void* userData)
    {
         auto pThis = reinterpret_cast<record*>(userData);  // get back the this pointer.
         return pThis->recordCallback( /* all parameters, except userData */);
    }
}; 
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19