From 0d0cf6440548fb46f35368ed06a4f26880e24558 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Mon, 30 Dec 2019 00:10:20 +0100 Subject: [PATCH] Changed the legato logic The midistate now counts the active notes instead of it being per-region --- src/sfizz/MidiState.cpp | 15 +++++++++++++++ src/sfizz/MidiState.h | 11 +++++++++++ src/sfizz/Region.cpp | 11 ++--------- src/sfizz/Region.h | 1 - src/sfizz/Synth.cpp | 2 ++ tests/RegionTriggersT.cpp | 10 ++++++++++ 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index bbee8ef6..1db8585d 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -14,7 +14,21 @@ void sfz::MidiState::noteOnEvent(int noteNumber, uint8_t velocity) noexcept if (noteNumber >= 0 && noteNumber < 128) { lastNoteVelocities[noteNumber] = velocity; noteOnTimes[noteNumber] = std::chrono::steady_clock::now(); + activeNotes++; } + +} + +void sfz::MidiState::noteOffEvent(int noteNumber, uint8_t velocity [[maybe_unused]]) noexcept +{ + ASSERT(noteNumber >= 0 && noteNumber <= 127); + ASSERT(velocity >= 0 && velocity <= 127); + + if (noteNumber >= 0 && noteNumber < 128) { + activeNotes--; + ASSERT(activeNotes >= 0); + } + } float sfz::MidiState::getNoteDuration(int noteNumber) const @@ -78,6 +92,7 @@ void sfz::MidiState::reset() noexcept ccValue = 0; pitchBend = 0; + activeNotes = 0; } void sfz::MidiState::resetAllControllers() noexcept diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index 97ed5c91..e69c76f4 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -23,6 +23,16 @@ public: */ void noteOnEvent(int noteNumber, uint8_t velocity) noexcept; + /** + * @brief Update the state after a note off event + * + * @param noteNumber + * @param velocity + */ + void noteOffEvent(int noteNumber, uint8_t velocity) noexcept; + + int getActiveNotes() const noexcept { return activeNotes; } + /** * @brief Register a note off and get the note duration * @@ -91,6 +101,7 @@ private: template using MidiNoteArray = std::array; using NoteOnTime = std::chrono::steady_clock::time_point; + int activeNotes { 0 }; /** * @brief Stores the note on times. * diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 0a44dacd..48db7e30 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -523,9 +523,6 @@ bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randVal const bool keyOk = keyRange.containsWithEnd(noteNumber); if (keyOk) { - // Update the number of notes playing for the region - activeNotesInRange++; - // Sequence activation sequenceCounter += 1; if ((sequenceCounter % sequenceLength) == sequencePosition - 1) @@ -552,9 +549,9 @@ bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randVal const bool velOk = velocityRange.containsWithEnd(velocity); const bool randOk = randRange.contains(randValue) || (randValue == 1.0f && randRange.getEnd() == 1.0f); - const bool firstLegatoNote = (trigger == SfzTrigger::first && activeNotesInRange == 0); + const bool firstLegatoNote = (trigger == SfzTrigger::first && midiState.getActiveNotes() == 1); const bool attackTrigger = (trigger == SfzTrigger::attack); - const bool notFirstLegatoNote = (trigger == SfzTrigger::legato && activeNotesInRange > 0); + const bool notFirstLegatoNote = (trigger == SfzTrigger::legato && midiState.getActiveNotes() > 1); return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote); } @@ -571,10 +568,6 @@ bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity [[maybe_unuse const bool keyOk = keyRange.containsWithEnd(noteNumber); - // Update the number of notes playing for the region - if (keyOk) - activeNotesInRange--; - if (!isSwitchedOn()) return false; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index b308ef00..ebe08187 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -319,7 +319,6 @@ private: bool triggerOnCC { false }; absl::string_view defaultPath { "" }; - int activeNotesInRange { -1 }; int sequenceCounter { 0 }; std::uniform_real_distribution volumeDistribution { -sfz::Default::ampRandom, sfz::Default::ampRandom }; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index a218e522..4030347b 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -417,6 +417,8 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity [[maybe_unu ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); + midiState.noteOffEvent(noteNumber, velocity); + AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) return; diff --git a/tests/RegionTriggersT.cpp b/tests/RegionTriggersT.cpp index 11f1cb33..089f8d6d 100644 --- a/tests/RegionTriggersT.cpp +++ b/tests/RegionTriggersT.cpp @@ -152,10 +152,15 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "first" }); + midiState.noteOnEvent(40, 64); REQUIRE(region.registerNoteOn(40, 64, 0.5f)); + midiState.noteOnEvent(41, 64); REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); + midiState.noteOffEvent(40, 0); region.registerNoteOff(40, 0, 0.5f); + midiState.noteOffEvent(41, 0); region.registerNoteOff(41, 0, 0.5f); + midiState.noteOnEvent(42, 64); REQUIRE(region.registerNoteOn(42, 64, 0.5f)); } @@ -164,10 +169,15 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "legato" }); + midiState.noteOnEvent(40, 64); REQUIRE(!region.registerNoteOn(40, 64, 0.5f)); + midiState.noteOnEvent(41, 64); REQUIRE(region.registerNoteOn(41, 64, 0.5f)); + midiState.noteOffEvent(40, 64); region.registerNoteOff(40, 0, 0.5f); + midiState.noteOffEvent(41, 64); region.registerNoteOff(41, 0, 0.5f); + midiState.noteOnEvent(42, 64); REQUIRE(!region.registerNoteOn(42, 64, 0.5f)); } }