Need help with MixerAudioSource (Multiple sources starting at varying intervals)

Made a game and are trying to add multiple sound effects, all from binary resource wav files. These multiple sound effects may or may not be playing while another is playing, and sometimes while one specific is playing, it may be instructed to play again (restarted), and optimally if I restart again, any earlier same sound that has not finished should continue playing until end also.

I figure that I eventually should instead be using a polyphonic synthesizer, but for now until I learned a lot more, I am trying to keep it as simple as possible.

Here is what I got. In my MainCompent.h file;

class MainComponent	:	public AudioAppComponent, 
						public Timer,
						public ChangeListener,
						public MixerAudioSource
{
public:
	//==============================================================================
	MainComponent();
	~MainComponent();

	//==============================================================================
	void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override;
	void getNextAudioBlock(const AudioSourceChannelInfo& bufferToFill) override;
	void releaseResources() override;

	void changeListenerCallback(ChangeBroadcaster* source) override;

	AudioFormatManager formatManager;
	std::unique_ptr<AudioFormatReaderSource> readerSource;
	AudioTransportSource transportSource;
	MixerAudioSource mixer;

And in my MainComponent.cpp;

	formatManager.registerBasicFormats();
	
	transportSource.addChangeListener(this);
	auto readerBrickHit = formatManager.createReaderFor(new MemoryInputStream(BinaryData::BrickHit_wav, BinaryData::BrickHit_wavSize, false));
	if (readerBrickHit != nullptr)
	{
		std::unique_ptr<AudioFormatReaderSource> newSource(new AudioFormatReaderSource(readerBrickHit, true));
		
		transportSource.setSource(newSource.get(), 0, nullptr, readerBrickHit->sampleRate);
		readerSource.reset(newSource.release());
	}

and further down in same file I got;

MainComponent::~MainComponent()
{
    // This shuts down the audio device and clears the audio source.
    shutdownAudio();
}

void MainComponent::changeListenerCallback(ChangeBroadcaster* source)
{
	if (source == &transportSource)
	{
		transportSource.setPosition(0.0);
		if (transportSource.isPlaying()) transportSource.start();
	}
}

//==============================================================================
void MainComponent::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
{
	transportSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
}

void MainComponent::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
{
	if (readerSource.get() == nullptr)
		{
			bufferToFill.clearActiveBufferRegion();
			return;
		}
	transportSource.getNextAudioBlock(bufferToFill);
}

void MainComponent::releaseResources()
{
	transportSource.releaseResources();
}

And finally anywhere in same I just do transportSource.start(); to start the one sound effect.

Now I need to add more sound effects, wav files, and be able to start each whenever needed. I figure I need to something like this;
mixer.addInputSource(new AudioFormatReaderSource(readerBrickHit, true), true); and perhaps only need one transport but if that is the case I certainly do not know how to specify which sample (source) to start playing whenever needed.

If somebody could help me get going with a quick example I would appreciate it a lot.

I am getting closer to my goal with the below code, except each sound effect only plays one time, and then does not play when started again.

In header I got;

void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override;
	void getNextAudioBlock(const AudioSourceChannelInfo& bufferToFill) override;
	void releaseResources() override;

	void changeListenerCallback(ChangeBroadcaster* source) override;

	AudioFormatManager formatManager;
	std::unique_ptr<AudioFormatReaderSource> readerSourceBrick, readerSourceBorder ;
	AudioTransportSource transportSourceBrick, transportSourceBorder;
	MixerAudioSource mixer;

And in cpp I got;

formatManager.registerBasicFormats();
	
	transportSourceBrick.addChangeListener(this);
	transportSourceBorder.addChangeListener(this);

	auto readerBrickHit = formatManager.createReaderFor(new MemoryInputStream(BinaryData::BrickHit_wav, BinaryData::BrickHit_wavSize, false));
	if (readerBrickHit != nullptr)
	{
		std::unique_ptr<AudioFormatReaderSource> newSource(new AudioFormatReaderSource(readerBrickHit, true));
		mixer.addInputSource(newSource.get(), true);
		transportSourceBrick.setSource(newSource.get(), 0, nullptr, readerBrickHit->sampleRate);
		readerSourceBrick.reset(newSource.release());
		transportSourceBrick.setGain(1.0f);
	}

	auto readerBorderHit = formatManager.createReaderFor(new MemoryInputStream(BinaryData::BorderHit_wav, BinaryData::BorderHit_wavSize, false));
	if (readerBorderHit != nullptr)
	{
		std::unique_ptr<AudioFormatReaderSource> newSource(new AudioFormatReaderSource(readerBorderHit, true));
		mixer.addInputSource(newSource.get(), true);
		transportSourceBorder.setSource(newSource.get(), 0, nullptr, readerBorderHit->sampleRate);
		readerSourceBorder.reset(newSource.release());
		transportSourceBorder.setGain(1.0f);
	}	

and further down I got;

void MainComponent::changeListenerCallback(ChangeBroadcaster* source)
{
	if (source == &transportSourceBrick)
	{
		transportSourceBrick.setPosition(0.0);
		if (transportSourceBrick.isPlaying()) transportSourceBrick.start();
	}
	else if (source == &transportSourceBorder)
	{
		transportSourceBorder.setPosition(0.0);
		if (transportSourceBorder.isPlaying()) transportSourceBorder.start();
	}
}

//==============================================================================
void MainComponent::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
{
	mixer.prepareToPlay(samplesPerBlockExpected, sampleRate);
}

void MainComponent::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
{
	if (readerSourceBrick.get() == nullptr || readerSourceBorder.get() == nullptr)
		{
			bufferToFill.clearActiveBufferRegion();
			return;
		}

	mixer.getNextAudioBlock(bufferToFill);
}

void MainComponent::releaseResources()
{
	mixer.releaseResources();
}

Any ideas of what I am doing wrong?

My first inclination would be to see if you should be adding the transportSource to the mixer, not newSource?

1 Like

Yep you are definitely right, I am just not doing it quite right, as the sounds now play nicely, but I get a heap error when exiting the app, and I know it’s because of my little experience with pointers. This is what worked, expect for the the heap error when exiting app;

if (readerBrickHit != nullptr)
	{
		std::unique_ptr<AudioFormatReaderSource> newSource(new AudioFormatReaderSource(readerBrickHit, true));
		transportSourceBrick.setSource(newSource.get(), 0, nullptr, readerBrickHit->sampleRate);
		readerSourceBrick.reset(newSource.release());
		mixer.addInputSource(&transportSourceBrick, true);
		transportSourceBrick.setGain(1.0f);
	}

Again, read the docs… :slight_smile: second parameter of addInputSource, “if true, then this source will be deleted when no longer needed by the mixer.”. In your case it should be false, because your AudioTransportSources are not dynamically allocated.

1 Like

Eureka - Thank you VERY much!