How to override JUCE findVoiceToSteal to stop protecting top and bottom voice?


#1

I’m trying to override the FindVoiceToSteal so that it doesn’t protect the top and bottom voice.

In principle I would want it to take:

  1. Same MIDI note

  2. Voice that is playing but released.

  3. Oldest current playing voice
    I got it basically working but I’m not sure if I did it right. Here’s where I am. The default code is here:

     SynthesiserVoice* findVoiceToSteal(SynthesiserSound* soundToPlay,
     int /*midiChannel*/, int midiNoteNumber) const override
     {
     	// This voice-stealing algorithm applies the following heuristics:
     	// - Re-use the oldest notes first
     	// - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
    
     	// apparently you are trying to render audio without having any voices...
     	jassert(!voices.isEmpty());
    
     	// These are the voices we want to protect (ie: only steal if unavoidable)
     	//SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
     	//SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase
    
     	// this is a list of voices we can steal, sorted by how long they've been running
     	Array<SynthesiserVoice*> usableVoices;
     	usableVoices.ensureStorageAllocated(voices.size());
    
     	for (auto* voice : voices)
     	{
     		if (voice->canPlaySound(soundToPlay))
     		{
     			jassert(voice->isVoiceActive()); // We wouldn't be here otherwise
    
     			usableVoices.add(voice);
    
     			// NB: Using a functor rather than a lambda here due to scare-stories about
     			// compilers generating code containing heap allocations..
     			struct Sorter
     			{
     				bool operator() (const SynthesiserVoice* a, const SynthesiserVoice* b) const noexcept { return a->wasStartedBefore(*b); }
     			};
    
     			std::sort(usableVoices.begin(), usableVoices.end(), Sorter());
    
     			//if (!voice->isPlayingButReleased()) // Don't protect released notes
     			//{
     				//auto note = voice->getCurrentlyPlayingNote();
    
     				//if (low == nullptr || note < low->getCurrentlyPlayingNote())
     					//low = voice;
    
     				//if (top == nullptr || note > top->getCurrentlyPlayingNote())
     					//top = voice;
     			//}
     		}
     	}
    
     	// Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
     	//if (top == low)
     		//top = nullptr;
    
     	// The oldest note that's playing with the target pitch is ideal..
     	for (auto* voice : usableVoices)
     		if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
     			return voice;
    
     	// Oldest voice that has been released (no finger on it and not held by sustain pedal)
     	for (auto* voice : usableVoices)
     		//if (voice != low && voice != top && voice->isPlayingButReleased())
     		if (voice->isPlayingButReleased())
     			return voice;
    
     	// Oldest voice that doesn't have a finger on it:
     	for (auto* voice : usableVoices)
     		//if (voice != low && voice != top && !voice->isKeyDown())
     		if (!voice->isKeyDown())
     			return voice;
    
     	// Oldest voice that isn't protected
     	for (auto* voice : usableVoices)
     		//if (voice != low && voice != top)
     		//if (voice != low && voice != top)
     			return voice;
    
     	// We've only got "protected" voices now: lowest note takes priority
     	//jassert(low != nullptr);
    
     	// Duophonic synth: give priority to the bass note:
     	//if (top != nullptr)
     	//	return top;
    
     	//return low;
     }
    

It seems to work but it gives me the warning “warning C4715: ‘SynthesiserInherited::findVoiceToSteal’: not all control paths return a value” which I’m not sure how to fix.

Thanks.


#2

To comment only on this issue:
Simply add a catch all return nullptr; at the very end and make the caller able to deal with that scenario, then the compile warning goes away…


#3

You need to return something if usableVoices is empty you could return nullptr and maybe add a jassert before hand if you think the return statement should never be hit.