From 61d1f1963cf4a76196a1ec8ccc455197f4fb1c7c Mon Sep 17 00:00:00 2001 From: paulfd Date: Mon, 16 Sep 2019 11:48:51 +0200 Subject: [PATCH] Added support for the rt_decay opcode --- sfizz/Defaults.h | 2 ++ sfizz/MidiState.h | 23 +++++++++++++++++++++++ sfizz/Region.cpp | 11 +++++++++-- sfizz/Region.h | 4 +++- sfizz/Synth.cpp | 14 +++++++++++--- sfizz/Voice.cpp | 2 +- tests/RegionCrossfadesT.cpp | 23 +++++++++++++++++++++++ 7 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 sfizz/MidiState.h diff --git a/sfizz/Defaults.h b/sfizz/Defaults.h index d56b3b04..a4119be6 100644 --- a/sfizz/Defaults.h +++ b/sfizz/Defaults.h @@ -113,6 +113,8 @@ namespace Default inline constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power }; inline constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power }; inline constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power }; + inline constexpr float rtDecay { 0.0f }; + inline constexpr Range rtDecayRange { 0.0f, 200.0f }; // Performance parameters: pitch inline constexpr uint8_t pitchKeycenter { 60 }; diff --git a/sfizz/MidiState.h b/sfizz/MidiState.h new file mode 100644 index 00000000..05574306 --- /dev/null +++ b/sfizz/MidiState.h @@ -0,0 +1,23 @@ +#include +#include + +namespace sfz +{ + inline std::array noteOnTimes { }; + inline void setNoteOnTime(int noteNumber) + { + if (noteNumber >= 0 && noteNumber < 128) + noteOnTimes[noteNumber] = std::chrono::steady_clock::now(); + } + + inline float getNoteDuration(int noteNumber) + { + if (noteNumber >= 0 && noteNumber < 128) { + const auto noteOffTime = std::chrono::steady_clock::now(); + const auto duration = std::chrono::duration_cast>(noteOffTime - noteOnTimes[noteNumber]); + return duration.count(); + } + + return 0.0f; + } +} \ No newline at end of file diff --git a/sfizz/Region.cpp b/sfizz/Region.cpp index f9a10e60..5967621c 100644 --- a/sfizz/Region.cpp +++ b/sfizz/Region.cpp @@ -27,6 +27,7 @@ #include "Debug.h" #include "Opcode.h" #include "StringViewHelpers.h" +#include "MidiState.h" #include "absl/strings/str_replace.h" #include @@ -381,6 +382,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) DBG("Unknown crossfade power curve: " << std::string(opcode.value)); } break; + case hash("rt_decay"): + setValueFromOpcode(opcode, rtDecay, Default::rtDecayRange); + break; // Performance parameters: pitch case hash("pitch_keycenter"): @@ -629,9 +633,12 @@ float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) noexc return centsFactor(pitchVariationInCents); } -float sfz::Region::getBaseVolumedB() noexcept +float sfz::Region::getBaseVolumedB(int noteNumber) noexcept { - return volume + volumeDistribution(Random::randomGenerator); + auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator); + if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key) + baseVolumedB -= rtDecay * getNoteDuration(noteNumber); + return baseVolumedB; } float sfz::Region::getBaseGain() noexcept diff --git a/sfizz/Region.h b/sfizz/Region.h index 3739c379..f253c9e4 100644 --- a/sfizz/Region.h +++ b/sfizz/Region.h @@ -57,7 +57,7 @@ struct Region { float getBasePitchVariation(int noteNumber, uint8_t velocity) noexcept; float getNoteGain(int noteNumber, uint8_t velocity) noexcept; float getCrossfadeGain(const CCValueArray& ccState) noexcept; - float getBaseVolumedB() noexcept; + float getBaseVolumedB(int noteNumber) noexcept; float getBaseGain() noexcept; float velocityCurve(uint8_t velocity) const noexcept; uint32_t getOffset() noexcept; @@ -136,6 +136,8 @@ struct Region { SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve }; CCMap> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN + float rtDecay { Default::rtDecay }; // rt_decay + // Performance parameters: pitch uint8_t pitchKeycenter { Default::pitchKeycenter }; // pitch_keycenter diff --git a/sfizz/Synth.cpp b/sfizz/Synth.cpp index 0e6d8a36..e298504c 100644 --- a/sfizz/Synth.cpp +++ b/sfizz/Synth.cpp @@ -22,6 +22,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Synth.h" +#include "MidiState.h" #include "Config.h" #include "Debug.h" #include "ScopedFTZ.h" @@ -309,7 +310,10 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity) noexcept { - ASSERT(noteNumber <= 128); + ASSERT(noteNumber < 128); + ASSERT(noteNumber >= 0); + + setNoteOnTime(noteNumber); auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { @@ -334,7 +338,9 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocity) noexcept { - ASSERT(noteNumber <= 128); + ASSERT(noteNumber < 128); + ASSERT(noteNumber >= 0); + auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& voice : voices) voice->registerNoteOff(delay, channel, noteNumber, velocity); @@ -356,7 +362,9 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept { - ASSERT(ccNumber <= 128); + ASSERT(ccNumber < 128); + ASSERT(ccNumber >= 0); + for (auto& voice : voices) voice->registerCC(delay, channel, ccNumber, ccValue); diff --git a/sfizz/Voice.cpp b/sfizz/Voice.cpp index a59c8e5b..9ffb5f1b 100644 --- a/sfizz/Voice.cpp +++ b/sfizz/Voice.cpp @@ -54,7 +54,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number, speedRatio = static_cast(region->sampleRate / this->sampleRate); pitchRatio = region->getBasePitchVariation(number, value); - baseVolumedB = region->getBaseVolumedB(); + baseVolumedB = region->getBaseVolumedB(number); auto volumedB { baseVolumedB }; if (region->volumeCC) diff --git a/tests/RegionCrossfadesT.cpp b/tests/RegionCrossfadesT.cpp index 8d730e1c..510e7446 100644 --- a/tests/RegionCrossfadesT.cpp +++ b/tests/RegionCrossfadesT.cpp @@ -21,9 +21,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "Defaults.h" #include "Region.h" #include "catch2/catch.hpp" #include +#include +#include +#include "MidiState.h" using namespace Catch::literals; TEST_CASE("[Region] Crossfade in on key") @@ -257,4 +261,23 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") region.parseOpcode({ "amp_veltrack", "-100" }); REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) ); REQUIRE( region.getNoteGain(64, 0) == 1.0_a ); +} + +TEST_CASE("[Region] rt_decay") +{ + sfz::Region region {}; + region.parseOpcode({ "sample", "*sine" }); + region.parseOpcode({ "trigger", "release" }); + region.parseOpcode({ "rt_decay", "10" }); + sfz::setNoteOnTime(64); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) ); + region.parseOpcode({ "rt_decay", "20" }); + sfz::setNoteOnTime(64); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) ); + region.parseOpcode({ "trigger", "attack" }); + sfz::setNoteOnTime(64); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) ); } \ No newline at end of file