diff --git a/extras/Demo/Source/Demos/AudioSynthesiserDemo.cpp b/extras/Demo/Source/Demos/AudioSynthesiserDemo.cpp index f2e9b9aa24..f885ff9b81 100644 --- a/extras/Demo/Source/Demos/AudioSynthesiserDemo.cpp +++ b/extras/Demo/Source/Demos/AudioSynthesiserDemo.cpp @@ -63,7 +63,7 @@ struct SineWaveVoice : public SynthesiserVoice angleDelta = cyclesPerSample * 2.0 * double_Pi; } - void stopNote (bool allowTailOff) override + void stopNote (float /*velocity*/, bool allowTailOff) override { if (allowTailOff) { diff --git a/extras/audio plugin demo/Source/PluginProcessor.cpp b/extras/audio plugin demo/Source/PluginProcessor.cpp index 61781c2dda..913df37879 100644 --- a/extras/audio plugin demo/Source/PluginProcessor.cpp +++ b/extras/audio plugin demo/Source/PluginProcessor.cpp @@ -38,11 +38,12 @@ public: bool canPlaySound (SynthesiserSound* sound) override { - return dynamic_cast (sound) != 0; + return dynamic_cast (sound) != nullptr; } void startNote (int midiNoteNumber, float velocity, - SynthesiserSound* /*sound*/, int /*currentPitchWheelPosition*/) override + SynthesiserSound* /*sound*/, + int /*currentPitchWheelPosition*/) override { currentAngle = 0.0; level = velocity * 0.15; @@ -54,7 +55,7 @@ public: angleDelta = cyclesPerSample * 2.0 * double_Pi; } - void stopNote (bool allowTailOff) override + void stopNote (float velocity, bool allowTailOff) override { if (allowTailOff) { diff --git a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 7a72d0083a..464e3ec56b 100644 --- a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -163,10 +163,7 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu : numSamples; if (numThisTime > 0) - { - for (int i = voices.size(); --i >= 0;) - voices.getUnchecked (i)->renderNextBlock (outputBuffer, startSample, numThisTime); - } + renderVoices (outputBuffer, startSample, numThisTime); if (useEvent) handleMidiEvent (m); @@ -176,6 +173,12 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu } } +void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples) +{ + for (int i = voices.size(); --i >= 0;) + voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples); +} + void Synthesiser::handleMidiEvent (const MidiMessage& m) { if (m.isNoteOn()) @@ -184,7 +187,7 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) } else if (m.isNoteOff()) { - noteOff (m.getChannel(), m.getNoteNumber(), true); + noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true); } else if (m.isAllNotesOff() || m.isAllSoundOff()) { @@ -230,7 +233,7 @@ void Synthesiser::noteOn (const int midiChannel, if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel)) - stopVoice (voice, true); + stopVoice (voice, 1.0f, true); } startVoice (findFreeVoice (sound, shouldStealNotes), @@ -248,7 +251,7 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice, if (voice != nullptr && sound != nullptr) { if (voice->currentlyPlayingSound != nullptr) - voice->stopNote (false); + voice->stopNote (0.0f, false); voice->startNote (midiNoteNumber, velocity, sound, lastPitchWheelValues [midiChannel - 1]); @@ -261,11 +264,11 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice, } } -void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff) +void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff) { jassert (voice != nullptr); - voice->stopNote (allowTailOff); + voice->stopNote (velocity, allowTailOff); // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()! jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0)); @@ -273,6 +276,7 @@ void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff) void Synthesiser::noteOff (const int midiChannel, const int midiNoteNumber, + const float velocity, const bool allowTailOff) { const ScopedLock sl (lock); @@ -291,7 +295,7 @@ void Synthesiser::noteOff (const int midiChannel, voice->keyIsDown = false; if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown)) - stopVoice (voice, allowTailOff); + stopVoice (voice, velocity, allowTailOff); } } } @@ -379,7 +383,7 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) SynthesiserVoice* const voice = voices.getUnchecked (i); if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown) - stopVoice (voice, true); + stopVoice (voice, 1.0f, true); } sustainPedalsDown.clearBit (midiChannel); @@ -400,7 +404,7 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown) if (isDown) voice->sostenutoPedalDown = true; else if (voice->sostenutoPedalDown) - stopVoice (voice, true); + stopVoice (voice, 1.0f, true); } } } diff --git a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index 9f55d073b2..a43d614691 100644 --- a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -127,6 +127,8 @@ public: This will be called during the rendering callback, so must be fast and thread-safe. + The velocity indicates how quickly the note was released - 0 is slowly, 1 is quickly. + If allowTailOff is false or the voice doesn't want to tail-off, then it must stop all sound immediately, and must call clearCurrentNote() to reset the state of this voice and allow the synth to reassign it another sound. @@ -136,7 +138,7 @@ public: finishes playing (during the rendering callback), it must make sure that it calls clearCurrentNote(). */ - virtual void stopNote (bool allowTailOff) = 0; + virtual void stopNote (float velocity, bool allowTailOff) = 0; /** Called to let the voice know that the pitch wheel has been moved. This will be called during the rendering callback, so must be fast and thread-safe. @@ -235,6 +237,11 @@ private: SynthesiserSound::Ptr currentlyPlayingSound; bool keyIsDown, sostenutoPedalDown; + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // Note the new parameters for this method. + virtual int stopNote (bool) { return 0; } + #endif + JUCE_LEAK_DETECTOR (SynthesiserVoice) }; @@ -365,6 +372,7 @@ public: */ virtual void noteOff (int midiChannel, int midiNoteNumber, + float velocity, bool allowTailOff); /** Turns off all notes. @@ -474,6 +482,13 @@ protected: /** The last pitch-wheel values for each midi channel. */ int lastPitchWheelValues [16]; + /** Renders the voices for the given range. + By default this just calls renderNextBlock() on each voice, but you may need + to override it to handle custom cases. + */ + virtual void renderVoices (AudioSampleBuffer& outputAudio, + int startSample, int numSamples); + /** Searches through the voices to find one that's not currently playing, and which can play the given sound. @@ -511,11 +526,12 @@ private: bool shouldStealNotes; BigInteger sustainPedalsDown; - void stopVoice (SynthesiserVoice*, bool allowTailOff); + void stopVoice (SynthesiserVoice*, float velocity, bool allowTailOff); #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note the new parameters for this method. + // Note the new parameters for these methods. virtual int findFreeVoice (const bool) const { return 0; } + virtual int noteOff (int, int, int) { return 0; } #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) diff --git a/modules/juce_audio_formats/sampler/juce_Sampler.cpp b/modules/juce_audio_formats/sampler/juce_Sampler.cpp index 9dbe7b3c56..475b50af23 100644 --- a/modules/juce_audio_formats/sampler/juce_Sampler.cpp +++ b/modules/juce_audio_formats/sampler/juce_Sampler.cpp @@ -127,7 +127,7 @@ void SamplerVoice::startNote (const int midiNoteNumber, } } -void SamplerVoice::stopNote (const bool allowTailOff) +void SamplerVoice::stopNote (float /*velocity*/, bool allowTailOff) { if (allowTailOff) { @@ -197,7 +197,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa if (attackReleaseLevel <= 0.0f) { - stopNote (false); + stopNote (0.0f, false); break; } } @@ -216,7 +216,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa if (sourceSamplePosition > playingSound->length) { - stopNote (false); + stopNote (0.0f, false); break; } } diff --git a/modules/juce_audio_formats/sampler/juce_Sampler.h b/modules/juce_audio_formats/sampler/juce_Sampler.h index 51133add9c..f77a9b8d60 100644 --- a/modules/juce_audio_formats/sampler/juce_Sampler.h +++ b/modules/juce_audio_formats/sampler/juce_Sampler.h @@ -124,7 +124,7 @@ public: bool canPlaySound (SynthesiserSound*) override; void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; - void stopNote (bool allowTailOff) override; + void stopNote (float velocity, bool allowTailOff) override; void pitchWheelMoved (int newValue); void controllerMoved (int controllerNumber, int newValue) override;