From 82ebb639a76f3eb98e2f9f88c3c4aac7a9256cbe Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 27 Jul 2020 17:32:25 +0200 Subject: [PATCH] Remove unused code, update tests --- src/CMakeLists.txt | 1 - src/sfizz/ModifierHelpers.h | 78 +---------------- src/sfizz/Modifiers.h | 92 -------------------- src/sfizz/Opcode.h | 2 +- src/sfizz/Region.cpp | 39 ++------- src/sfizz/Region.h | 8 +- src/sfizz/Synth.cpp | 5 -- src/sfizz/Synth.h | 5 +- src/sfizz/Voice.cpp | 48 ---------- src/sfizz/Voice.h | 24 ----- tests/CMakeLists.txt | 2 + tests/EventEnvelopesT.cpp | 3 +- tests/FilesT.cpp | 11 ++- tests/RegionT.cpp | 169 +++++++++++++++++++----------------- tests/RegionTHelpers.cpp | 41 +++++++++ tests/RegionTHelpers.h | 28 ++++++ 16 files changed, 182 insertions(+), 374 deletions(-) delete mode 100644 src/sfizz/Modifiers.h create mode 100644 tests/RegionTHelpers.cpp create mode 100644 tests/RegionTHelpers.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0343c5e..a1199157 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -73,7 +73,6 @@ set (SFIZZ_HEADERS sfizz/MathHelpers.h sfizz/MidiState.h sfizz/ModifierHelpers.h - sfizz/Modifiers.h sfizz/NumericId.h sfizz/OnePoleFilter.h sfizz/Oversampler.h diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index 609b16ff..e9b76361 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -8,8 +8,7 @@ #include "Range.h" #include "Defaults.h" -#include "Modifiers.h" -#include "Resources.h" +#include "SfzHelpers.h" #include "absl/types/span.h" namespace sfz { @@ -257,77 +256,4 @@ void pitchBendEnvelope(const EventVector& events, absl::Span envelope, F& multiplicativeEnvelope(events, envelope, std::forward(lambda)); } -/** - * @brief Builds a linear envelope, possibly quantized, based on the events fetched - * from a midi state and the modifier data. This is a helper function for recurrent - * code in the voice logic. - * - * @tparam F - * @param resources - * @param span - * @param ccData - * @param lambda - */ -template -void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) -{ - const auto& events = resources.midiState.getCCEvents(ccData.cc); - const auto& curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.step == 0.0f) { - linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }); - } else { - const float stepSize { lambda(ccData.data.step) }; - linearEnvelope( - events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }, - stepSize); - } -} - -/** - * @brief Builds a multiplicative envelope, possibly quantized, based on the events fetched - * from a midi state and the modifier data. This is a helper function for recurrent - * code in the voice logic. - * - * @tparam F - * @param resources - * @param span - * @param ccData - * @param lambda - */ -template -void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) -{ - const auto& events = resources.midiState.getCCEvents(ccData.cc); - const auto& curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.step == 0.0f) { - multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }); - } else { - const float stepSize { lambda(ccData.data.step) }; - multiplicativeEnvelope( - events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }, - stepSize); - } -} - -/** - * @brief Alias for a simple linear modifier with no lambda - * - * @tparam F - * @param resources - * @param span - * @param ccData - * @param lambda - */ -inline void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) -{ - linearModifier(resources, span, ccData, [](float x) { return x; }); -} -} +} // namespace sfz diff --git a/src/sfizz/Modifiers.h b/src/sfizz/Modifiers.h deleted file mode 100644 index 4868d112..00000000 --- a/src/sfizz/Modifiers.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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 - -#pragma once -#include "Config.h" -#include -#include -#include -#include -#include - -namespace sfz { - -/** - * @brief Base modifier class - * - */ -struct Modifier { - float value { 0.0f }; - float step { 0.0f }; - uint8_t curve { 0 }; - uint8_t smooth { 0 }; - static_assert(config::maxCurves - 1 <= std::numeric_limits::max(), "The curve type in the Modifier struct cannot support the required number of curves"); -}; - -enum class Mod : size_t { - amplitude = 0, - pan, - width, - position, - pitch, - volume, - sentinel -}; - -/** - * @brief Vectors of elements indexed on modifiers with casting and iterators - * - * @tparam T - */ -template -class ModifierVector : public std::vector { -public: - T& operator[](sfz::Mod idx) { return this->std::vector::operator[](static_cast(idx)); } - const T& operator[](sfz::Mod idx) const { return this->std::vector::operator[](static_cast(idx)); } -}; - -/** - * @brief Array of elements indexed on modifiers with casting and iterators - * - * @tparam T - */ -template -class ModifierArray { -public: - using ContainerType = typename std::array; - using iterator = typename ContainerType::iterator; - using const_iterator = typename ContainerType::const_iterator; - ModifierArray() = default; - ModifierArray(T val) - { - std::fill(underlying.begin(), underlying.end(), val); - } - ModifierArray(std::array&& array) : underlying(array) {} - T& operator[](sfz::Mod idx) { return underlying.operator[](static_cast(idx)); } - const T& operator[](sfz::Mod idx) const { return underlying.operator[](static_cast(idx)); } - iterator begin() { return underlying.begin(); } - iterator end() { return underlying.end(); } - const_iterator begin() const { return underlying.begin(); } - const_iterator end() const { return underlying.end(); } -private: - ContainerType underlying {}; -}; - -/** - * @brief Helper for iterating over all possible modifiers. - * Should fail at compile time if you update the modifiers but not this. - * - */ -static const ModifierArray allModifiers {{ - Mod::amplitude, - Mod::pan, - Mod::width, - Mod::position, - Mod::pitch, - Mod::volume -}}; - -} diff --git a/src/sfizz/Opcode.h b/src/sfizz/Opcode.h index 87b25666..4d15f844 100644 --- a/src/sfizz/Opcode.h +++ b/src/sfizz/Opcode.h @@ -13,7 +13,7 @@ #include "absl/types/optional.h" #include "absl/meta/type_traits.h" #include "absl/strings/ascii.h" -#include +#include "absl/strings/string_view.h" #include #include #include diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index c8775c0b..685d7587 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -379,35 +379,35 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) setValueFromOpcode(opcode, volume, Default::volumeRange); break; case_any_ccN("volume"): // also gain - processGenericCc(opcode, Default::volumeCCRange, &modifiers[Mod::volume], ModKey::createNXYZ(ModId::Volume, id)); + processGenericCc(opcode, Default::volumeCCRange, ModKey::createNXYZ(ModId::Volume, id)); break; case hash("amplitude"): if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) amplitude = normalizePercents(*value); break; case_any_ccN("amplitude"): - processGenericCc(opcode, Default::amplitudeRange, &modifiers[Mod::amplitude], ModKey::createNXYZ(ModId::Amplitude, id)); + processGenericCc(opcode, Default::amplitudeRange, ModKey::createNXYZ(ModId::Amplitude, id)); break; case hash("pan"): if (auto value = readOpcode(opcode.value, Default::panRange)) pan = normalizePercents(*value); break; case_any_ccN("pan"): - processGenericCc(opcode, Default::panCCRange, &modifiers[Mod::pan], ModKey::createNXYZ(ModId::Pan, id)); + processGenericCc(opcode, Default::panCCRange, ModKey::createNXYZ(ModId::Pan, id)); break; case hash("position"): if (auto value = readOpcode(opcode.value, Default::positionRange)) position = normalizePercents(*value); break; case_any_ccN("position"): - processGenericCc(opcode, Default::positionCCRange, &modifiers[Mod::position], ModKey::createNXYZ(ModId::Position, id)); + processGenericCc(opcode, Default::positionCCRange, ModKey::createNXYZ(ModId::Position, id)); break; case hash("width"): if (auto value = readOpcode(opcode.value, Default::widthRange)) width = normalizePercents(*value); break; case_any_ccN("width"): - processGenericCc(opcode, Default::widthCCRange, &modifiers[Mod::width], ModKey::createNXYZ(ModId::Width, id)); + processGenericCc(opcode, Default::widthCCRange, ModKey::createNXYZ(ModId::Width, id)); break; case hash("amp_keycenter"): setValueFromOpcode(opcode, ampKeycenter, Default::keyRange); @@ -771,7 +771,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) setValueFromOpcode(opcode, tune, Default::tuneRange); break; case_any_ccN("pitch"): // also tune - processGenericCc(opcode, Default::tuneCCRange, &modifiers[Mod::pitch], ModKey::createNXYZ(ModId::Pitch, id)); + processGenericCc(opcode, Default::tuneCCRange, ModKey::createNXYZ(ModId::Pitch, id)); break; case hash("bend_up"): // also bendup setValueFromOpcode(opcode, bendUp, Default::bendBoundRange); @@ -925,7 +925,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return true; } -bool sfz::Region::processGenericCc(const Opcode& opcode, Range range, CCMap *ccMap, const ModKey& target) +bool sfz::Region::processGenericCc(const Opcode& opcode, Range range, const ModKey& target) { if (!opcode.isAnyCcN()) return false; @@ -934,31 +934,6 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, Range range, CCM if (ccNumber >= config::numCCs) return false; - // TODO obsolete after implementing mod matrix - if (ccMap) { - Modifier& modifier = (*ccMap)[ccNumber]; - switch (opcode.category) { - case kOpcodeOnCcN: - setValueFromOpcode(opcode, modifier.value, range); - break; - case kOpcodeCurveCcN: - setValueFromOpcode(opcode, modifier.curve, Default::curveCCRange); - break; - case kOpcodeStepCcN: - { - const Range stepCCRange { 0.0f, std::max(std::abs(range.getStart()), std::abs(range.getEnd())) }; - setValueFromOpcode(opcode, modifier.step, stepCCRange); - } - break; - case kOpcodeSmoothCcN: - setValueFromOpcode(opcode, modifier.smooth, Default::smoothCCRange); - break; - default: - assert(false); - break; - } - } - if (target) { // search an existing connection of same CC number and target // if it exists, modify, otherwise create diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index da044fd9..0426d6d7 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -17,9 +17,9 @@ #include "MidiState.h" #include "FileId.h" #include "NumericId.h" -#include "Modifiers.h" #include "modulations/ModKey.h" #include "absl/types/optional.h" +#include "absl/strings/string_view.h" #include #include #include @@ -241,12 +241,11 @@ struct Region { * * @param opcode * @param range - * @param ccMap * @param target * @return true if the opcode was properly read and stored. * @return false */ - bool processGenericCc(const Opcode& opcode, Range range, CCMap *ccMap, const ModKey& target); + bool processGenericCc(const Opcode& opcode, Range range, const ModKey& target); void offsetAllKeys(int offset) noexcept; @@ -370,9 +369,6 @@ struct Region { // Effects std::vector gainToEffect; - // Modifiers - ModifierArray> modifiers; - bool triggerOnCC { false }; // whether the region triggers on CC events or note events bool triggerOnNote { true }; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 7bae9797..cb1ba8c4 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -452,7 +452,6 @@ void sfz::Synth::finalizeSfzLoad() size_t maxFilters { 0 }; size_t maxEQs { 0 }; - ModifierArray maxModifiers { 0 }; while (currentRegionIndex < currentRegionCount) { auto region = regions[currentRegionIndex].get(); @@ -563,8 +562,6 @@ void sfz::Synth::finalizeSfzLoad() region->registerTempo(2.0f); maxFilters = max(maxFilters, region->filters.size()); maxEQs = max(maxEQs, region->equalizers.size()); - for (const auto& mod : allModifiers) - maxModifiers[mod] = max(maxModifiers[mod], region->modifiers[mod].size()); ++currentRegionIndex; } @@ -574,7 +571,6 @@ void sfz::Synth::finalizeSfzLoad() settingsPerVoice.maxFilters = maxFilters; settingsPerVoice.maxEQs = maxEQs; - settingsPerVoice.maxModifiers = maxModifiers; applySettingsPerVoice(); @@ -1346,7 +1342,6 @@ void sfz::Synth::applySettingsPerVoice() for (auto& voice : voices) { voice->setMaxFiltersPerVoice(settingsPerVoice.maxFilters); voice->setMaxEQsPerVoice(settingsPerVoice.maxEQs); - voice->prepareSmoothers(settingsPerVoice.maxModifiers); } } diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 5c41078d..8133b676 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -18,11 +18,11 @@ #include "parser/Parser.h" #include "VoiceStealing.h" #include "utility/SpinMutex.h" -#include "absl/types/span.h" +#include #include +#include #include #include -#include #include namespace sfz { @@ -772,7 +772,6 @@ private: struct SettingsPerVoice { size_t maxFilters { 0 }; size_t maxEQs { 0 }; - ModifierArray maxModifiers { 0 }; }; SettingsPerVoice settingsPerVoice; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index a9ced8b5..9f1b9197 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -141,33 +141,6 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, bendSmoother.reset(centsFactor(region->getBendInCents(resources.midiState.getPitchBend()))); egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, value, sampleRate); - for (auto& modId : allModifiers) { - ASSERT(modifierSmoothers[modId].size() >= region->modifiers[modId].size()); - forEachWithSmoother(modId, [modId, this](const CCData& mod, Smoother& smoother) { - const auto ccValue = resources.midiState.getCCValue(mod.cc); - const auto& curve = resources.curves.getCurve(mod.data.curve); - const auto finalValue = curve.evalNormalized(ccValue) * mod.data.value; - switch (modId) { - case Mod::volume: - smoother.reset(db2mag(finalValue)); - break; - case Mod::pitch: - smoother.reset(centsFactor(finalValue)); - break; - case Mod::amplitude: - case Mod::pan: - case Mod::width: - case Mod::position: - smoother.reset(normalizePercents(finalValue)); - break; - default: - smoother.reset(finalValue); - break; - } - smoother.setSmoothing(mod.data.smooth, sampleRate); - }); - } - resources.modMatrix.initVoice(id); } @@ -882,12 +855,6 @@ void sfz::Voice::switchState(State s) } } -void sfz::Voice::prepareSmoothers(const ModifierArray& numModifiers) -{ - for (auto& mod : allModifiers) - modifierSmoothers[mod].resize(numModifiers[mod]); -} - void sfz::Voice::pitchEnvelope(absl::Span pitchSpan) noexcept { const auto numFrames = pitchSpan.size(); @@ -918,21 +885,6 @@ void sfz::Voice::pitchEnvelope(absl::Span pitchSpan) noexcept void sfz::Voice::resetSmoothers() noexcept { - for (auto& mod : allModifiers) { - const auto resetValue = [mod] { - switch (mod) { - case Mod::volume: // fallthrough - case Mod::pitch: - return 1.0f; - default: - return 0.0f; - } - }(); - - for (auto& smoother : modifierSmoothers[mod]) { - smoother.reset(resetValue); - } - } bendSmoother.reset(1.0f); gainSmoother.reset(0.0f); } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index f567c5e6..52a7f0c8 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -302,8 +302,6 @@ public: Duration getLastFilterDuration() const noexcept { return filterDuration; } Duration getLastPanningDuration() const noexcept { return panningDuration; } - void prepareSmoothers(const ModifierArray& numModifiers); - private: /** * @brief Fill a span with data from a file source. This is the first step @@ -390,27 +388,6 @@ private: */ void removeVoiceFromRing() noexcept; - /** - * @brief Helper function to iterate jointly on modifiers and smoothers - * for a given modulation target of type sfz::Mod - * - * @tparam F - * @param modId - * @param lambda - */ - template - void forEachWithSmoother(sfz::Mod modId, F&& lambda) - { - size_t count = region->modifiers[modId].size(); - ASSERT(modifierSmoothers[modId].size() >= count); - auto mod = region->modifiers[modId].begin(); - auto smoother = modifierSmoothers[modId].begin(); - for (size_t i = 0; i < count; ++i) { - lambda(*mod, *smoother); - incrementAll(mod, smoother); - } - } - /** * @brief Initialize frequency and gain coefficients for the oscillators. */ @@ -479,7 +456,6 @@ private: fast_real_distribution uniformNoiseDist { -config::uniformNoiseBounds, config::uniformNoiseBounds }; fast_gaussian_generator gaussianNoiseDist { 0.0f, config::noiseVariance }; - ModifierArray> modifierSmoothers; Smoother gainSmoother; Smoother bendSmoother; Smoother xfadeSmoother; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9b7155c5..4018edee 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,8 @@ project(sfizz) set(SFIZZ_TEST_SOURCES RegionT.cpp + RegionTHelpers.h + RegionTHelpers.cpp ParsingT.cpp HelpersT.cpp HelpersT.cpp diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 64bcb7e4..01287d3e 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -261,6 +261,7 @@ TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") REQUIRE(approxEqual(output, expected)); } +#if 0 TEST_CASE("[linearModifiers] Compare with envelopes") { sfz::Resources resources; @@ -360,4 +361,4 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes") }); REQUIRE(approxEqual(output, envelope)); } - +#endif diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 73ea4ab0..b47181fe 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -4,8 +4,11 @@ // 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 "RegionTHelpers.h" #include "sfizz/Synth.h" #include "sfizz/SfzHelpers.h" +#include "sfizz/modulations/ModId.h" +#include "sfizz/modulations/ModKey.h" #include "catch2/catch.hpp" #include "ghc/fs_std.hpp" #if defined(__APPLE__) @@ -356,9 +359,11 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines") REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 ); REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 ); - REQUIRE(!synth.getRegionView(2)->modifiers[Mod::amplitude].empty()); - REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].contains(10)); - REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].getWithDefault(10).value == 34.0f); + + const ModKey target = ModKey::createNXYZ(ModId::Amplitude, synth.getRegionView(2)->getId()); + const RegionCCView view(*synth.getRegionView(2), target); + REQUIRE(!view.empty()); + REQUIRE(view.at(10).value == 34.0f); } TEST_CASE("[Files] Specific bug: relative path with backslashes") diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index ecfea499..472ab8fc 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -4,10 +4,14 @@ // 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 "RegionTHelpers.h" #include "sfizz/MidiState.h" #include "sfizz/Region.h" #include "sfizz/SfzHelpers.h" +#include "sfizz/modulations/ModId.h" +#include "sfizz/modulations/ModKey.h" #include "catch2/catch.hpp" +#include using namespace Catch::literals; using namespace sfz::literals; using namespace sfz; @@ -541,28 +545,29 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("pan_oncc") { - REQUIRE(region.modifiers[Mod::pan].empty()); + const ModKey target = ModKey::createNXYZ(ModId::Pan, region.getId()); + const RegionCCView view(region, target); + REQUIRE(view.empty()); region.parseOpcode({ "pan_oncc45", "4.2" }); - REQUIRE(region.modifiers[Mod::pan].contains(45)); - REQUIRE(region.modifiers[Mod::pan][45].value == 4.2_a); + REQUIRE(view.at(45).value == 4.2_a); region.parseOpcode({ "pan_curvecc17", "18" }); - REQUIRE(region.modifiers[Mod::pan][17].curve == 18); + REQUIRE(view.at(17).curve == 18); region.parseOpcode({ "pan_curvecc17", "15482" }); - REQUIRE(region.modifiers[Mod::pan][17].curve == 255); + REQUIRE(view.at(17).curve == 255); region.parseOpcode({ "pan_curvecc17", "-2" }); - REQUIRE(region.modifiers[Mod::pan][17].curve == 0); + REQUIRE(view.at(17).curve == 0); region.parseOpcode({ "pan_smoothcc14", "85" }); - REQUIRE(region.modifiers[Mod::pan][14].smooth == 85); + REQUIRE(view.at(14).smooth == 85); region.parseOpcode({ "pan_smoothcc14", "15482" }); - REQUIRE(region.modifiers[Mod::pan][14].smooth == 100); + REQUIRE(view.at(14).smooth == 100); region.parseOpcode({ "pan_smoothcc14", "-2" }); - REQUIRE(region.modifiers[Mod::pan][14].smooth == 0); + REQUIRE(view.at(14).smooth == 0); region.parseOpcode({ "pan_stepcc120", "24" }); - REQUIRE(region.modifiers[Mod::pan][120].step == 24.0_a); + REQUIRE(view.at(120).step == 24.0_a); region.parseOpcode({ "pan_stepcc120", "15482" }); - REQUIRE(region.modifiers[Mod::pan][120].step == 200.0_a); + REQUIRE(view.at(120).step == 200.0_a); region.parseOpcode({ "pan_stepcc120", "-2" }); - REQUIRE(region.modifiers[Mod::pan][120].step == 0.0f); + REQUIRE(view.at(120).step == 0.0f); } SECTION("width") @@ -580,28 +585,29 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("width_oncc") { - REQUIRE(region.modifiers[Mod::width].empty()); + const ModKey target = ModKey::createNXYZ(ModId::Width, region.getId()); + const RegionCCView view(region, target); + REQUIRE(view.empty()); region.parseOpcode({ "width_oncc45", "4.2" }); - REQUIRE(region.modifiers[Mod::width].contains(45)); - REQUIRE(region.modifiers[Mod::width][45].value == 4.2_a); + REQUIRE(view.at(45).value == 4.2_a); region.parseOpcode({ "width_curvecc17", "18" }); - REQUIRE(region.modifiers[Mod::width][17].curve == 18); + REQUIRE(view.at(17).curve == 18); region.parseOpcode({ "width_curvecc17", "15482" }); - REQUIRE(region.modifiers[Mod::width][17].curve == 255); + REQUIRE(view.at(17).curve == 255); region.parseOpcode({ "width_curvecc17", "-2" }); - REQUIRE(region.modifiers[Mod::width][17].curve == 0); + REQUIRE(view.at(17).curve == 0); region.parseOpcode({ "width_smoothcc14", "85" }); - REQUIRE(region.modifiers[Mod::width][14].smooth == 85); + REQUIRE(view.at(14).smooth == 85); region.parseOpcode({ "width_smoothcc14", "15482" }); - REQUIRE(region.modifiers[Mod::width][14].smooth == 100); + REQUIRE(view.at(14).smooth == 100); region.parseOpcode({ "width_smoothcc14", "-2" }); - REQUIRE(region.modifiers[Mod::width][14].smooth == 0); + REQUIRE(view.at(14).smooth == 0); region.parseOpcode({ "width_stepcc120", "24" }); - REQUIRE(region.modifiers[Mod::width][120].step == 24.0_a); + REQUIRE(view.at(120).step == 24.0_a); region.parseOpcode({ "width_stepcc120", "15482" }); - REQUIRE(region.modifiers[Mod::width][120].step == 200.0_a); + REQUIRE(view.at(120).step == 200.0_a); region.parseOpcode({ "width_stepcc120", "-20" }); - REQUIRE(region.modifiers[Mod::width][120].step == 0.0f); + REQUIRE(view.at(120).step == 0.0f); } SECTION("position") @@ -619,28 +625,29 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("position_oncc") { - REQUIRE(region.modifiers[Mod::position].empty()); + const ModKey target = ModKey::createNXYZ(ModId::Position, region.getId()); + const RegionCCView view(region, target); + REQUIRE(view.empty()); region.parseOpcode({ "position_oncc45", "4.2" }); - REQUIRE(region.modifiers[Mod::position].contains(45)); - REQUIRE(region.modifiers[Mod::position][45].value == 4.2_a); + REQUIRE(view.at(45).value == 4.2_a); region.parseOpcode({ "position_curvecc17", "18" }); - REQUIRE(region.modifiers[Mod::position][17].curve == 18); + REQUIRE(view.at(17).curve == 18); region.parseOpcode({ "position_curvecc17", "15482" }); - REQUIRE(region.modifiers[Mod::position][17].curve == 255); + REQUIRE(view.at(17).curve == 255); region.parseOpcode({ "position_curvecc17", "-2" }); - REQUIRE(region.modifiers[Mod::position][17].curve == 0); + REQUIRE(view.at(17).curve == 0); region.parseOpcode({ "position_smoothcc14", "85" }); - REQUIRE(region.modifiers[Mod::position][14].smooth == 85); + REQUIRE(view.at(14).smooth == 85); region.parseOpcode({ "position_smoothcc14", "15482" }); - REQUIRE(region.modifiers[Mod::position][14].smooth == 100); + REQUIRE(view.at(14).smooth == 100); region.parseOpcode({ "position_smoothcc14", "-2" }); - REQUIRE(region.modifiers[Mod::position][14].smooth == 0); + REQUIRE(view.at(14).smooth == 0); region.parseOpcode({ "position_stepcc120", "24" }); - REQUIRE(region.modifiers[Mod::position][120].step == 24.0_a); + REQUIRE(view.at(120).step == 24.0_a); region.parseOpcode({ "position_stepcc120", "15482" }); - REQUIRE(region.modifiers[Mod::position][120].step == 200.0_a); + REQUIRE(view.at(120).step == 200.0_a); region.parseOpcode({ "position_stepcc120", "-2" }); - REQUIRE(region.modifiers[Mod::position][120].step == 0.0f); + REQUIRE(view.at(120).step == 0.0f); } SECTION("amp_keycenter") @@ -1641,95 +1648,93 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("amplitude_cc") { - REQUIRE(region.modifiers[Mod::amplitude].empty()); + const ModKey target = ModKey::createNXYZ(ModId::Amplitude, region.getId()); + const RegionCCView view(region, target); + REQUIRE(view.empty()); region.parseOpcode({ "amplitude_cc1", "40" }); - REQUIRE(region.modifiers[Mod::amplitude].contains(1)); - REQUIRE(region.modifiers[Mod::amplitude][1].value == 40.0_a); + REQUIRE(view.at(1).value == 40.0_a); region.parseOpcode({ "amplitude_oncc2", "30" }); - REQUIRE(region.modifiers[Mod::amplitude].contains(2)); - REQUIRE(region.modifiers[Mod::amplitude][2].value == 30.0_a); + REQUIRE(view.at(2).value == 30.0_a); region.parseOpcode({ "amplitude_curvecc17", "18" }); - REQUIRE(region.modifiers[Mod::amplitude][17].curve == 18); + REQUIRE(view.at(17).curve == 18); region.parseOpcode({ "amplitude_curvecc17", "15482" }); - REQUIRE(region.modifiers[Mod::amplitude][17].curve == 255); + REQUIRE(view.at(17).curve == 255); region.parseOpcode({ "amplitude_curvecc17", "-2" }); - REQUIRE(region.modifiers[Mod::amplitude][17].curve == 0); + REQUIRE(view.at(17).curve == 0); region.parseOpcode({ "amplitude_smoothcc14", "85" }); - REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 85); + REQUIRE(view.at(14).smooth == 85); region.parseOpcode({ "amplitude_smoothcc14", "15482" }); - REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 100); + REQUIRE(view.at(14).smooth == 100); region.parseOpcode({ "amplitude_smoothcc14", "-2" }); - REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 0); + REQUIRE(view.at(14).smooth == 0); region.parseOpcode({ "amplitude_stepcc120", "24" }); - REQUIRE(region.modifiers[Mod::amplitude][120].step == 24.0_a); + REQUIRE(view.at(120).step == 24.0_a); region.parseOpcode({ "amplitude_stepcc120", "15482" }); - REQUIRE(region.modifiers[Mod::amplitude][120].step == 100.0_a); + REQUIRE(view.at(120).step == 100.0_a); region.parseOpcode({ "amplitude_stepcc120", "-2" }); - REQUIRE(region.modifiers[Mod::amplitude][120].step == 0.0f); + REQUIRE(view.at(120).step == 0.0f); } SECTION("volume_oncc/gain_cc") { - REQUIRE(region.modifiers[Mod::volume].empty()); + const ModKey target = ModKey::createNXYZ(ModId::Volume, region.getId()); + const RegionCCView view(region, target); + REQUIRE(view.empty()); region.parseOpcode({ "gain_cc1", "40" }); - REQUIRE(region.modifiers[Mod::volume].contains(1)); - REQUIRE(region.modifiers[Mod::volume][1].value == 40_a); + REQUIRE(view.at(1).value == 40_a); region.parseOpcode({ "volume_oncc2", "-76" }); - REQUIRE(region.modifiers[Mod::volume].contains(2)); - REQUIRE(region.modifiers[Mod::volume][2].value == -76.0_a); + REQUIRE(view.at(2).value == -76.0_a); region.parseOpcode({ "gain_oncc4", "-1" }); - REQUIRE(region.modifiers[Mod::volume].contains(4)); - REQUIRE(region.modifiers[Mod::volume][4].value == -1.0_a); + REQUIRE(view.at(4).value == -1.0_a); region.parseOpcode({ "volume_curvecc17", "18" }); - REQUIRE(region.modifiers[Mod::volume][17].curve == 18); + REQUIRE(view.at(17).curve == 18); region.parseOpcode({ "volume_curvecc17", "15482" }); - REQUIRE(region.modifiers[Mod::volume][17].curve == 255); + REQUIRE(view.at(17).curve == 255); region.parseOpcode({ "volume_curvecc17", "-2" }); - REQUIRE(region.modifiers[Mod::volume][17].curve == 0); + REQUIRE(view.at(17).curve == 0); region.parseOpcode({ "volume_smoothcc14", "85" }); - REQUIRE(region.modifiers[Mod::volume][14].smooth == 85); + REQUIRE(view.at(14).smooth == 85); region.parseOpcode({ "volume_smoothcc14", "15482" }); - REQUIRE(region.modifiers[Mod::volume][14].smooth == 100); + REQUIRE(view.at(14).smooth == 100); region.parseOpcode({ "volume_smoothcc14", "-2" }); - REQUIRE(region.modifiers[Mod::volume][14].smooth == 0); + REQUIRE(view.at(14).smooth == 0); region.parseOpcode({ "volume_stepcc120", "24" }); - REQUIRE(region.modifiers[Mod::volume][120].step == 24.0f); + REQUIRE(view.at(120).step == 24.0f); region.parseOpcode({ "volume_stepcc120", "15482" }); - REQUIRE(region.modifiers[Mod::volume][120].step == 144.0f); + REQUIRE(view.at(120).step == 144.0f); region.parseOpcode({ "volume_stepcc120", "-2" }); - REQUIRE(region.modifiers[Mod::volume][120].step == 0.0f); + REQUIRE(view.at(120).step == 0.0f); } SECTION("tune_cc/pitch_cc") { - REQUIRE(region.modifiers[Mod::pitch].empty()); + const ModKey target = ModKey::createNXYZ(ModId::Pitch, region.getId()); + const RegionCCView view(region, target); + REQUIRE(view.empty()); region.parseOpcode({ "pitch_cc1", "40" }); - REQUIRE(region.modifiers[Mod::pitch].contains(1)); - REQUIRE(region.modifiers[Mod::pitch][1].value == 40.0); + REQUIRE(view.at(1).value == 40.0); region.parseOpcode({ "tune_oncc2", "-76" }); - REQUIRE(region.modifiers[Mod::pitch].contains(2)); - REQUIRE(region.modifiers[Mod::pitch][2].value == -76.0); + REQUIRE(view.at(2).value == -76.0); region.parseOpcode({ "pitch_oncc4", "-1" }); - REQUIRE(region.modifiers[Mod::pitch].contains(4)); - REQUIRE(region.modifiers[Mod::pitch][4].value == -1.0); + REQUIRE(view.at(4).value == -1.0); region.parseOpcode({ "tune_curvecc17", "18" }); - REQUIRE(region.modifiers[Mod::pitch][17].curve == 18); + REQUIRE(view.at(17).curve == 18); region.parseOpcode({ "pitch_curvecc17", "15482" }); - REQUIRE(region.modifiers[Mod::pitch][17].curve == 255); + REQUIRE(view.at(17).curve == 255); region.parseOpcode({ "tune_curvecc17", "-2" }); - REQUIRE(region.modifiers[Mod::pitch][17].curve == 0); + REQUIRE(view.at(17).curve == 0); region.parseOpcode({ "pitch_smoothcc14", "85" }); - REQUIRE(region.modifiers[Mod::pitch][14].smooth == 85); + REQUIRE(view.at(14).smooth == 85); region.parseOpcode({ "tune_smoothcc14", "15482" }); - REQUIRE(region.modifiers[Mod::pitch][14].smooth == 100); + REQUIRE(view.at(14).smooth == 100); region.parseOpcode({ "pitch_smoothcc14", "-2" }); - REQUIRE(region.modifiers[Mod::pitch][14].smooth == 0); + REQUIRE(view.at(14).smooth == 0); region.parseOpcode({ "tune_stepcc120", "24" }); - REQUIRE(region.modifiers[Mod::pitch][120].step == 24.0f); + REQUIRE(view.at(120).step == 24.0f); region.parseOpcode({ "pitch_stepcc120", "15482" }); - REQUIRE(region.modifiers[Mod::pitch][120].step == 9600.0f); + REQUIRE(view.at(120).step == 9600.0f); region.parseOpcode({ "tune_stepcc120", "-2" }); - REQUIRE(region.modifiers[Mod::pitch][120].step == 0.0f); + REQUIRE(view.at(120).step == 0.0f); } } diff --git a/tests/RegionTHelpers.cpp b/tests/RegionTHelpers.cpp new file mode 100644 index 00000000..d55f05ad --- /dev/null +++ b/tests/RegionTHelpers.cpp @@ -0,0 +1,41 @@ +// 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 "RegionTHelpers.h" +#include "sfizz/modulations/ModId.h" + +size_t RegionCCView::size() const +{ + size_t count = 0; + for (const sfz::Region::Connection& conn : region_.connections) + count += match(conn); + return count; +} + +bool RegionCCView::empty() const +{ + for (const sfz::Region::Connection& conn : region_.connections) + if (match(conn)) + return false; + return true; +} + +sfz::ModKey::Parameters RegionCCView::at(int cc) const +{ + for (const sfz::Region::Connection& conn : region_.connections) { + if (match(conn)) { + const sfz::ModKey::Parameters p = conn.first.parameters(); + if (p.cc == cc) + return p; + } + } + throw std::out_of_range("Region CC"); +} + +bool RegionCCView::match(const sfz::Region::Connection& conn) const +{ + return conn.first.id() == sfz::ModId::Controller && conn.second == target_; +} diff --git a/tests/RegionTHelpers.h b/tests/RegionTHelpers.h new file mode 100644 index 00000000..e9fcf897 --- /dev/null +++ b/tests/RegionTHelpers.h @@ -0,0 +1,28 @@ +// 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 + +#pragma once +#include "sfizz/Region.h" +#include "sfizz/modulations/ModKey.h" + +class RegionCCView { +public: + RegionCCView(const sfz::Region& region, sfz::ModKey target) + : region_(region), target_(target) + { + } + + size_t size() const; + bool empty() const; + sfz::ModKey::Parameters at(int cc) const; + +private: + bool match(const sfz::Region::Connection& conn) const; + +private: + const sfz::Region& region_; + sfz::ModKey target_; +};