Problem converting Synthesiser to MPESynthesiser - notes clicking on/off at start and finish of MIDI keypress (ie. not continuing to render after note let go)


I have converted one of my synthesisers to an MPESynthesiser.

In short, I basically just replaced the void startNote (int midiNoteNumber, float velocity, SynthesiserSound* sound, int currentPitchWheelPosition) override to void noteStarted() override and void stopNote (float velocity, bool allowTailOff) override to void noteStopped(bool allowTailOff) override

It is working. But there is a problem. In the MPESynthesiser in legacy mode (all I’ve tested so far) the notes click on and off like the voices are abruptly starting and stopping processing with the beginning and end of each MIDI keypress.

My synth is one based on excited resonant bandpasses which ought to continue to resonate and be processed even after the MIDI note has stopped. Under the stopNote/noteStopped functions I have in both:

    allowTailOff = false;

Because I’m not using any JUCE based “tailoff” and the note should be considered clear from the perspective of JUCE voice stealing.

In the regular Synthesiser it continues processing the voice and the bandpass output after I let go of the note (as intended), but in the MPE version, it just goes dead silent (clicks off) whenever I release a MIDI note. So it seems the MPE version is only rendering each voice while the MIDI note is being pressed and then it stops rendering as soon as the note stops.

Is there some obvious problem or difference that could account for this? Any other info or trouble shooting that might be recommended to sort it out?

In my pluginProcessor.cpp I have as my two processBlock functions for the two Synthesiser versions:

//mySynth.renderNextBlockCustom(buffer, midiMessages, 0, buffer.getNumSamples());
mMpeSynth.renderNextBlockCustom(buffer, midiMessages, 0, buffer.getNumSamples());

I am controlling the addition and deletion of voices for both synth versions with a function in pluginProcessor.cpp as follows:

void AudioPlugInAudioProcessor::updateNumVoices()
	const ScopedLock voiceLock(lock);

	const int numVoices =

	/** legacy synthesiser */

	for (int i = 0; i < numVoices; i++)
		// new voice
		SynthVoice* voice = new SynthVoice(&parameters);

		// add voice to synth

		// track voices in array

	mySynth.addSound(new SynthSound());

	/** mpe synthesiser */
	const int currentNumVoices = mMpeSynth.getNumVoices();

	if (numVoices < currentNumVoices) {

	else if (numVoices > currentNumVoices) {

		for (int i = 0; i < (numVoices - currentNumVoices); i++) {
			mMpeSynth.addVoice(new MPESynthesiserVoiceInherited(&parameters));


Which is only supposed to be called on startup and when my numVoices knob is changed.