diff --git a/sfizz/MidiState.h b/sfizz/MidiState.h index 05574306..ea8a178e 100644 --- a/sfizz/MidiState.h +++ b/sfizz/MidiState.h @@ -4,10 +4,13 @@ namespace sfz { inline std::array noteOnTimes { }; - inline void setNoteOnTime(int noteNumber) + inline std::array lastNoteVelocities { }; + inline void noteOn(int noteNumber, uint8_t velocity) { - if (noteNumber >= 0 && noteNumber < 128) + if (noteNumber >= 0 && noteNumber < 128) { + lastNoteVelocities[noteNumber] = velocity; noteOnTimes[noteNumber] = std::chrono::steady_clock::now(); + } } inline float getNoteDuration(int noteNumber) @@ -20,4 +23,12 @@ namespace sfz return 0.0f; } + + inline uint8_t getNoteVelocity(int noteNumber) + { + if (noteNumber >= 0 && noteNumber < 128) + return lastNoteVelocities[noteNumber]; + + return 0; + } } \ No newline at end of file diff --git a/sfizz/Region.cpp b/sfizz/Region.cpp index 5967621c..d9f98b62 100644 --- a/sfizz/Region.cpp +++ b/sfizz/Region.cpp @@ -518,10 +518,6 @@ bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity, else sequenceSwitched = false; - // Velocity memory for release_key and for sw_vel=previous - if (trigger == SfzTrigger::release_key || velocityOverride == SfzVelocityOverride::previous) - lastNoteVelocities[noteNumber] = velocity; - if (previousNote) { if (*previousNote == noteNumber) previousKeySwitched = true; @@ -568,9 +564,10 @@ bool sfz::Region::registerNoteOff(int channel, int noteNumber, uint8_t velocity if (!isSwitchedOn()) return false; + const bool velOk = velocityRange.containsWithEnd(velocity); const bool randOk = randRange.contains(randValue); const bool releaseTrigger = (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key); - return keyOk && chanOk && randOk && releaseTrigger; + return keyOk && velOk && chanOk && randOk && releaseTrigger; } bool sfz::Region::registerCC(int channel, int ccNumber, uint8_t ccValue) noexcept @@ -721,10 +718,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve); // Amplitude velocity tracking - if (trigger == SfzTrigger::release_key) - baseGain *= velocityCurve(lastNoteVelocities[noteNumber]); - else - baseGain *= velocityCurve(velocity); + baseGain *= velocityCurve(velocity); // Crossfades related to velocity baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve); diff --git a/sfizz/Region.h b/sfizz/Region.h index f253c9e4..352b8798 100644 --- a/sfizz/Region.h +++ b/sfizz/Region.h @@ -108,7 +108,6 @@ struct Region { // Region logic: triggers SfzTrigger trigger { Default::trigger }; // trigger - std::array lastNoteVelocities; // Keeps the velocities of the previous note-ons if the region has the trigger release_key CCMap> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN // Performance parameters: amplifier diff --git a/sfizz/Synth.cpp b/sfizz/Synth.cpp index e298504c..e59d66ea 100644 --- a/sfizz/Synth.cpp +++ b/sfizz/Synth.cpp @@ -313,7 +313,7 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); - setNoteOnTime(noteNumber); + sfz::noteOn(noteNumber, velocity); auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { @@ -341,17 +341,18 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); + auto replacedVelocity = velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity; auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& voice : voices) - voice->registerNoteOff(delay, channel, noteNumber, velocity); + voice->registerNoteOff(delay, channel, noteNumber, replacedVelocity); for (auto& region : noteActivationLists[noteNumber]) { - if (region->registerNoteOff(channel, noteNumber, velocity, randValue)) { + if (region->registerNoteOff(channel, noteNumber, replacedVelocity, randValue)) { auto voice = findFreeVoice(); if (voice == nullptr) continue; - voice->startVoice(region, delay, channel, noteNumber, velocity, Voice::TriggerType::NoteOff); + voice->startVoice(region, delay, channel, noteNumber, replacedVelocity, Voice::TriggerType::NoteOff); if (!region->isGenerator()) { voice->expectFileData(fileTicket); filePool.enqueueLoading(voice, region->sample, region->trueSampleEnd(), fileTicket++); diff --git a/tests/RegionCrossfadesT.cpp b/tests/RegionCrossfadesT.cpp index 510e7446..e933c661 100644 --- a/tests/RegionCrossfadesT.cpp +++ b/tests/RegionCrossfadesT.cpp @@ -269,15 +269,15 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "trigger", "release" }); region.parseOpcode({ "rt_decay", "10" }); - sfz::setNoteOnTime(64); + sfz::noteOn(64, 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); + sfz::noteOn(64, 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); + sfz::noteOn(64, 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