diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 3986ad66..b84b680f 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -217,6 +217,12 @@ bool ADSREnvelope::isSmoothing() noexcept return currentState != State::Done; } +template +int ADSREnvelope::getRemainingDelay() const noexcept +{ + return delay; +} + template void ADSREnvelope::startRelease(int releaseDelay) noexcept { diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index 20733032..af8abe02 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -75,6 +75,12 @@ public: * @return false */ bool isSmoothing() noexcept; + /** + * @brief Get the remaining delay samples + * + * @return int + */ + int getRemainingDelay() const noexcept; private: enum class State { diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 9dd59f1b..9fa4ba2b 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -127,12 +127,17 @@ void sfz::Voice::prepareEGEnvelope(int channel, int delay, uint8_t velocity) noe bool sfz::Voice::isFree() const noexcept { - return (region == nullptr); + return (state == State::idle); } void sfz::Voice::release(int delay) noexcept { - if (state == State::playing) { + if (state != State::playing) + return; + + if (egEnvelope.getRemainingDelay() >= (delay - initialDelay)) { + reset(); + } else { state = State::release; egEnvelope.startRelease(delay); } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 7f883d6c..01788fb0 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -203,4 +203,20 @@ TEST_CASE("[Synth] Reset all controllers") synth.cc(0, 0, 121, 64); REQUIRE( synth.getMidiState().getCCValue(0, 12) == 0 ); REQUIRE( synth.getMidiState().getCCValue(1, 12) == 64 ); -} \ No newline at end of file +} + +TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice") +{ + sfz::Synth synth; + synth.setSamplesPerBlock(1024); + synth.setNumVoices(1); + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/delay_release.sfz"); + synth.noteOn(0, 1, 60, 63); + REQUIRE( !synth.getVoiceView(0)->isFree() ); + synth.noteOff(100, 1, 60, 63); + REQUIRE( synth.getVoiceView(0)->isFree() ); + synth.noteOn(200, 1, 60, 63); + REQUIRE( !synth.getVoiceView(0)->isFree() ); + synth.noteOff(1000, 1, 60, 63); + REQUIRE( !synth.getVoiceView(0)->isFree() ); +} diff --git a/tests/TestFiles/delay_release.sfz b/tests/TestFiles/delay_release.sfz new file mode 100644 index 00000000..f9882c23 --- /dev/null +++ b/tests/TestFiles/delay_release.sfz @@ -0,0 +1 @@ + delay=0.005 sample=dummy1.wav