From aed0e13745a2499b92d30ecffa9752e49d53272a Mon Sep 17 00:00:00 2001 From: jules Date: Thu, 10 Aug 2017 15:49:42 +0100 Subject: [PATCH] Workaround for an edge-case involving sustain + sostenuto pedal parsing in the Synthesiser class. Also gave that class a quick spring-clean internally --- .../synthesisers/juce_Synthesiser.cpp | 151 ++++-------------- .../synthesisers/juce_Synthesiser.h | 18 +-- 2 files changed, 41 insertions(+), 128 deletions(-) diff --git a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 3e0fd4b7ee..96a52711a5 100644 --- a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -24,20 +24,8 @@ SynthesiserSound::SynthesiserSound() {} SynthesiserSound::~SynthesiserSound() {} //============================================================================== -SynthesiserVoice::SynthesiserVoice() - : currentSampleRate (44100.0), - currentlyPlayingNote (-1), - currentPlayingMidiChannel (0), - noteOnTime (0), - keyIsDown (false), - sustainPedalDown (false), - sostenutoPedalDown (false) -{ -} - -SynthesiserVoice::~SynthesiserVoice() -{ -} +SynthesiserVoice::SynthesiserVoice() {} +SynthesiserVoice::~SynthesiserVoice() {} bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const { @@ -83,11 +71,6 @@ void SynthesiserVoice::renderNextBlock (AudioBuffer& outputBuffer, //============================================================================== Synthesiser::Synthesiser() - : sampleRate (0), - lastNoteOnCounter (0), - minimumSubBlockSize (32), - subBlockSubdivisionIsStrict (false), - shouldStealNotes (true) { for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i) lastPitchWheelValues[i] = 0x2000; @@ -159,13 +142,11 @@ void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) if (sampleRate != newRate) { const ScopedLock sl (lock); - allNotesOff (0, false); - sampleRate = newRate; - for (int i = voices.size(); --i >= 0;) - voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate); + for (auto* voice : voices) + voice->setCurrentPlaybackSampleRate (newRate); } } @@ -230,25 +211,19 @@ void Synthesiser::processNextBlock (AudioBuffer& outputAudio, } // explicit template instantiation -template void Synthesiser::processNextBlock (AudioBuffer& outputAudio, - const MidiBuffer& midiData, - int startSample, - int numSamples); -template void Synthesiser::processNextBlock (AudioBuffer& outputAudio, - const MidiBuffer& midiData, - int startSample, - int numSamples); +template void Synthesiser::processNextBlock (AudioBuffer&, const MidiBuffer&, int, int); +template void Synthesiser::processNextBlock (AudioBuffer&, const MidiBuffer&, int, int); void Synthesiser::renderVoices (AudioBuffer& buffer, int startSample, int numSamples) { - for (int i = voices.size(); --i >= 0;) - voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples); + for (auto* voice : voices) + voice->renderNextBlock (buffer, startSample, numSamples); } void Synthesiser::renderVoices (AudioBuffer& buffer, int startSample, int numSamples) { - for (int i = voices.size(); --i >= 0;) - voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples); + for (auto* voice : voices) + voice->renderNextBlock (buffer, startSample, numSamples); } void Synthesiser::handleMidiEvent (const MidiMessage& m) @@ -298,23 +273,15 @@ void Synthesiser::noteOn (const int midiChannel, { const ScopedLock sl (lock); - for (int i = sounds.size(); --i >= 0;) + for (auto* sound : sounds) { - SynthesiserSound* const sound = sounds.getUnchecked(i); - - if (sound->appliesToNote (midiNoteNumber) - && sound->appliesToChannel (midiChannel)) + if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel)) { // If hitting a note that's still ringing, stop it first (it could be // still playing because of the sustain or sostenuto pedal). - for (int j = voices.size(); --j >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (j); - - if (voice->getCurrentlyPlayingNote() == midiNoteNumber - && voice->isPlayingChannel (midiChannel)) + for (auto* voice : voices) + if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel)) stopVoice (voice, 1.0f, true); - } startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes), sound, midiChannel, midiNoteNumber, velocity); @@ -363,10 +330,8 @@ void Synthesiser::noteOff (const int midiChannel, { const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) + for (auto* voice : voices) { - SynthesiserVoice* const voice = voices.getUnchecked (i); - if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel)) { @@ -391,13 +356,9 @@ void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff) { const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - + for (auto* voice : voices) if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) voice->stopNote (1.0f, allowTailOff); - } sustainPedalsDown.clear(); } @@ -406,13 +367,9 @@ void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue) { const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - + for (auto* voice : voices) if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) voice->pitchWheelMoved (wheelValue); - } } void Synthesiser::handleController (const int midiChannel, @@ -429,40 +386,28 @@ void Synthesiser::handleController (const int midiChannel, const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - + for (auto* voice : voices) if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) voice->controllerMoved (controllerNumber, controllerValue); - } } void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue) { const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - + for (auto* voice : voices) if (voice->getCurrentlyPlayingNote() == midiNoteNumber && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))) voice->aftertouchChanged (aftertouchValue); - } } void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue) { const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - + for (auto* voice : voices) if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) voice->channelPressureChanged (channelPressureValue); - } } void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) @@ -474,25 +419,19 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) { sustainPedalsDown.setBit (midiChannel); - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - + for (auto* voice : voices) if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown()) voice->sustainPedalDown = true; - } } else { - for (int i = voices.size(); --i >= 0;) + for (auto* voice : voices) { - SynthesiserVoice* const voice = voices.getUnchecked (i); - if (voice->isPlayingChannel (midiChannel)) { voice->sustainPedalDown = false; - if (! voice->isKeyDown()) + if (! (voice->isKeyDown() || voice->isSostenutoPedalDown())) stopVoice (voice, 1.0f, true); } } @@ -506,10 +445,8 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown) jassert (midiChannel > 0 && midiChannel <= 16); const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) + for (auto* voice : voices) { - SynthesiserVoice* const voice = voices.getUnchecked (i); - if (voice->isPlayingChannel (midiChannel)) { if (isDown) @@ -539,13 +476,9 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, { const ScopedLock sl (lock); - for (int i = 0; i < voices.size(); ++i) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - + for (auto* voice : voices) if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay)) return voice; - } if (stealIfNoneAvailable) return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber); @@ -569,7 +502,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, // - 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.size() > 0); + 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 @@ -579,10 +512,8 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, Array usableVoices; usableVoices.ensureStorageAllocated (voices.size()); - for (int i = 0; i < voices.size(); ++i) + for (auto* voice : voices) { - SynthesiserVoice* const voice = voices.getUnchecked (i); - if (voice->canPlaySound (soundToPlay)) { jassert (voice->isVoiceActive()); // We wouldn't be here otherwise @@ -592,7 +523,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, if (! voice->isPlayingButReleased()) // Don't protect released notes { - const int note = voice->getCurrentlyPlayingNote(); + auto note = voice->getCurrentlyPlayingNote(); if (low == nullptr || note < low->getCurrentlyPlayingNote()) low = voice; @@ -607,43 +538,25 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, if (top == low) top = nullptr; - const int numUsableVoices = usableVoices.size(); - // The oldest note that's playing with the target pitch is ideal.. - for (int i = 0; i < numUsableVoices; ++i) - { - SynthesiserVoice* const voice = usableVoices.getUnchecked (i); - + 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 (int i = 0; i < numUsableVoices; ++i) - { - SynthesiserVoice* const voice = usableVoices.getUnchecked (i); - + for (auto* voice : usableVoices) if (voice != low && voice != top && voice->isPlayingButReleased()) return voice; - } // Oldest voice that doesn't have a finger on it: - for (int i = 0; i < numUsableVoices; ++i) - { - SynthesiserVoice* const voice = usableVoices.getUnchecked (i); - + for (auto* voice : usableVoices) if (voice != low && voice != top && ! voice->isKeyDown()) return voice; - } // Oldest voice that isn't protected - for (int i = 0; i < numUsableVoices; ++i) - { - SynthesiserVoice* const voice = usableVoices.getUnchecked (i); - + for (auto* voice : usableVoices) if (voice != low && voice != top) return voice; - } // We've only got "protected" voices now: lowest note takes priority jassert (low != nullptr); diff --git a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index 51369a9ea1..961e90188b 100644 --- a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -249,11 +249,11 @@ private: //============================================================================== friend class Synthesiser; - double currentSampleRate; - int currentlyPlayingNote, currentPlayingMidiChannel; - uint32 noteOnTime; + double currentSampleRate = 44100.0; + int currentlyPlayingNote = -1, currentPlayingMidiChannel = 0; + uint32 noteOnTime = 0; SynthesiserSound::Ptr currentlyPlayingSound; - bool keyIsDown, sustainPedalDown, sostenutoPedalDown; + bool keyIsDown = false, sustainPedalDown = false, sostenutoPedalDown = false; AudioBuffer tempBuffer; @@ -615,11 +615,11 @@ private: int startSample, int numSamples); //============================================================================== - double sampleRate; - uint32 lastNoteOnCounter; - int minimumSubBlockSize; - bool subBlockSubdivisionIsStrict; - bool shouldStealNotes; + double sampleRate = 0; + uint32 lastNoteOnCounter = 0; + int minimumSubBlockSize = 32; + bool subBlockSubdivisionIsStrict = false; + bool shouldStealNotes = true; BigInteger sustainPedalsDown; #if JUCE_CATCH_DEPRECATED_CODE_MISUSE