| @@ -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) | |||
| { | |||
| @@ -38,11 +38,12 @@ public: | |||
| bool canPlaySound (SynthesiserSound* sound) override | |||
| { | |||
| return dynamic_cast <SineWaveSound*> (sound) != 0; | |||
| return dynamic_cast<SineWaveSound*> (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) | |||
| { | |||
| @@ -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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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) | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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; | |||