Implementation of ADSR in SamplerVoice leads to clicking when playing notes very fast

Hey all,
I’m just starting out with JUCE so please bear with me :slight_smile:

What I am trying to build:
I try to build a Drum-Sampler within the Juce Framework, where a Syntesiser-Object is supplied with derived classes from SynthesiserSound, where i hold the sample data plus an ADSR-envelope (I used this Implementation: ADSR by Nigel Redmon on and a panning control plus a derived class from SynthesiserVoice, where all the rendering is happening.
Because it is a Drum-Sampler no pitching is applied to the sounds and they only should be playing when a certain midi note gets triggerd.

The Implementation:
In my class derived from SynthesiserVoice I have overidden the startNote(), stopNote(), and renderNextBlock() - functions to cope with my special case.

void CTAGSampVoice::startNote(int midiNoteNumber, float velocity, SynthesiserSound * 
sampSound, int pitchWheel)
	if( auto* playingSound = static_cast<CTAGSampSound*> (sampSound))

	SamplerVoice::startNote(midiNoteNumber, velocity, sampSound, pitchWheel);


void CTAGSampVoice::stopNote(float velocity, bool allowTailOff)
	if (auto* playingSound = static_cast<CTAGSampSound*> 
		if (playingSound->getEnvelopeGenerator().getState() == EnvelopeGenerator::env_idle)
	SamplerVoice::stopNote(velocity, allowTailOff);	


void CTAGSampVoice::renderNextBlock(AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
	auto* leftData = outputBuffer.getWritePointer(0, startSample);
	auto* rightData = outputBuffer.getWritePointer(1, startSample);
	if (auto* playingSound = static_cast<CTAGSampSound*> (getCurrentlyPlayingSound().get()))
		SamplerVoice::renderNextBlock(outputBuffer, startSample, numSamples);
		PanPos& p = playingSound->getPanPos();
		EnvelopeGenerator& e = playingSound->getEnvelopeGenerator();
		float envVal;
		for (int sample = 0; sample < numSamples; sample++)
			envVal = e.process();
			leftData[sample] *= envVal;
			rightData[sample] *= envVal;
			leftData[sample] = p.processLeftChannel(leftData[sample]);
			rightData[sample] = p.processRightChannel(rightData[sample]);

The Problem:
My Problem now is, that when I press the same Key two times very fast I get a clicking between the two sounds when applying the envelope. I think that the problem is, that the sound playing is not finished and gets killed by the next one but without a fade applied at the end, because the release phase from the envelope hasn’t finished yet. So I think I would need to apply a small fade out to the first sound. But I really don’t now how to do this and I havn’t found any resources online which tackle this. So hopefully somebody here on the forum could help me out, I allready tried a lot but nothing worked.

Surround your code with triple backticks so it appears formatted correctly.

Ok, thank you. I editeded the post and now the code is displaying correctly.

Just make pairs of everything, the sampler synth, the ADSR envelopes, etc…
Then when a new sample is triggered, it runs through the alternate chain to the last one. When your second sample is triggered, ramp the volume of the first down to zero over the length of a buffer (in Juce’s audio buffer class, there’s a method called “applyGainWithRamp()” or something like that). Then stop processing the first.

The DSP overhead for having two chains running at once is only felt for that one buffer, and well worthwhile in terms of running smooth crossovers without clicks.

Trank you for your help. I will try this tomorrow.

You could also use multiple voices. Check if the voice is active, if yes, add another voice, and let the previous one finish it’s release.

Hi , it is an old topic but i am trying to use the same library and i couldn’t do it. When we have synthesiser voice and sound classes and their methods such as startNote or StopNote , it is comprehensible that it is going to know when it starts or goes to release but how can i spot those points if i dont have buttons to press ?How can i know that there is a note started or went to release? Is there a method in plugin processor class that can detect this?