C++ question

Posted by: andym

C++ question - 12/10/2004 13:05

Right then, this is taxing my poor brain.....

I've written a simple mp3 player using MAD and JACK in C. This works a treat. I now want to encapsulate this into a class and stick in a Qt application. I've copied the code into a class structure and I'm trying to compile it. JACK uses a callback mechanism to send data to the sound system. Under C i used 4 functions to assign callbacks to functions. I've added these functions into the class. The class definition looks like this:

Code:

class MP3Play : public QWidget
{
public:
MP3Play( QWidget *parent=0, const char *name=0 );
int Load(QString);
private:
const char *ProgName;
int default_driver;
char *buffer;
int buf_size;
int sample;
int iCount;
SNDFILE *sf;

//typedef double sample_t;
typedef jack_default_audio_sample_t sample_t;

struct mad_stream Stream;
struct mad_frame Frame;
struct mad_synth Synth;
mad_timer_t Timer;
unsigned char InputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD], OutputBuffer[OUTPUT_BUFFER_SIZE], *OutputPtr, *GuardPtr;
const unsigned char *OutputBufferEnd;

int Status, i, iSpill, bdecode;
unsigned long FrameCount;
bstdfile_t *BstdFile;
FILE *InputFp;

sample_t spill_l[1152];
sample_t spill_r[1152];

FILE *mp3file;
jack_client_t *client;
const char **ports;

/*Our output port*/
jack_port_t *output_port_l,*output_port_r;

/*The current sample rate*/
jack_nframes_t sr;

int process (jack_nframes_t, void *);
int srate (jack_nframes_t, void *);
void error (const char *);
void jack_shutdown (void *);
};



The JACK specific initalisation code looks this:
Code:

jack_set_error_function (MP3Play::error);
jack_set_process_callback (client, MP3Play::process, 0);
jack_set_sample_rate_callback (client, MP3Play::srate, 0);
jack_on_shutdown (client, MP3Play::jack_shutdown, 0);



The compiler errors look like this:
Code:

mp3playback.cpp: In member function `int MP3Play::Load(QString)':
mp3playback.cpp:118: error: no matches converting function `error' to type `
void (*)(const char*)'
mp3playback.cpp:76: error: candidates are: void MP3Play::error(const char*)
mp3playback.cpp:126: error: no matches converting function `process' to type `
int (*)(unsigned int, void*)'
mp3playback.cpp:74: error: candidates are: int MP3Play::process(unsigned int,
void*)
mp3playback.cpp:127: error: no matches converting function `srate' to type `int
(*)(unsigned int, void*)'
mp3playback.cpp:75: error: candidates are: int MP3Play::srate(unsigned int,
void*)
mp3playback.cpp:128: error: no matches converting function `jack_shutdown' to
type `void (*)(void*)'
mp3playback.cpp:77: error: candidates are: void MP3Play::jack_shutdown(void*)



All I want is to completely encapsulate the code into a class, so I can initialise as many players I need.

Any pointers? (Pardon the pun)
Posted by: peter

Re: C++ question - 12/10/2004 13:13

Quote:
Any pointers? (Pardon the pun)

Those C++ member functions are not compatible with normal C functions (because they get the "this" pointer passed to them). If you want to pass C++ member functions as C callbacks, they must be static member functions. If you pass the address of your class as the "client" pointer, you can then call member functions inside your static callback functions:

Code:
class MP3Play {
static void process_callback(int nframes, void *clientptr)
{
MP3Play *me = (MP3Play*)clientptr;
me->callback(nframes);
}

void callback(int nframes) ...
};

jack_set_process_callback( &MP3Play::process_callback );



Peter
Posted by: andym

Re: C++ question - 12/10/2004 13:35

I don't think I'm reading these changes properly. So for each callback function (4 of them) I need to create a 'wrapper' function which takes an extra argument containing the pointer to the class? Because I've done this and compiler now complains that it can't use the new function as the callback.
Posted by: peter

Re: C++ question - 12/10/2004 13:49

Quote:
Because I've done this and compiler now complains that it can't use the new function as the callback.

The function you use as the callback must have exactly the right signature (type). If the callback system doesn't allow you to pass the pointer to the class to your callback, you're stuffed, as then the static ("this"-less) C++ function can't call a nonstatic (with "this") member, because it can't come up with the value of "this". Fundamentally, if you've got two sets of callbacks in your program, how does the callback know which instance it's being called for?

Without looking at JACK, I can't be more specific, but alternatively, if you've only got one set of these callbacks in your program, you could keep the MP3Play pointer in a global.

Either way the callbacks you pass to jack_xxx must be either static members or not members of any class.

Peter
Posted by: andym

Re: C++ question - 12/10/2004 17:36

Okay, head up bottom time.... I think you've cracked it, it must've been my addled brain at work this afternoon. I'm pretty sure I know what's wrong. One issue is the error handler (which turns out to be optional) seems to be completely different to the other three. I was initially trying to make that work. Looking again at the API, your code and an example i've found on sourceforge I think I know the way to continue.

I owe you a pint. Shame there's nothing work related going on in Cambridge.
Posted by: andym

Re: C++ question - 13/10/2004 08:21

Okay just got round to making the changes, it now appears the linker can't resolve the 3 new functions. I just created the new public functions added the relevant code to get the pointer and call the non-static function.

The linker says it's an undefined reference to the functions by the looks of it from when they're set as the callbacks.
Posted by: peter

Re: C++ question - 13/10/2004 10:51

Quote:
Okay just got round to making the changes, it now appears the linker can't resolve the 3 new functions. I just created the new public functions added the relevant code to get the pointer and call the non-static function.

The linker says it's an undefined reference to the functions by the looks of it from when they're set as the callbacks.

Let me guess, the functions themselves are defined in a .cpp file but the calls to jack_add_callback_foo() are in a .c file?

If so you need to mark the callback functions (both in the header and the .cpp file) as being extern "C".

Peter
Posted by: andym

Re: C++ question - 13/10/2004 11:25

Nope, just an idiotic mistake. It's now working!

Thanks for your help. I'll always think of myself as an engineer first and a programmer second.