| @@ -63,7 +63,7 @@ struct SineWaveVoice : public SynthesiserVoice | |||||
| angleDelta = cyclesPerSample * 2.0 * double_Pi; | angleDelta = cyclesPerSample * 2.0 * double_Pi; | ||||
| } | } | ||||
| void stopNote (bool allowTailOff) override | |||||
| void stopNote (float /*velocity*/, bool allowTailOff) override | |||||
| { | { | ||||
| if (allowTailOff) | if (allowTailOff) | ||||
| { | { | ||||
| @@ -38,11 +38,12 @@ public: | |||||
| bool canPlaySound (SynthesiserSound* sound) override | bool canPlaySound (SynthesiserSound* sound) override | ||||
| { | { | ||||
| return dynamic_cast <SineWaveSound*> (sound) != 0; | |||||
| return dynamic_cast<SineWaveSound*> (sound) != nullptr; | |||||
| } | } | ||||
| void startNote (int midiNoteNumber, float velocity, | void startNote (int midiNoteNumber, float velocity, | ||||
| SynthesiserSound* /*sound*/, int /*currentPitchWheelPosition*/) override | |||||
| SynthesiserSound* /*sound*/, | |||||
| int /*currentPitchWheelPosition*/) override | |||||
| { | { | ||||
| currentAngle = 0.0; | currentAngle = 0.0; | ||||
| level = velocity * 0.15; | level = velocity * 0.15; | ||||
| @@ -54,7 +55,7 @@ public: | |||||
| angleDelta = cyclesPerSample * 2.0 * double_Pi; | angleDelta = cyclesPerSample * 2.0 * double_Pi; | ||||
| } | } | ||||
| void stopNote (bool allowTailOff) override | |||||
| void stopNote (float velocity, bool allowTailOff) override | |||||
| { | { | ||||
| if (allowTailOff) | if (allowTailOff) | ||||
| { | { | ||||
| @@ -163,10 +163,7 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu | |||||
| : numSamples; | : numSamples; | ||||
| if (numThisTime > 0) | if (numThisTime > 0) | ||||
| { | |||||
| for (int i = voices.size(); --i >= 0;) | |||||
| voices.getUnchecked (i)->renderNextBlock (outputBuffer, startSample, numThisTime); | |||||
| } | |||||
| renderVoices (outputBuffer, startSample, numThisTime); | |||||
| if (useEvent) | if (useEvent) | ||||
| handleMidiEvent (m); | 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) | void Synthesiser::handleMidiEvent (const MidiMessage& m) | ||||
| { | { | ||||
| if (m.isNoteOn()) | if (m.isNoteOn()) | ||||
| @@ -184,7 +187,7 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) | |||||
| } | } | ||||
| else if (m.isNoteOff()) | else if (m.isNoteOff()) | ||||
| { | { | ||||
| noteOff (m.getChannel(), m.getNoteNumber(), true); | |||||
| noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true); | |||||
| } | } | ||||
| else if (m.isAllNotesOff() || m.isAllSoundOff()) | else if (m.isAllNotesOff() || m.isAllSoundOff()) | ||||
| { | { | ||||
| @@ -230,7 +233,7 @@ void Synthesiser::noteOn (const int midiChannel, | |||||
| if (voice->getCurrentlyPlayingNote() == midiNoteNumber | if (voice->getCurrentlyPlayingNote() == midiNoteNumber | ||||
| && voice->isPlayingChannel (midiChannel)) | && voice->isPlayingChannel (midiChannel)) | ||||
| stopVoice (voice, true); | |||||
| stopVoice (voice, 1.0f, true); | |||||
| } | } | ||||
| startVoice (findFreeVoice (sound, shouldStealNotes), | startVoice (findFreeVoice (sound, shouldStealNotes), | ||||
| @@ -248,7 +251,7 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice, | |||||
| if (voice != nullptr && sound != nullptr) | if (voice != nullptr && sound != nullptr) | ||||
| { | { | ||||
| if (voice->currentlyPlayingSound != nullptr) | if (voice->currentlyPlayingSound != nullptr) | ||||
| voice->stopNote (false); | |||||
| voice->stopNote (0.0f, false); | |||||
| voice->startNote (midiNoteNumber, velocity, sound, | voice->startNote (midiNoteNumber, velocity, sound, | ||||
| lastPitchWheelValues [midiChannel - 1]); | 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); | jassert (voice != nullptr); | ||||
| voice->stopNote (allowTailOff); | |||||
| voice->stopNote (velocity, allowTailOff); | |||||
| // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()! | // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()! | ||||
| jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0)); | 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, | void Synthesiser::noteOff (const int midiChannel, | ||||
| const int midiNoteNumber, | const int midiNoteNumber, | ||||
| const float velocity, | |||||
| const bool allowTailOff) | const bool allowTailOff) | ||||
| { | { | ||||
| const ScopedLock sl (lock); | const ScopedLock sl (lock); | ||||
| @@ -291,7 +295,7 @@ void Synthesiser::noteOff (const int midiChannel, | |||||
| voice->keyIsDown = false; | voice->keyIsDown = false; | ||||
| if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown)) | 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); | SynthesiserVoice* const voice = voices.getUnchecked (i); | ||||
| if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown) | if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown) | ||||
| stopVoice (voice, true); | |||||
| stopVoice (voice, 1.0f, true); | |||||
| } | } | ||||
| sustainPedalsDown.clearBit (midiChannel); | sustainPedalsDown.clearBit (midiChannel); | ||||
| @@ -400,7 +404,7 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown) | |||||
| if (isDown) | if (isDown) | ||||
| voice->sostenutoPedalDown = true; | voice->sostenutoPedalDown = true; | ||||
| else if (voice->sostenutoPedalDown) | else if (voice->sostenutoPedalDown) | ||||
| stopVoice (voice, true); | |||||
| stopVoice (voice, 1.0f, true); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -127,6 +127,8 @@ public: | |||||
| This will be called during the rendering callback, so must be fast and thread-safe. | 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 | 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 | sound immediately, and must call clearCurrentNote() to reset the state of this voice | ||||
| and allow the synth to reassign it another sound. | 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 | finishes playing (during the rendering callback), it must make sure that it calls | ||||
| clearCurrentNote(). | 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. | /** 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. | This will be called during the rendering callback, so must be fast and thread-safe. | ||||
| @@ -235,6 +237,11 @@ private: | |||||
| SynthesiserSound::Ptr currentlyPlayingSound; | SynthesiserSound::Ptr currentlyPlayingSound; | ||||
| bool keyIsDown, sostenutoPedalDown; | 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) | JUCE_LEAK_DETECTOR (SynthesiserVoice) | ||||
| }; | }; | ||||
| @@ -365,6 +372,7 @@ public: | |||||
| */ | */ | ||||
| virtual void noteOff (int midiChannel, | virtual void noteOff (int midiChannel, | ||||
| int midiNoteNumber, | int midiNoteNumber, | ||||
| float velocity, | |||||
| bool allowTailOff); | bool allowTailOff); | ||||
| /** Turns off all notes. | /** Turns off all notes. | ||||
| @@ -474,6 +482,13 @@ protected: | |||||
| /** The last pitch-wheel values for each midi channel. */ | /** The last pitch-wheel values for each midi channel. */ | ||||
| int lastPitchWheelValues [16]; | 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 | /** Searches through the voices to find one that's not currently playing, and which | ||||
| can play the given sound. | can play the given sound. | ||||
| @@ -511,11 +526,12 @@ private: | |||||
| bool shouldStealNotes; | bool shouldStealNotes; | ||||
| BigInteger sustainPedalsDown; | BigInteger sustainPedalsDown; | ||||
| void stopVoice (SynthesiserVoice*, bool allowTailOff); | |||||
| void stopVoice (SynthesiserVoice*, float velocity, bool allowTailOff); | |||||
| #if JUCE_CATCH_DEPRECATED_CODE_MISUSE | #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 findFreeVoice (const bool) const { return 0; } | ||||
| virtual int noteOff (int, int, int) { return 0; } | |||||
| #endif | #endif | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) | ||||
| @@ -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) | if (allowTailOff) | ||||
| { | { | ||||
| @@ -197,7 +197,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa | |||||
| if (attackReleaseLevel <= 0.0f) | if (attackReleaseLevel <= 0.0f) | ||||
| { | { | ||||
| stopNote (false); | |||||
| stopNote (0.0f, false); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -216,7 +216,7 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa | |||||
| if (sourceSamplePosition > playingSound->length) | if (sourceSamplePosition > playingSound->length) | ||||
| { | { | ||||
| stopNote (false); | |||||
| stopNote (0.0f, false); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -124,7 +124,7 @@ public: | |||||
| bool canPlaySound (SynthesiserSound*) override; | bool canPlaySound (SynthesiserSound*) override; | ||||
| void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) 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 pitchWheelMoved (int newValue); | ||||
| void controllerMoved (int controllerNumber, int newValue) override; | void controllerMoved (int controllerNumber, int newValue) override; | ||||