ChangeListener / ChangeBroadcaster and Multiple Inheritance


#1

I just got caught out by this so I thought I’d document it…

I have a subclass of AudioTransportSource I use for playing sound files:

[code]/**
A wrapper class for playing audio files.

Being an AudioTransportSource this controls the playback of a positionable audio stream,
handling the starting/stopping and sample-rate conversion.
*/
class AudioFilePlayer : public AudioTransportSource
{
public:

/** Constuct an empty AudioFilePlayer. */
AudioFilePlayer()
{
	currentAudioFileSource = 0;
}

/** Construct a new AudioFilePlayer with a given path.
	@param path The path of the audio file. */
AudioFilePlayer(const String& path)
{
	currentAudioFileSource = 0;
	setFile(path);
}

~AudioFilePlayer()
{
	setSource (0);
	deleteAndZero (currentAudioFileSource);
}

/** Play the audio file from the start. */
void startFromZero()
{
	if(currentAudioFileSource == 0) return;
	
	// reset position to 0.0
	setPosition (0.0);
	start();
}

/** Open and get ready to play a given audio file path. */
bool setFile(const String& path)
{
	// should really delete/reset any exisiting data 
	stop();
    setSource (0);
    deleteAndZero (currentAudioFileSource);
	
	// OK now let's add the new file
	AudioFormatReader* reader = audioFormatReaderFromFile(path);
	
	if (reader != 0)
	{										
		// we SHOULD let the AudioFormatReaderSource delete the reader for us..
		currentAudioFileSource = new AudioFormatReaderSource (reader, true);
		
		// ..and plug it into our transport source
		setSource (currentAudioFileSource, 32768, reader->sampleRate);
		
		return true;
	}
	
	return false;
}

private:
AudioFormatReader* audioFormatReaderFromFile(const String& path)
{
File audioFile (path);

	// get a format manager and set it up with the basic types (wav and aiff).
	AudioFormatManager formatManager;
	formatManager.registerBasicFormats();
	return formatManager.createReaderFor (audioFile);
}
// this is the actual stream that's going to read from the audio file.
AudioFormatReaderSource* currentAudioFileSource;

};[/code]

I’ve then used this with multiple inheritance with a component, so something like:

class SoundfilePlayButton : public Component, public AudioFilePlayer { //...etc };

…now in my main component say I have multiple SoundfilePlayButtons and I can the register a ChangeListener for callbacks when the AudioTransportSource starts/stops:

[code]class MainComponent : public Component,
public ChangeListener
{
SoundfilePlayButton *soundfilePlayButton1, soundfilePlayButton2; // etc

public:
MainComponent()
{
//… create SoundfilePlayButtons and set the soundfiles to play …

	soundfilePlayButton1->addChangeListener(this);
	soundfilePlayButton2->addChangeListener(this);
	// etc
}

//...

void changeListenerCallback (void* pointer)
{
	// doesn't work:
	if(pointer == soundfilePlayButton1)
	{
		if(soundfilePlayButton1.isPlaying()) { /* ... */ } else { /* ... */ }
	}
	else if(pointer == soundfilePlayButton2)
	{
		if(soundfilePlayButton2.isPlaying()) { /* ... */ } else { /* ... */ }
	}
}

//...

};[/code]

…and I scratched my head for ages as to why the pointers didn’t match in the changeListenerCallback (even though I was getting the callbacks). Of course the pointer being broadcasted by the ChangeBroadcaster is the AudioTransportSource. Although AudioFilePlayer uses single inheritance from AudioTransportSource. The SoundfilePlayButton inherits from Component first and AudioFilePlayer (and therefore AudioTransportSource) second. However, casting the SoundfilePlayButton to an AudioTransportSource gets the correct pointer, e.g.:

void changeListenerCallback (void* pointer) { if(pointer == dynamic_cast<AudioTransportSource*>(soundfilePlayButton1)) { if(soundfilePlayButton1.isPlaying()) { /* ... */ } else { /* ... */ } } else if(pointer == dynamic_cast<AudioTransportSource*>(soundfilePlayButton2)) { if(soundfilePlayButton2.isPlaying()) { /* ... */ } else { /* ... */ } } }