From f8801e78e428b2c0a911fd21df6651e75767d7b8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 16 Mar 2020 23:47:54 +0100 Subject: [PATCH 01/19] Change numCC to uint16_t and increase the max number of ccs --- src/sfizz/Config.h | 2 +- src/sfizz/Defaults.h | 2 +- src/sfizz/MidiState.cpp | 2 +- src/sfizz/Synth.cpp | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index e1bab309..eda45b81 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -52,7 +52,7 @@ namespace config { constexpr float A440 { 440.0 }; constexpr size_t powerHistoryLength { 16 }; constexpr float voiceStealingThreshold { 0.00001f }; - constexpr uint8_t numCCs { 143 }; + constexpr uint16_t numCCs { 512 }; constexpr int chunkSize { 1024 }; constexpr float defaultAmpEGRelease { 0.02f }; constexpr int filtersInPool { maxVoices * 2 }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 71741b5a..8daf2384 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -69,7 +69,7 @@ namespace Default // Region logic: MIDI conditions constexpr Range channelRange { 1, 16 }; constexpr Range midiChannelRange { 0, 15 }; - constexpr Range ccNumberRange { 0, config::numCCs }; + constexpr Range ccNumberRange { 0, config::numCCs }; constexpr Range ccValueRange { 0, 127 }; constexpr uint8_t cc { 0 }; constexpr Range bendRange { -8192, 8192 }; diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 0c812af4..23fd7ab9 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -104,7 +104,7 @@ void sfz::MidiState::reset(int delay) noexcept void sfz::MidiState::resetAllControllers(int delay) noexcept { - for (int idx = 0; idx < config::numCCs; idx++) + for (unsigned idx = 0; idx < config::numCCs; idx++) cc[idx] = 0; pitchBend = 0; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 9ef64406..fd36c0e0 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -379,14 +379,14 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) noteActivationLists[note].push_back(region); } - for (auto cc = 0; cc < config::numCCs; cc++) { + for (unsigned cc = 0; cc < config::numCCs; cc++) { if (region->ccTriggers.contains(cc) || region->ccConditions.contains(cc)) ccActivationLists[cc].push_back(region); } // Defaults - for (int ccIndex = 0; ccIndex < config::numCCs; ccIndex++) { - region->registerCC(ccIndex, resources.midiState.getCCValue(ccIndex)); + for (unsigned cc = 0; cc < config::numCCs; cc++) { + region->registerCC(cc, resources.midiState.getCCValue(cc)); } if (defaultSwitch) { @@ -953,12 +953,12 @@ void sfz::Synth::resetAllControllers(int delay) noexcept resources.midiState.resetAllControllers(delay); for (auto& voice: voices) { voice->registerPitchWheel(delay, 0); - for (int cc = 0; cc < config::numCCs; ++cc) + for (unsigned cc = 0; cc < config::numCCs; ++cc) voice->registerCC(delay, cc, 0); } for (auto& region: regions) { - for (int cc = 0; cc < config::numCCs; ++cc) + for (unsigned cc = 0; cc < config::numCCs; ++cc) region->registerCC(cc, 0); } } From 9373f31945c9be2653cc8d76dc78ed02daaebe35 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 17 Mar 2020 00:17:18 +0100 Subject: [PATCH 02/19] Remove CCArray, and store floats internally --- src/sfizz/ADSREnvelope.cpp | 15 +++-- src/sfizz/EGDescription.h | 60 ++++++++++++------- src/sfizz/MidiState.cpp | 44 ++++++++++---- src/sfizz/MidiState.h | 44 ++++++++++++-- src/sfizz/Region.cpp | 6 +- src/sfizz/Region.h | 2 +- src/sfizz/SfzHelpers.h | 27 ++++----- src/sfizz/Voice.cpp | 4 +- tests/EGDescriptionT.cpp | 94 +++++++++++++++--------------- tests/MidiStateT.cpp | 8 +-- tests/RegionValueComputationsT.cpp | 56 +++++++++--------- 11 files changed, 209 insertions(+), 151 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 96333be2..21a76cf9 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -18,15 +18,14 @@ void ADSREnvelope::reset(const Region& region, const MidiState& state, int return static_cast(timeInSeconds * sampleRate); }; - const auto ccArray = state.getCCArray(); - this->delay = delay + secondsToSamples(region.amplitudeEG.getDelay(ccArray, velocity)); - this->attack = secondsToSamples(region.amplitudeEG.getAttack(ccArray, velocity)); - this->decay = secondsToSamples(region.amplitudeEG.getDecay(ccArray, velocity)); - this->release = secondsToSamples(region.amplitudeEG.getRelease(ccArray, velocity)); - this->hold = secondsToSamples(region.amplitudeEG.getHold(ccArray, velocity)); + this->delay = delay + secondsToSamples(region.amplitudeEG.getDelay(state, velocity)); + this->attack = secondsToSamples(region.amplitudeEG.getAttack(state, velocity)); + this->decay = secondsToSamples(region.amplitudeEG.getDecay(state, velocity)); + this->release = secondsToSamples(region.amplitudeEG.getRelease(state, velocity)); + this->hold = secondsToSamples(region.amplitudeEG.getHold(state, velocity)); this->peak = 1.0; - this->sustain = normalizePercents(region.amplitudeEG.getSustain(ccArray, velocity)); - this->start = this->peak * normalizePercents(region.amplitudeEG.getStart(ccArray, velocity)); + this->sustain = normalizePercents(region.amplitudeEG.getSustain(state, velocity)); + this->start = this->peak * normalizePercents(region.amplitudeEG.getStart(state, velocity)); releaseDelay = 0; shouldRelease = false; diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index f9c422f7..afdc99f5 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -29,6 +29,7 @@ #include "Macros.h" #include "LeakDetector.h" #include "SfzHelpers.h" +#include "MidiState.h" #include @@ -42,6 +43,23 @@ namespace sfz * TODO: should be updated for SFZ v2 * */ + +/** + * @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone. + * + * @param ccValues + * @param ccSwitch + * @param value + * @return float + */ +inline float ccSwitchedValue(const MidiState& state, const absl::optional>& ccSwitch, float value) noexcept +{ + if (ccSwitch) + return value + ccSwitch->value * normalizeCC(state.getCCValue(ccSwitch->cc)); + else + return value; +} + struct EGDescription { EGDescription() = default; @@ -76,80 +94,80 @@ struct EGDescription /** * @brief Get the attack with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getAttack(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getAttack(const MidiState &state, uint8_t velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccAttack, attack) + normalizeVelocity(velocity)*vel2attack); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccAttack, attack) + normalizeVelocity(velocity)*vel2attack); } /** * @brief Get the decay with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getDecay(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getDecay(const MidiState &state, uint8_t velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccDecay, decay) + normalizeVelocity(velocity)*vel2decay); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDecay, decay) + normalizeVelocity(velocity)*vel2decay); } /** * @brief Get the delay with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getDelay(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getDelay(const MidiState &state, uint8_t velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccDelay, delay) + normalizeVelocity(velocity)*vel2delay); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDelay, delay) + normalizeVelocity(velocity)*vel2delay); } /** * @brief Get the holding duration with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getHold(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getHold(const MidiState &state, uint8_t velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccHold, hold) + normalizeVelocity(velocity)*vel2hold); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccHold, hold) + normalizeVelocity(velocity)*vel2hold); } /** * @brief Get the release duration with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getRelease(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getRelease(const MidiState &state, uint8_t velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccRelease, release) + normalizeVelocity(velocity)*vel2release); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccRelease, release) + normalizeVelocity(velocity)*vel2release); } /** * @brief Get the starting level with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getStart(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getStart(const MidiState &state, uint8_t velocity) const noexcept { UNUSED(velocity); - return Default::egPercentRange.clamp(ccSwitchedValue(ccValues, ccStart, start)); + return Default::egPercentRange.clamp(ccSwitchedValue(state, ccStart, start)); } /** * @brief Get the sustain level with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getSustain(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getSustain(const MidiState &state, uint8_t velocity) const noexcept { - return Default::egPercentRange.clamp(ccSwitchedValue(ccValues, ccSustain, sustain) + normalizeVelocity(velocity)*vel2sustain); + return Default::egPercentRange.clamp(ccSwitchedValue(state, ccSustain, sustain) + normalizeVelocity(velocity)*vel2sustain); } LEAK_DETECTOR(EGDescription); }; diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 23fd7ab9..475a8352 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -14,9 +14,19 @@ sfz::MidiState::MidiState() } void sfz::MidiState::noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept +{ + noteOnEventNormalized(delay, noteNumber, normalizeVelocity(velocity)); +} + +void sfz::MidiState::noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept +{ + noteOffEventNormalized(delay, noteNumber, normalizeVelocity(velocity)); +} + +void sfz::MidiState::noteOnEventNormalized(int delay, int noteNumber, float velocity) noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); - ASSERT(velocity >= 0 && velocity <= 127); + ASSERT(velocity >= 0 && velocity <= 1.0); if (noteNumber >= 0 && noteNumber < 128) { lastNoteVelocities[noteNumber] = velocity; @@ -26,10 +36,10 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, uint8_t velocity) no } -void sfz::MidiState::noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::MidiState::noteOffEventNormalized(int delay, int noteNumber, float velocity) noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); - ASSERT(velocity >= 0 && velocity <= 127); + ASSERT(velocity >= 0.0 && velocity <= 1.0); UNUSED(velocity); if (noteNumber >= 0 && noteNumber < 128) { if (activeNotes > 0) @@ -52,12 +62,18 @@ float sfz::MidiState::getNoteDuration(int noteNumber) const } uint8_t sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept +{ + return denormalizeVelocity(getNoteVelocityNormalized(noteNumber)); +} + +float sfz::MidiState::getNoteVelocityNormalized(int noteNumber) const noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); return lastNoteVelocities[noteNumber]; } + void sfz::MidiState::pitchBendEvent(int delay, int pitchBendValue) noexcept { ASSERT(pitchBendValue >= -8192 && pitchBendValue <= 8192); @@ -72,24 +88,28 @@ int sfz::MidiState::getPitchBend() const noexcept void sfz::MidiState::ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept { - ASSERT(ccNumber >= 0 && ccNumber < config::numCCs); - ASSERT(ccValue >= 0 && ccValue <= 127); - - cc[ccNumber] = ccValue; + ccEventNormalized(delay, ccNumber, normalizeCC(ccValue)); } uint8_t sfz::MidiState::getCCValue(int ccNumber) const noexcept +{ + return denormalizeCC(getCCValueNormalized(ccNumber)); +} + +void sfz::MidiState::ccEventNormalized(int delay, int ccNumber, float ccValue) noexcept +{ + ASSERT(ccValue >= 0.0 && ccValue <= 1.0); + + cc[ccNumber] = ccValue; +} + +float sfz::MidiState::getCCValueNormalized(int ccNumber) const noexcept { ASSERT(ccNumber >= 0 && ccNumber < config::numCCs); return cc[ccNumber]; } -const sfz::SfzCCArray& sfz::MidiState::getCCArray() const noexcept -{ - return cc; -} - void sfz::MidiState::reset(int delay) noexcept { for (auto& velocity: lastNoteVelocities) diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index badb0e17..63d9b765 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -7,7 +7,6 @@ #pragma once #include #include -#include "SfzHelpers.h" #include "CCMap.h" #include "Range.h" @@ -31,6 +30,14 @@ public: */ void noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept; + /** + * @brief Update the state after a note on event + * + * @param noteNumber + * @param velocity + */ + void noteOnEventNormalized(int delay, int noteNumber, float velocity) noexcept; + /** * @brief Update the state after a note off event * @@ -39,6 +46,14 @@ public: */ void noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept; + /** + * @brief Update the state after a note off event + * + * @param noteNumber + * @param velocity + */ + void noteOffEventNormalized(int delay, int noteNumber, float velocity) noexcept; + int getActiveNotes() const noexcept { return activeNotes; } /** @@ -57,6 +72,14 @@ public: */ uint8_t getNoteVelocity(int noteNumber) const noexcept; + /** + * @brief Get the note on velocity for a given note + * + * @param noteNumber + * @return float + */ + float getNoteVelocityNormalized(int noteNumber) const noexcept; + /** * @brief Register a pitch bend event * @@ -79,6 +102,14 @@ public: */ void ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept; + /** + * @brief Register a CC event + * + * @param ccNumber + * @param ccValue + */ + void ccEventNormalized(int delay, int ccNumber, float ccValue) noexcept; + /** * @brief Get the CC value for CC number * @@ -88,11 +119,12 @@ public: uint8_t getCCValue(int ccNumber) const noexcept; /** - * @brief Get the full CC status + * @brief Get the CC value for CC number * - * @return const SfzCCArray& + * @param ccNumber + * @return float */ - const SfzCCArray& getCCArray() const noexcept; + float getCCValueNormalized(int ccNumber) const noexcept; /** * @brief Reset the midi state (does not impact the last note on time) @@ -140,12 +172,12 @@ private: * depressed notes. * */ - MidiNoteArray lastNoteVelocities; + MidiNoteArray lastNoteVelocities; /** * @brief Current known values for the CCs. * */ - SfzCCArray cc; + std::array cc; /** * Pitch bend status */ diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 808f031e..13df28d2 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1009,19 +1009,19 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept return baseGain; } -float sfz::Region::getCrossfadeGain(const sfz::SfzCCArray& ccState) const noexcept +float sfz::Region::getCrossfadeGain() const noexcept { float gain { 1.0f }; // Crossfades due to CC states for (const auto& valuePair : crossfadeCCInRange) { - const auto ccValue = ccState[valuePair.cc]; + const auto ccValue = midiState.getCCValue(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve); } for (const auto& valuePair : crossfadeCCOutRange) { - const auto ccValue = ccState[valuePair.cc]; + const auto ccValue = midiState.getCCValue(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 1fd0fa67..47a6dfe6 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -152,7 +152,7 @@ struct Region { * @param ccState * @return float */ - float getCrossfadeGain(const SfzCCArray& ccState) const noexcept; + float getCrossfadeGain() const noexcept; /** * @brief Get the base volume of the region depending on which note has been * pressed to trigger the region. diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 173eec03..a8126858 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -18,7 +18,6 @@ namespace sfz { -using SfzCCArray = std::array; using CCNamePair = std::pair; template @@ -83,6 +82,16 @@ constexpr T denormalize7Bits(float value) return static_cast(value * 127.0f); } +constexpr uint8_t denormalizeCC(float value) +{ + return denormalize7Bits(value); +} + +constexpr uint8_t denormalizeVelocity(float value) +{ + return denormalize7Bits(value); +} + template constexpr float normalize7Bits(T value) { @@ -141,22 +150,6 @@ constexpr float normalizeBend(float bendValue) return min(max(bendValue, -8191.0f), 8191.0f) / 8191.0f; } -/** - * @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone. - * - * @param ccValues - * @param ccSwitch - * @param value - * @return float - */ -inline float ccSwitchedValue(const SfzCCArray& ccValues, const absl::optional>& ccSwitch, float value) noexcept -{ - if (ccSwitch) - return value + ccSwitch->value * normalizeCC(ccValues[ccSwitch->cc]); - else - return value; -} - /** * @brief Convert a note in string to its equivalent midi note number * diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index e86375f2..71a06159 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -89,7 +89,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value gain += normalizeCC(resources.midiState.getCCValue(region->amplitudeCC->cc)) * normalizePercents(region->amplitudeCC->value); amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain)); - float crossfadeGain { region->getCrossfadeGain(resources.midiState.getCCArray()) }; + float crossfadeGain { region->getCrossfadeGain() }; crossfadeEnvelope.reset(Default::normalizedRange.clamp(crossfadeGain)); basePan = normalizePercents(region->pan); @@ -225,7 +225,7 @@ void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept } if (region->crossfadeCCInRange.contains(ccNumber) || region->crossfadeCCOutRange.contains(ccNumber)) { - const float crossfadeGain = region->getCrossfadeGain(resources.midiState.getCCArray()); + const float crossfadeGain = region->getCrossfadeGain(); crossfadeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(crossfadeGain)); } } diff --git a/tests/EGDescriptionT.cpp b/tests/EGDescriptionT.cpp index 70ce49ee..4e933e75 100644 --- a/tests/EGDescriptionT.cpp +++ b/tests/EGDescriptionT.cpp @@ -12,108 +12,108 @@ using namespace Catch::literals; TEST_CASE("[EGDescription] Attack range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.attack = 1; eg.vel2attack = -1.27f; eg.ccAttack = { 63, 1.27f }; - REQUIRE( eg.getAttack(ccArray, 0) == 1.0f ); - REQUIRE( eg.getAttack(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getAttack(ccArray, 127) == 1.0f ); - REQUIRE( eg.getAttack(ccArray, 0) == 2.27f ); + REQUIRE( eg.getAttack(state, 0) == 1.0f ); + REQUIRE( eg.getAttack(state, 127) == 0.0f ); + state.ccEvent(0, 63, 127); + REQUIRE( eg.getAttack(state, 127) == 1.0f ); + REQUIRE( eg.getAttack(state, 0) == 2.27f ); eg.ccAttack = { 63, 127.0f }; - REQUIRE( eg.getAttack(ccArray, 0) == 100.0f ); + REQUIRE( eg.getAttack(state, 0) == 100.0f ); } TEST_CASE("[EGDescription] Delay range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.delay = 1; eg.vel2delay = -1.27f; eg.ccDelay = { 63, 1.27f }; - REQUIRE( eg.getDelay(ccArray, 0) == 1.0f ); - REQUIRE( eg.getDelay(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getDelay(ccArray, 127) == 1.0f ); - REQUIRE( eg.getDelay(ccArray, 0) == 2.27f ); + REQUIRE( eg.getDelay(state, 0) == 1.0f ); + REQUIRE( eg.getDelay(state, 127) == 0.0f ); + state.ccEvent(0, 63, 127); + REQUIRE( eg.getDelay(state, 127) == 1.0f ); + REQUIRE( eg.getDelay(state, 0) == 2.27f ); eg.ccDelay = { 63, 127.0f }; - REQUIRE( eg.getDelay(ccArray, 0) == 100.0f ); + REQUIRE( eg.getDelay(state, 0) == 100.0f ); } TEST_CASE("[EGDescription] Decay range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.decay = 1.0f; eg.vel2decay = -1.27f; eg.ccDecay = { 63, 1.27f }; - REQUIRE( eg.getDecay(ccArray, 0) == 1.0f ); - REQUIRE( eg.getDecay(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getDecay(ccArray, 127) == 1.0f ); - REQUIRE( eg.getDecay(ccArray, 0) == 2.27f ); + REQUIRE( eg.getDecay(state, 0) == 1.0f ); + REQUIRE( eg.getDecay(state, 127) == 0.0f ); + state.ccEvent(0, 63, 127); + REQUIRE( eg.getDecay(state, 127) == 1.0f ); + REQUIRE( eg.getDecay(state, 0) == 2.27f ); eg.ccDecay = { 63, 127.0f }; - REQUIRE( eg.getDecay(ccArray, 0) == 100.0f ); + REQUIRE( eg.getDecay(state, 0) == 100.0f ); } TEST_CASE("[EGDescription] Release range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.release = 1; eg.vel2release = -1.27f; eg.ccRelease = { 63, 1.27f }; - REQUIRE( eg.getRelease(ccArray, 0) == 1.0f ); - REQUIRE( eg.getRelease(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getRelease(ccArray, 127) == 1.0f ); - REQUIRE( eg.getRelease(ccArray, 0) == 2.27f ); + REQUIRE( eg.getRelease(state, 0) == 1.0f ); + REQUIRE( eg.getRelease(state, 127) == 0.0f ); + state.ccEvent(0, 63, 127); + REQUIRE( eg.getRelease(state, 127) == 1.0f ); + REQUIRE( eg.getRelease(state, 0) == 2.27f ); eg.ccRelease = { 63, 127.0f }; - REQUIRE( eg.getRelease(ccArray, 0) == 100.0f ); + REQUIRE( eg.getRelease(state, 0) == 100.0f ); } TEST_CASE("[EGDescription] Hold range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.hold = 1; eg.vel2hold = -1.27f; eg.ccHold = { 63, 1.27f }; - REQUIRE( eg.getHold(ccArray, 0) == 1.0f ); - REQUIRE( eg.getHold(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getHold(ccArray, 127) == 1.0f ); - REQUIRE( eg.getHold(ccArray, 0) == 2.27f ); + REQUIRE( eg.getHold(state, 0) == 1.0f ); + REQUIRE( eg.getHold(state, 127) == 0.0f ); + state.ccEvent(0, 63, 127); + REQUIRE( eg.getHold(state, 127) == 1.0f ); + REQUIRE( eg.getHold(state, 0) == 2.27f ); eg.ccHold = { 63, 127.0f }; - REQUIRE( eg.getHold(ccArray, 0) == 100.0f ); + REQUIRE( eg.getHold(state, 0) == 100.0f ); } TEST_CASE("[EGDescription] Sustain level") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.sustain = 50; eg.vel2sustain = -100; eg.ccSustain = { 63, 100.0f }; - REQUIRE( eg.getSustain(ccArray, 0) == 50.0f ); - REQUIRE( eg.getSustain(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getSustain(ccArray, 127) == 50.0f ); + REQUIRE( eg.getSustain(state, 0) == 50.0f ); + REQUIRE( eg.getSustain(state, 127) == 0.0f ); + state.ccEvent(0, 63, 127); + REQUIRE( eg.getSustain(state, 127) == 50.0f ); eg.ccSustain = { 63, 200.0f }; - REQUIRE( eg.getSustain(ccArray, 0) == 100.0f ); + REQUIRE( eg.getSustain(state, 0) == 100.0f ); } TEST_CASE("[EGDescription] Start level") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.start = 0; eg.ccStart = { 63, 127.0f }; - REQUIRE( eg.getStart(ccArray, 0) == 0.0f ); - REQUIRE( eg.getStart(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getStart(ccArray, 0) == 100.0f ); + REQUIRE( eg.getStart(state, 0) == 0.0f ); + REQUIRE( eg.getStart(state, 127) == 0.0f ); + state.ccEvent(0, 63, 127); + REQUIRE( eg.getStart(state, 0) == 100.0f ); eg.ccStart = { 63, -127.0f }; - REQUIRE( eg.getStart(ccArray, 0) == 0.0f ); + REQUIRE( eg.getStart(state, 0) == 0.0f ); } diff --git a/tests/MidiStateT.cpp b/tests/MidiStateT.cpp index d06f2215..d4be5d21 100644 --- a/tests/MidiStateT.cpp +++ b/tests/MidiStateT.cpp @@ -18,21 +18,18 @@ using namespace Catch::literals; TEST_CASE("[MidiState] Initial values") { sfz::MidiState state; - for (auto& cc: state.getCCArray()) - REQUIRE( cc == 0 ); + for (unsigned cc = 0; cc < sfz::config::numCCs; cc++) + REQUIRE( state.getCCValue(cc) == 0 ); REQUIRE( state.getPitchBend() == 0 ); } TEST_CASE("[MidiState] Set and get CCs") { sfz::MidiState state; - const auto& cc = state.getCCArray(); state.ccEvent(0, 24, 23); state.ccEvent(0, 123, 124); REQUIRE(state.getCCValue(24) == 23); - REQUIRE(cc[24] == 23); REQUIRE(state.getCCValue(123) == 124); - REQUIRE(cc[123] == 124); } TEST_CASE("[MidiState] Set and get pitch bends") @@ -68,6 +65,5 @@ TEST_CASE("[MidiState] Set and get note velocities") TEST_CASE("[MidiState] Extended CCs") { sfz::MidiState state; - REQUIRE(state.getCCArray().size() >= 142); state.ccEvent(0, 142, 64); // should not trap } diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index eae54b0b..5c1ac179 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -166,13 +166,13 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_locc24", "20" }); region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.70711_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.86603_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); + midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); + midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); + midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 1.0_a ); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -184,13 +184,13 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.25_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.75_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); + midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.25_a ); + midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.75_a ); + midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 1.0_a ); } TEST_CASE("[Region] Crossfade out on CC") { @@ -200,13 +200,13 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_locc24", "20" }); region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.86603_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.70711_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); + midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); + midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); + midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 0.0_a ); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -218,13 +218,13 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.75_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.25_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); + midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.75_a ); + midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.25_a ); + midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 0.0_a ); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") From b5e578a7b8c4c50c9ac18047d87c597742a20ff8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 25 Mar 2020 22:20:37 +0100 Subject: [PATCH 03/19] Small map test --- benchmarks/BM_mapVsArray.cpp | 74 ++++++++++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 1 + 2 files changed, 75 insertions(+) create mode 100644 benchmarks/BM_mapVsArray.cpp diff --git a/benchmarks/BM_mapVsArray.cpp b/benchmarks/BM_mapVsArray.cpp new file mode 100644 index 00000000..59e62b27 --- /dev/null +++ b/benchmarks/BM_mapVsArray.cpp @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include +#include +#include +#include +#include "../src/sfizz/CCMap.h" +#include +#include + +constexpr int maxCC { 256 }; + +class MyFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) + { + std::random_device rd {}; + std::mt19937 gen { rd() }; + + std::array sourceCC; + absl::c_iota(sourceCC, 0); + absl::c_shuffle(sourceCC, gen); + ccs.resize(state.range(0)); + std::copy(sourceCC.begin(), sourceCC.begin() + state.range(0), ccs.begin()); + std::uniform_real_distribution distFloat { 0.1f, 1.0f }; + vector.resize(maxCC); + absl::c_generate(vector, [&]() { + return distFloat(gen); + }); + for (unsigned i = 0; i < state.range(0); ++i) { + map[ccs[i]] = vector[ccs[i]]; + } + } + + void TearDown(const ::benchmark::State& /* state */) + { + } + + std::vector ccs; + std::vector vector; + sfz::CCMap map { 1 }; +}; + +BENCHMARK_DEFINE_F(MyFixture, ArraySearch) +(benchmark::State& state) +{ + float value { 1 }; + for (auto _ : state) { + for (unsigned i = 0; i < state.range(0); ++i) { + value *= vector[ccs[i]]; + } + benchmark::DoNotOptimize(value); + } +} + +BENCHMARK_DEFINE_F(MyFixture, MapSearch) +(benchmark::State& state) +{ + float value { 0 }; + for (auto _ : state) { + for (unsigned i = 0; i < state.range(0); ++i) { + value *= map[ccs[i]]; + } + benchmark::DoNotOptimize(value); + } +} + +BENCHMARK_REGISTER_F(MyFixture, ArraySearch)->Range(4, maxCC); +BENCHMARK_REGISTER_F(MyFixture, MapSearch)->Range(4, maxCC); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 0aa090ae..fb666f0c 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -63,6 +63,7 @@ sfizz_add_benchmark(bm_interpolationCast BM_interpolationCast.cpp) sfizz_add_benchmark(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp) sfizz_add_benchmark(bm_maps BM_maps.cpp) target_link_libraries(bm_maps PRIVATE absl::flat_hash_map) +sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp) sfizz_add_benchmark(bm_logger BM_logger.cpp) target_link_libraries(bm_logger PRIVATE sfizz::sfizz) From e89d93cf56c24d0d44578678a3f1b5b748144b10 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 25 Mar 2020 23:23:40 +0100 Subject: [PATCH 04/19] Add literals for normalized 7 bit values --- src/sfizz/SfzHelpers.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index a8126858..2e904a5e 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -150,6 +150,18 @@ constexpr float normalizeBend(float bendValue) return min(max(bendValue, -8191.0f), 8191.0f) / 8191.0f; } +namespace literals +{ +inline float operator ""_norm(unsigned long long int value) +{ + if (value > 127) + value = 127; + + return normalize7Bits(value); +} +} + + /** * @brief Convert a note in string to its equivalent midi note number * From 2655418406f736220f44fd0388b48f8c1217ff1a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 25 Mar 2020 23:24:14 +0100 Subject: [PATCH 05/19] Add generic ranges for 7 bits and normalized values --- src/sfizz/Defaults.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 8daf2384..e735e9d3 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -53,6 +53,10 @@ namespace Default constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop }; constexpr Range loopRange { 0, std::numeric_limits::max() }; + // Global ranges + constexpr Range midi7Range { 0, 127 }; + constexpr Range normalizedRange { 0.0f, 1.0f }; + // Wavetable oscillator constexpr float oscillatorPhase { 0.0 }; constexpr Range oscillatorPhaseRange { -1.0, 360.0 }; @@ -97,7 +101,6 @@ namespace Default constexpr Range volumeCCRange { -144.0, 48.0 }; constexpr float amplitude { 100.0 }; constexpr Range amplitudeRange { 0.0, 100.0 }; - constexpr Range normalizedRange { 0.0, 1.0 }; constexpr float pan { 0.0 }; constexpr Range panRange { -100.0, 100.0 }; constexpr Range panCCRange { -200.0, 200.0 }; From 0e1e65135ee6aea4aaec3b3c64fd3511ec3e24b3 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 25 Mar 2020 23:25:32 +0100 Subject: [PATCH 06/19] Region and Synth use float velocities internally --- src/sfizz/Defaults.h | 6 +-- src/sfizz/Region.cpp | 87 ++++++++++++++++++++++++++++++++-------- src/sfizz/Region.h | 56 ++++++++++++++++++++++++-- src/sfizz/Synth.cpp | 94 ++++++++++++++++++++++---------------------- tests/FilesT.cpp | 14 ++++--- tests/RegionT.cpp | 52 ++++++++++++------------ 6 files changed, 206 insertions(+), 103 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index e735e9d3..b8921d99 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -68,7 +68,7 @@ namespace Default // Region logic: key mapping constexpr Range keyRange { 0, 127 }; - constexpr Range velocityRange { 0, 127 }; + constexpr Range velocityRange { 0.0f, 1.0f }; // Region logic: MIDI conditions constexpr Range channelRange { 1, 16 }; @@ -121,8 +121,8 @@ namespace Default constexpr Range ampRandomRange { 0.0, 24.0 }; constexpr Range crossfadeKeyInRange { 0, 0 }; constexpr Range crossfadeKeyOutRange { 127, 127 }; - constexpr Range crossfadeVelInRange { 0, 0 }; - constexpr Range crossfadeVelOutRange { 127, 127 }; + constexpr Range crossfadeVelInRange { 0.0f, 0.0f }; + constexpr Range crossfadeVelOutRange { 1.0f, 1.0f }; constexpr Range crossfadeCCInRange { 0, 0 }; constexpr Range crossfadeCCOutRange { 127, 127 }; constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 13df28d2..95151b47 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -14,6 +14,7 @@ #include "MidiState.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_cat.h" +#include "absl/algorithm/container.h" #include template @@ -138,10 +139,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange); break; case hash("lovel"): - setRangeStartFromOpcode(opcode, velocityRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + velocityRange.setStart(normalizeVelocity(*value)); break; case hash("hivel"): - setRangeEndFromOpcode(opcode, velocityRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + velocityRange.setEnd(normalizeVelocity(*value)); break; // Region logic: MIDI conditions @@ -303,8 +306,11 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("amp_velcurve_&"): { auto value = readOpcode(opcode.value, Default::ampVelcurveRange); + if (opcode.parameters.back() > 127) + return false; + if (value) - velocityPoints.emplace_back(opcode.parameters.back(), *value); + velocityPoints.emplace_back(normalizeVelocity(opcode.parameters.back()), *value); } break; case hash("xfin_lokey"): @@ -320,16 +326,20 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setRangeEndFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange); break; case hash("xfin_lovel"): - setRangeStartFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeVelInRange.setStart(normalizeVelocity(*value)); break; case hash("xfin_hivel"): - setRangeEndFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeVelInRange.setEnd(normalizeVelocity(*value)); break; case hash("xfout_lovel"): - setRangeStartFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeVelOutRange.setStart(normalizeVelocity(*value)); break; case hash("xfout_hivel"): - setRangeEndFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeVelOutRange.setEnd(normalizeVelocity(*value)); break; case hash("xf_keycurve"): switch (hash(opcode.value)) { @@ -773,6 +783,13 @@ bool sfz::Region::isSwitchedOn() const noexcept bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept { + return registerNoteOnNormalized(noteNumber, normalizeVelocity(velocity), randValue); +} + +bool sfz::Region::registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + if (keyswitchRange.containsWithEnd(noteNumber)) { if (keyswitch) { if (*keyswitch == noteNumber) @@ -825,6 +842,13 @@ bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randVal bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept { + return registerNoteOffNormalized(noteNumber, normalizeVelocity(velocity), randValue); +} + +bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + if (keyswitchRange.containsWithEnd(noteNumber)) { if (keyswitchDown && *keyswitchDown == noteNumber) keySwitched = false; @@ -893,11 +917,18 @@ void sfz::Region::registerTempo(float secondsPerQuarter) noexcept float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept { + return getBasePitchVariationNormalized(noteNumber, normalizeVelocity(velocity)); +} + +float sfz::Region::getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + std::uniform_int_distribution pitchDistribution { -pitchRandom, pitchRandom }; auto pitchVariationInCents = pitchKeytrack * (noteNumber - (int)pitchKeycenter); // note difference with pitch center pitchVariationInCents += tune; // sample tuning pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose - pitchVariationInCents += velocity / 127 * pitchVeltrack; // track velocity + pitchVariationInCents += velocity * pitchVeltrack; // track velocity pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes return centsFactor(pitchVariationInCents); } @@ -961,8 +992,13 @@ float crossfadeIn(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurv { if (value < crossfadeRange.getStart()) return 0.0f; + + const auto length = static_cast(crossfadeRange.length()); + if (length == 0.0f) + return 1.0f; + else if (value < crossfadeRange.getEnd()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / std::max(static_cast(crossfadeRange.length()), 1.0f); + const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; if (curve == SfzCrossfadeCurve::power) return sqrt(crossfadePosition); if (curve == SfzCrossfadeCurve::gain) @@ -977,8 +1013,13 @@ float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCur { if (value > crossfadeRange.getEnd()) return 0.0f; + + const auto length = static_cast(crossfadeRange.length()); + if (length == 0.0f) + return 1.0f; + else if (value > crossfadeRange.getStart()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / std::max(static_cast(crossfadeRange.length()), 1.0f); + const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; if (curve == SfzCrossfadeCurve::power) return std::sqrt(1 - crossfadePosition); if (curve == SfzCrossfadeCurve::gain) @@ -990,6 +1031,13 @@ float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCur float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept { + return getNoteGainNormalized(noteNumber, normalizeVelocity(velocity)); +} + +float sfz::Region::getNoteGainNormalized(int noteNumber, float velocity) const noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + float baseGain { 1.0f }; // Amplitude key tracking @@ -1000,7 +1048,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve); // Amplitude velocity tracking - baseGain *= velocityCurve(velocity); + baseGain *= velocityCurveNormalized(velocity); // Crossfades related to velocity baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve); @@ -1031,25 +1079,30 @@ float sfz::Region::getCrossfadeGain() const noexcept float sfz::Region::velocityCurve(uint8_t velocity) const noexcept { - float gain { 1.0f }; + return velocityCurveNormalized(normalizeVelocity(velocity)); +} +float sfz::Region::velocityCurveNormalized(float velocity) const noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + + float gain { 1.0f }; if (velocityPoints.size() > 0) { // Custom velocity curve - auto after = std::find_if(velocityPoints.begin(), velocityPoints.end(), [velocity](const std::pair& val) { return val.first >= velocity; }); + auto after = absl::c_find_if(velocityPoints, [velocity](const std::pair& val) { return val.first >= velocity; }); auto before = after == velocityPoints.begin() ? velocityPoints.begin() : after - 1; // Linear interpolation float relativePositionInSegment { - static_cast(velocity - before->first) / static_cast(after->first - before->first) + (velocity - before->first) / (after->first - before->first) }; float segmentEndpoints { after->second - before->second }; gain *= relativePositionInSegment * segmentEndpoints; } else { // Standard velocity curve - const float floatVelocity { static_cast(velocity) / 127.0f }; // FIXME: Maybe there's a prettier way to check the boundaries? const float gaindB = [&]() { if (ampVeltrack >= 0) - return floatVelocity == 0.0f ? -90.0f : 40 * std::log(floatVelocity) / std::log(10.0f); + return velocity == 0.0f ? -90.0f : 40 * std::log(velocity) / std::log(10.0f); else - return floatVelocity == 1.0f ? -90.0f : 40 * std::log(1 - floatVelocity) / std::log(10.0f); + return velocity == 1.0f ? -90.0f : 40 * std::log(1 - velocity) / std::log(10.0f); }(); gain *= db2mag( gaindB * std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd()); } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 47a6dfe6..7466507c 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -98,6 +98,30 @@ struct Region { * @return false */ bool registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept; + /** + * @brief Register a new note on event. The region may be switched on or off using keys so + * this function updates the keyswitches state. + * + * @param noteNumber + * @param velocity + * @param randValue a random value between 0 and 1 used to randomize a bit the region activations + * and vary the samples + * @return true if the region should trigger on this event. + * @return false + */ + bool registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept; + /** + * @brief Register a new note off event. The region may be switched on or off using keys so + * this function updates the keyswitches state. + * + * @param noteNumber + * @param velocity + * @param randValue a random value between 0 and 1 used to randomize a bit the region activations + * and vary the samples + * @return true if the region should trigger on this event. + * @return false + */ + bool registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept; /** * @brief Register a new CC event. The region may be switched on or off using CCs so * this function checks if it indeeds need to activate or not. @@ -136,6 +160,15 @@ struct Region { * @return float */ float getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept; + /** + * @brief Get the base pitch of the region depending on which note has been + * pressed and at which velocity. + * + * @param noteNumber + * @param velocity + * @return float + */ + float getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept; /** * @brief Get the note-related gain of the region depending on which note has been * pressed and at which velocity. @@ -145,6 +178,15 @@ struct Region { * @return float */ float getNoteGain(int noteNumber, uint8_t velocity) const noexcept; + /** + * @brief Get the note-related gain of the region depending on which note has been + * pressed and at which velocity. + * + * @param noteNumber + * @param velocity + * @return float + */ + float getNoteGainNormalized(int noteNumber, float velocity) const noexcept; /** * @brief Get the additional crossfade gain of the region depending on the * CC values @@ -179,6 +221,12 @@ struct Region { * @return float */ float velocityCurve(uint8_t velocity) const noexcept; + /** + * @brief Computes the gain value related to the velocity of the note + * + * @return float + */ + float velocityCurveNormalized(float velocity) const noexcept; /** * @brief Get the region offset in samples * @@ -243,7 +291,7 @@ struct Region { // Region logic: key mapping Range keyRange { Default::keyRange }; //lokey, hikey and key - Range velocityRange { Default::velocityRange }; // hivel and lovel + Range velocityRange { Default::velocityRange }; // hivel and lovel // Region logic: MIDI conditions Range bendRange { Default::bendRange }; // hibend and lobend @@ -282,12 +330,12 @@ struct Region { uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter float ampKeytrack { Default::ampKeytrack }; // amp_keytrack float ampVeltrack { Default::ampVeltrack }; // amp_keytrack - std::vector> velocityPoints; // amp_velcurve_N + std::vector> velocityPoints; // amp_velcurve_N float ampRandom { Default::ampRandom }; // amp_random Range crossfadeKeyInRange { Default::crossfadeKeyInRange }; Range crossfadeKeyOutRange { Default::crossfadeKeyOutRange }; - Range crossfadeVelInRange { Default::crossfadeVelInRange }; - Range crossfadeVelOutRange { Default::crossfadeVelOutRange }; + Range crossfadeVelInRange { Default::crossfadeVelInRange }; + Range crossfadeVelOutRange { Default::crossfadeVelOutRange }; SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve }; SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve }; SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve }; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index fd36c0e0..e6d4f294 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -44,7 +44,7 @@ sfz::Synth::~Synth() std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - for (auto& voice: voices) + for (auto& voice : voices) voice->reset(); resources.filePool.emptyFileLoadingQueues(); @@ -129,11 +129,11 @@ void sfz::Synth::clear() std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - for (auto &voice: voices) + for (auto& voice : voices) voice->reset(); - for (auto& list: noteActivationLists) + for (auto& list : noteActivationLists) list.clear(); - for (auto& list: ccActivationLists) + for (auto& list : ccActivationLists) list.clear(); regions.clear(); effectBuses.clear(); @@ -275,17 +275,19 @@ void sfz::Synth::handleEffectOpcodes(const std::vector& members) void addEndpointsToVelocityCurve(sfz::Region& region) { if (region.velocityPoints.size() > 0) { - absl::c_sort(region.velocityPoints, [](const std::pair& lhs, const std::pair& rhs) { return lhs.first < rhs.first; }); + const auto velocityStart = sfz::Default::velocityRange.getStart(); + const auto velocityEnd = sfz::Default::velocityRange.getEnd(); + absl::c_sort(region.velocityPoints, [](const std::pair& lhs, const std::pair& rhs) { return lhs.first < rhs.first; }); if (region.ampVeltrack > 0) { - if (region.velocityPoints.back().first != sfz::Default::velocityRange.getEnd()) - region.velocityPoints.push_back(std::make_pair(127, 1.0f)); - if (region.velocityPoints.front().first != sfz::Default::velocityRange.getStart()) - region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(0, 0.0f)); + if (region.velocityPoints.front().first != velocityStart) + region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityStart, velocityStart)); + if (region.velocityPoints.back().first != velocityEnd) + region.velocityPoints.push_back(std::make_pair(velocityEnd, velocityEnd)); } else { - if (region.velocityPoints.front().first != sfz::Default::velocityRange.getEnd()) - region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(127, 0.0f)); - if (region.velocityPoints.back().first != sfz::Default::velocityRange.getStart()) - region.velocityPoints.push_back(std::make_pair(0, 1.0f)); + if (region.velocityPoints.front().first != velocityEnd) + region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityEnd, velocityStart)); + if (region.velocityPoints.back().first != velocityStart) + region.velocityPoints.push_back(std::make_pair(velocityStart, velocityEnd)); } } } @@ -341,8 +343,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) if (region->loopRange.getEnd() == Default::loopRange.getEnd()) region->loopRange.setEnd(region->sampleEnd); - if (fileInformation->loopBegin != Default::loopRange.getStart() && - fileInformation->loopEnd != Default::loopRange.getEnd()) { + if (fileInformation->loopBegin != Default::loopRange.getStart() && fileInformation->loopEnd != Default::loopRange.getEnd()) { if (region->loopRange.getStart() == Default::loopRange.getStart()) region->loopRange.setStart(fileInformation->loopBegin); @@ -374,8 +375,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) } for (auto note = 0; note < 128; note++) { - if (region->keyRange.containsWithEnd(note) || - (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note))) + if (region->keyRange.containsWithEnd(note) || (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note))) noteActivationLists[note].push_back(region); } @@ -396,16 +396,13 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) // Set the default frequencies on equalizers if needed if (region->equalizers.size() > 0 - && region->equalizers[0].frequency == Default::eqFrequencyUnset) - { + && region->equalizers[0].frequency == Default::eqFrequencyUnset) { region->equalizers[0].frequency = Default::eqFrequency1; if (region->equalizers.size() > 1 - && region->equalizers[1].frequency == Default::eqFrequencyUnset) - { + && region->equalizers[1].frequency == Default::eqFrequencyUnset) { region->equalizers[1].frequency = Default::eqFrequency2; if (region->equalizers.size() > 2 - && region->equalizers[2].frequency == Default::eqFrequencyUnset) - { + && region->equalizers[2].frequency == Default::eqFrequencyUnset) { region->equalizers[2].frequency = Default::eqFrequency3; } } @@ -425,7 +422,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) regions.resize(remainingRegions); modificationTime = checkModificationTime(); - for (auto& voice: voices) { + for (auto& voice : voices) { voice->setMaxFiltersPerVoice(maxFilters); voice->setMaxEQsPerVoice(maxEQs); } @@ -468,7 +465,6 @@ int sfz::Synth::getNumActiveVoices() const noexcept void sfz::Synth::garbageCollect() noexcept { - } void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept @@ -487,7 +483,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept for (auto& voice : voices) voice->setSamplesPerBlock(samplesPerBlock); - for (auto& bus: effectBuses) { + for (auto& bus : effectBuses) { if (bus) bus->setSamplesPerBlock(samplesPerBlock); } @@ -507,7 +503,7 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept resources.filterPool.setSampleRate(sampleRate); resources.eqPool.setSampleRate(sampleRate); - for (auto& bus: effectBuses) { + for (auto& bus : effectBuses) { if (bus) bus->setSampleRate(sampleRate); } @@ -532,7 +528,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept { // Prepare the effect inputs. They are mixes of per-region outputs. ScopedTiming logger { callbackBreakdown.effects }; - for (auto& bus: effectBuses) { + for (auto& bus : effectBuses) { if (bus) bus->clearInputs(numFrames); } @@ -576,7 +572,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept // without any , the signal is just going to flow through it. ScopedTiming logger { callbackBreakdown.effects, ScopedTiming::Operation::addToDuration }; - for (auto& bus: effectBuses) { + for (auto& bus : effectBuses) { if (bus) { bus->process(numFrames); bus->mixOutputsTo(buffer, tempMixNode, numFrames); @@ -711,11 +707,11 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.pitchBendEvent(delay, pitch); - for (auto& region: regions) { + for (auto& region : regions) { region->registerPitchWheel(pitch); } - for (auto& voice: voices) { + for (auto& voice : voices) { voice->registerPitchWheel(delay, pitch); } } @@ -753,10 +749,9 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const if (model.empty()) model = config::midnamModel; - doc.append_child(pugi::node_doctype).set_value( - "MIDINameDocument PUBLIC" - " \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\"" - " \"http://www.midi.org/dtds/MIDINameDocument10.dtd\""); + doc.append_child(pugi::node_doctype).set_value("MIDINameDocument PUBLIC" + " \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\"" + " \"http://www.midi.org/dtds/MIDINameDocument10.dtd\""); pugi::xml_node root = doc.append_child("MIDINameDocument"); @@ -767,9 +762,11 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const pugi::xml_node device = root.append_child("MasterDeviceNames"); device.append_child("Manufacturer") - .append_child(pugi::node_pcdata).set_value(std::string(manufacturer).c_str()); + .append_child(pugi::node_pcdata) + .set_value(std::string(manufacturer).c_str()); device.append_child("Model") - .append_child(pugi::node_pcdata).set_value(std::string(model).c_str()); + .append_child(pugi::node_pcdata) + .set_value(std::string(model).c_str()); { pugi::xml_node devmode = device.append_child("CustomDeviceMode"); @@ -795,7 +792,8 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const } chns.append_child("UsesControlNameList") - .append_attribute("Name").set_value("Controls"); + .append_attribute("Name") + .set_value("Controls"); } { @@ -876,7 +874,7 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept void sfz::Synth::resetVoices(int numVoices) { - AtomicDisabler callbackDisabler{ canEnterCallback }; + AtomicDisabler callbackDisabler { canEnterCallback }; while (inCallback) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -885,7 +883,7 @@ void sfz::Synth::resetVoices(int numVoices) for (int i = 0; i < numVoices; ++i) voices.push_back(absl::make_unique(resources)); - for (auto& voice: voices) { + for (auto& voice : voices) { voice->setSampleRate(this->sampleRate); voice->setSamplesPerBlock(this->samplesPerBlock); } @@ -896,12 +894,12 @@ void sfz::Synth::resetVoices(int numVoices) void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept { - AtomicDisabler callbackDisabler{ canEnterCallback }; + AtomicDisabler callbackDisabler { canEnterCallback }; while (inCallback) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - for (auto& voice: voices) + for (auto& voice : voices) voice->reset(); resources.filePool.emptyFileLoadingQueues(); @@ -916,7 +914,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept { - AtomicDisabler callbackDisabler{ canEnterCallback }; + AtomicDisabler callbackDisabler { canEnterCallback }; while (inCallback) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -951,13 +949,13 @@ void sfz::Synth::resetAllControllers(int delay) noexcept return; resources.midiState.resetAllControllers(delay); - for (auto& voice: voices) { + for (auto& voice : voices) { voice->registerPitchWheel(delay, 0); for (unsigned cc = 0; cc < config::numCCs; ++cc) voice->registerCC(delay, cc, 0); } - for (auto& region: regions) { + for (auto& region : regions) { for (unsigned cc = 0; cc < config::numCCs; ++cc) region->registerCC(cc, 0); } @@ -996,13 +994,13 @@ void sfz::Synth::disableLogging() noexcept void sfz::Synth::allSoundOff() noexcept { - AtomicDisabler callbackDisabler{ canEnterCallback }; + AtomicDisabler callbackDisabler { canEnterCallback }; while (inCallback) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - for (auto &voice: voices) + for (auto& voice : voices) voice->reset(); - for (auto& effectBus: effectBuses) + for (auto& effectBus : effectBuses) effectBus->clear(); } diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 6284ec6c..c58976a5 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -5,9 +5,11 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Synth.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" #include "ghc/fs_std.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("[Files] Single region (regions_one.sfz)") { @@ -137,11 +139,11 @@ TEST_CASE("[Files] Group from AVL") REQUIRE(synth.getRegionView(i)->volume == 6.0f); REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range(36, 36)); } - REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range(1, 26)); - REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range(27, 52)); - REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range(53, 77)); - REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range(78, 102)); - REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range(103, 127)); + REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range(1_norm, 26_norm)); + REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range(27_norm, 52_norm)); + REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range(53_norm, 77_norm)); + REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range(78_norm, 102_norm)); + REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range(103_norm, 127_norm)); } TEST_CASE("[Files] Full hierarchy") @@ -232,7 +234,7 @@ TEST_CASE("[Files] Pizz basic") REQUIRE(synth.getNumRegions() == 4); for (int i = 0; i < synth.getNumRegions(); ++i) { REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range(12, 22)); - REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range(97, 127)); + REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range(97_norm, 127_norm)); REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21); REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range(0, 13)); } diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index f65ad406..35af3f8c 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -6,8 +6,10 @@ #include "sfizz/MidiState.h" #include "sfizz/Region.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("[Region] Parsing opcodes") { @@ -204,19 +206,19 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("lovel, hivel") { - REQUIRE(region.velocityRange == sfz::Range(0, 127)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "lovel", "37" }); - REQUIRE(region.velocityRange == sfz::Range(37, 127)); + REQUIRE(region.velocityRange == sfz::Range(37_norm, 127_norm)); region.parseOpcode({ "lovel", "128" }); - REQUIRE(region.velocityRange == sfz::Range(127, 127)); + REQUIRE(region.velocityRange == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "lovel", "-3" }); - REQUIRE(region.velocityRange == sfz::Range(0, 127)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "hivel", "65" }); - REQUIRE(region.velocityRange == sfz::Range(0, 65)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 65_norm)); region.parseOpcode({ "hivel", "-1" }); - REQUIRE(region.velocityRange == sfz::Range(0, 0)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "hivel", "128" }); - REQUIRE(region.velocityRange == sfz::Range(0, 127)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); } SECTION("lobend, hibend") @@ -563,9 +565,9 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("amp_velcurve") { region.parseOpcode({ "amp_velcurve_6", "0.4" }); - REQUIRE(region.velocityPoints.back() == std::make_pair(6, 0.4f)); + REQUIRE(region.velocityPoints.back() == std::make_pair(6_norm, 0.4f)); region.parseOpcode({ "amp_velcurve_127", "-1.0" }); - REQUIRE(region.velocityPoints.back() == std::make_pair(127, 0.0f)); + REQUIRE(region.velocityPoints.back() == std::make_pair(127_norm, 0.0f)); } SECTION("xfin_lokey, xfin_hikey") @@ -589,21 +591,21 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("xfin_lovel, xfin_hivel") { - REQUIRE(region.crossfadeVelInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_lovel", "4" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(4, 4)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(4_norm, 4_norm)); region.parseOpcode({ "xfin_lovel", "128" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfin_lovel", "59" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hivel", "59" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59, 59)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfin_hivel", "128" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hivel", "0" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_hivel", "-1" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); } SECTION("xfout_lokey, xfout_hikey") @@ -627,21 +629,21 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("xfout_lovel, xfout_hivel") { - REQUIRE(region.crossfadeVelOutRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "4" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(4, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(4_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "128" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "59" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hivel", "59" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59, 59)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfout_hivel", "128" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hivel", "0" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfout_hivel", "-1" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(0_norm, 0_norm)); } SECTION("xfin_locc, xfin_hicc") From 601126db0d741372ad08a2ad1463d1d670c5a529 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 25 Mar 2020 23:48:17 +0100 Subject: [PATCH 07/19] Normalize CCs internally in Synth and region --- src/sfizz/Defaults.h | 11 +++++----- src/sfizz/Region.cpp | 49 +++++++++++++++++++++++++++++++++++--------- src/sfizz/Region.h | 18 ++++++++++++---- src/sfizz/Synth.cpp | 2 +- tests/FilesT.cpp | 2 +- tests/RegionT.cpp | 44 +++++++++++++++++++-------------------- 6 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index b8921d99..9e6f0d84 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -68,14 +68,13 @@ namespace Default // Region logic: key mapping constexpr Range keyRange { 0, 127 }; - constexpr Range velocityRange { 0.0f, 1.0f }; + constexpr auto velocityRange = normalizedRange; // Region logic: MIDI conditions constexpr Range channelRange { 1, 16 }; constexpr Range midiChannelRange { 0, 15 }; constexpr Range ccNumberRange { 0, config::numCCs }; - constexpr Range ccValueRange { 0, 127 }; - constexpr uint8_t cc { 0 }; + constexpr auto ccValueRange = normalizedRange; constexpr Range bendRange { -8192, 8192 }; constexpr int bend { 0 }; constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current }; @@ -92,7 +91,7 @@ namespace Default // Region logic: Triggers constexpr SfzTrigger trigger { SfzTrigger::attack }; - constexpr Range ccTriggerValueRange{ 0, 127 }; + constexpr Range ccTriggerValueRange = normalizedRange; // Performance parameters: amplifier constexpr float globalVolume { -7.35f }; @@ -123,8 +122,8 @@ namespace Default constexpr Range crossfadeKeyOutRange { 127, 127 }; constexpr Range crossfadeVelInRange { 0.0f, 0.0f }; constexpr Range crossfadeVelOutRange { 1.0f, 1.0f }; - constexpr Range crossfadeCCInRange { 0, 0 }; - constexpr Range crossfadeCCOutRange { 127, 127 }; + constexpr Range crossfadeCCInRange { 0.0f, 0.0f }; + constexpr Range crossfadeCCOutRange { 1.0f, 1.0f }; constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 95151b47..61082200 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -155,10 +155,16 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setRangeEndFromOpcode(opcode, bendRange, Default::bendRange); break; case hash("locc&"): - setRangeStartFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccConditions[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("hicc&"): - setRangeEndFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("sw_lokey"): setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange); @@ -250,11 +256,17 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; case hash("on_locc&"): // fallthrough case hash("start_locc&"): - setRangeStartFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("on_hicc&"): // fallthrough case hash("start_hicc&"): - setRangeEndFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; // Performance parameters: amplifier @@ -366,16 +378,28 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) } break; case hash("xfin_locc&"): - setRangeStartFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCInRange[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("xfin_hicc&"): - setRangeEndFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCInRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("xfout_locc&"): - setRangeStartFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCOutRange[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("xfout_hicc&"): - setRangeEndFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCOutRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("xf_cccurve"): switch (hash(opcode.value)) { @@ -872,6 +896,11 @@ bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, floa } bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept +{ + return registerCCNormalized(ccNumber, normalizeCC(ccValue)); +} + +bool sfz::Region::registerCCNormalized(int ccNumber, float ccValue) noexcept { if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue)) ccSwitched.set(ccNumber, true); @@ -1063,13 +1092,13 @@ float sfz::Region::getCrossfadeGain() const noexcept // Crossfades due to CC states for (const auto& valuePair : crossfadeCCInRange) { - const auto ccValue = midiState.getCCValue(valuePair.cc); + const auto ccValue = midiState.getCCValueNormalized(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve); } for (const auto& valuePair : crossfadeCCOutRange) { - const auto ccValue = midiState.getCCValue(valuePair.cc); + const auto ccValue = midiState.getCCValueNormalized(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 7466507c..b97e90c0 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -132,6 +132,16 @@ struct Region { * @return false */ bool registerCC(int ccNumber, uint8_t ccValue) noexcept; + /** + * @brief Register a new CC event. The region may be switched on or off using CCs so + * this function checks if it indeeds need to activate or not. + * + * @param ccNumber + * @param ccValue + * @return true if the region should trigger on this event + * @return false + */ + bool registerCCNormalized(int ccNumber, float ccValue) noexcept; /** * @brief Register a new pitch wheel event. * @@ -295,7 +305,7 @@ struct Region { // Region logic: MIDI conditions Range bendRange { Default::bendRange }; // hibend and lobend - CCMap> ccConditions { Default::ccValueRange }; + CCMap> ccConditions { Default::ccValueRange }; Range keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey absl::optional keyswitch {}; // sw_last absl::optional keyswitchUp {}; // sw_up @@ -314,7 +324,7 @@ struct Region { // Region logic: triggers SfzTrigger trigger { Default::trigger }; // trigger - CCMap> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN + CCMap> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN // Performance parameters: amplifier float volume { Default::volume }; // volume @@ -339,8 +349,8 @@ struct Region { SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve }; SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve }; SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve }; - CCMap> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN - CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN + CCMap> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN + CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN float rtDecay { Default::rtDecay }; // rt_decay // Filters and EQs diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index e6d4f294..7bec6cbf 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -181,7 +181,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) case hash("Set_cc&"): // fallthrough case hash("set_cc&"): if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) { - const auto ccValue = readOpcode(member.value, Default::ccValueRange).value_or(0); + const auto ccValue = readOpcode(member.value, Default::midi7Range).value_or(0); resources.midiState.ccEvent(0, member.parameters.back(), ccValue); } break; diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index c58976a5..7bc99da4 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -236,7 +236,7 @@ TEST_CASE("[Files] Pizz basic") REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range(12, 22)); REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range(97_norm, 127_norm)); REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21); - REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range(0, 13)); + REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range(0_norm, 13_norm)); } REQUIRE(synth.getRegionView(0)->randRange == sfz::Range(0, 0.25)); REQUIRE(synth.getRegionView(1)->randRange == sfz::Range(0.25, 0.5)); diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 35af3f8c..c5338b49 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -240,16 +240,16 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("locc, hicc") { - REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range(0, 127)); - REQUIRE(region.ccConditions[127] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.ccConditions[127] == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "locc6", "4" }); - REQUIRE(region.ccConditions[6] == sfz::Range(4, 127)); + REQUIRE(region.ccConditions[6] == sfz::Range(4_norm, 127_norm)); region.parseOpcode({ "locc12", "-128" }); - REQUIRE(region.ccConditions[12] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions[12] == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "hicc65", "39" }); - REQUIRE(region.ccConditions[65] == sfz::Range(0, 39)); + REQUIRE(region.ccConditions[65] == sfz::Range(0_norm, 39_norm)); region.parseOpcode({ "hicc127", "135" }); - REQUIRE(region.ccConditions[127] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions[127] == sfz::Range(0_norm, 127_norm)); } SECTION("sw_lokey, sw_hikey") @@ -427,10 +427,10 @@ TEST_CASE("[Region] Parsing opcodes") } region.parseOpcode({ "on_locc45", "15" }); REQUIRE(region.ccTriggers.contains(45)); - REQUIRE(region.ccTriggers[45] == sfz::Range(15, 127)); + REQUIRE(region.ccTriggers[45] == sfz::Range(15_norm, 127_norm)); region.parseOpcode({ "on_hicc4", "47" }); REQUIRE(region.ccTriggers.contains(45)); - REQUIRE(region.ccTriggers[4] == sfz::Range(0, 47)); + REQUIRE(region.ccTriggers[4] == sfz::Range(0_norm, 47_norm)); } SECTION("volume") @@ -650,38 +650,38 @@ TEST_CASE("[Region] Parsing opcodes") { REQUIRE(!region.crossfadeCCInRange.contains(4)); region.parseOpcode({ "xfin_locc4", "4" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(4, 4)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(4_norm, 4_norm)); region.parseOpcode({ "xfin_locc4", "128" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(127, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfin_locc4", "59" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hicc4", "59" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 59)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfin_hicc4", "128" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hicc4", "0" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_hicc4", "-1" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0_norm, 0_norm)); } SECTION("xfout_locc, xfout_hicc") { REQUIRE(!region.crossfadeCCOutRange.contains(4)); region.parseOpcode({ "xfout_locc4", "4" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(4, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(4_norm, 127_norm)); region.parseOpcode({ "xfout_locc4", "128" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(127, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_locc4", "59" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hicc4", "59" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 59)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfout_hicc4", "128" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hicc4", "0" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfout_hicc4", "-1" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0_norm, 0_norm)); } SECTION("xf_keycurve") From e7686d233e9895a0fd54ea9788208c35776511cc Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:12:30 +0100 Subject: [PATCH 08/19] Use float internally in the voices --- src/sfizz/Config.h | 2 +- src/sfizz/Region.cpp | 1 + src/sfizz/SIMDHelpers.h | 2 +- src/sfizz/Synth.cpp | 27 +++++++++++++------------- src/sfizz/Synth.h | 4 ++-- src/sfizz/Voice.cpp | 43 +++++++++++++++++++++-------------------- src/sfizz/Voice.h | 12 ++++++------ 7 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index eda45b81..0af2263f 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -43,7 +43,7 @@ namespace config { constexpr int allNotesOffCC { 123 }; constexpr int omniOffCC { 124 }; constexpr int omniOnCC { 125 }; - constexpr int halfCCThreshold { 64 }; + constexpr float halfCCThreshold { 0.5f }; constexpr int centPerSemitone { 100 }; constexpr float virtuallyZero { 0.00005f }; constexpr float fastReleaseDuration { 0.01f }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 61082200..3d1304a0 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -902,6 +902,7 @@ bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept bool sfz::Region::registerCCNormalized(int ccNumber, float ccValue) noexcept { + ASSERT(ccValue >= 0.0f && ccValue <= 1.0f); if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue)) ccSwitched.set(ccNumber, true); else diff --git a/src/sfizz/SIMDHelpers.h b/src/sfizz/SIMDHelpers.h index e8134809..2e217c3a 100644 --- a/src/sfizz/SIMDHelpers.h +++ b/src/sfizz/SIMDHelpers.h @@ -523,7 +523,7 @@ void multiplyAdd(absl::Span gain, absl::Span void multiplyAdd(const T gain, absl::Span input, absl::Span output) noexcept { - ASSERT(input.size() <= output.size()); + // ASSERT(input.size() <= output.size()); auto* in = input.begin(); auto* out = output.begin(); auto* sentinel = out + std::min(output.size(), input.size()); diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 7bec6cbf..9724ce20 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -600,15 +600,15 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept { ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); - + const auto normalizedVelocity = normalizeVelocity(velocity); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.noteOnEvent(delay, noteNumber, velocity); + resources.midiState.noteOnEventNormalized(delay, noteNumber, normalizedVelocity); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) return; - noteOnDispatch(delay, noteNumber, velocity); + noteOnDispatch(delay, noteNumber, normalizedVelocity); } void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept @@ -627,7 +627,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept // FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a // way in sfz to specify that a release trigger should NOT use the note-on velocity? // auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity); - const auto replacedVelocity = resources.midiState.getNoteVelocity(noteNumber); + const auto replacedVelocity = resources.midiState.getNoteVelocityNormalized(noteNumber); for (auto& voice : voices) voice->registerNoteOff(delay, noteNumber, replacedVelocity); @@ -635,11 +635,11 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept noteOffDispatch(delay, noteNumber, replacedVelocity); } -void sfz::Synth::noteOffDispatch(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Synth::noteOffDispatch(int delay, int noteNumber, float velocity) noexcept { const auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { - if (region->registerNoteOff(noteNumber, velocity, randValue)) { + if (region->registerNoteOffNormalized(noteNumber, velocity, randValue)) { auto voice = findFreeVoice(); if (voice == nullptr) continue; @@ -649,11 +649,11 @@ void sfz::Synth::noteOffDispatch(int delay, int noteNumber, uint8_t velocity) no } } -void sfz::Synth::noteOnDispatch(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexcept { const auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { - if (region->registerNoteOn(noteNumber, velocity, randValue)) { + if (region->registerNoteOnNormalized(noteNumber, velocity, randValue)) { for (auto& voice : voices) { if (voice->checkOffGroup(delay, region->group)) noteOffDispatch(delay, voice->getTriggerNumber(), voice->getTriggerValue()); @@ -672,9 +672,10 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept { ASSERT(ccNumber < config::numCCs); ASSERT(ccNumber >= 0); + const auto normalizedCC = normalizeCC(ccValue); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.ccEvent(delay, ccNumber, ccValue); + resources.midiState.ccEventNormalized(delay, ccNumber, normalizedCC); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) @@ -686,7 +687,7 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept } for (auto& voice : voices) - voice->registerCC(delay, ccNumber, ccValue); + voice->registerCC(delay, ccNumber, normalizedCC); for (auto& region : ccActivationLists[ccNumber]) { if (region->registerCC(ccNumber, ccValue)) { @@ -694,7 +695,7 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept if (voice == nullptr) continue; - voice->startVoice(region, delay, ccNumber, ccValue, Voice::TriggerType::CC); + voice->startVoice(region, delay, ccNumber, normalizedCC, Voice::TriggerType::CC); } } } @@ -952,12 +953,12 @@ void sfz::Synth::resetAllControllers(int delay) noexcept for (auto& voice : voices) { voice->registerPitchWheel(delay, 0); for (unsigned cc = 0; cc < config::numCCs; ++cc) - voice->registerCC(delay, cc, 0); + voice->registerCC(delay, cc, 0.0f); } for (auto& region : regions) { for (unsigned cc = 0; cc < config::numCCs; ++cc) - region->registerCC(cc, 0); + region->registerCCNormalized(cc, 0.0f); } } diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index f42d67e3..b18a453b 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -463,8 +463,8 @@ private: fs::file_time_type checkModificationTime(); - void noteOnDispatch(int delay, int noteNumber, uint8_t velocity) noexcept; - void noteOffDispatch(int delay, int noteNumber, uint8_t velocity) noexcept; + void noteOnDispatch(int delay, int noteNumber, float velocity) noexcept; + void noteOffDispatch(int delay, int noteNumber, float velocity) noexcept; // Opcode memory; these are used to build regions, as a new region // will integrate opcodes from the group, master and global block diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 71a06159..f0ffddfe 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -24,8 +24,10 @@ sfz::Voice::Voice(sfz::Resources& resources) waveOscillator.init(sampleRate); } -void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value, sfz::Voice::TriggerType triggerType) noexcept +void sfz::Voice::startVoice(Region* region, int delay, int number, float value, sfz::Voice::TriggerType triggerType) noexcept { + ASSERT(value >= 0.0f && value <= 1.0f); + this->triggerType = triggerType; triggerNumber = number; triggerValue = value; @@ -71,22 +73,21 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value } speedRatio = static_cast(currentPromise->sampleRate / this->sampleRate); } - - pitchRatio = region->getBasePitchVariation(number, value); + pitchRatio = region->getBasePitchVariationNormalized(number, value); baseVolumedB = region->getBaseVolumedB(number); auto volumedB = baseVolumedB; if (region->volumeCC) - volumedB += normalizeCC(resources.midiState.getCCValue(region->volumeCC->cc)) * region->volumeCC->value; + volumedB += resources.midiState.getCCValueNormalized(region->volumeCC->cc) * region->volumeCC->value; volumeEnvelope.reset(db2mag(Default::volumeRange.clamp(volumedB))); baseGain = region->getBaseGain(); if (triggerType != TriggerType::CC) - baseGain *= region->getNoteGain(number, value); + baseGain *= region->getNoteGainNormalized(number, value); float gain { baseGain }; if (region->amplitudeCC) - gain += normalizeCC(resources.midiState.getCCValue(region->amplitudeCC->cc)) * normalizePercents(region->amplitudeCC->value); + gain += resources.midiState.getCCValueNormalized(region->amplitudeCC->cc) * normalizePercents(region->amplitudeCC->value); amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain)); float crossfadeGain { region->getCrossfadeGain() }; @@ -95,19 +96,19 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value basePan = normalizePercents(region->pan); auto pan = basePan; if (region->panCC) - pan += normalizeCC(resources.midiState.getCCValue(region->panCC->cc)) * normalizePercents(region->panCC->value); + pan += resources.midiState.getCCValueNormalized(region->panCC->cc) * normalizePercents(region->panCC->value); panEnvelope.reset(Default::symmetricNormalizedRange.clamp(pan)); basePosition = normalizePercents(region->position); auto position = basePosition; if (region->positionCC) - position += normalizeCC(resources.midiState.getCCValue(region->positionCC->cc)) * normalizePercents(region->positionCC->value); + position += resources.midiState.getCCValueNormalized(region->positionCC->cc) * normalizePercents(region->positionCC->value); positionEnvelope.reset(Default::symmetricNormalizedRange.clamp(position)); baseWidth = normalizePercents(region->width); auto width = baseWidth; if (region->widthCC) - width += normalizeCC(resources.midiState.getCCValue(region->widthCC->cc)) * normalizePercents(region->widthCC->value); + width += resources.midiState.getCCValueNormalized(region->widthCC->cc) * normalizePercents(region->widthCC->value); widthEnvelope.reset(Default::symmetricNormalizedRange.clamp(width)); pitchBendEnvelope.setFunction([region](float pitchValue){ @@ -123,13 +124,13 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value const unsigned numChannels = region->isStereo ? 2 : 1; for (auto& filter: region->filters) { - auto newFilter = resources.filterPool.getFilter(filter, numChannels, number, value); + auto newFilter = resources.filterPool.getFilter(filter, numChannels, number, denormalize7Bits(value)); if (newFilter) filters.push_back(newFilter); } for (auto& eq: region->equalizers) { - auto newEQ = resources.eqPool.getEQ(eq, numChannels, value); + auto newEQ = resources.eqPool.getEQ(eq, numChannels, denormalize7Bits(value)); if (newEQ) equalizers.push_back(newEQ); } @@ -139,7 +140,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value initialDelay = delay + static_cast(region->getDelay() * sampleRate); baseFrequency = midiNoteFrequency(number); bendStepFactor = centsFactor(region->bendStep); - egEnvelope.reset(*region, resources.midiState, delay, value, sampleRate); + egEnvelope.reset(*region, resources.midiState, delay, denormalize7Bits(value), sampleRate); } bool sfz::Voice::isFree() const noexcept @@ -159,7 +160,7 @@ void sfz::Voice::release(int delay, bool fastRelease) noexcept } } -void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noexcept { UNUSED(velocity); if (region == nullptr) @@ -174,12 +175,12 @@ void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity) no if (region->loopMode == SfzLoopMode::one_shot) return; - if (!region->checkSustain || resources.midiState.getCCValue(config::sustainCC) < config::halfCCThreshold) + if (!region->checkSustain || resources.midiState.getCCValueNormalized(config::sustainCC) < config::halfCCThreshold) release(delay); } } -void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept +void sfz::Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept { if (region == nullptr) return; @@ -200,27 +201,27 @@ void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept delay = max(delay, minEnvelopeDelay); if (region->amplitudeCC && ccNumber == region->amplitudeCC->cc) { - const float newGain { baseGain + normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->value) }; + const float newGain { baseGain + ccValue * normalizePercents(region->amplitudeCC->value) }; amplitudeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(newGain)); } if (region->volumeCC && ccNumber == region->volumeCC->cc) { - const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->value }; + const float newVolumedB { baseVolumedB + ccValue * region->volumeCC->value }; volumeEnvelope.registerEvent(delay, db2mag(Default::volumeRange.clamp(newVolumedB))); } if (region->panCC && ccNumber == region->panCC->cc) { - const float newPan { basePan + normalizeCC(ccValue) * normalizePercents(region->panCC->value) }; + const float newPan { basePan + ccValue * normalizePercents(region->panCC->value) }; panEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPan)); } if (region->positionCC && ccNumber == region->positionCC->cc) { - const float newPosition { basePosition + normalizeCC(ccValue) * normalizePercents(region->positionCC->value) }; + const float newPosition { basePosition + ccValue * normalizePercents(region->positionCC->value) }; positionEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPosition)); } if (region->widthCC && ccNumber == region->widthCC->cc) { - const float newWidth { baseWidth + normalizeCC(ccValue) * normalizePercents(region->widthCC->value) }; + const float newWidth { baseWidth + ccValue * normalizePercents(region->widthCC->value) }; widthEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newWidth)); } @@ -552,7 +553,7 @@ int sfz::Voice::getTriggerNumber() const noexcept return triggerNumber; } -uint8_t sfz::Voice::getTriggerValue() const noexcept +float sfz::Voice::getTriggerValue() const noexcept { return triggerValue; } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 291b1a38..1772bf45 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -81,7 +81,7 @@ public: * @param value * @param triggerType */ - void startVoice(Region* region, int delay, int number, uint8_t value, TriggerType triggerType) noexcept; + void startVoice(Region* region, int delay, int number, float value, TriggerType triggerType) noexcept; /** * @brief Register a note-off event; this may trigger a release. @@ -90,7 +90,7 @@ public: * @param noteNumber * @param velocity */ - void registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept; + void registerNoteOff(int delay, int noteNumber, float velocity) noexcept; /** * @brief Register a CC event; this may trigger a release. If the voice is playing and its * region has CC modifiers, it will use this value to compute the CC envelope to apply to the @@ -100,7 +100,7 @@ public: * @param ccNumber * @param ccValue */ - void registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept; + void registerCC(int delay, int ccNumber, float ccValue) noexcept; /** * @brief Register a pitch wheel event; for now this does nothing * @@ -163,9 +163,9 @@ public: /** * @brief Get the value that triggered the voice (note velocity or cc value) * - * @return uint8_t + * @return float */ - uint8_t getTriggerValue() const noexcept; + float getTriggerValue() const noexcept; /** * @brief Get the type of trigger * @@ -261,7 +261,7 @@ private: TriggerType triggerType; int triggerNumber; - uint8_t triggerValue; + float triggerValue; absl::optional triggerDelay; float speedRatio { 1.0 }; From cc8944e4523678e419b8176536a8fc5e66c4f07d Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:25:26 +0100 Subject: [PATCH 09/19] Everyone should now use the normalized versions --- src/sfizz/EGDescription.h | 2 +- src/sfizz/MidiState.h | 2 +- src/sfizz/Synth.cpp | 17 ++-- tests/EGDescriptionT.cpp | 16 +-- tests/FilesT.cpp | 4 +- tests/MidiStateT.cpp | 30 +++--- tests/RegionActivationT.cpp | 148 ++++++++++++++-------------- tests/RegionTriggersT.cpp | 150 +++++++++++++++-------------- tests/RegionValueComputationsT.cpp | 63 ++++++------ tests/SynthT.cpp | 6 +- 10 files changed, 225 insertions(+), 213 deletions(-) diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index afdc99f5..c84717e6 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -55,7 +55,7 @@ namespace sfz inline float ccSwitchedValue(const MidiState& state, const absl::optional>& ccSwitch, float value) noexcept { if (ccSwitch) - return value + ccSwitch->value * normalizeCC(state.getCCValue(ccSwitch->cc)); + return value + ccSwitch->value * state.getCCValueNormalized(ccSwitch->cc); else return value; } diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index 63d9b765..36c8d00e 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -152,7 +152,7 @@ public: T modulate(T value, const CCMap& modifiers, const Range& validRange, const modFunction& lambda = addToBase) const noexcept { for (auto& mod: modifiers) { - lambda(value, normalizeCC(getCCValue(mod.cc)) * mod.value); + lambda(value, getCCValueNormalized(mod.cc) * mod.value); } return validRange.clamp(value); } diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 9724ce20..d3e2e4dd 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -181,8 +181,9 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) case hash("Set_cc&"): // fallthrough case hash("set_cc&"): if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) { - const auto ccValue = readOpcode(member.value, Default::midi7Range).value_or(0); - resources.midiState.ccEvent(0, member.parameters.back(), ccValue); + const auto ccValue = readOpcode(member.value, Default::midi7Range); + if (ccValue) + resources.midiState.ccEventNormalized(0, member.parameters.back(), normalizeCC(*ccValue)); } break; case hash("Label_cc&"): // fallthrough @@ -386,12 +387,12 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) // Defaults for (unsigned cc = 0; cc < config::numCCs; cc++) { - region->registerCC(cc, resources.midiState.getCCValue(cc)); + region->registerCCNormalized(cc, resources.midiState.getCCValueNormalized(cc)); } if (defaultSwitch) { - region->registerNoteOn(*defaultSwitch, 127, 1.0); - region->registerNoteOff(*defaultSwitch, 0, 1.0); + region->registerNoteOnNormalized(*defaultSwitch, 1.0f, 1.0f); + region->registerNoteOffNormalized(*defaultSwitch, 0.0f, 1.0f); } // Set the default frequencies on equalizers if needed @@ -616,9 +617,9 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); UNUSED(velocity); - + const auto normalizedVelocity = normalizeVelocity(velocity); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.noteOffEvent(delay, noteNumber, velocity); + resources.midiState.noteOffEventNormalized(delay, noteNumber, normalizedVelocity); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) @@ -690,7 +691,7 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept voice->registerCC(delay, ccNumber, normalizedCC); for (auto& region : ccActivationLists[ccNumber]) { - if (region->registerCC(ccNumber, ccValue)) { + if (region->registerCCNormalized(ccNumber, normalizedCC)) { auto voice = findFreeVoice(); if (voice == nullptr) continue; diff --git a/tests/EGDescriptionT.cpp b/tests/EGDescriptionT.cpp index 4e933e75..8bbccc10 100644 --- a/tests/EGDescriptionT.cpp +++ b/tests/EGDescriptionT.cpp @@ -6,8 +6,10 @@ #include "sfizz/EGDescription.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("[EGDescription] Attack range") { @@ -18,7 +20,7 @@ TEST_CASE("[EGDescription] Attack range") eg.ccAttack = { 63, 1.27f }; REQUIRE( eg.getAttack(state, 0) == 1.0f ); REQUIRE( eg.getAttack(state, 127) == 0.0f ); - state.ccEvent(0, 63, 127); + state.ccEventNormalized(0, 63, 127_norm); REQUIRE( eg.getAttack(state, 127) == 1.0f ); REQUIRE( eg.getAttack(state, 0) == 2.27f ); eg.ccAttack = { 63, 127.0f }; @@ -34,7 +36,7 @@ TEST_CASE("[EGDescription] Delay range") eg.ccDelay = { 63, 1.27f }; REQUIRE( eg.getDelay(state, 0) == 1.0f ); REQUIRE( eg.getDelay(state, 127) == 0.0f ); - state.ccEvent(0, 63, 127); + state.ccEventNormalized(0, 63, 127_norm); REQUIRE( eg.getDelay(state, 127) == 1.0f ); REQUIRE( eg.getDelay(state, 0) == 2.27f ); eg.ccDelay = { 63, 127.0f }; @@ -50,7 +52,7 @@ TEST_CASE("[EGDescription] Decay range") eg.ccDecay = { 63, 1.27f }; REQUIRE( eg.getDecay(state, 0) == 1.0f ); REQUIRE( eg.getDecay(state, 127) == 0.0f ); - state.ccEvent(0, 63, 127); + state.ccEventNormalized(0, 63, 127_norm); REQUIRE( eg.getDecay(state, 127) == 1.0f ); REQUIRE( eg.getDecay(state, 0) == 2.27f ); eg.ccDecay = { 63, 127.0f }; @@ -66,7 +68,7 @@ TEST_CASE("[EGDescription] Release range") eg.ccRelease = { 63, 1.27f }; REQUIRE( eg.getRelease(state, 0) == 1.0f ); REQUIRE( eg.getRelease(state, 127) == 0.0f ); - state.ccEvent(0, 63, 127); + state.ccEventNormalized(0, 63, 127_norm); REQUIRE( eg.getRelease(state, 127) == 1.0f ); REQUIRE( eg.getRelease(state, 0) == 2.27f ); eg.ccRelease = { 63, 127.0f }; @@ -82,7 +84,7 @@ TEST_CASE("[EGDescription] Hold range") eg.ccHold = { 63, 1.27f }; REQUIRE( eg.getHold(state, 0) == 1.0f ); REQUIRE( eg.getHold(state, 127) == 0.0f ); - state.ccEvent(0, 63, 127); + state.ccEventNormalized(0, 63, 127_norm); REQUIRE( eg.getHold(state, 127) == 1.0f ); REQUIRE( eg.getHold(state, 0) == 2.27f ); eg.ccHold = { 63, 127.0f }; @@ -98,7 +100,7 @@ TEST_CASE("[EGDescription] Sustain level") eg.ccSustain = { 63, 100.0f }; REQUIRE( eg.getSustain(state, 0) == 50.0f ); REQUIRE( eg.getSustain(state, 127) == 0.0f ); - state.ccEvent(0, 63, 127); + state.ccEventNormalized(0, 63, 127_norm); REQUIRE( eg.getSustain(state, 127) == 50.0f ); eg.ccSustain = { 63, 200.0f }; REQUIRE( eg.getSustain(state, 0) == 100.0f ); @@ -112,7 +114,7 @@ TEST_CASE("[EGDescription] Start level") eg.ccStart = { 63, 127.0f }; REQUIRE( eg.getStart(state, 0) == 0.0f ); REQUIRE( eg.getStart(state, 127) == 0.0f ); - state.ccEvent(0, 63, 127); + state.ccEventNormalized(0, 63, 127_norm); REQUIRE( eg.getStart(state, 0) == 100.0f ); eg.ccStart = { 63, -127.0f }; REQUIRE( eg.getStart(state, 0) == 0.0f ); diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 7bc99da4..fa45e69a 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -355,8 +355,8 @@ TEST_CASE("[Files] Set CC applies properly") { sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz"); - REQUIRE(synth.getMidiState().getCCValue(142) == 63); - REQUIRE(synth.getMidiState().getCCValue(61) == 122); + REQUIRE(synth.getMidiState().getCCValueNormalized(142) == 63_norm); + REQUIRE(synth.getMidiState().getCCValueNormalized(61) == 122_norm); } TEST_CASE("[Files] Note and octave offsets") diff --git a/tests/MidiStateT.cpp b/tests/MidiStateT.cpp index d4be5d21..4529ecb4 100644 --- a/tests/MidiStateT.cpp +++ b/tests/MidiStateT.cpp @@ -11,25 +11,27 @@ */ #include "sfizz/MidiState.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" #include "absl/strings/string_view.h" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("[MidiState] Initial values") { sfz::MidiState state; for (unsigned cc = 0; cc < sfz::config::numCCs; cc++) - REQUIRE( state.getCCValue(cc) == 0 ); + REQUIRE( state.getCCValueNormalized(cc) == 0_norm ); REQUIRE( state.getPitchBend() == 0 ); } TEST_CASE("[MidiState] Set and get CCs") { sfz::MidiState state; - state.ccEvent(0, 24, 23); - state.ccEvent(0, 123, 124); - REQUIRE(state.getCCValue(24) == 23); - REQUIRE(state.getCCValue(123) == 124); + state.ccEventNormalized(0, 24, 23_norm); + state.ccEventNormalized(0, 123, 124_norm); + REQUIRE(state.getCCValueNormalized(24) == 23_norm); + REQUIRE(state.getCCValueNormalized(123) == 124_norm); } TEST_CASE("[MidiState] Set and get pitch bends") @@ -45,25 +47,25 @@ TEST_CASE("[MidiState] Reset") { sfz::MidiState state; state.pitchBendEvent(0, 894); - state.noteOnEvent(0, 64, 24); - state.ccEvent(0, 123, 124); + state.noteOnEventNormalized(0, 64, 24_norm); + state.ccEventNormalized(0, 123, 124_norm); state.reset(0); REQUIRE(state.getPitchBend() == 0); - REQUIRE(state.getNoteVelocity(64) == 0); - REQUIRE(state.getCCValue(123) == 0); + REQUIRE(state.getNoteVelocityNormalized(64) == 0_norm); + REQUIRE(state.getCCValueNormalized(123) == 0_norm); } TEST_CASE("[MidiState] Set and get note velocities") { sfz::MidiState state; - state.noteOnEvent(0, 64, 24); - REQUIRE(+state.getNoteVelocity(64) == 24); - state.noteOnEvent(0, 64, 123); - REQUIRE(+state.getNoteVelocity(64) == 123); + state.noteOnEventNormalized(0, 64, 24_norm); + REQUIRE(+state.getNoteVelocityNormalized(64) == 24_norm); + state.noteOnEventNormalized(0, 64, 123_norm); + REQUIRE(+state.getNoteVelocityNormalized(64) == 123_norm); } TEST_CASE("[MidiState] Extended CCs") { sfz::MidiState state; - state.ccEvent(0, 142, 64); // should not trap + state.ccEventNormalized(0, 142, 64_norm); // should not trap } diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index 95821441..02caf8f2 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -5,8 +5,10 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Region.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("Region activation", "Region tests") { @@ -16,7 +18,7 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sample", "*sine" }); SECTION("Basic state") { - region.registerCC(4, 0); + region.registerCCNormalized(4, 0_norm); REQUIRE(region.isSwitchedOn()); } @@ -24,19 +26,19 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "locc4", "56" }); region.parseOpcode({ "hicc4", "59" }); - region.registerCC(4, 0); + region.registerCCNormalized(4, 0_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(4, 57); + region.registerCCNormalized(4, 57_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 56); + region.registerCCNormalized(4, 56_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 59); + region.registerCCNormalized(4, 59_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 43); + region.registerCCNormalized(4, 43_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(4, 65); + region.registerCCNormalized(4, 65_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(6, 57); + region.registerCCNormalized(6, 57_norm); REQUIRE(!region.isSwitchedOn()); } @@ -46,26 +48,26 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "hicc4", "59" }); region.parseOpcode({ "locc54", "18" }); region.parseOpcode({ "hicc54", "27" }); - region.registerCC(4, 0); - region.registerCC(54, 0); + region.registerCCNormalized(4, 0_norm); + region.registerCCNormalized(54, 0_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(4, 57); + region.registerCCNormalized(4, 57_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(54, 19); + region.registerCCNormalized(54, 19_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(54, 18); + region.registerCCNormalized(54, 18_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(54, 27); + region.registerCCNormalized(54, 27_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 56); + region.registerCCNormalized(4, 56_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 59); + region.registerCCNormalized(4, 59_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(54, 2); + region.registerCCNormalized(54, 2_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(54, 26); + region.registerCCNormalized(54, 26_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 65); + region.registerCCNormalized(4, 65_norm); REQUIRE(!region.isSwitchedOn()); } @@ -116,13 +118,13 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "sw_last", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 64, 0.5f); + region.registerNoteOffNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOnNormalized(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOffNormalized(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_last with non-default keyswitch range") @@ -131,20 +133,20 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_last", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64, 0.5f); + region.registerNoteOnNormalized(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0, 0.5f); + region.registerNoteOffNormalized(60, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(60, 64, 0.5f); + region.registerNoteOnNormalized(60, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(60, 0, 0.5f); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOffNormalized(60, 0_norm, 0.5f); + region.registerNoteOnNormalized(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOffNormalized(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_down with non-default keyswitch range") @@ -153,20 +155,20 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_down", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64, 0.5f); + region.registerNoteOnNormalized(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0, 0.5f); + region.registerNoteOffNormalized(60, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64, 0.5f); + region.registerNoteOnNormalized(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0, 0.5f); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOffNormalized(60, 0_norm, 0.5f); + region.registerNoteOnNormalized(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOffNormalized(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_up with non-default keyswitch range") @@ -175,16 +177,16 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_up", "40" }); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOnNormalized(41, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOffNormalized(41, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } @@ -192,20 +194,20 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "sw_previous", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOnNormalized(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOffNormalized(41, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOnNormalized(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOffNormalized(41, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); } @@ -215,17 +217,17 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "1" }); region.parseOpcode({ "key", "40" }); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); } SECTION("Sequences: length 2, position 2") @@ -234,17 +236,17 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "2" }); region.parseOpcode({ "key", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } SECTION("Sequences: length 3, position 2") @@ -253,21 +255,21 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "2" }); region.parseOpcode({ "key", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOnNormalized(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } } diff --git a/tests/RegionTriggersT.cpp b/tests/RegionTriggersT.cpp index fe7f4569..32d91090 100644 --- a/tests/RegionTriggersT.cpp +++ b/tests/RegionTriggersT.cpp @@ -5,8 +5,10 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Region.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("Basic triggers", "Region triggers") { @@ -17,44 +19,44 @@ TEST_CASE("Basic triggers", "Region triggers") SECTION("key") { region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); - REQUIRE(!region.registerCC(63, 64)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOffNormalized(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOnNormalized(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCCNormalized(63, 64_norm)); } SECTION("lokey and hikey") { region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "42" }); - REQUIRE(!region.registerNoteOn(39, 64, 0.5f)); - REQUIRE(region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(40, 64, 0.5f)); - REQUIRE(region.registerNoteOn(41, 64, 0.5f)); - REQUIRE(region.registerNoteOn(42, 64, 0.5f)); - REQUIRE(!region.registerNoteOn(43, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(42, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(42, 64, 0.5f)); - REQUIRE(!region.registerCC(63, 64)); + REQUIRE(!region.registerNoteOnNormalized(39, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOffNormalized(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOnNormalized(41, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOnNormalized(42, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOnNormalized(43, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOffNormalized(42, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOffNormalized(42, 64_norm, 0.5f)); + REQUIRE(!region.registerCCNormalized(63, 64_norm)); } SECTION("key and release trigger") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "trigger", "release" }); - REQUIRE(!region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(region.registerNoteOff(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(41, 64, 0.5f)); - REQUIRE(!region.registerCC(63, 64)); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOffNormalized(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOnNormalized(41, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOffNormalized(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCCNormalized(63, 64_norm)); } SECTION("key and release_key trigger") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "trigger", "release_key" }); - REQUIRE(!region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(region.registerNoteOff(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(41, 64, 0.5f)); - REQUIRE(!region.registerCC(63, 64)); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOffNormalized(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOnNormalized(41, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOffNormalized(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCCNormalized(63, 64_norm)); } // TODO: first and legato triggers SECTION("lovel and hivel") @@ -62,11 +64,11 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lovel", "60" }); region.parseOpcode({ "hivel", "70" }); - REQUIRE(region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(region.registerNoteOn(40, 60, 0.5f)); - REQUIRE(region.registerNoteOn(40, 70, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 71, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 59, 0.5f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOnNormalized(40, 60_norm, 0.5f)); + REQUIRE(region.registerNoteOnNormalized(40, 70_norm, 0.5f)); + REQUIRE(!region.registerNoteOnNormalized(41, 71_norm, 0.5f)); + REQUIRE(!region.registerNoteOnNormalized(41, 59_norm, 0.5f)); } SECTION("lorand and hirand") @@ -74,54 +76,54 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lorand", "0.35" }); region.parseOpcode({ "hirand", "0.40" }); - REQUIRE(!region.registerNoteOn(40, 64, 0.34f)); - REQUIRE(region.registerNoteOn(40, 64, 0.35f)); - REQUIRE(region.registerNoteOn(40, 64, 0.36f)); - REQUIRE(region.registerNoteOn(40, 64, 0.37f)); - REQUIRE(region.registerNoteOn(40, 64, 0.38f)); - REQUIRE(region.registerNoteOn(40, 64, 0.39f)); - REQUIRE(!region.registerNoteOn(40, 64, 0.40f)); - REQUIRE(!region.registerNoteOn(40, 64, 0.41f)); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.34f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.35f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.36f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.37f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.38f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.39f)); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.40f)); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.41f)); } SECTION("lorand and hirand on 1.0f") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lorand", "0.35" }); - REQUIRE(!region.registerNoteOn(40, 64, 0.34f)); - REQUIRE(region.registerNoteOn(40, 64, 0.35f)); - REQUIRE(region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.34f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.35f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 1.0f)); } SECTION("Disable key trigger") { region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 1.0f)); region.parseOpcode({ "hikey", "-1" }); - REQUIRE(!region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 1.0f)); region.parseOpcode({ "hikey", "40" }); - REQUIRE(region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 1.0f)); region.parseOpcode({ "key", "-1" }); - REQUIRE(!region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 1.0f)); region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 1.0f)); } SECTION("on_loccN, on_hiccN") { region.parseOpcode({ "on_locc47", "64" }); region.parseOpcode({ "on_hicc47", "68" }); - REQUIRE(!region.registerCC(47, 63)); - REQUIRE(!region.registerCC(47, 64)); - REQUIRE(!region.registerCC(47, 65)); + REQUIRE(!region.registerCCNormalized(47, 63_norm)); + REQUIRE(!region.registerCCNormalized(47, 64_norm)); + REQUIRE(!region.registerCCNormalized(47, 65_norm)); region.parseOpcode({ "hikey", "-1" }); - REQUIRE(region.registerCC(47, 64)); - REQUIRE(region.registerCC(47, 65)); - REQUIRE(region.registerCC(47, 66)); - REQUIRE(region.registerCC(47, 67)); - REQUIRE(region.registerCC(47, 68)); - REQUIRE(!region.registerCC(47, 69)); - REQUIRE(!region.registerCC(40, 64)); + REQUIRE(region.registerCCNormalized(47, 64_norm)); + REQUIRE(region.registerCCNormalized(47, 65_norm)); + REQUIRE(region.registerCCNormalized(47, 66_norm)); + REQUIRE(region.registerCCNormalized(47, 67_norm)); + REQUIRE(region.registerCCNormalized(47, 68_norm)); + REQUIRE(!region.registerCCNormalized(47, 69_norm)); + REQUIRE(!region.registerCCNormalized(40, 64_norm)); } } @@ -135,16 +137,16 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "first" }); - midiState.noteOnEvent(0, 40, 64); - REQUIRE(region.registerNoteOn(40, 64, 0.5f)); - midiState.noteOnEvent(0, 41, 64); - REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); - midiState.noteOffEvent(0, 40, 0); - region.registerNoteOff(40, 0, 0.5f); - midiState.noteOffEvent(0, 41, 0); - region.registerNoteOff(41, 0, 0.5f); - midiState.noteOnEvent(0, 42, 64); - REQUIRE(region.registerNoteOn(42, 64, 0.5f)); + midiState.noteOnEventNormalized(0, 40, 64_norm); + REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + midiState.noteOnEventNormalized(0, 41, 64_norm); + REQUIRE(!region.registerNoteOnNormalized(41, 64_norm, 0.5f)); + midiState.noteOffEventNormalized(0, 40, 0_norm); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); + midiState.noteOffEventNormalized(0, 41, 0_norm); + region.registerNoteOffNormalized(41, 0_norm, 0.5f); + midiState.noteOnEventNormalized(0, 42, 64_norm); + REQUIRE(region.registerNoteOnNormalized(42, 64_norm, 0.5f)); } SECTION("Second note playing") @@ -152,15 +154,15 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "legato" }); - midiState.noteOnEvent(0, 40, 64); - REQUIRE(!region.registerNoteOn(40, 64, 0.5f)); - midiState.noteOnEvent(0, 41, 64); - REQUIRE(region.registerNoteOn(41, 64, 0.5f)); - midiState.noteOffEvent(0, 40, 64); - region.registerNoteOff(40, 0, 0.5f); - midiState.noteOffEvent(0, 41, 64); - region.registerNoteOff(41, 0, 0.5f); - midiState.noteOnEvent(0, 42, 64); - REQUIRE(!region.registerNoteOn(42, 64, 0.5f)); + midiState.noteOnEventNormalized(0, 40, 64_norm); + REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + midiState.noteOnEventNormalized(0, 41, 64_norm); + REQUIRE(region.registerNoteOnNormalized(41, 64_norm, 0.5f)); + midiState.noteOffEventNormalized(0, 40, 64_norm); + region.registerNoteOffNormalized(40, 0_norm, 0.5f); + midiState.noteOffEventNormalized(0, 41, 64_norm); + region.registerNoteOffNormalized(41, 0_norm, 0.5f); + midiState.noteOnEventNormalized(0, 42, 64_norm); + REQUIRE(!region.registerNoteOnNormalized(42, 64_norm, 0.5f)); } } diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index 5c1ac179..ea768c9b 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -12,6 +12,7 @@ #include #include using namespace Catch::literals; +using namespace sfz::literals; constexpr int numRandomTests { 64 }; TEST_CASE("[Region] Crossfade in on key") { @@ -166,13 +167,13 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_locc24", "20" }); region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.5_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEventNormalized(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEventNormalized(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEventNormalized(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEventNormalized(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); + midiState.ccEventNormalized(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); + midiState.ccEventNormalized(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEventNormalized(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -184,13 +185,13 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.25_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.5_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.75_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEventNormalized(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEventNormalized(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEventNormalized(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); + midiState.ccEventNormalized(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEventNormalized(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); + midiState.ccEventNormalized(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEventNormalized(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); } TEST_CASE("[Region] Crossfade out on CC") { @@ -200,13 +201,13 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_locc24", "20" }); region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.5_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEventNormalized(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEventNormalized(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEventNormalized(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); + midiState.ccEventNormalized(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); + midiState.ccEventNormalized(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEventNormalized(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEventNormalized(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -218,13 +219,13 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.75_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.5_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.25_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEventNormalized(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEventNormalized(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEventNormalized(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); + midiState.ccEventNormalized(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEventNormalized(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); + midiState.ccEventNormalized(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEventNormalized(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") @@ -265,15 +266,15 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "trigger", "release" }); region.parseOpcode({ "rt_decay", "10" }); - midiState.noteOnEvent(0, 64, 64); + midiState.noteOnEventNormalized(0, 64, 64_norm); 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" }); - midiState.noteOnEvent(0, 64, 64); + midiState.noteOnEventNormalized(0, 64, 64_norm); 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" }); - midiState.noteOnEvent(0, 64, 64); + midiState.noteOnEventNormalized(0, 64, 64_norm); std::this_thread::sleep_for(std::chrono::milliseconds(100)); REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) ); } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 70806b5e..2f2f98bc 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -5,8 +5,10 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Synth.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; constexpr int blockSize { 256 }; @@ -139,9 +141,9 @@ TEST_CASE("[Synth] Reset all controllers") { sfz::Synth synth; synth.cc(0, 12, 64); - REQUIRE( synth.getMidiState().getCCValue(12) == 64 ); + REQUIRE( synth.getMidiState().getCCValueNormalized(12) == 64_norm ); synth.cc(0, 121, 64); - REQUIRE( synth.getMidiState().getCCValue(12) == 0 ); + REQUIRE( synth.getMidiState().getCCValueNormalized(12) == 0_norm ); } TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice") From 78851082eaada7985850484cf43f26d0929fcfc3 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:27:28 +0100 Subject: [PATCH 10/19] Add some assertions to check --- src/sfizz/Voice.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index f0ffddfe..30aa6fd2 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -162,7 +162,9 @@ void sfz::Voice::release(int delay, bool fastRelease) noexcept void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noexcept { + ASSERT(velocity >= 0.0 && velocity <= 1.0); UNUSED(velocity); + if (region == nullptr) return; @@ -182,6 +184,7 @@ void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noex void sfz::Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept { + ASSERT(ccValue >= 0.0 && ccValue <= 1.0); if (region == nullptr) return; From 1e2a18359ec7ff1f646ed688719ac3187205a7d2 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:30:17 +0100 Subject: [PATCH 11/19] Remove getNoteGain --- tests/RegionValueComputationsT.cpp | 124 ++++++++++++++--------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index ea768c9b..bb3f205d 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -21,9 +21,9 @@ TEST_CASE("[Region] Crossfade in on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "3" }); - REQUIRE( region.getNoteGain(2, 127) == 0.70711_a ); - REQUIRE( region.getNoteGain(1, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(3, 127) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(2, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGainNormalized(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(3, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on key - 2") @@ -33,12 +33,12 @@ TEST_CASE("[Region] Crossfade in on key - 2") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); - REQUIRE( region.getNoteGain(1, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(2, 127) == 0.5_a ); - REQUIRE( region.getNoteGain(3, 127) == 0.70711_a ); - REQUIRE( region.getNoteGain(4, 127) == 0.86603_a ); - REQUIRE( region.getNoteGain(5, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(6, 127) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(2, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGainNormalized(3, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGainNormalized(4, 127_norm) == 0.86603_a ); + REQUIRE( region.getNoteGainNormalized(5, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(6, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on key - gain") @@ -49,11 +49,11 @@ TEST_CASE("[Region] Crossfade in on key - gain") region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE( region.getNoteGain(1, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(2, 127) == 0.25_a ); - REQUIRE( region.getNoteGain(3, 127) == 0.5_a ); - REQUIRE( region.getNoteGain(4, 127) == 0.75_a ); - REQUIRE( region.getNoteGain(5, 127) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(2, 127_norm) == 0.25_a ); + REQUIRE( region.getNoteGainNormalized(3, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGainNormalized(4, 127_norm) == 0.75_a ); + REQUIRE( region.getNoteGainNormalized(5, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade out on key") @@ -63,13 +63,13 @@ TEST_CASE("[Region] Crossfade out on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); - REQUIRE( region.getNoteGain(50, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(51, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(52, 127) == 0.86603_a ); - REQUIRE( region.getNoteGain(53, 127) == 0.70711_a ); - REQUIRE( region.getNoteGain(54, 127) == 0.5_a ); - REQUIRE( region.getNoteGain(55, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(56, 127) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(50, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(51, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(52, 127_norm) == 0.86603_a ); + REQUIRE( region.getNoteGainNormalized(53, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGainNormalized(54, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGainNormalized(55, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(56, 127_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade out on key - gain") @@ -80,13 +80,13 @@ TEST_CASE("[Region] Crossfade out on key - gain") region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE( region.getNoteGain(50, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(51, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(52, 127) == 0.75_a ); - REQUIRE( region.getNoteGain(53, 127) == 0.5_a ); - REQUIRE( region.getNoteGain(54, 127) == 0.25_a ); - REQUIRE( region.getNoteGain(55, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(56, 127) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(50, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(51, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(52, 127_norm) == 0.75_a ); + REQUIRE( region.getNoteGainNormalized(53, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGainNormalized(54, 127_norm) == 0.25_a ); + REQUIRE( region.getNoteGainNormalized(55, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(56, 127_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade in on velocity") @@ -97,13 +97,13 @@ TEST_CASE("[Region] Crossfade in on velocity") region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(1, 19) == 0.0_a ); - REQUIRE( region.getNoteGain(1, 20) == 0.0_a ); - REQUIRE( region.getNoteGain(2, 21) == 0.5_a ); - REQUIRE( region.getNoteGain(3, 22) == 0.70711_a ); - REQUIRE( region.getNoteGain(4, 23) == 0.86603_a ); - REQUIRE( region.getNoteGain(5, 24) == 1.0_a ); - REQUIRE( region.getNoteGain(6, 25) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(1, 19_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(1, 20_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(2, 21_norm) == 0.5_a ); + REQUIRE( region.getNoteGainNormalized(3, 22_norm) == 0.70711_a ); + REQUIRE( region.getNoteGainNormalized(4, 23_norm) == 0.86603_a ); + REQUIRE( region.getNoteGainNormalized(5, 24_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(6, 25_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on vel - gain") @@ -115,13 +115,13 @@ TEST_CASE("[Region] Crossfade in on vel - gain") region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(1, 19) == 0.0_a ); - REQUIRE( region.getNoteGain(1, 20) == 0.0_a ); - REQUIRE( region.getNoteGain(2, 21) == 0.25_a ); - REQUIRE( region.getNoteGain(3, 22) == 0.5_a ); - REQUIRE( region.getNoteGain(4, 23) == 0.75_a ); - REQUIRE( region.getNoteGain(5, 24) == 1.0_a ); - REQUIRE( region.getNoteGain(5, 25) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(1, 19_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(1, 20_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(2, 21_norm) == 0.25_a ); + REQUIRE( region.getNoteGainNormalized(3, 22_norm) == 0.5_a ); + REQUIRE( region.getNoteGainNormalized(4, 23_norm) == 0.75_a ); + REQUIRE( region.getNoteGainNormalized(5, 24_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(5, 25_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade out on vel") @@ -132,13 +132,13 @@ TEST_CASE("[Region] Crossfade out on vel") region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(5, 50) == 1.0_a ); - REQUIRE( region.getNoteGain(5, 51) == 1.0_a ); - REQUIRE( region.getNoteGain(5, 52) == 0.86603_a ); - REQUIRE( region.getNoteGain(5, 53) == 0.70711_a ); - REQUIRE( region.getNoteGain(5, 54) == 0.5_a ); - REQUIRE( region.getNoteGain(5, 55) == 0.0_a ); - REQUIRE( region.getNoteGain(5, 56) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(5, 50_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(5, 51_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(5, 52_norm) == 0.86603_a ); + REQUIRE( region.getNoteGainNormalized(5, 53_norm) == 0.70711_a ); + REQUIRE( region.getNoteGainNormalized(5, 54_norm) == 0.5_a ); + REQUIRE( region.getNoteGainNormalized(5, 55_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(5, 56_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade out on vel - gain") @@ -150,13 +150,13 @@ TEST_CASE("[Region] Crossfade out on vel - gain") region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(56, 50) == 1.0_a ); - REQUIRE( region.getNoteGain(56, 51) == 1.0_a ); - REQUIRE( region.getNoteGain(56, 52) == 0.75_a ); - REQUIRE( region.getNoteGain(56, 53) == 0.5_a ); - REQUIRE( region.getNoteGain(56, 54) == 0.25_a ); - REQUIRE( region.getNoteGain(56, 55) == 0.0_a ); - REQUIRE( region.getNoteGain(56, 56) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(56, 50_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(56, 51_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(56, 52_norm) == 0.75_a ); + REQUIRE( region.getNoteGainNormalized(56, 53_norm) == 0.5_a ); + REQUIRE( region.getNoteGainNormalized(56, 54_norm) == 0.25_a ); + REQUIRE( region.getNoteGainNormalized(56, 55_norm) == 0.0_a ); + REQUIRE( region.getNoteGainNormalized(56, 56_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade in on CC") @@ -234,8 +234,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(64, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(64, 0) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(64, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(64, 0_norm) == 1.0_a ); } @@ -245,8 +245,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "100" }); - REQUIRE( region.getNoteGain(64, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(64, 0) == Approx(0.0).margin(0.0001) ); + REQUIRE( region.getNoteGainNormalized(64, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(64, 0_norm) == Approx(0.0).margin(0.0001) ); } TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") @@ -255,8 +255,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "-100" }); - REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) ); - REQUIRE( region.getNoteGain(64, 0) == 1.0_a ); + REQUIRE( region.getNoteGainNormalized(64, 127_norm) == Approx(0.0).margin(0.0001) ); + REQUIRE( region.getNoteGainNormalized(64, 0_norm) == 1.0_a ); } TEST_CASE("[Region] rt_decay") From 7ed7720a546b01b6d8709d7c866e98e8e63e3503 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:32:02 +0100 Subject: [PATCH 12/19] Remove unnormalized versions from region --- src/sfizz/Region.cpp | 30 ----------------------- src/sfizz/Region.h | 58 -------------------------------------------- 2 files changed, 88 deletions(-) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 3d1304a0..fd2a5e6d 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -805,11 +805,6 @@ bool sfz::Region::isSwitchedOn() const noexcept return keySwitched && previousKeySwitched && sequenceSwitched && pitchSwitched && bpmSwitched && aftertouchSwitched && ccSwitched.all(); } -bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept -{ - return registerNoteOnNormalized(noteNumber, normalizeVelocity(velocity), randValue); -} - bool sfz::Region::registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -864,11 +859,6 @@ bool sfz::Region::registerNoteOnNormalized(int noteNumber, float velocity, float return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote); } -bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept -{ - return registerNoteOffNormalized(noteNumber, normalizeVelocity(velocity), randValue); -} - bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -895,11 +885,6 @@ bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, floa return keyOk && velOk && randOk && releaseTrigger; } -bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept -{ - return registerCCNormalized(ccNumber, normalizeCC(ccValue)); -} - bool sfz::Region::registerCCNormalized(int ccNumber, float ccValue) noexcept { ASSERT(ccValue >= 0.0f && ccValue <= 1.0f); @@ -945,11 +930,6 @@ void sfz::Region::registerTempo(float secondsPerQuarter) noexcept bpmSwitched = false; } -float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept -{ - return getBasePitchVariationNormalized(noteNumber, normalizeVelocity(velocity)); -} - float sfz::Region::getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -1059,11 +1039,6 @@ float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCur return 1.0f; } -float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept -{ - return getNoteGainNormalized(noteNumber, normalizeVelocity(velocity)); -} - float sfz::Region::getNoteGainNormalized(int noteNumber, float velocity) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -1107,11 +1082,6 @@ float sfz::Region::getCrossfadeGain() const noexcept return gain; } -float sfz::Region::velocityCurve(uint8_t velocity) const noexcept -{ - return velocityCurveNormalized(normalizeVelocity(velocity)); -} - float sfz::Region::velocityCurveNormalized(float velocity) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index b97e90c0..428c0a4b 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -74,30 +74,6 @@ struct Region { * @return false */ bool isSwitchedOn() const noexcept; - /** - * @brief Register a new note on event. The region may be switched on or off using keys so - * this function updates the keyswitches state. - * - * @param noteNumber - * @param velocity - * @param randValue a random value between 0 and 1 used to randomize a bit the region activations - * and vary the samples - * @return true if the region should trigger on this event. - * @return false - */ - bool registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept; - /** - * @brief Register a new note off event. The region may be switched on or off using keys so - * this function updates the keyswitches state. - * - * @param noteNumber - * @param velocity - * @param randValue a random value between 0 and 1 used to randomize a bit the region activations - * and vary the samples - * @return true if the region should trigger on this event. - * @return false - */ - bool registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept; /** * @brief Register a new note on event. The region may be switched on or off using keys so * this function updates the keyswitches state. @@ -122,16 +98,6 @@ struct Region { * @return false */ bool registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept; - /** - * @brief Register a new CC event. The region may be switched on or off using CCs so - * this function checks if it indeeds need to activate or not. - * - * @param ccNumber - * @param ccValue - * @return true if the region should trigger on this event - * @return false - */ - bool registerCC(int ccNumber, uint8_t ccValue) noexcept; /** * @brief Register a new CC event. The region may be switched on or off using CCs so * this function checks if it indeeds need to activate or not. @@ -161,15 +127,6 @@ struct Region { */ void registerTempo(float secondsPerQuarter) noexcept; - /** - * @brief Get the base pitch of the region depending on which note has been - * pressed and at which velocity. - * - * @param noteNumber - * @param velocity - * @return float - */ - float getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept; /** * @brief Get the base pitch of the region depending on which note has been * pressed and at which velocity. @@ -179,15 +136,6 @@ struct Region { * @return float */ float getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept; - /** - * @brief Get the note-related gain of the region depending on which note has been - * pressed and at which velocity. - * - * @param noteNumber - * @param velocity - * @return float - */ - float getNoteGain(int noteNumber, uint8_t velocity) const noexcept; /** * @brief Get the note-related gain of the region depending on which note has been * pressed and at which velocity. @@ -225,12 +173,6 @@ struct Region { * @return float */ float getPhase() const noexcept; - /** - * @brief Computes the gain value related to the velocity of the note - * - * @return float - */ - float velocityCurve(uint8_t velocity) const noexcept; /** * @brief Computes the gain value related to the velocity of the note * From c4db2bba560b1fa78e280033a64d2370077c8457 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:34:32 +0100 Subject: [PATCH 13/19] Refactor the float versions --- src/sfizz/Region.cpp | 14 +-- src/sfizz/Region.h | 12 +-- src/sfizz/Synth.cpp | 14 +-- src/sfizz/Voice.cpp | 4 +- tests/RegionActivationT.cpp | 146 ++++++++++++++--------------- tests/RegionTriggersT.cpp | 128 ++++++++++++------------- tests/RegionValueComputationsT.cpp | 124 ++++++++++++------------ 7 files changed, 221 insertions(+), 221 deletions(-) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index fd2a5e6d..1ab1bf9e 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -805,7 +805,7 @@ bool sfz::Region::isSwitchedOn() const noexcept return keySwitched && previousKeySwitched && sequenceSwitched && pitchSwitched && bpmSwitched && aftertouchSwitched && ccSwitched.all(); } -bool sfz::Region::registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept +bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue) noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -859,7 +859,7 @@ bool sfz::Region::registerNoteOnNormalized(int noteNumber, float velocity, float return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote); } -bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept +bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValue) noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -885,7 +885,7 @@ bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, floa return keyOk && velOk && randOk && releaseTrigger; } -bool sfz::Region::registerCCNormalized(int ccNumber, float ccValue) noexcept +bool sfz::Region::registerCC(int ccNumber, float ccValue) noexcept { ASSERT(ccValue >= 0.0f && ccValue <= 1.0f); if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue)) @@ -930,7 +930,7 @@ void sfz::Region::registerTempo(float secondsPerQuarter) noexcept bpmSwitched = false; } -float sfz::Region::getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept +float sfz::Region::getBasePitchVariation(int noteNumber, float velocity) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -1039,7 +1039,7 @@ float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCur return 1.0f; } -float sfz::Region::getNoteGainNormalized(int noteNumber, float velocity) const noexcept +float sfz::Region::getNoteGain(int noteNumber, float velocity) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -1053,7 +1053,7 @@ float sfz::Region::getNoteGainNormalized(int noteNumber, float velocity) const n baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve); // Amplitude velocity tracking - baseGain *= velocityCurveNormalized(velocity); + baseGain *= velocityCurve(velocity); // Crossfades related to velocity baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve); @@ -1082,7 +1082,7 @@ float sfz::Region::getCrossfadeGain() const noexcept return gain; } -float sfz::Region::velocityCurveNormalized(float velocity) const noexcept +float sfz::Region::velocityCurve(float velocity) const noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 428c0a4b..eb3adb13 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -85,7 +85,7 @@ struct Region { * @return true if the region should trigger on this event. * @return false */ - bool registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept; + bool registerNoteOn(int noteNumber, float velocity, float randValue) noexcept; /** * @brief Register a new note off event. The region may be switched on or off using keys so * this function updates the keyswitches state. @@ -97,7 +97,7 @@ struct Region { * @return true if the region should trigger on this event. * @return false */ - bool registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept; + bool registerNoteOff(int noteNumber, float velocity, float randValue) noexcept; /** * @brief Register a new CC event. The region may be switched on or off using CCs so * this function checks if it indeeds need to activate or not. @@ -107,7 +107,7 @@ struct Region { * @return true if the region should trigger on this event * @return false */ - bool registerCCNormalized(int ccNumber, float ccValue) noexcept; + bool registerCC(int ccNumber, float ccValue) noexcept; /** * @brief Register a new pitch wheel event. * @@ -135,7 +135,7 @@ struct Region { * @param velocity * @return float */ - float getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept; + float getBasePitchVariation(int noteNumber, float velocity) const noexcept; /** * @brief Get the note-related gain of the region depending on which note has been * pressed and at which velocity. @@ -144,7 +144,7 @@ struct Region { * @param velocity * @return float */ - float getNoteGainNormalized(int noteNumber, float velocity) const noexcept; + float getNoteGain(int noteNumber, float velocity) const noexcept; /** * @brief Get the additional crossfade gain of the region depending on the * CC values @@ -178,7 +178,7 @@ struct Region { * * @return float */ - float velocityCurveNormalized(float velocity) const noexcept; + float velocityCurve(float velocity) const noexcept; /** * @brief Get the region offset in samples * diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index d3e2e4dd..dd23d4fe 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -387,12 +387,12 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) // Defaults for (unsigned cc = 0; cc < config::numCCs; cc++) { - region->registerCCNormalized(cc, resources.midiState.getCCValueNormalized(cc)); + region->registerCC(cc, resources.midiState.getCCValueNormalized(cc)); } if (defaultSwitch) { - region->registerNoteOnNormalized(*defaultSwitch, 1.0f, 1.0f); - region->registerNoteOffNormalized(*defaultSwitch, 0.0f, 1.0f); + region->registerNoteOn(*defaultSwitch, 1.0f, 1.0f); + region->registerNoteOff(*defaultSwitch, 0.0f, 1.0f); } // Set the default frequencies on equalizers if needed @@ -640,7 +640,7 @@ void sfz::Synth::noteOffDispatch(int delay, int noteNumber, float velocity) noex { const auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { - if (region->registerNoteOffNormalized(noteNumber, velocity, randValue)) { + if (region->registerNoteOff(noteNumber, velocity, randValue)) { auto voice = findFreeVoice(); if (voice == nullptr) continue; @@ -654,7 +654,7 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc { const auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { - if (region->registerNoteOnNormalized(noteNumber, velocity, randValue)) { + if (region->registerNoteOn(noteNumber, velocity, randValue)) { for (auto& voice : voices) { if (voice->checkOffGroup(delay, region->group)) noteOffDispatch(delay, voice->getTriggerNumber(), voice->getTriggerValue()); @@ -691,7 +691,7 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept voice->registerCC(delay, ccNumber, normalizedCC); for (auto& region : ccActivationLists[ccNumber]) { - if (region->registerCCNormalized(ccNumber, normalizedCC)) { + if (region->registerCC(ccNumber, normalizedCC)) { auto voice = findFreeVoice(); if (voice == nullptr) continue; @@ -959,7 +959,7 @@ void sfz::Synth::resetAllControllers(int delay) noexcept for (auto& region : regions) { for (unsigned cc = 0; cc < config::numCCs; ++cc) - region->registerCCNormalized(cc, 0.0f); + region->registerCC(cc, 0.0f); } } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 30aa6fd2..779e514e 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -73,7 +73,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, } speedRatio = static_cast(currentPromise->sampleRate / this->sampleRate); } - pitchRatio = region->getBasePitchVariationNormalized(number, value); + pitchRatio = region->getBasePitchVariation(number, value); baseVolumedB = region->getBaseVolumedB(number); auto volumedB = baseVolumedB; @@ -83,7 +83,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, baseGain = region->getBaseGain(); if (triggerType != TriggerType::CC) - baseGain *= region->getNoteGainNormalized(number, value); + baseGain *= region->getNoteGain(number, value); float gain { baseGain }; if (region->amplitudeCC) diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index 02caf8f2..c2aae1d1 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -18,7 +18,7 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sample", "*sine" }); SECTION("Basic state") { - region.registerCCNormalized(4, 0_norm); + region.registerCC(4, 0_norm); REQUIRE(region.isSwitchedOn()); } @@ -26,19 +26,19 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "locc4", "56" }); region.parseOpcode({ "hicc4", "59" }); - region.registerCCNormalized(4, 0_norm); + region.registerCC(4, 0_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCCNormalized(4, 57_norm); + region.registerCC(4, 57_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(4, 56_norm); + region.registerCC(4, 56_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(4, 59_norm); + region.registerCC(4, 59_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(4, 43_norm); + region.registerCC(4, 43_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCCNormalized(4, 65_norm); + region.registerCC(4, 65_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCCNormalized(6, 57_norm); + region.registerCC(6, 57_norm); REQUIRE(!region.isSwitchedOn()); } @@ -48,26 +48,26 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "hicc4", "59" }); region.parseOpcode({ "locc54", "18" }); region.parseOpcode({ "hicc54", "27" }); - region.registerCCNormalized(4, 0_norm); - region.registerCCNormalized(54, 0_norm); + region.registerCC(4, 0_norm); + region.registerCC(54, 0_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCCNormalized(4, 57_norm); + region.registerCC(4, 57_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCCNormalized(54, 19_norm); + region.registerCC(54, 19_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(54, 18_norm); + region.registerCC(54, 18_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(54, 27_norm); + region.registerCC(54, 27_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(4, 56_norm); + region.registerCC(4, 56_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(4, 59_norm); + region.registerCC(4, 59_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(54, 2_norm); + region.registerCC(54, 2_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCCNormalized(54, 26_norm); + region.registerCC(54, 26_norm); REQUIRE(region.isSwitchedOn()); - region.registerCCNormalized(4, 65_norm); + region.registerCC(4, 65_norm); REQUIRE(!region.isSwitchedOn()); } @@ -118,13 +118,13 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "sw_last", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 64_norm, 0.5f); + region.registerNoteOff(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(41, 64_norm, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(41, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_last with non-default keyswitch range") @@ -133,20 +133,20 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_last", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(60, 64_norm, 0.5f); + region.registerNoteOn(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(60, 0_norm, 0.5f); + region.registerNoteOff(60, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(60, 64_norm, 0.5f); + region.registerNoteOn(60, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(60, 0_norm, 0.5f); - region.registerNoteOnNormalized(41, 64_norm, 0.5f); + region.registerNoteOff(60, 0_norm, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(41, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_down with non-default keyswitch range") @@ -155,20 +155,20 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_down", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(60, 64_norm, 0.5f); + region.registerNoteOn(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(60, 0_norm, 0.5f); + region.registerNoteOff(60, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(60, 64_norm, 0.5f); + region.registerNoteOn(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(60, 0_norm, 0.5f); - region.registerNoteOnNormalized(41, 64_norm, 0.5f); + region.registerNoteOff(60, 0_norm, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(41, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_up with non-default keyswitch range") @@ -177,16 +177,16 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_up", "40" }); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(41, 64_norm, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); - region.registerNoteOffNormalized(41, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } @@ -194,20 +194,20 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "sw_previous", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(41, 64_norm, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); - region.registerNoteOffNormalized(41, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(41, 64_norm, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(41, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); } @@ -217,17 +217,17 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "1" }); region.parseOpcode({ "key", "40" }); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); } SECTION("Sequences: length 2, position 2") @@ -236,17 +236,17 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "2" }); region.parseOpcode({ "key", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } SECTION("Sequences: length 3, position 2") @@ -255,21 +255,21 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "2" }); region.parseOpcode({ "key", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOnNormalized(40, 64_norm, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } } diff --git a/tests/RegionTriggersT.cpp b/tests/RegionTriggersT.cpp index 32d91090..276c0587 100644 --- a/tests/RegionTriggersT.cpp +++ b/tests/RegionTriggersT.cpp @@ -19,44 +19,44 @@ TEST_CASE("Basic triggers", "Region triggers") SECTION("key") { region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOffNormalized(40, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOnNormalized(41, 64_norm, 0.5f)); - REQUIRE(!region.registerCCNormalized(63, 64_norm)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCC(63, 64_norm)); } SECTION("lokey and hikey") { region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "42" }); - REQUIRE(!region.registerNoteOnNormalized(39, 64_norm, 0.5f)); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOffNormalized(40, 64_norm, 0.5f)); - REQUIRE(region.registerNoteOnNormalized(41, 64_norm, 0.5f)); - REQUIRE(region.registerNoteOnNormalized(42, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOnNormalized(43, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOffNormalized(42, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOffNormalized(42, 64_norm, 0.5f)); - REQUIRE(!region.registerCCNormalized(63, 64_norm)); + REQUIRE(!region.registerNoteOn(39, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(41, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(42, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(43, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(42, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(42, 64_norm, 0.5f)); + REQUIRE(!region.registerCC(63, 64_norm)); } SECTION("key and release trigger") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "trigger", "release" }); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.5f)); - REQUIRE(region.registerNoteOffNormalized(40, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOnNormalized(41, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOffNormalized(41, 64_norm, 0.5f)); - REQUIRE(!region.registerCCNormalized(63, 64_norm)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOff(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCC(63, 64_norm)); } SECTION("key and release_key trigger") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "trigger", "release_key" }); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.5f)); - REQUIRE(region.registerNoteOffNormalized(40, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOnNormalized(41, 64_norm, 0.5f)); - REQUIRE(!region.registerNoteOffNormalized(41, 64_norm, 0.5f)); - REQUIRE(!region.registerCCNormalized(63, 64_norm)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOff(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCC(63, 64_norm)); } // TODO: first and legato triggers SECTION("lovel and hivel") @@ -64,11 +64,11 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lovel", "60" }); region.parseOpcode({ "hivel", "70" }); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.5f)); - REQUIRE(region.registerNoteOnNormalized(40, 60_norm, 0.5f)); - REQUIRE(region.registerNoteOnNormalized(40, 70_norm, 0.5f)); - REQUIRE(!region.registerNoteOnNormalized(41, 71_norm, 0.5f)); - REQUIRE(!region.registerNoteOnNormalized(41, 59_norm, 0.5f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(40, 60_norm, 0.5f)); + REQUIRE(region.registerNoteOn(40, 70_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 71_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 59_norm, 0.5f)); } SECTION("lorand and hirand") @@ -76,54 +76,54 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lorand", "0.35" }); region.parseOpcode({ "hirand", "0.40" }); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.34f)); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.35f)); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.36f)); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.37f)); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.38f)); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.39f)); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.40f)); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.41f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.34f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.35f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.36f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.37f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.38f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.39f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.40f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.41f)); } SECTION("lorand and hirand on 1.0f") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lorand", "0.35" }); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.34f)); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.35f)); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 1.0f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.34f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.35f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f)); } SECTION("Disable key trigger") { region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 1.0f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f)); region.parseOpcode({ "hikey", "-1" }); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 1.0f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 1.0f)); region.parseOpcode({ "hikey", "40" }); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 1.0f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f)); region.parseOpcode({ "key", "-1" }); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 1.0f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 1.0f)); region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 1.0f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f)); } SECTION("on_loccN, on_hiccN") { region.parseOpcode({ "on_locc47", "64" }); region.parseOpcode({ "on_hicc47", "68" }); - REQUIRE(!region.registerCCNormalized(47, 63_norm)); - REQUIRE(!region.registerCCNormalized(47, 64_norm)); - REQUIRE(!region.registerCCNormalized(47, 65_norm)); + REQUIRE(!region.registerCC(47, 63_norm)); + REQUIRE(!region.registerCC(47, 64_norm)); + REQUIRE(!region.registerCC(47, 65_norm)); region.parseOpcode({ "hikey", "-1" }); - REQUIRE(region.registerCCNormalized(47, 64_norm)); - REQUIRE(region.registerCCNormalized(47, 65_norm)); - REQUIRE(region.registerCCNormalized(47, 66_norm)); - REQUIRE(region.registerCCNormalized(47, 67_norm)); - REQUIRE(region.registerCCNormalized(47, 68_norm)); - REQUIRE(!region.registerCCNormalized(47, 69_norm)); - REQUIRE(!region.registerCCNormalized(40, 64_norm)); + REQUIRE(region.registerCC(47, 64_norm)); + REQUIRE(region.registerCC(47, 65_norm)); + REQUIRE(region.registerCC(47, 66_norm)); + REQUIRE(region.registerCC(47, 67_norm)); + REQUIRE(region.registerCC(47, 68_norm)); + REQUIRE(!region.registerCC(47, 69_norm)); + REQUIRE(!region.registerCC(40, 64_norm)); } } @@ -138,15 +138,15 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "first" }); midiState.noteOnEventNormalized(0, 40, 64_norm); - REQUIRE(region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); midiState.noteOnEventNormalized(0, 41, 64_norm); - REQUIRE(!region.registerNoteOnNormalized(41, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); midiState.noteOffEventNormalized(0, 40, 0_norm); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); midiState.noteOffEventNormalized(0, 41, 0_norm); - region.registerNoteOffNormalized(41, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); midiState.noteOnEventNormalized(0, 42, 64_norm); - REQUIRE(region.registerNoteOnNormalized(42, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(42, 64_norm, 0.5f)); } SECTION("Second note playing") @@ -155,14 +155,14 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "legato" }); midiState.noteOnEventNormalized(0, 40, 64_norm); - REQUIRE(!region.registerNoteOnNormalized(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f)); midiState.noteOnEventNormalized(0, 41, 64_norm); - REQUIRE(region.registerNoteOnNormalized(41, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(41, 64_norm, 0.5f)); midiState.noteOffEventNormalized(0, 40, 64_norm); - region.registerNoteOffNormalized(40, 0_norm, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); midiState.noteOffEventNormalized(0, 41, 64_norm); - region.registerNoteOffNormalized(41, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); midiState.noteOnEventNormalized(0, 42, 64_norm); - REQUIRE(!region.registerNoteOnNormalized(42, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(42, 64_norm, 0.5f)); } } diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index bb3f205d..dd4193c3 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -21,9 +21,9 @@ TEST_CASE("[Region] Crossfade in on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "3" }); - REQUIRE( region.getNoteGainNormalized(2, 127_norm) == 0.70711_a ); - REQUIRE( region.getNoteGainNormalized(1, 127_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(3, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(2, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(3, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on key - 2") @@ -33,12 +33,12 @@ TEST_CASE("[Region] Crossfade in on key - 2") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); - REQUIRE( region.getNoteGainNormalized(1, 127_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(2, 127_norm) == 0.5_a ); - REQUIRE( region.getNoteGainNormalized(3, 127_norm) == 0.70711_a ); - REQUIRE( region.getNoteGainNormalized(4, 127_norm) == 0.86603_a ); - REQUIRE( region.getNoteGainNormalized(5, 127_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(6, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(2, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(3, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(4, 127_norm) == 0.86603_a ); + REQUIRE( region.getNoteGain(5, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(6, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on key - gain") @@ -49,11 +49,11 @@ TEST_CASE("[Region] Crossfade in on key - gain") region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE( region.getNoteGainNormalized(1, 127_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(2, 127_norm) == 0.25_a ); - REQUIRE( region.getNoteGainNormalized(3, 127_norm) == 0.5_a ); - REQUIRE( region.getNoteGainNormalized(4, 127_norm) == 0.75_a ); - REQUIRE( region.getNoteGainNormalized(5, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(2, 127_norm) == 0.25_a ); + REQUIRE( region.getNoteGain(3, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(4, 127_norm) == 0.75_a ); + REQUIRE( region.getNoteGain(5, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade out on key") @@ -63,13 +63,13 @@ TEST_CASE("[Region] Crossfade out on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); - REQUIRE( region.getNoteGainNormalized(50, 127_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(51, 127_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(52, 127_norm) == 0.86603_a ); - REQUIRE( region.getNoteGainNormalized(53, 127_norm) == 0.70711_a ); - REQUIRE( region.getNoteGainNormalized(54, 127_norm) == 0.5_a ); - REQUIRE( region.getNoteGainNormalized(55, 127_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(56, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(50, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(51, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(52, 127_norm) == 0.86603_a ); + REQUIRE( region.getNoteGain(53, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(54, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(55, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(56, 127_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade out on key - gain") @@ -80,13 +80,13 @@ TEST_CASE("[Region] Crossfade out on key - gain") region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE( region.getNoteGainNormalized(50, 127_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(51, 127_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(52, 127_norm) == 0.75_a ); - REQUIRE( region.getNoteGainNormalized(53, 127_norm) == 0.5_a ); - REQUIRE( region.getNoteGainNormalized(54, 127_norm) == 0.25_a ); - REQUIRE( region.getNoteGainNormalized(55, 127_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(56, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(50, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(51, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(52, 127_norm) == 0.75_a ); + REQUIRE( region.getNoteGain(53, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(54, 127_norm) == 0.25_a ); + REQUIRE( region.getNoteGain(55, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(56, 127_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade in on velocity") @@ -97,13 +97,13 @@ TEST_CASE("[Region] Crossfade in on velocity") region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGainNormalized(1, 19_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(1, 20_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(2, 21_norm) == 0.5_a ); - REQUIRE( region.getNoteGainNormalized(3, 22_norm) == 0.70711_a ); - REQUIRE( region.getNoteGainNormalized(4, 23_norm) == 0.86603_a ); - REQUIRE( region.getNoteGainNormalized(5, 24_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(6, 25_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(1, 19_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(1, 20_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(2, 21_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(3, 22_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(4, 23_norm) == 0.86603_a ); + REQUIRE( region.getNoteGain(5, 24_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(6, 25_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on vel - gain") @@ -115,13 +115,13 @@ TEST_CASE("[Region] Crossfade in on vel - gain") region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGainNormalized(1, 19_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(1, 20_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(2, 21_norm) == 0.25_a ); - REQUIRE( region.getNoteGainNormalized(3, 22_norm) == 0.5_a ); - REQUIRE( region.getNoteGainNormalized(4, 23_norm) == 0.75_a ); - REQUIRE( region.getNoteGainNormalized(5, 24_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(5, 25_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(1, 19_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(1, 20_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(2, 21_norm) == 0.25_a ); + REQUIRE( region.getNoteGain(3, 22_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(4, 23_norm) == 0.75_a ); + REQUIRE( region.getNoteGain(5, 24_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(5, 25_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade out on vel") @@ -132,13 +132,13 @@ TEST_CASE("[Region] Crossfade out on vel") region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGainNormalized(5, 50_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(5, 51_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(5, 52_norm) == 0.86603_a ); - REQUIRE( region.getNoteGainNormalized(5, 53_norm) == 0.70711_a ); - REQUIRE( region.getNoteGainNormalized(5, 54_norm) == 0.5_a ); - REQUIRE( region.getNoteGainNormalized(5, 55_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(5, 56_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(5, 50_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(5, 51_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(5, 52_norm) == 0.86603_a ); + REQUIRE( region.getNoteGain(5, 53_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(5, 54_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(5, 55_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(5, 56_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade out on vel - gain") @@ -150,13 +150,13 @@ TEST_CASE("[Region] Crossfade out on vel - gain") region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGainNormalized(56, 50_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(56, 51_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(56, 52_norm) == 0.75_a ); - REQUIRE( region.getNoteGainNormalized(56, 53_norm) == 0.5_a ); - REQUIRE( region.getNoteGainNormalized(56, 54_norm) == 0.25_a ); - REQUIRE( region.getNoteGainNormalized(56, 55_norm) == 0.0_a ); - REQUIRE( region.getNoteGainNormalized(56, 56_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(56, 50_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(56, 51_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(56, 52_norm) == 0.75_a ); + REQUIRE( region.getNoteGain(56, 53_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(56, 54_norm) == 0.25_a ); + REQUIRE( region.getNoteGain(56, 55_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(56, 56_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade in on CC") @@ -234,8 +234,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGainNormalized(64, 127_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(64, 0_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(64, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(64, 0_norm) == 1.0_a ); } @@ -245,8 +245,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "100" }); - REQUIRE( region.getNoteGainNormalized(64, 127_norm) == 1.0_a ); - REQUIRE( region.getNoteGainNormalized(64, 0_norm) == Approx(0.0).margin(0.0001) ); + REQUIRE( region.getNoteGain(64, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(64, 0_norm) == Approx(0.0).margin(0.0001) ); } TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") @@ -255,8 +255,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "-100" }); - REQUIRE( region.getNoteGainNormalized(64, 127_norm) == Approx(0.0).margin(0.0001) ); - REQUIRE( region.getNoteGainNormalized(64, 0_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(64, 127_norm) == Approx(0.0).margin(0.0001) ); + REQUIRE( region.getNoteGain(64, 0_norm) == 1.0_a ); } TEST_CASE("[Region] rt_decay") From a1dba6b60f384d1da0957cdd33737d9277fc9c32 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:35:56 +0100 Subject: [PATCH 14/19] Remove the uint8 versions from midistate --- src/sfizz/MidiState.cpp | 25 ------------------------- src/sfizz/MidiState.h | 39 --------------------------------------- 2 files changed, 64 deletions(-) diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 475a8352..34332ea5 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -13,16 +13,6 @@ sfz::MidiState::MidiState() reset(0); } -void sfz::MidiState::noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept -{ - noteOnEventNormalized(delay, noteNumber, normalizeVelocity(velocity)); -} - -void sfz::MidiState::noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept -{ - noteOffEventNormalized(delay, noteNumber, normalizeVelocity(velocity)); -} - void sfz::MidiState::noteOnEventNormalized(int delay, int noteNumber, float velocity) noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); @@ -61,11 +51,6 @@ float sfz::MidiState::getNoteDuration(int noteNumber) const return 0.0f; } -uint8_t sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept -{ - return denormalizeVelocity(getNoteVelocityNormalized(noteNumber)); -} - float sfz::MidiState::getNoteVelocityNormalized(int noteNumber) const noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); @@ -86,16 +71,6 @@ int sfz::MidiState::getPitchBend() const noexcept return pitchBend; } -void sfz::MidiState::ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept -{ - ccEventNormalized(delay, ccNumber, normalizeCC(ccValue)); -} - -uint8_t sfz::MidiState::getCCValue(int ccNumber) const noexcept -{ - return denormalizeCC(getCCValueNormalized(ccNumber)); -} - void sfz::MidiState::ccEventNormalized(int delay, int ccNumber, float ccValue) noexcept { ASSERT(ccValue >= 0.0 && ccValue <= 1.0); diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index 36c8d00e..cfe77bb8 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -22,13 +22,6 @@ class MidiState { public: MidiState(); - /** - * @brief Update the state after a note on event - * - * @param noteNumber - * @param velocity - */ - void noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept; /** * @brief Update the state after a note on event @@ -38,14 +31,6 @@ public: */ void noteOnEventNormalized(int delay, int noteNumber, float velocity) noexcept; - /** - * @brief Update the state after a note off event - * - * @param noteNumber - * @param velocity - */ - void noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept; - /** * @brief Update the state after a note off event * @@ -64,14 +49,6 @@ public: */ float getNoteDuration(int noteNumber) const; - /** - * @brief Get the note on velocity for a given note - * - * @param noteNumber - * @return uint8_t - */ - uint8_t getNoteVelocity(int noteNumber) const noexcept; - /** * @brief Get the note on velocity for a given note * @@ -94,14 +71,6 @@ public: */ int getPitchBend() const noexcept; - /** - * @brief Register a CC event - * - * @param ccNumber - * @param ccValue - */ - void ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept; - /** * @brief Register a CC event * @@ -110,14 +79,6 @@ public: */ void ccEventNormalized(int delay, int ccNumber, float ccValue) noexcept; - /** - * @brief Get the CC value for CC number - * - * @param ccNumber - * @return uint8_t - */ - uint8_t getCCValue(int ccNumber) const noexcept; - /** * @brief Get the CC value for CC number * From 4c2e0ab6b9281f5a26c51114531a3b0abdd67ddd Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:37:25 +0100 Subject: [PATCH 15/19] Refactor the midistate functions --- src/sfizz/EGDescription.h | 2 +- src/sfizz/MidiState.cpp | 10 ++--- src/sfizz/MidiState.h | 12 +++--- src/sfizz/Region.cpp | 4 +- src/sfizz/Synth.cpp | 12 +++--- src/sfizz/Voice.cpp | 12 +++--- tests/EGDescriptionT.cpp | 14 +++---- tests/FilesT.cpp | 4 +- tests/MidiStateT.cpp | 28 +++++++------- tests/RegionTriggersT.cpp | 20 +++++----- tests/RegionValueComputationsT.cpp | 62 +++++++++++++++--------------- tests/SynthT.cpp | 4 +- 12 files changed, 92 insertions(+), 92 deletions(-) diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index c84717e6..3064b3c8 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -55,7 +55,7 @@ namespace sfz inline float ccSwitchedValue(const MidiState& state, const absl::optional>& ccSwitch, float value) noexcept { if (ccSwitch) - return value + ccSwitch->value * state.getCCValueNormalized(ccSwitch->cc); + return value + ccSwitch->value * state.getCCValue(ccSwitch->cc); else return value; } diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 34332ea5..6e8ccac3 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -13,7 +13,7 @@ sfz::MidiState::MidiState() reset(0); } -void sfz::MidiState::noteOnEventNormalized(int delay, int noteNumber, float velocity) noexcept +void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); ASSERT(velocity >= 0 && velocity <= 1.0); @@ -26,7 +26,7 @@ void sfz::MidiState::noteOnEventNormalized(int delay, int noteNumber, float velo } -void sfz::MidiState::noteOffEventNormalized(int delay, int noteNumber, float velocity) noexcept +void sfz::MidiState::noteOffEvent(int delay, int noteNumber, float velocity) noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); ASSERT(velocity >= 0.0 && velocity <= 1.0); @@ -51,7 +51,7 @@ float sfz::MidiState::getNoteDuration(int noteNumber) const return 0.0f; } -float sfz::MidiState::getNoteVelocityNormalized(int noteNumber) const noexcept +float sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); @@ -71,14 +71,14 @@ int sfz::MidiState::getPitchBend() const noexcept return pitchBend; } -void sfz::MidiState::ccEventNormalized(int delay, int ccNumber, float ccValue) noexcept +void sfz::MidiState::ccEvent(int delay, int ccNumber, float ccValue) noexcept { ASSERT(ccValue >= 0.0 && ccValue <= 1.0); cc[ccNumber] = ccValue; } -float sfz::MidiState::getCCValueNormalized(int ccNumber) const noexcept +float sfz::MidiState::getCCValue(int ccNumber) const noexcept { ASSERT(ccNumber >= 0 && ccNumber < config::numCCs); diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index cfe77bb8..09c4ce78 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -29,7 +29,7 @@ public: * @param noteNumber * @param velocity */ - void noteOnEventNormalized(int delay, int noteNumber, float velocity) noexcept; + void noteOnEvent(int delay, int noteNumber, float velocity) noexcept; /** * @brief Update the state after a note off event @@ -37,7 +37,7 @@ public: * @param noteNumber * @param velocity */ - void noteOffEventNormalized(int delay, int noteNumber, float velocity) noexcept; + void noteOffEvent(int delay, int noteNumber, float velocity) noexcept; int getActiveNotes() const noexcept { return activeNotes; } @@ -55,7 +55,7 @@ public: * @param noteNumber * @return float */ - float getNoteVelocityNormalized(int noteNumber) const noexcept; + float getNoteVelocity(int noteNumber) const noexcept; /** * @brief Register a pitch bend event @@ -77,7 +77,7 @@ public: * @param ccNumber * @param ccValue */ - void ccEventNormalized(int delay, int ccNumber, float ccValue) noexcept; + void ccEvent(int delay, int ccNumber, float ccValue) noexcept; /** * @brief Get the CC value for CC number @@ -85,7 +85,7 @@ public: * @param ccNumber * @return float */ - float getCCValueNormalized(int ccNumber) const noexcept; + float getCCValue(int ccNumber) const noexcept; /** * @brief Reset the midi state (does not impact the last note on time) @@ -113,7 +113,7 @@ public: T modulate(T value, const CCMap& modifiers, const Range& validRange, const modFunction& lambda = addToBase) const noexcept { for (auto& mod: modifiers) { - lambda(value, getCCValueNormalized(mod.cc) * mod.value); + lambda(value, getCCValue(mod.cc) * mod.value); } return validRange.clamp(value); } diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 1ab1bf9e..77ab9083 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1068,13 +1068,13 @@ float sfz::Region::getCrossfadeGain() const noexcept // Crossfades due to CC states for (const auto& valuePair : crossfadeCCInRange) { - const auto ccValue = midiState.getCCValueNormalized(valuePair.cc); + const auto ccValue = midiState.getCCValue(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve); } for (const auto& valuePair : crossfadeCCOutRange) { - const auto ccValue = midiState.getCCValueNormalized(valuePair.cc); + const auto ccValue = midiState.getCCValue(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); } diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index dd23d4fe..a3efca3e 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -183,7 +183,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) { const auto ccValue = readOpcode(member.value, Default::midi7Range); if (ccValue) - resources.midiState.ccEventNormalized(0, member.parameters.back(), normalizeCC(*ccValue)); + resources.midiState.ccEvent(0, member.parameters.back(), normalizeCC(*ccValue)); } break; case hash("Label_cc&"): // fallthrough @@ -387,7 +387,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) // Defaults for (unsigned cc = 0; cc < config::numCCs; cc++) { - region->registerCC(cc, resources.midiState.getCCValueNormalized(cc)); + region->registerCC(cc, resources.midiState.getCCValue(cc)); } if (defaultSwitch) { @@ -603,7 +603,7 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept ASSERT(noteNumber >= 0); const auto normalizedVelocity = normalizeVelocity(velocity); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.noteOnEventNormalized(delay, noteNumber, normalizedVelocity); + resources.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) @@ -619,7 +619,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept UNUSED(velocity); const auto normalizedVelocity = normalizeVelocity(velocity); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.noteOffEventNormalized(delay, noteNumber, normalizedVelocity); + resources.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) @@ -628,7 +628,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept // FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a // way in sfz to specify that a release trigger should NOT use the note-on velocity? // auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity); - const auto replacedVelocity = resources.midiState.getNoteVelocityNormalized(noteNumber); + const auto replacedVelocity = resources.midiState.getNoteVelocity(noteNumber); for (auto& voice : voices) voice->registerNoteOff(delay, noteNumber, replacedVelocity); @@ -676,7 +676,7 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept const auto normalizedCC = normalizeCC(ccValue); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.ccEventNormalized(delay, ccNumber, normalizedCC); + resources.midiState.ccEvent(delay, ccNumber, normalizedCC); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 779e514e..1979c08e 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -78,7 +78,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, baseVolumedB = region->getBaseVolumedB(number); auto volumedB = baseVolumedB; if (region->volumeCC) - volumedB += resources.midiState.getCCValueNormalized(region->volumeCC->cc) * region->volumeCC->value; + volumedB += resources.midiState.getCCValue(region->volumeCC->cc) * region->volumeCC->value; volumeEnvelope.reset(db2mag(Default::volumeRange.clamp(volumedB))); baseGain = region->getBaseGain(); @@ -87,7 +87,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, float gain { baseGain }; if (region->amplitudeCC) - gain += resources.midiState.getCCValueNormalized(region->amplitudeCC->cc) * normalizePercents(region->amplitudeCC->value); + gain += resources.midiState.getCCValue(region->amplitudeCC->cc) * normalizePercents(region->amplitudeCC->value); amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain)); float crossfadeGain { region->getCrossfadeGain() }; @@ -96,19 +96,19 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, basePan = normalizePercents(region->pan); auto pan = basePan; if (region->panCC) - pan += resources.midiState.getCCValueNormalized(region->panCC->cc) * normalizePercents(region->panCC->value); + pan += resources.midiState.getCCValue(region->panCC->cc) * normalizePercents(region->panCC->value); panEnvelope.reset(Default::symmetricNormalizedRange.clamp(pan)); basePosition = normalizePercents(region->position); auto position = basePosition; if (region->positionCC) - position += resources.midiState.getCCValueNormalized(region->positionCC->cc) * normalizePercents(region->positionCC->value); + position += resources.midiState.getCCValue(region->positionCC->cc) * normalizePercents(region->positionCC->value); positionEnvelope.reset(Default::symmetricNormalizedRange.clamp(position)); baseWidth = normalizePercents(region->width); auto width = baseWidth; if (region->widthCC) - width += resources.midiState.getCCValueNormalized(region->widthCC->cc) * normalizePercents(region->widthCC->value); + width += resources.midiState.getCCValue(region->widthCC->cc) * normalizePercents(region->widthCC->value); widthEnvelope.reset(Default::symmetricNormalizedRange.clamp(width)); pitchBendEnvelope.setFunction([region](float pitchValue){ @@ -177,7 +177,7 @@ void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noex if (region->loopMode == SfzLoopMode::one_shot) return; - if (!region->checkSustain || resources.midiState.getCCValueNormalized(config::sustainCC) < config::halfCCThreshold) + if (!region->checkSustain || resources.midiState.getCCValue(config::sustainCC) < config::halfCCThreshold) release(delay); } } diff --git a/tests/EGDescriptionT.cpp b/tests/EGDescriptionT.cpp index 8bbccc10..291e222d 100644 --- a/tests/EGDescriptionT.cpp +++ b/tests/EGDescriptionT.cpp @@ -20,7 +20,7 @@ TEST_CASE("[EGDescription] Attack range") eg.ccAttack = { 63, 1.27f }; REQUIRE( eg.getAttack(state, 0) == 1.0f ); REQUIRE( eg.getAttack(state, 127) == 0.0f ); - state.ccEventNormalized(0, 63, 127_norm); + state.ccEvent(0, 63, 127_norm); REQUIRE( eg.getAttack(state, 127) == 1.0f ); REQUIRE( eg.getAttack(state, 0) == 2.27f ); eg.ccAttack = { 63, 127.0f }; @@ -36,7 +36,7 @@ TEST_CASE("[EGDescription] Delay range") eg.ccDelay = { 63, 1.27f }; REQUIRE( eg.getDelay(state, 0) == 1.0f ); REQUIRE( eg.getDelay(state, 127) == 0.0f ); - state.ccEventNormalized(0, 63, 127_norm); + state.ccEvent(0, 63, 127_norm); REQUIRE( eg.getDelay(state, 127) == 1.0f ); REQUIRE( eg.getDelay(state, 0) == 2.27f ); eg.ccDelay = { 63, 127.0f }; @@ -52,7 +52,7 @@ TEST_CASE("[EGDescription] Decay range") eg.ccDecay = { 63, 1.27f }; REQUIRE( eg.getDecay(state, 0) == 1.0f ); REQUIRE( eg.getDecay(state, 127) == 0.0f ); - state.ccEventNormalized(0, 63, 127_norm); + state.ccEvent(0, 63, 127_norm); REQUIRE( eg.getDecay(state, 127) == 1.0f ); REQUIRE( eg.getDecay(state, 0) == 2.27f ); eg.ccDecay = { 63, 127.0f }; @@ -68,7 +68,7 @@ TEST_CASE("[EGDescription] Release range") eg.ccRelease = { 63, 1.27f }; REQUIRE( eg.getRelease(state, 0) == 1.0f ); REQUIRE( eg.getRelease(state, 127) == 0.0f ); - state.ccEventNormalized(0, 63, 127_norm); + state.ccEvent(0, 63, 127_norm); REQUIRE( eg.getRelease(state, 127) == 1.0f ); REQUIRE( eg.getRelease(state, 0) == 2.27f ); eg.ccRelease = { 63, 127.0f }; @@ -84,7 +84,7 @@ TEST_CASE("[EGDescription] Hold range") eg.ccHold = { 63, 1.27f }; REQUIRE( eg.getHold(state, 0) == 1.0f ); REQUIRE( eg.getHold(state, 127) == 0.0f ); - state.ccEventNormalized(0, 63, 127_norm); + state.ccEvent(0, 63, 127_norm); REQUIRE( eg.getHold(state, 127) == 1.0f ); REQUIRE( eg.getHold(state, 0) == 2.27f ); eg.ccHold = { 63, 127.0f }; @@ -100,7 +100,7 @@ TEST_CASE("[EGDescription] Sustain level") eg.ccSustain = { 63, 100.0f }; REQUIRE( eg.getSustain(state, 0) == 50.0f ); REQUIRE( eg.getSustain(state, 127) == 0.0f ); - state.ccEventNormalized(0, 63, 127_norm); + state.ccEvent(0, 63, 127_norm); REQUIRE( eg.getSustain(state, 127) == 50.0f ); eg.ccSustain = { 63, 200.0f }; REQUIRE( eg.getSustain(state, 0) == 100.0f ); @@ -114,7 +114,7 @@ TEST_CASE("[EGDescription] Start level") eg.ccStart = { 63, 127.0f }; REQUIRE( eg.getStart(state, 0) == 0.0f ); REQUIRE( eg.getStart(state, 127) == 0.0f ); - state.ccEventNormalized(0, 63, 127_norm); + state.ccEvent(0, 63, 127_norm); REQUIRE( eg.getStart(state, 0) == 100.0f ); eg.ccStart = { 63, -127.0f }; REQUIRE( eg.getStart(state, 0) == 0.0f ); diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index fa45e69a..52d71e0d 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -355,8 +355,8 @@ TEST_CASE("[Files] Set CC applies properly") { sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz"); - REQUIRE(synth.getMidiState().getCCValueNormalized(142) == 63_norm); - REQUIRE(synth.getMidiState().getCCValueNormalized(61) == 122_norm); + REQUIRE(synth.getMidiState().getCCValue(142) == 63_norm); + REQUIRE(synth.getMidiState().getCCValue(61) == 122_norm); } TEST_CASE("[Files] Note and octave offsets") diff --git a/tests/MidiStateT.cpp b/tests/MidiStateT.cpp index 4529ecb4..b00bbe64 100644 --- a/tests/MidiStateT.cpp +++ b/tests/MidiStateT.cpp @@ -21,17 +21,17 @@ TEST_CASE("[MidiState] Initial values") { sfz::MidiState state; for (unsigned cc = 0; cc < sfz::config::numCCs; cc++) - REQUIRE( state.getCCValueNormalized(cc) == 0_norm ); + REQUIRE( state.getCCValue(cc) == 0_norm ); REQUIRE( state.getPitchBend() == 0 ); } TEST_CASE("[MidiState] Set and get CCs") { sfz::MidiState state; - state.ccEventNormalized(0, 24, 23_norm); - state.ccEventNormalized(0, 123, 124_norm); - REQUIRE(state.getCCValueNormalized(24) == 23_norm); - REQUIRE(state.getCCValueNormalized(123) == 124_norm); + state.ccEvent(0, 24, 23_norm); + state.ccEvent(0, 123, 124_norm); + REQUIRE(state.getCCValue(24) == 23_norm); + REQUIRE(state.getCCValue(123) == 124_norm); } TEST_CASE("[MidiState] Set and get pitch bends") @@ -47,25 +47,25 @@ TEST_CASE("[MidiState] Reset") { sfz::MidiState state; state.pitchBendEvent(0, 894); - state.noteOnEventNormalized(0, 64, 24_norm); - state.ccEventNormalized(0, 123, 124_norm); + state.noteOnEvent(0, 64, 24_norm); + state.ccEvent(0, 123, 124_norm); state.reset(0); REQUIRE(state.getPitchBend() == 0); - REQUIRE(state.getNoteVelocityNormalized(64) == 0_norm); - REQUIRE(state.getCCValueNormalized(123) == 0_norm); + REQUIRE(state.getNoteVelocity(64) == 0_norm); + REQUIRE(state.getCCValue(123) == 0_norm); } TEST_CASE("[MidiState] Set and get note velocities") { sfz::MidiState state; - state.noteOnEventNormalized(0, 64, 24_norm); - REQUIRE(+state.getNoteVelocityNormalized(64) == 24_norm); - state.noteOnEventNormalized(0, 64, 123_norm); - REQUIRE(+state.getNoteVelocityNormalized(64) == 123_norm); + state.noteOnEvent(0, 64, 24_norm); + REQUIRE(+state.getNoteVelocity(64) == 24_norm); + state.noteOnEvent(0, 64, 123_norm); + REQUIRE(+state.getNoteVelocity(64) == 123_norm); } TEST_CASE("[MidiState] Extended CCs") { sfz::MidiState state; - state.ccEventNormalized(0, 142, 64_norm); // should not trap + state.ccEvent(0, 142, 64_norm); // should not trap } diff --git a/tests/RegionTriggersT.cpp b/tests/RegionTriggersT.cpp index 276c0587..75f5d9b5 100644 --- a/tests/RegionTriggersT.cpp +++ b/tests/RegionTriggersT.cpp @@ -137,15 +137,15 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "first" }); - midiState.noteOnEventNormalized(0, 40, 64_norm); + midiState.noteOnEvent(0, 40, 64_norm); REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); - midiState.noteOnEventNormalized(0, 41, 64_norm); + midiState.noteOnEvent(0, 41, 64_norm); REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); - midiState.noteOffEventNormalized(0, 40, 0_norm); + midiState.noteOffEvent(0, 40, 0_norm); region.registerNoteOff(40, 0_norm, 0.5f); - midiState.noteOffEventNormalized(0, 41, 0_norm); + midiState.noteOffEvent(0, 41, 0_norm); region.registerNoteOff(41, 0_norm, 0.5f); - midiState.noteOnEventNormalized(0, 42, 64_norm); + midiState.noteOnEvent(0, 42, 64_norm); REQUIRE(region.registerNoteOn(42, 64_norm, 0.5f)); } @@ -154,15 +154,15 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "legato" }); - midiState.noteOnEventNormalized(0, 40, 64_norm); + midiState.noteOnEvent(0, 40, 64_norm); REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f)); - midiState.noteOnEventNormalized(0, 41, 64_norm); + midiState.noteOnEvent(0, 41, 64_norm); REQUIRE(region.registerNoteOn(41, 64_norm, 0.5f)); - midiState.noteOffEventNormalized(0, 40, 64_norm); + midiState.noteOffEvent(0, 40, 64_norm); region.registerNoteOff(40, 0_norm, 0.5f); - midiState.noteOffEventNormalized(0, 41, 64_norm); + midiState.noteOffEvent(0, 41, 64_norm); region.registerNoteOff(41, 0_norm, 0.5f); - midiState.noteOnEventNormalized(0, 42, 64_norm); + midiState.noteOnEvent(0, 42, 64_norm); REQUIRE(!region.registerNoteOn(42, 64_norm, 0.5f)); } } diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index dd4193c3..31566e76 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -167,13 +167,13 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_locc24", "20" }); region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.ccEventNormalized(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEventNormalized(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEventNormalized(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); - midiState.ccEventNormalized(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); - midiState.ccEventNormalized(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); - midiState.ccEventNormalized(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEventNormalized(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); + midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); + midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -185,13 +185,13 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.ccEventNormalized(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEventNormalized(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEventNormalized(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); - midiState.ccEventNormalized(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); - midiState.ccEventNormalized(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); - midiState.ccEventNormalized(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEventNormalized(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); + midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); + midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); } TEST_CASE("[Region] Crossfade out on CC") { @@ -201,13 +201,13 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_locc24", "20" }); region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.ccEventNormalized(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEventNormalized(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEventNormalized(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); - midiState.ccEventNormalized(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); - midiState.ccEventNormalized(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); - midiState.ccEventNormalized(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEventNormalized(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); + midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); + midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -219,13 +219,13 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.ccEventNormalized(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEventNormalized(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); - midiState.ccEventNormalized(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); - midiState.ccEventNormalized(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); - midiState.ccEventNormalized(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); - midiState.ccEventNormalized(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); - midiState.ccEventNormalized(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); + midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); + midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") @@ -266,15 +266,15 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "trigger", "release" }); region.parseOpcode({ "rt_decay", "10" }); - midiState.noteOnEventNormalized(0, 64, 64_norm); + midiState.noteOnEvent(0, 64, 64_norm); 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" }); - midiState.noteOnEventNormalized(0, 64, 64_norm); + midiState.noteOnEvent(0, 64, 64_norm); 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" }); - midiState.noteOnEventNormalized(0, 64, 64_norm); + midiState.noteOnEvent(0, 64, 64_norm); std::this_thread::sleep_for(std::chrono::milliseconds(100)); REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) ); } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 2f2f98bc..4411d12b 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -141,9 +141,9 @@ TEST_CASE("[Synth] Reset all controllers") { sfz::Synth synth; synth.cc(0, 12, 64); - REQUIRE( synth.getMidiState().getCCValueNormalized(12) == 64_norm ); + REQUIRE( synth.getMidiState().getCCValue(12) == 64_norm ); synth.cc(0, 121, 64); - REQUIRE( synth.getMidiState().getCCValueNormalized(12) == 0_norm ); + REQUIRE( synth.getMidiState().getCCValue(12) == 0_norm ); } TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice") From de0d86fa098c39250c8e8a31eb1d6848e6a578e1 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:46:48 +0100 Subject: [PATCH 16/19] Use float velocity in filters and EQ --- src/sfizz/EQPool.cpp | 10 +++++----- src/sfizz/EQPool.h | 4 ++-- src/sfizz/FilterPool.cpp | 8 +++++--- src/sfizz/FilterPool.h | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/sfizz/EQPool.cpp b/src/sfizz/EQPool.cpp index da8c9c2b..c3883124 100644 --- a/src/sfizz/EQPool.cpp +++ b/src/sfizz/EQPool.cpp @@ -15,17 +15,17 @@ void sfz::EQHolder::reset() eq.clear(); } -void sfz::EQHolder::setup(const EQDescription& description, unsigned numChannels, uint8_t velocity) +void sfz::EQHolder::setup(const EQDescription& description, unsigned numChannels, float velocity) { + ASSERT(velocity >= 0.0f && velocity <= 1.0f); eq.setType(description.type); eq.setChannels(numChannels); this->description = &description; - const auto normalizedVelocity = normalizeVelocity(velocity); // Setup the base values - baseFrequency = description.frequency + normalizedVelocity * description.vel2frequency; + baseFrequency = description.frequency + velocity * description.vel2frequency; baseBandwidth = description.bandwidth; - baseGain = description.gain + normalizedVelocity * description.vel2gain; + baseGain = description.gain + velocity * description.vel2gain; // Setup the modulated values lastFrequency = midiState.modulate(baseFrequency, description.frequencyCC, Default::eqFrequencyRange); @@ -84,7 +84,7 @@ sfz::EQPool::EQPool(const MidiState& state, int numEQs) setnumEQs(numEQs); } -sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, uint8_t velocity) +sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, float velocity) { AtomicGuard guard { givingOutEQs }; if (!canGiveOutEQs) diff --git a/src/sfizz/EQPool.h b/src/sfizz/EQPool.h index 879a2cdd..9890c10a 100644 --- a/src/sfizz/EQPool.h +++ b/src/sfizz/EQPool.h @@ -20,7 +20,7 @@ public: * @param numChannels the number of channels for the EQ * @param description the triggering velocity/value */ - void setup(const EQDescription& description, unsigned numChannels, uint8_t velocity); + void setup(const EQDescription& description, unsigned numChannels, float velocity); /** * @brief Process a block of stereo inputs * @@ -90,7 +90,7 @@ public: * @param velocity the triggering note velocity/value * @return EQHolderPtr release this when done with the filter; no deallocation will be done */ - EQHolderPtr getEQ(const EQDescription& description, unsigned numChannels, uint8_t velocity); + EQHolderPtr getEQ(const EQDescription& description, unsigned numChannels, float velocity); /** * @brief Get the number of active EQs * diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index 3be79d7f..96f97308 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -16,8 +16,10 @@ void sfz::FilterHolder::reset() filter.clear(); } -void sfz::FilterHolder::setup(const FilterDescription& description, unsigned numChannels, int noteNumber, uint8_t velocity) +void sfz::FilterHolder::setup(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity) { + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + this->description = &description; filter.setType(description.type); filter.setChannels(numChannels); @@ -30,7 +32,7 @@ void sfz::FilterHolder::setup(const FilterDescription& description, unsigned num } const auto keytrack = description.keytrack * (noteNumber - description.keycenter); baseCutoff *= centsFactor(keytrack); - const auto veltrack = static_cast(description.veltrack) * normalizeVelocity(velocity); + const auto veltrack = static_cast(description.veltrack) * velocity; baseCutoff *= centsFactor(veltrack); baseCutoff = Default::filterCutoffRange.clamp(baseCutoff); @@ -83,7 +85,7 @@ sfz::FilterPool::FilterPool(const MidiState& state, int numFilters) setNumFilters(numFilters); } -sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, uint8_t velocity) +sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity) { AtomicGuard guard { givingOutFilters }; if (!canGiveOutFilters) diff --git a/src/sfizz/FilterPool.h b/src/sfizz/FilterPool.h index 792777c6..fb1a8aac 100644 --- a/src/sfizz/FilterPool.h +++ b/src/sfizz/FilterPool.h @@ -21,7 +21,7 @@ public: * @param noteNumber the triggering note number * @param velocity the triggering note velocity/value */ - void setup(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast(Default::filterKeycenter), uint8_t velocity = 0); + void setup(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast(Default::filterKeycenter), float velocity = 0); /** * @brief Process a block of stereo inputs * @@ -94,7 +94,7 @@ public: * @param velocity the triggering note velocity * @return FilterHolderPtr release this when done with the filter; no deallocation will be done */ - FilterHolderPtr getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast(Default::filterKeycenter), uint8_t velocity = 0); + FilterHolderPtr getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast(Default::filterKeycenter), float velocity = 0); /** * @brief Get the number of active filters * From 787fb963f3f45421790fb7a24c0b9f175f2853d4 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:46:58 +0100 Subject: [PATCH 17/19] Use float velocities in envelopes --- src/sfizz/ADSREnvelope.cpp | 2 +- src/sfizz/ADSREnvelope.h | 2 +- src/sfizz/EGDescription.h | 83 ++++++++++++++++++++------------------ src/sfizz/Voice.cpp | 2 +- tests/ADSREnvelopeT.cpp | 36 ++++++++--------- tests/EGDescriptionT.cpp | 66 +++++++++++++++--------------- 6 files changed, 97 insertions(+), 94 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 21a76cf9..0acbdbe3 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -12,7 +12,7 @@ namespace sfz { template -void ADSREnvelope::reset(const Region& region, const MidiState& state, int delay, uint8_t velocity, float sampleRate) noexcept +void ADSREnvelope::reset(const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept { auto secondsToSamples = [sampleRate](Type timeInSeconds) { return static_cast(timeInSeconds * sampleRate); diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index f2468a09..6a647d03 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -29,7 +29,7 @@ public: * @param delay * @param velocity */ - void reset(const Region& region, const MidiState& state, int delay, uint8_t velocity, float sampleRate) noexcept; + void reset(const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept; /** * @brief Get the next value for the envelope * diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index 3064b3c8..ee44f754 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -32,9 +32,7 @@ #include "MidiState.h" #include - -namespace sfz -{ +namespace sfz { /** * @brief A description for an SFZ envelope generator, with its envelope parameters * and possible CC modulation. This is a structure to be integrated directly in a @@ -60,36 +58,35 @@ inline float ccSwitchedValue(const MidiState& state, const absl::optional> ccAttack; - absl::optional> ccDecay; - absl::optional> ccDelay; - absl::optional> ccHold; - absl::optional> ccRelease; - absl::optional> ccStart; - absl::optional> ccSustain; + absl::optional> ccAttack; + absl::optional> ccDecay; + absl::optional> ccDelay; + absl::optional> ccHold; + absl::optional> ccRelease; + absl::optional> ccStart; + absl::optional> ccSustain; /** * @brief Get the attack with possibly a CC modifier and a velocity modifier @@ -98,9 +95,10 @@ struct EGDescription * @param velocity * @return float */ - float getAttack(const MidiState &state, uint8_t velocity) const noexcept + float getAttack(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, ccAttack, attack) + normalizeVelocity(velocity)*vel2attack); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccAttack, attack) + velocity * vel2attack); } /** * @brief Get the decay with possibly a CC modifier and a velocity modifier @@ -109,9 +107,10 @@ struct EGDescription * @param velocity * @return float */ - float getDecay(const MidiState &state, uint8_t velocity) const noexcept + float getDecay(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDecay, decay) + normalizeVelocity(velocity)*vel2decay); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDecay, decay) + velocity * vel2decay); } /** * @brief Get the delay with possibly a CC modifier and a velocity modifier @@ -120,9 +119,10 @@ struct EGDescription * @param velocity * @return float */ - float getDelay(const MidiState &state, uint8_t velocity) const noexcept + float getDelay(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDelay, delay) + normalizeVelocity(velocity)*vel2delay); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDelay, delay) + velocity * vel2delay); } /** * @brief Get the holding duration with possibly a CC modifier and a velocity modifier @@ -131,9 +131,10 @@ struct EGDescription * @param velocity * @return float */ - float getHold(const MidiState &state, uint8_t velocity) const noexcept + float getHold(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, ccHold, hold) + normalizeVelocity(velocity)*vel2hold); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccHold, hold) + velocity * vel2hold); } /** * @brief Get the release duration with possibly a CC modifier and a velocity modifier @@ -142,9 +143,10 @@ struct EGDescription * @param velocity * @return float */ - float getRelease(const MidiState &state, uint8_t velocity) const noexcept + float getRelease(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, ccRelease, release) + normalizeVelocity(velocity)*vel2release); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccRelease, release) + velocity * vel2release); } /** * @brief Get the starting level with possibly a CC modifier and a velocity modifier @@ -153,7 +155,7 @@ struct EGDescription * @param velocity * @return float */ - float getStart(const MidiState &state, uint8_t velocity) const noexcept + float getStart(const MidiState& state, float velocity) const noexcept { UNUSED(velocity); return Default::egPercentRange.clamp(ccSwitchedValue(state, ccStart, start)); @@ -165,9 +167,10 @@ struct EGDescription * @param velocity * @return float */ - float getSustain(const MidiState &state, uint8_t velocity) const noexcept + float getSustain(const MidiState& state, float velocity) const noexcept { - return Default::egPercentRange.clamp(ccSwitchedValue(state, ccSustain, sustain) + normalizeVelocity(velocity)*vel2sustain); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egPercentRange.clamp(ccSwitchedValue(state, ccSustain, sustain) + velocity * vel2sustain); } LEAK_DETECTOR(EGDescription); }; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 1979c08e..79d7ccbe 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -140,7 +140,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, initialDelay = delay + static_cast(region->getDelay() * sampleRate); baseFrequency = midiNoteFrequency(number); bendStepFactor = centsFactor(region->bendStep); - egEnvelope.reset(*region, resources.midiState, delay, denormalize7Bits(value), sampleRate); + egEnvelope.reset(*region, resources.midiState, delay, value, sampleRate); } bool sfz::Voice::isFree() const noexcept diff --git a/tests/ADSREnvelopeT.cpp b/tests/ADSREnvelopeT.cpp index f11945b4..22783f3d 100644 --- a/tests/ADSREnvelopeT.cpp +++ b/tests/ADSREnvelopeT.cpp @@ -49,14 +49,14 @@ TEST_CASE("[ADSREnvelope] Attack") sfz::Region region { state }; region.amplitudeEG.attack = 0.02f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array output; std::array expected { 0.5f, 1.0f, 1.0f, 1.0f, 1.0f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -69,14 +69,14 @@ TEST_CASE("[ADSREnvelope] Attack again") sfz::Region region { state }; region.amplitudeEG.attack = 0.03f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array output; std::array expected { 0.33333f, 0.66667f, 1.0f, 1.0f, 1.0f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -90,7 +90,7 @@ TEST_CASE("[ADSREnvelope] Release") region.amplitudeEG.attack = 0.02f; region.amplitudeEG.release = 0.04f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(2); std::array output; std::array expected { 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f }; @@ -98,7 +98,7 @@ TEST_CASE("[ADSREnvelope] Release") out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(2); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); @@ -114,14 +114,14 @@ TEST_CASE("[ADSREnvelope] Delay") region.amplitudeEG.release = 0.04f; region.amplitudeEG.delay = 0.02f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); @@ -138,13 +138,13 @@ TEST_CASE("[ADSREnvelope] Lower sustain") region.amplitudeEG.delay = 0.02f; region.amplitudeEG.sustain = 50.0f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -161,13 +161,13 @@ TEST_CASE("[ADSREnvelope] Decay") region.amplitudeEG.sustain = 50.0f; region.amplitudeEG.decay = 0.02f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.707107f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5 }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -185,13 +185,13 @@ TEST_CASE("[ADSREnvelope] Hold") region.amplitudeEG.decay = 0.02f; region.amplitudeEG.hold = 0.02f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.707107f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -208,7 +208,7 @@ TEST_CASE("[ADSREnvelope] Hold with release") region.amplitudeEG.sustain = 50.0f; region.amplitudeEG.decay = 0.02f; region.amplitudeEG.hold = 0.02f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(8); std::array output; std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.707107f, 0.5f, 0.05f, 0.005f, 0.0005f, 0.00005f, 0.0f, 0.0f }; @@ -216,7 +216,7 @@ TEST_CASE("[ADSREnvelope] Hold with release") out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(8); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); @@ -234,14 +234,14 @@ TEST_CASE("[ADSREnvelope] Hold with release 2") region.amplitudeEG.sustain = 50.0f; region.amplitudeEG.decay = 0.02f; region.amplitudeEG.hold = 0.02f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); std::array output; std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f, 0.0f, 0.0 }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); diff --git a/tests/EGDescriptionT.cpp b/tests/EGDescriptionT.cpp index 291e222d..305a60fe 100644 --- a/tests/EGDescriptionT.cpp +++ b/tests/EGDescriptionT.cpp @@ -18,13 +18,13 @@ TEST_CASE("[EGDescription] Attack range") eg.attack = 1; eg.vel2attack = -1.27f; eg.ccAttack = { 63, 1.27f }; - REQUIRE( eg.getAttack(state, 0) == 1.0f ); - REQUIRE( eg.getAttack(state, 127) == 0.0f ); + REQUIRE( eg.getAttack(state, 0_norm) == 1.0f ); + REQUIRE( eg.getAttack(state, 127_norm) == 0.0f ); state.ccEvent(0, 63, 127_norm); - REQUIRE( eg.getAttack(state, 127) == 1.0f ); - REQUIRE( eg.getAttack(state, 0) == 2.27f ); + REQUIRE( eg.getAttack(state, 127_norm) == 1.0f ); + REQUIRE( eg.getAttack(state, 0_norm) == 2.27f ); eg.ccAttack = { 63, 127.0f }; - REQUIRE( eg.getAttack(state, 0) == 100.0f ); + REQUIRE( eg.getAttack(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Delay range") @@ -34,13 +34,13 @@ TEST_CASE("[EGDescription] Delay range") eg.delay = 1; eg.vel2delay = -1.27f; eg.ccDelay = { 63, 1.27f }; - REQUIRE( eg.getDelay(state, 0) == 1.0f ); - REQUIRE( eg.getDelay(state, 127) == 0.0f ); + REQUIRE( eg.getDelay(state, 0_norm) == 1.0f ); + REQUIRE( eg.getDelay(state, 127_norm) == 0.0f ); state.ccEvent(0, 63, 127_norm); - REQUIRE( eg.getDelay(state, 127) == 1.0f ); - REQUIRE( eg.getDelay(state, 0) == 2.27f ); + REQUIRE( eg.getDelay(state, 127_norm) == 1.0f ); + REQUIRE( eg.getDelay(state, 0_norm) == 2.27f ); eg.ccDelay = { 63, 127.0f }; - REQUIRE( eg.getDelay(state, 0) == 100.0f ); + REQUIRE( eg.getDelay(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Decay range") @@ -50,13 +50,13 @@ TEST_CASE("[EGDescription] Decay range") eg.decay = 1.0f; eg.vel2decay = -1.27f; eg.ccDecay = { 63, 1.27f }; - REQUIRE( eg.getDecay(state, 0) == 1.0f ); - REQUIRE( eg.getDecay(state, 127) == 0.0f ); + REQUIRE( eg.getDecay(state, 0_norm) == 1.0f ); + REQUIRE( eg.getDecay(state, 127_norm) == 0.0f ); state.ccEvent(0, 63, 127_norm); - REQUIRE( eg.getDecay(state, 127) == 1.0f ); - REQUIRE( eg.getDecay(state, 0) == 2.27f ); + REQUIRE( eg.getDecay(state, 127_norm) == 1.0f ); + REQUIRE( eg.getDecay(state, 0_norm) == 2.27f ); eg.ccDecay = { 63, 127.0f }; - REQUIRE( eg.getDecay(state, 0) == 100.0f ); + REQUIRE( eg.getDecay(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Release range") @@ -66,13 +66,13 @@ TEST_CASE("[EGDescription] Release range") eg.release = 1; eg.vel2release = -1.27f; eg.ccRelease = { 63, 1.27f }; - REQUIRE( eg.getRelease(state, 0) == 1.0f ); - REQUIRE( eg.getRelease(state, 127) == 0.0f ); + REQUIRE( eg.getRelease(state, 0_norm) == 1.0f ); + REQUIRE( eg.getRelease(state, 127_norm) == 0.0f ); state.ccEvent(0, 63, 127_norm); - REQUIRE( eg.getRelease(state, 127) == 1.0f ); - REQUIRE( eg.getRelease(state, 0) == 2.27f ); + REQUIRE( eg.getRelease(state, 127_norm) == 1.0f ); + REQUIRE( eg.getRelease(state, 0_norm) == 2.27f ); eg.ccRelease = { 63, 127.0f }; - REQUIRE( eg.getRelease(state, 0) == 100.0f ); + REQUIRE( eg.getRelease(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Hold range") @@ -82,13 +82,13 @@ TEST_CASE("[EGDescription] Hold range") eg.hold = 1; eg.vel2hold = -1.27f; eg.ccHold = { 63, 1.27f }; - REQUIRE( eg.getHold(state, 0) == 1.0f ); - REQUIRE( eg.getHold(state, 127) == 0.0f ); + REQUIRE( eg.getHold(state, 0_norm) == 1.0f ); + REQUIRE( eg.getHold(state, 127_norm) == 0.0f ); state.ccEvent(0, 63, 127_norm); - REQUIRE( eg.getHold(state, 127) == 1.0f ); - REQUIRE( eg.getHold(state, 0) == 2.27f ); + REQUIRE( eg.getHold(state, 127_norm) == 1.0f ); + REQUIRE( eg.getHold(state, 0_norm) == 2.27f ); eg.ccHold = { 63, 127.0f }; - REQUIRE( eg.getHold(state, 0) == 100.0f ); + REQUIRE( eg.getHold(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Sustain level") @@ -98,12 +98,12 @@ TEST_CASE("[EGDescription] Sustain level") eg.sustain = 50; eg.vel2sustain = -100; eg.ccSustain = { 63, 100.0f }; - REQUIRE( eg.getSustain(state, 0) == 50.0f ); - REQUIRE( eg.getSustain(state, 127) == 0.0f ); + REQUIRE( eg.getSustain(state, 0_norm) == 50.0f ); + REQUIRE( eg.getSustain(state, 127_norm) == 0.0f ); state.ccEvent(0, 63, 127_norm); - REQUIRE( eg.getSustain(state, 127) == 50.0f ); + REQUIRE( eg.getSustain(state, 127_norm) == 50.0f ); eg.ccSustain = { 63, 200.0f }; - REQUIRE( eg.getSustain(state, 0) == 100.0f ); + REQUIRE( eg.getSustain(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Start level") @@ -112,10 +112,10 @@ TEST_CASE("[EGDescription] Start level") sfz::MidiState state; eg.start = 0; eg.ccStart = { 63, 127.0f }; - REQUIRE( eg.getStart(state, 0) == 0.0f ); - REQUIRE( eg.getStart(state, 127) == 0.0f ); + REQUIRE( eg.getStart(state, 0_norm) == 0.0f ); + REQUIRE( eg.getStart(state, 127_norm) == 0.0f ); state.ccEvent(0, 63, 127_norm); - REQUIRE( eg.getStart(state, 0) == 100.0f ); + REQUIRE( eg.getStart(state, 0_norm) == 100.0f ); eg.ccStart = { 63, -127.0f }; - REQUIRE( eg.getStart(state, 0) == 0.0f ); + REQUIRE( eg.getStart(state, 0_norm) == 0.0f ); } From 769f3cfdd7f2179447803ac415a60fcad4c5ad18 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:51:05 +0100 Subject: [PATCH 18/19] Correct a bug in Voice with the value passed to filters --- src/sfizz/Voice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 79d7ccbe..7bd044c5 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -124,13 +124,13 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, const unsigned numChannels = region->isStereo ? 2 : 1; for (auto& filter: region->filters) { - auto newFilter = resources.filterPool.getFilter(filter, numChannels, number, denormalize7Bits(value)); + auto newFilter = resources.filterPool.getFilter(filter, numChannels, number, value); if (newFilter) filters.push_back(newFilter); } for (auto& eq: region->equalizers) { - auto newEQ = resources.eqPool.getEQ(eq, numChannels, denormalize7Bits(value)); + auto newEQ = resources.eqPool.getEQ(eq, numChannels, value); if (newEQ) equalizers.push_back(newEQ); } From 16af73ffa02e35628aa099d9a39fd4c89cf5b463 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 01:05:07 +0100 Subject: [PATCH 19/19] explicit narrowing conversion --- src/sfizz/Region.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 77ab9083..7ab2dbfb 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -938,7 +938,7 @@ float sfz::Region::getBasePitchVariation(int noteNumber, float velocity) const n auto pitchVariationInCents = pitchKeytrack * (noteNumber - (int)pitchKeycenter); // note difference with pitch center pitchVariationInCents += tune; // sample tuning pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose - pitchVariationInCents += velocity * pitchVeltrack; // track velocity + pitchVariationInCents += static_cast(velocity * pitchVeltrack); // track velocity pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes return centsFactor(pitchVariationInCents); }