Possible Audio Source Bug

Hello

I’m creating a simple looping application and i’m having a lot of trouble with audioSources. I apologise if this isn’t a bug and purely my mistake, i’ve only been using juce for a few months, but i’ve gone over the code many times and can’t see why this is happening. I’ve managed to narrow it down to something to do with audio sources, but i’ll try and explain in more detail.

My application has a metronome that generates its sound from an adapted version of the synthesiser in the juce demo, that is that is creates the sound using all the of the synthVoice stuff etc and then uses an audioSourcePlayer to play it back. (I know theres probably easier ways to do this but this is a uni project and its good to learn all of this stuff).
Theres another part of my program that records audio into a file and then plays this back through an audioSourcePlayer. However recently i’ve wanted to get rid of this audioSourcePlayer and use an AudioIODeviceCallback to simplify the recording to a floating point array as its easier to get the timing correct.
The problem is as soon as i try to get rid of the audioSourcePlayer used for playing back the recording, the metronome sound goes crazy and on occasion the program makes a nasty sound at start up. These two parts have no connected variables and nothing needs to be changed in my metronome, the only thing they share is the audioDeviceManager. The metronome sound returns to normal when i replace the original audio recorder, but i’d really like to get rid of it.

I have spent all day going over everything, and can’t find a solution, is this a bug of some kind?

cheers

joe

Can you provide a small sample program that demonstrates the problem?

Hi

Sorry i’m new to all this, do you just want me to copy and paste some of the code into this, because the programs spread over a number of files. I’m currently rebuilding a new project that just plays the metronome and i’m still getting the same problem, its almost like the sound generator relies on something else and that i keep removing it. I’ll paste in my sound generating file and see what you think…

#include “MetronomeSoundGenerator.h”

SineWaveVoice::SineWaveVoice()
: angleDelta (0.0),
tailOff (0.0)
{
}

bool SineWaveVoice::canPlaySound (SynthesiserSound* sound)
{
return dynamic_cast <SineWaveSound*> (sound) != 0;
}

void SineWaveVoice::startNote (const int midiNoteNumber, const float velocity,
SynthesiserSound* sound, const int currentPitchWheelPosition)
{
currentAngle = 0.0;
level = velocity * 0.15;
tailOff = 0.0;

double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
double cyclesPerSample = cyclesPerSecond / getSampleRate();

angleDelta = cyclesPerSample * 2.0 * double_Pi;

}

void SineWaveVoice::stopNote (const bool allowTailOff)
{
if (allowTailOff)
{
// start a tail-off by setting this flag. The render callback will pick up on
// this and do a fade out, calling clearCurrentNote() when it’s finished.

	if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
		// stopNote method could be called more than once.
		tailOff = 1.0;
}
else
{
	// we're being told to stop playing immediately, so reset everything..
	
	clearCurrentNote();
	angleDelta = 0.0;
}

}

void SineWaveVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples)
{
if (angleDelta != 0.0)
{
if (tailOff > 0)
{
while (–numSamples >= 0)
{
const float currentSample = (float) (sin (currentAngle) * level * tailOff);

			for (int i = outputBuffer.getNumChannels(); --i >= 0;)
				*outputBuffer.getSampleData (i, startSample) += currentSample;
			
			currentAngle += angleDelta;
			++startSample;
			
			tailOff *= 0.99;
			
			if (tailOff <= 0.005)
			{
				clearCurrentNote();
				
				angleDelta = 0.0;
				break;
			}
		}
	}
	else
	{
		while (--numSamples >= 0)
		{
			const float currentSample = (float) (sin (currentAngle) * level);
			
			for (int i = outputBuffer.getNumChannels(); --i >= 0;)
				*outputBuffer.getSampleData (i, startSample) += currentSample;
			
			currentAngle += angleDelta;
			++startSample;
		}
	}
}

}

void SineWaveVoice::pitchWheelMoved (const int newValue){}
void SineWaveVoice::controllerMoved (const int controllerNumber,
const int newValue){}

MetronomeSoundGenerator::MetronomeSoundGenerator(AudioDeviceManager& audioDeviceManager_)
: audioDeviceManager (audioDeviceManager_), Thread(“Click End”)
{
audioSourcePlayer = new AudioSourcePlayer;
audioSourcePlayer->setSource (this);
audioSourcePlayer->setGain(1.0);

synth = new Synthesiser;
midiCollector = new MidiMessageCollector;

for (int i = 4; --i >= 0;)
{
	synth->addVoice (new SineWaveVoice());   // These voices will play our custom sine-wave sounds..
	//synth.addVoice (new SamplerVoice());    // and these ones play the sampled sounds
}

// and add some sounds for them to play...
synth->addSound (new SineWaveSound());

audioDeviceManager.addAudioCallback (audioSourcePlayer);

}

MetronomeSoundGenerator::~MetronomeSoundGenerator()
{
audioSourcePlayer->setSource (0);
audioDeviceManager.removeAudioCallback (audioSourcePlayer);
deleteAndZero(audioSourcePlayer);
deleteAndZero(synth);
deleteAndZero(midiCollector);
}

void MetronomeSoundGenerator::startClick (int midiNoteNumber)
{
synth->noteOn (1, midiNoteNumber, 1);
startThread(1);
}
void MetronomeSoundGenerator::stopClick (int midiNoteNumber)
{
synth->noteOff (1, midiNoteNumber, false);
}

void MetronomeSoundGenerator::prepareToPlay (int samplesPerBlockExpected,
double sampleRate)
{
midiCollector->reset (sampleRate);
synth->setCurrentPlaybackSampleRate (sampleRate);
}

void MetronomeSoundGenerator::releaseResources()
{
}

void MetronomeSoundGenerator::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
{
// the synth always adds its output to the audio buffer, so we have to clear it
// first…
bufferToFill.clearActiveBufferRegion();

// fill a midi buffer with incoming messages from the midi input.
MidiBuffer incomingMidi;
midiCollector->removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples);

// and now get the synth to process the midi events and generate its output.
synth->renderNextBlock (*bufferToFill.buffer, incomingMidi, 0, bufferToFill.numSamples);

}

void MetronomeSoundGenerator::run()
{
wait(100);
stopClick(100);
stopClick(85);
}

Thanks

Just wondering about

synth->renderNextBlock (*bufferToFill.buffer, incomingMidi, 0, bufferToFill.numSamples);

shouldn’t that read:

synth->renderNextBlock (*bufferToFill.buffer, incomingMidi, bufferToFill.startSample, bufferToFill.numSamples);

PS use the “[code]” tags

Alright

I’ve found why the sound goes weird, and now i would like to know if theres anyway to still use this system.
My metronome works by counting samples within an audio callback, that way it stays perfectly in time. By stopping this callback the metronome sound returns to normal.

The strange thing is the sound was fine until i removed the recording code which used another audioSamplePlayer and as i said before those two parts were only linked by the device manager. Is there a limit to callbacks and things like this or is it just strange?

Sounds like you’re doing something wrong to be honest

I’m not going to sugar coat it, i definitely am. i’ve gone through my code and stopped all the audio call backs and now the sounds correct again.

My lecturer told me that you could have as many callbacks as you need, is this the case? and they are all running from the same audioDeviceManager.

At least i know why the sounds funny now, i can begin to find out how to sort it out.

cheers

/* If more than one callback is active, they will all be given the same input data, and their outputs will be summed. */