diff --git a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 1aa248bb04..2f0b13bd32 100644 --- a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -58,6 +58,11 @@ void SynthesiserVoice::clearCurrentNote() void SynthesiserVoice::aftertouchChanged (int) {} +bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept +{ + return noteOnTime < other.noteOnTime; +} + //============================================================================== Synthesiser::Synthesiser() : sampleRate (0), @@ -407,12 +412,11 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) } //============================================================================== -SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, - const bool stealIfNoneAvailable) const +SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, const bool stealIfNoneAvailable) const { const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) + for (int i = 0; i < voices.size(); ++i) { SynthesiserVoice* const voice = voices.getUnchecked (i); @@ -421,22 +425,25 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, } if (stealIfNoneAvailable) - { - // currently this just steals the one that's been playing the longest, but could be made a bit smarter.. - SynthesiserVoice* oldest = nullptr; - - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - - if (voice->canPlaySound (soundToPlay) - && (oldest == nullptr || oldest->noteOnTime > voice->noteOnTime)) - oldest = voice; - } - - jassert (oldest != nullptr); - return oldest; - } + return findVoiceToSteal (soundToPlay); return nullptr; } + +SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay) const +{ + // currently this just steals the one that's been playing the longest, but could be made a bit smarter.. + SynthesiserVoice* oldest = nullptr; + + for (int i = 0; i < voices.size(); ++i) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->canPlaySound (soundToPlay) + && (oldest == nullptr || voice->wasStartedBefore (*oldest))) + oldest = voice; + } + + jassert (oldest != nullptr); + return oldest; +} diff --git a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index 7202b4b284..069253ba15 100644 --- a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -199,6 +199,9 @@ public: /** Returns true if the sostenuto pedal is currently active for this voice. */ bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; } + /** Returns true if this voice started playing its current note before the other voice did. */ + bool wasStartedBefore (const SynthesiserVoice& other) const noexcept; + protected: //============================================================================== /** Returns the current target sample rate at which rendering is being done. @@ -481,6 +484,12 @@ protected: virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay, const bool stealIfNoneAvailable) const; + /** Chooses a voice that is most suitable for being re-used. + The default method returns the one that has been playing for the longest, but + you may want to override this and do something more cunning instead. + */ + virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay) const; + /** Starts a specified voice playing a particular sound. You'll probably never need to call this, it's used internally by noteOn(), but