diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index bb86115b..bfd7f3f8 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -11,22 +11,30 @@ namespace sfz { +template +Type ADSREnvelope::secondsToSamples (Type timeInSeconds) const noexcept +{ + return static_cast(timeInSeconds * sampleRate); +}; + +template +Type ADSREnvelope::secondsToLinRate (Type timeInSeconds) const noexcept +{ + timeInSeconds = std::max(timeInSeconds, config::virtuallyZero); + return 1 / (sampleRate * timeInSeconds); +}; + +template +Type ADSREnvelope::secondsToExpRate (Type timeInSeconds) const noexcept +{ + timeInSeconds = std::max(25e-3, timeInSeconds); + return std::exp(-8.0 / (timeInSeconds * sampleRate)); +}; + template void ADSREnvelope::reset(const EGDescription& desc, const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept { - auto secondsToSamples = [sampleRate](Type timeInSeconds) { - return static_cast(timeInSeconds * sampleRate); - }; - - auto secondsToLinRate = [sampleRate](Type timeInSeconds) { - timeInSeconds = std::max(timeInSeconds, config::virtuallyZero); - return 1 / (sampleRate * timeInSeconds); - }; - - auto secondsToExpRate = [sampleRate](Type timeInSeconds) { - timeInSeconds = std::max(25e-3, timeInSeconds); - return std::exp(-8.0 / (timeInSeconds * sampleRate)); - }; + this->sampleRate = sampleRate; this->delay = delay + secondsToSamples(desc.getDelay(state, velocity)); this->attackStep = secondsToLinRate(desc.getAttack(state, velocity)); @@ -208,13 +216,16 @@ int ADSREnvelope::getRemainingDelay() const noexcept } template -void ADSREnvelope::startRelease(int releaseDelay, bool fastRelease) noexcept +void ADSREnvelope::startRelease(int releaseDelay) noexcept { shouldRelease = true; this->releaseDelay = releaseDelay; +} - if (fastRelease) - this->releaseRate = 0; +template +void ADSREnvelope::setReleaseTime(Type timeInSeconds) noexcept +{ + releaseRate = secondsToExpRate(timeInSeconds); } } diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index 608567c6..892388f7 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -44,15 +44,18 @@ public: * @param output */ void getBlock(absl::Span output) noexcept; + /** + * @brief Set the release time for the envelope + * + * @param timeInSeconds + */ + void setReleaseTime(Type timeInSeconds) noexcept; /** * @brief Start the envelope release after a delay. * * @param releaseDelay the delay before releasing in samples - * @param fastRelease whether the release should be fast (i.e. 0 or so) or - * follow the release duration that was set when - * initializing the envelope */ - void startRelease(int releaseDelay, bool fastRelease = false) noexcept; + void startRelease(int releaseDelay) noexcept; /** * @brief Is the envelope smoothing? * @@ -75,6 +78,11 @@ public: int getRemainingDelay() const noexcept; private: + float sampleRate { config::defaultSampleRate }; + Type secondsToSamples (Type timeInSeconds) const noexcept; + Type secondsToLinRate (Type timeInSeconds) const noexcept; + Type secondsToExpRate (Type timeInSeconds) const noexcept; + enum class State { Delay, Attack, diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index b4f9598a..d9ade7f8 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -155,7 +155,7 @@ bool sfz::Voice::isFree() const noexcept return (state == State::idle); } -void sfz::Voice::release(int delay, bool fastRelease) noexcept +void sfz::Voice::release(int delay) noexcept { if (state != State::playing) return; @@ -163,10 +163,21 @@ void sfz::Voice::release(int delay, bool fastRelease) noexcept if (egEnvelope.getRemainingDelay() > delay) { switchState(State::cleanMeUp); } else { - egEnvelope.startRelease(delay, fastRelease); + egEnvelope.startRelease(delay); } } +void sfz::Voice::off(int delay) noexcept +{ + if (region->offMode == SfzOffMode::fast) { + egEnvelope.setReleaseTime( Default::offTime ); + } else if (region->offMode == SfzOffMode::time) { + egEnvelope.setReleaseTime(region->offTime); + } + + release(delay); +} + void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noexcept { ASSERT(velocity >= 0.0 && velocity <= 1.0); @@ -561,7 +572,8 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept << " for sample " << region->sampleId); } #endif - egEnvelope.startRelease(i, true); + egEnvelope.setReleaseTime(0.0f); + egEnvelope.startRelease(i); fill(indices->subspan(i), sampleEnd); fill(coeffs->subspan(i), 1.0f); break; @@ -695,7 +707,7 @@ bool sfz::Voice::checkOffGroup(int delay, uint32_t group) noexcept return false; if (triggerType == TriggerType::NoteOn && region->offBy == group) { - release(delay, region->offMode == SfzOffMode::fast); + off(delay); return true; } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 52a7f0c8..ce473c7b 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -288,7 +288,15 @@ public: * @param delay * @param fastRelease whether to do a normal release or cut the voice abruptly */ - void release(int delay, bool fastRelease = false) noexcept; + void release(int delay) noexcept; + + /** + * @brief Off the voice (steal). This will respect the off mode of the region + * and set the envelopes if necessary. + * + * @param delay + */ + void off(int delay) noexcept; /** * @brief gets the age of the Voice