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

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.

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…

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.