diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index daf0872a..346525e3 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -122,7 +122,8 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept break; } while (count < size) { - currentValue = std::max(sustain, currentValue + transitionDelta); + if (currentValue > sustain) + currentValue = std::max(sustain, currentValue + transitionDelta); output[count++] = currentValue; } break; @@ -173,6 +174,14 @@ void ADSREnvelope::startRelease(int releaseDelay) noexcept this->releaseDelay = releaseDelay; } +void ADSREnvelope::cancelRelease(int delay) noexcept +{ + (void)delay; + currentState = State::Sustain; + shouldRelease = false; + this->releaseDelay = -1; +} + void ADSREnvelope::setReleaseTime(Float timeInSeconds) noexcept { releaseRate = secondsToExpRate(timeInSeconds); diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index f1d3d668..ace1da7b 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -49,6 +49,12 @@ public: * @param releaseDelay the delay before releasing in samples */ void startRelease(int releaseDelay) noexcept; + /** + * @brief Cancel a release and get back into sustain. + * + * @param delay + */ + void cancelRelease(int delay) noexcept; /** * @brief Is the envelope smoothing? * diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index fea3f196..ed3623b2 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -676,6 +676,12 @@ void Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept if (impl.noteIsOff_ && region.loopMode != LoopMode::one_shot && sostenutoPedalReleaseCondition && sustainPedalReleaseCondition) release(delay); + + if (region.checkSustain && (impl.sustainState_ == Impl::SustainState::Sustaining) + && impl.released() && (region.trigger != Trigger::release && region.trigger != Trigger::release_key) ) { + ModMatrix& modMatrix = impl.resources_.getModMatrix(); + modMatrix.cancelRelease(impl.id_, impl.region_->getId(), delay); + } } void Voice::registerPitchWheel(int delay, float pitch) noexcept diff --git a/src/sfizz/modulations/ModGenerator.h b/src/sfizz/modulations/ModGenerator.h index 7bb6c329..55fe46ba 100644 --- a/src/sfizz/modulations/ModGenerator.h +++ b/src/sfizz/modulations/ModGenerator.h @@ -49,6 +49,15 @@ public: */ virtual void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { (void)sourceKey; (void)voiceId; (void)delay; } + /** + * @brief Cancel the release and get back into sustain + * + * @param sourceKey identifier of the source to release + * @param voiceId the particular voice to initialize, if per-voice + * @param delay the frame time when it happens + */ + virtual void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { (void)sourceKey; (void)voiceId; (void)delay; } + /** * @brief Generate a cycle of the modulator * diff --git a/src/sfizz/modulations/ModMatrix.cpp b/src/sfizz/modulations/ModMatrix.cpp index 35ab05f7..98a92c86 100644 --- a/src/sfizz/modulations/ModMatrix.cpp +++ b/src/sfizz/modulations/ModMatrix.cpp @@ -281,6 +281,19 @@ void ModMatrix::releaseVoice(NumericId voiceId, NumericId regionI } } +void ModMatrix::cancelRelease(NumericId voiceId, NumericId regionId, unsigned delay) +{ + Impl& impl = *impl_; + + ASSERT(regionId); + + const auto idNumber = static_cast(regionId.number()); + for (auto idx: impl.sourceIndicesForRegion_[idNumber]) { + const Impl::Source& source = impl.sources_[idx]; + source.gen->cancelRelease(source.key, voiceId, delay); + } +} + void ModMatrix::beginCycle(unsigned numFrames) { Impl& impl = *impl_; diff --git a/src/sfizz/modulations/ModMatrix.h b/src/sfizz/modulations/ModMatrix.h index 2cef2c0e..2aa59fe4 100644 --- a/src/sfizz/modulations/ModMatrix.h +++ b/src/sfizz/modulations/ModMatrix.h @@ -116,6 +116,11 @@ public: */ void releaseVoice(NumericId voiceId, NumericId regionId, unsigned delay); + /** + * @brief Cancel release for a given voice. + */ + void cancelRelease(NumericId voiceId, NumericId regionId, unsigned delay); + /** * @brief Start modulation processing for the entire cycle. * This clears all the buffers. diff --git a/src/sfizz/modulations/sources/ADSREnvelope.cpp b/src/sfizz/modulations/sources/ADSREnvelope.cpp index 1189791b..0ec5af8e 100644 --- a/src/sfizz/modulations/sources/ADSREnvelope.cpp +++ b/src/sfizz/modulations/sources/ADSREnvelope.cpp @@ -18,6 +18,52 @@ ADSREnvelopeSource::ADSREnvelopeSource(VoiceManager& manager, MidiState& state) { } +ADSREnvelope* getEG(Voice* voice, const ModKey& key) +{ + ADSREnvelope* eg = nullptr; + if (!voice) + return eg; + + switch (key.id()) { + case ModId::AmpEG: + eg = voice->getAmplitudeEG(); + break; + case ModId::PitchEG: + eg = voice->getPitchEG(); + break; + case ModId::FilEG: + eg = voice->getFilterEG(); + break; + default: + return eg; + } + + return eg; +} + +const EGDescription* getEGDescription(const Region* region, const ModKey& key) +{ + const EGDescription* desc = nullptr; + if (!region) + return desc; + + switch (key.id()) { + case ModId::AmpEG: + desc = ®ion->amplitudeEG; + break; + case ModId::PitchEG: + desc = &*region->pitchEG; + break; + case ModId::FilEG: + desc = &*region->filterEG; + break; + default: + return desc; + } + + return desc; +} + void ADSREnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { Voice* voice = voiceManager_.getVoiceById(voiceId); @@ -27,29 +73,9 @@ void ADSREnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, } const Region* region = voice->getRegion(); - ADSREnvelope* eg = nullptr; - const EGDescription* desc = nullptr; - - switch (sourceKey.id()) { - case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); - desc = ®ion->amplitudeEG; - break; - case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); - desc = &*region->pitchEG; - break; - case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); - desc = &*region->filterEG; - break; - default: - ASSERTFALSE; - return; - } + ADSREnvelope* eg = getEG(voice, sourceKey); + const EGDescription* desc = getEGDescription(region, sourceKey); + ASSERT(eg); const TriggerEvent& triggerEvent = voice->getTriggerEvent(); const float sampleRate = voice->getSampleRate(); @@ -64,27 +90,24 @@ void ADSREnvelopeSource::release(const ModKey& sourceKey, NumericId voice return; } - ADSREnvelope* eg = nullptr; + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); - switch (sourceKey.id()) { - case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); - break; - case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); - break; - case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); - break; - default: + eg->startRelease(delay); +} + +void ADSREnvelopeSource::cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) +{ + Voice* voice = voiceManager_.getVoiceById(voiceId); + if (!voice) { ASSERTFALSE; return; } - eg->startRelease(delay); + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); + + eg->cancelRelease(delay); } void ADSREnvelopeSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) @@ -95,25 +118,8 @@ void ADSREnvelopeSource::generate(const ModKey& sourceKey, NumericId voic return; } - ADSREnvelope* eg = nullptr; - - switch (sourceKey.id()) { - case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); - break; - case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); - break; - case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); - break; - default: - ASSERTFALSE; - return; - } + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); eg->getBlock(buffer); } diff --git a/src/sfizz/modulations/sources/ADSREnvelope.h b/src/sfizz/modulations/sources/ADSREnvelope.h index d52a9ca0..436127a7 100644 --- a/src/sfizz/modulations/sources/ADSREnvelope.h +++ b/src/sfizz/modulations/sources/ADSREnvelope.h @@ -17,6 +17,7 @@ public: explicit ADSREnvelopeSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; + void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: diff --git a/src/sfizz/modulations/sources/ChannelAftertouch.cpp b/src/sfizz/modulations/sources/ChannelAftertouch.cpp index 96658aee..4f54c67b 100644 --- a/src/sfizz/modulations/sources/ChannelAftertouch.cpp +++ b/src/sfizz/modulations/sources/ChannelAftertouch.cpp @@ -23,13 +23,6 @@ void ChannelAftertouchSource::init(const ModKey& sourceKey, NumericId voi UNUSED(delay); } -void ChannelAftertouchSource::release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) -{ - UNUSED(sourceKey); - UNUSED(voiceId); - UNUSED(delay); -} - void ChannelAftertouchSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) { UNUSED(sourceKey); diff --git a/src/sfizz/modulations/sources/ChannelAftertouch.h b/src/sfizz/modulations/sources/ChannelAftertouch.h index d162b7ec..f6ddd5a7 100644 --- a/src/sfizz/modulations/sources/ChannelAftertouch.h +++ b/src/sfizz/modulations/sources/ChannelAftertouch.h @@ -16,7 +16,6 @@ class ChannelAftertouchSource : public ModGenerator { public: explicit ChannelAftertouchSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; - void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: diff --git a/src/sfizz/modulations/sources/PolyAftertouch.cpp b/src/sfizz/modulations/sources/PolyAftertouch.cpp index ce91f7bb..5c4ef22c 100644 --- a/src/sfizz/modulations/sources/PolyAftertouch.cpp +++ b/src/sfizz/modulations/sources/PolyAftertouch.cpp @@ -23,13 +23,6 @@ void PolyAftertouchSource::init(const ModKey& sourceKey, NumericId voiceI UNUSED(delay); } -void PolyAftertouchSource::release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) -{ - UNUSED(sourceKey); - UNUSED(voiceId); - UNUSED(delay); -} - void PolyAftertouchSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) { UNUSED(sourceKey); diff --git a/src/sfizz/modulations/sources/PolyAftertouch.h b/src/sfizz/modulations/sources/PolyAftertouch.h index c70ea360..5d37ee2a 100644 --- a/src/sfizz/modulations/sources/PolyAftertouch.h +++ b/src/sfizz/modulations/sources/PolyAftertouch.h @@ -16,7 +16,6 @@ class PolyAftertouchSource : public ModGenerator { public: explicit PolyAftertouchSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; - void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: