diff --git a/benchmarks/BM_envelopes.cpp b/benchmarks/BM_envelopes.cpp index dc470e45..bbb75fb6 100644 --- a/benchmarks/BM_envelopes.cpp +++ b/benchmarks/BM_envelopes.cpp @@ -46,9 +46,8 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, Linear)(benchmark::State& state) { } } -BENCHMARK_DEFINE_F(EnvelopeFixture, LinearNoEvent)(benchmark::State& state) { - for (auto _ : state) - { +BENCHMARK_DEFINE_F(EnvelopeFixture, LinearNoEvent) (benchmark::State& state) { + for (auto _ : state) { sfz::EventVector events { { 0, dist(gen) } }; @@ -79,9 +78,8 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, Multiplicative)(benchmark::State& state) { } } -BENCHMARK_DEFINE_F(EnvelopeFixture, MultiplicativeNoEvent)(benchmark::State& state) { - for (auto _ : state) - { +BENCHMARK_DEFINE_F(EnvelopeFixture, MultiplicativeNoEvent) (benchmark::State& state) { + for (auto _ : state) { sfz::EventVector events { { 0, dist(gen) } }; diff --git a/benchmarks/BM_filterModulation.cpp b/benchmarks/BM_filterModulation.cpp index f97a9b54..caa4eebf 100644 --- a/benchmarks/BM_filterModulation.cpp +++ b/benchmarks/BM_filterModulation.cpp @@ -7,6 +7,7 @@ #include "SIMDHelpers.h" #include "OnePoleFilter.h" #include "SfzFilter.h" +#include "SfzHelpers.h" #include "ScopedFTZ.h" #include "SfzHelpers.h" #include diff --git a/benchmarks/BM_smoothers.cpp b/benchmarks/BM_smoothers.cpp new file mode 100644 index 00000000..920fa808 --- /dev/null +++ b/benchmarks/BM_smoothers.cpp @@ -0,0 +1,57 @@ +// 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 "SIMDHelpers.h" +#include +#include +#include +#include +#include +#include +#include "ModifierHelpers.h" +#include "Smoothers.h" +#include "absl/types/span.h" + +class SmootherFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) + { + input = std::vector(state.range(0)); + output = std::vector(state.range(0)); + std::generate(input.begin(), input.end(), [&]() { return dist(gen); }); + sfz::cumsum(input, absl::MakeSpan(input)); + } + + void TearDown(const ::benchmark::State& /* state */) + { + } + std::random_device rd {}; + std::mt19937 gen { rd() }; + std::uniform_real_distribution dist { 0.5, 4 }; + std::vector input; + std::vector output; +}; + +BENCHMARK_DEFINE_F(SmootherFixture, Linear) (benchmark::State& state) +{ + sfz::Smoother smoother; + smoother.setSmoothing(10, sfz::config::defaultSampleRate); + for (auto _ : state) { + smoother.process(input, absl::MakeSpan(output)); + } +} + +// BENCHMARK_DEFINE_F(SmootherFixture, Multiplicative)(benchmark::State& state) { +// sfz::MultiplicativeSmoother smoother; +// smoother.setSmoothing(10, sfz::config::defaultSampleRate); +// for (auto _ : state) +// { +// smoother.process(input, absl::MakeSpan(output)); +// } +// } + +BENCHMARK_REGISTER_F(SmootherFixture, Linear)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +// BENCHMARK_REGISTER_F(SmootherFixture, Multiplicative)->RangeMultiplier(4)->Range(1 << 2, 1 << 12); +BENCHMARK_MAIN(); diff --git a/benchmarks/BM_vaFilters.cpp b/benchmarks/BM_vaFilters.cpp new file mode 100644 index 00000000..2a04fe63 --- /dev/null +++ b/benchmarks/BM_vaFilters.cpp @@ -0,0 +1,59 @@ +// 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 "SIMDHelpers.h" +#include "OnePoleFilter.h" +#include "SfzFilter.h" +#include "ScopedFTZ.h" +#include +#include +#include +#include +#include +#include + +constexpr int blockSize { 256 }; +constexpr float sampleRate { 48000.0f }; + +class FilterFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State&) + { + input = std::vector(blockSize); + output = std::vector(blockSize); + std::generate(input.begin(), input.end(), [&]() { return dist(gen); }); + } + + void TearDown(const ::benchmark::State& /* state */) + { + } + std::random_device rd {}; + std::mt19937 gen { rd() }; + std::normal_distribution dist { 1, 0.2 }; + std::vector input; + std::vector output; +}; + +BENCHMARK_DEFINE_F(FilterFixture, OnePole_VA) (benchmark::State& state) +{ + ScopedFTZ ftz; + sfz::OnePoleFilter filter; + for (auto _ : state) { + filter.processLowpass(input, absl::MakeSpan(output)); + } +} + +BENCHMARK_DEFINE_F(FilterFixture, OnePoleMul_VA) (benchmark::State& state) +{ + ScopedFTZ ftz; + sfz::OnePoleFilterMul filter; + for (auto _ : state) { + filter.processLowpass(input, absl::MakeSpan(output)); + } +} +BENCHMARK_REGISTER_F(FilterFixture, OnePole_VA); +BENCHMARK_REGISTER_F(FilterFixture, OnePoleMul_VA); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index d36e4547..3c0082a1 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -62,6 +62,8 @@ sfizz_add_benchmark(bm_random BM_random.cpp) sfizz_add_benchmark(bm_logger BM_logger.cpp) target_link_libraries(bm_logger PRIVATE sfizz::sfizz) +sfizz_add_benchmark(bm_smoothers BM_smoothers.cpp) +target_link_libraries(bm_smoothers PRIVATE sfizz::sfizz) if (TARGET sfizz-samplerate) sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a75f96db..67c3b36f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ set (SFIZZ_SOURCES sfizz/Logger.cpp sfizz/SfzFilter.cpp sfizz/Curve.cpp + sfizz/Smoothers.cpp sfizz/Wavetables.cpp sfizz/Tuning.cpp sfizz/RTSemaphore.cpp diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 4b0a5b67..17e5d07c 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -26,6 +26,7 @@ enum class Oversampling: int { namespace config { constexpr float defaultSampleRate { 48000 }; + constexpr float maxSampleRate { 192000 }; constexpr int defaultSamplesPerBlock { 1024 }; constexpr int maxBlockSize { 8192 }; constexpr int bufferPoolSize { 6 }; @@ -39,6 +40,9 @@ namespace config { constexpr int numBackgroundThreads { 4 }; constexpr int numVoices { 64 }; constexpr unsigned maxVoices { 256 }; + constexpr unsigned smoothingSteps { 512 }; + constexpr uint8_t gainSmoothing { 10 }; + constexpr unsigned powerTableSizeExponent { 11 }; constexpr int maxFilePromises { maxVoices }; constexpr int sustainCC { 64 }; constexpr int allSoundOffCC { 120 }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 0b92670c..defb675b 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -82,8 +82,9 @@ namespace Default // Region logic: MIDI conditions constexpr Range channelRange { 1, 16 }; constexpr Range midiChannelRange { 0, 15 }; - constexpr Range stepCCRange { 0, 127 }; + constexpr Range stepCCRange { 0, 127 }; constexpr Range smoothCCRange { 0, 127 }; + constexpr float smoothTauPerStep { 3e-3 }; constexpr Range curveCCRange { 0, 255 }; constexpr Range ccNumberRange { 0, config::numCCs }; constexpr auto ccValueRange = normalizedRange; @@ -202,6 +203,7 @@ namespace Default constexpr int bendUp { 200 }; // No range here because the bounds can be inverted constexpr int bendDown { -200 }; constexpr int bendStep { 1 }; + constexpr uint8_t bendSmooth { 0 }; // Envelope generators constexpr float attack { 0 }; diff --git a/src/sfizz/MathHelpers.h b/src/sfizz/MathHelpers.h index b77ee5ee..70ce204a 100644 --- a/src/sfizz/MathHelpers.h +++ b/src/sfizz/MathHelpers.h @@ -254,6 +254,11 @@ constexpr Type sqrtTwo() { return static_cast(1.41421356237309504880168872 template constexpr Type sqrtTwoInv() { return static_cast(0.707106781186547524400844362104849039284835937688474036588); }; +constexpr unsigned int mask(int x) +{ + return (1U << x) - 1; +} + /** * @brief lround for positive values * This optimizes a bit better by ignoring the negative code path @@ -493,7 +498,6 @@ constexpr bool checkSpanSizes(const absl::Span& span1, Others... others) #define CHECK_SPAN_SIZES(...) SFIZZ_CHECK(checkSpanSizes(__VA_ARGS__)) - class ScopedRoundingMode { public: ScopedRoundingMode() = delete; diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index e3d8006e..f853b136 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -2,12 +2,11 @@ #include "Range.h" #include "Defaults.h" -#include "SfzHelpers.h" +#include "Modifiers.h" #include "Resources.h" #include "absl/types/span.h" namespace sfz { - /** * @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...) */ diff --git a/src/sfizz/Modifiers.h b/src/sfizz/Modifiers.h new file mode 100644 index 00000000..1de3405e --- /dev/null +++ b/src/sfizz/Modifiers.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include + +namespace sfz { + +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 +}; + +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)); } +}; + +template +class ModifierArray : public std::array { +public: + T& operator[](sfz::Mod idx) { return this->std::array::operator[](static_cast(idx)); } + const T& operator[](sfz::Mod idx) const { return this->std::array::operator[](static_cast(idx)); } +}; + +/** + * @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/Region.cpp b/src/sfizz/Region.cpp index 73dff35a..5084c221 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -351,35 +351,35 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) setValueFromOpcode(opcode, volume, Default::volumeRange); break; case_any_ccN("volume"): // also gain - processGenericCc(opcode, Default::volumeCCRange, &volumeCC); + processGenericCc(opcode, Default::volumeCCRange, &modifiers[Mod::volume]); break; case hash("amplitude"): if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) amplitude = normalizePercents(*value); break; case_any_ccN("amplitude"): - processGenericCc(opcode, Default::amplitudeRange, &litudeCC); + processGenericCc(opcode, Default::amplitudeRange, &modifiers[Mod::amplitude]); break; case hash("pan"): if (auto value = readOpcode(opcode.value, Default::panRange)) pan = normalizePercents(*value); break; case_any_ccN("pan"): - processGenericCc(opcode, Default::panCCRange, &panCC); + processGenericCc(opcode, Default::panCCRange, &modifiers[Mod::pan]); break; case hash("position"): if (auto value = readOpcode(opcode.value, Default::positionRange)) position = normalizePercents(*value); break; case_any_ccN("position"): - processGenericCc(opcode, Default::positionCCRange, &positionCC); + processGenericCc(opcode, Default::positionCCRange, &modifiers[Mod::position]); break; case hash("width"): if (auto value = readOpcode(opcode.value, Default::widthRange)) width = normalizePercents(*value); break; case_any_ccN("width"): - processGenericCc(opcode, Default::widthCCRange, &widthCC); + processGenericCc(opcode, Default::widthCCRange, &modifiers[Mod::width]); break; case hash("amp_keycenter"): setValueFromOpcode(opcode, ampKeycenter, Default::keyRange); @@ -742,7 +742,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) setValueFromOpcode(opcode, tune, Default::tuneRange); break; case_any_ccN("pitch"): // also tune - processGenericCc(opcode, Default::tuneCCRange, &tuneCC); + processGenericCc(opcode, Default::tuneCCRange, &modifiers[Mod::pitch]); break; case hash("bend_up"): // also bendup setValueFromOpcode(opcode, bendUp, Default::bendBoundRange); @@ -753,6 +753,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) case hash("bend_step"): setValueFromOpcode(opcode, bendStep, Default::bendStepRange); break; + case hash("bend_smooth"): + setValueFromOpcode(opcode, bendSmooth, Default::smoothCCRange); + break; // Amplitude Envelope case hash("ampeg_attack"): @@ -1219,3 +1222,9 @@ float sfz::Region::getGainToEffectBus(unsigned number) const noexcept return gainToEffect[number]; } + +float sfz::Region::getBendInCents(float bend) const noexcept +{ + const auto bendInCents = bend > 0.0f ? bend * static_cast(bendUp) : -bend * static_cast(bendDown); + return centsFactor(bendInCents); +} diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index e363116b..3b09232c 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -17,6 +17,7 @@ #include "MidiState.h" #include "FileId.h" #include "NumericId.h" +#include "Modifiers.h" #include "absl/types/optional.h" #include #include @@ -195,6 +196,14 @@ struct Region { * @return float */ float velocityCurve(float velocity) const noexcept; + /** + * @brief Get the cents factor for a given bend value between -1 and 1 + * + * @param bend + * @return float + */ + float getBendInCents(float bend) const noexcept; + /** * @brief Get the region offset in samples * @@ -309,11 +318,6 @@ struct Region { float pan { normalizePercents(Default::pan) }; // pan float width { normalizePercents(Default::width) }; // width float position { normalizePercents(Default::position) }; // position - CCMap volumeCC { {} }; // volume_oncc - CCMap amplitudeCC { {} }; // amplitude_oncc - CCMap panCC { {} }; // pan_oncc - CCMap widthCC { {} }; // width_oncc - CCMap positionCC { {} }; // position_oncc uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter float ampKeytrack { Default::ampKeytrack }; // amp_keytrack float ampVeltrack { Default::ampVeltrack }; // amp_keytrack @@ -342,10 +346,10 @@ struct Region { int pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack int transpose { Default::transpose }; // transpose int tune { Default::tune }; // tune - CCMap tuneCC { {} }; int bendUp { Default::bendUp }; int bendDown { Default::bendDown }; int bendStep { Default::bendStep }; + uint8_t bendSmooth { Default::bendSmooth }; // Envelopes EGDescription amplitudeEG; @@ -357,6 +361,9 @@ struct Region { // Effects std::vector gainToEffect; + // Modifiers + ModifierArray> modifiers; + private: const MidiState& midiState; bool keySwitched { true }; diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 43060eaa..191812e4 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -17,29 +17,20 @@ #include "absl/meta/type_traits.h" #include "Defaults.h" -namespace sfz -{ +namespace sfz { using CCNamePair = std::pair; using NoteNamePair = std::pair; template using MidiNoteArray = std::array; -template +template struct CCData { int cc; ValueType data; static_assert(config::numCCs - 1 < std::numeric_limits::max(), "The cc type in the CCData struct cannot support the required number of CCs"); }; -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"); -}; - template struct CCDataComparator { bool operator()(const CCData& ccData, const int& cc) @@ -106,13 +97,13 @@ struct MidiEventValueComparator { * @param centsPerOctave * @return constexpr float */ -template +template constexpr float centsFactor(T cents, T centsPerOctave = 1200) { return std::pow(2.0f, static_cast(cents) / centsPerOctave); } -template::value, int> = 0> +template ::value, int> = 0> constexpr T denormalize7Bits(float value) { return static_cast(value * 127.0f); @@ -128,10 +119,10 @@ constexpr uint8_t denormalizeVelocity(float value) return denormalize7Bits(value); } -template +template constexpr float normalize7Bits(T value) { - return static_cast(min(max(value, T{ 0 }), T{ 127 })) / 127.0f; + return static_cast(min(max(value, T { 0 }), T { 127 })) / 127.0f; } /** @@ -141,7 +132,7 @@ constexpr float normalize7Bits(T value) * @param ccValue * @return constexpr float */ -template +template constexpr float normalizeCC(T ccValue) { return normalize7Bits(ccValue); @@ -154,13 +145,12 @@ constexpr float normalizeCC(T ccValue) * @param ccValue * @return constexpr float */ -template +template constexpr float normalizeVelocity(T velocity) { return normalize7Bits(velocity); } - /** * @brief Normalize a percentage between 0 and 1 * @@ -168,7 +158,7 @@ constexpr float normalizeVelocity(T velocity) * @param percentValue * @return constexpr float */ -template +template constexpr float normalizePercents(T percentValue) { return percentValue * 0.01f; @@ -212,6 +202,12 @@ inline CXX14_CONSTEXPR Type vaGain(Type cutoff, Type sampleRate) return std::tan(cutoff / sampleRate * pi()); } +template +inline CXX14_CONSTEXPR Type vaGain(Type cutoff, Type sampleRate) +{ + return std::tan(cutoff / sampleRate * pi()); +} + /** * @brief From a source view, find the next sfz header and its members and * return them, while updating the source by removing this header @@ -275,4 +271,3 @@ bool findDefine(absl::string_view line, absl::string_view& variable, absl::strin bool findInclude(absl::string_view line, std::string& path); } // namespace sfz - diff --git a/src/sfizz/Smoothers.cpp b/src/sfizz/Smoothers.cpp new file mode 100644 index 00000000..9f40b67d --- /dev/null +++ b/src/sfizz/Smoothers.cpp @@ -0,0 +1,30 @@ +#include "Smoothers.h" + +namespace sfz { + +Smoother::Smoother() +{ +} + +void Smoother::setSmoothing(uint8_t smoothValue, float sampleRate) +{ + smoothing = (smoothValue > 0); + if (smoothing) { + filter.setGain(std::tan(1.0f / (2 * Default::smoothTauPerStep * smoothValue * sampleRate))); + } +} + +void Smoother::reset(float value) +{ + filter.reset(value); +} + +void Smoother::process(absl::Span input, absl::Span output) +{ + if (smoothing) + filter.processLowpass(input, output); + else + copy(input, output); +} + +} diff --git a/src/sfizz/Smoothers.h b/src/sfizz/Smoothers.h new file mode 100644 index 00000000..4793f423 --- /dev/null +++ b/src/sfizz/Smoothers.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Config.h" +#include "Defaults.h" +#include "MathHelpers.h" +#include "SfzHelpers.h" +#include "OnePoleFilter.h" +#include "SIMDHelpers.h" +#include + +namespace sfz { +class Smoother { +public: + Smoother(); + void setSmoothing(uint8_t smoothValue, float sampleRate); + void reset(float value = 0.0f); + void process(absl::Span input, absl::Span output); + +private: + bool smoothing { false }; + OnePoleFilter filter {}; +}; + +} diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 422e922b..49c3c3db 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -9,6 +9,7 @@ #include "Debug.h" #include "Macros.h" #include "MidiState.h" +#include "ModifierHelpers.h" #include "ScopedFTZ.h" #include "StringViewHelpers.h" #include "pugixml.hpp" @@ -374,6 +375,7 @@ void sfz::Synth::finalizeSfzLoad() size_t maxFilters { 0 }; size_t maxEQs { 0 }; + ModifierArray maxModifiers { 0 }; while (currentRegionIndex < currentRegionCount) { auto region = regions[currentRegionIndex].get(); @@ -480,6 +482,8 @@ 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; } @@ -490,6 +494,7 @@ void sfz::Synth::finalizeSfzLoad() for (auto& voice : voices) { voice->setMaxFiltersPerVoice(maxFilters); voice->setMaxEQsPerVoice(maxEQs); + voice->prepareSmoothers(maxModifiers); } } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 63a723de..d6033dd1 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -24,6 +24,8 @@ sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources) for (WavetableOscillator& osc : waveOscillators) osc.init(sampleRate); + gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); + for (auto & filter : channelEnvelopeFilters) filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); } @@ -97,6 +99,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, baseGain = region->getBaseGain(); if (triggerType != TriggerType::CC) baseGain *= region->getNoteGain(number, value); + gainSmoother.reset(); // Check that we can handle the number of filters; filters should be cleared here ASSERT((filters.capacity() - filters.size()) >= region->filters.size()); @@ -120,7 +123,26 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, initialDelay = delay + static_cast(region->getDelay() * sampleRate); baseFrequency = resources.tuning.getFrequencyOfKey(number); bendStepFactor = centsFactor(region->bendStep); + bendSmoother.setSmoothing(region->bendSmooth, sampleRate); + bendSmoother.reset(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) { + switch (modId) { + case Mod::volume: + smoother.reset(db2mag(resources.midiState.getCCValue(mod.cc) * mod.data.value)); + break; + case Mod::pitch: + smoother.reset(centsFactor(resources.midiState.getCCValue(mod.cc) * mod.data.value)); + break; + default: + smoother.reset(resources.midiState.getCCValue(mod.cc) * mod.data.value); + } + smoother.setSmoothing(mod.data.smooth, sampleRate); + }); + } } int sfz::Voice::getCurrentSampleQuality() const noexcept @@ -206,6 +228,7 @@ void sfz::Voice::registerTempo(int delay, float secondsPerQuarter) noexcept void sfz::Voice::setSampleRate(float sampleRate) noexcept { this->sampleRate = sampleRate; + gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); for (auto & filter : channelEnvelopeFilters) filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); @@ -283,10 +306,11 @@ void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept // Amplitude envelope applyGain1(baseGain, modulationSpan); - for (const auto& mod : region->amplitudeCC) { + forEachWithSmoother(Mod::amplitude, [&](const CCData& mod, Smoother& smoother) { linearModifier(resources, *tempSpan, mod, normalizePercents); + smoother.process(*tempSpan, *tempSpan); applyGain(*tempSpan, modulationSpan); - } + }); // Crossfade envelopes for (const auto& mod : region->crossfadeCCInRange) { @@ -306,12 +330,16 @@ void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept // Volume envelope applyGain1(db2mag(baseVolumedB), modulationSpan); - for (const auto& mod : region->volumeCC) { + forEachWithSmoother(Mod::volume, [&](const CCData& mod, Smoother& smoother) { multiplicativeModifier(resources, *tempSpan, mod, [](float x) { return db2mag(x); }); + smoother.process(*tempSpan, *tempSpan); applyGain(*tempSpan, modulationSpan); - } + }); + + // Smooth the gain transitions + gainSmoother.process(modulationSpan, modulationSpan); } void sfz::Voice::ampStageMono(AudioSpan buffer) noexcept @@ -360,10 +388,11 @@ void sfz::Voice::panStageMono(AudioSpan buffer) noexcept // Apply panning fill(*modulationSpan, region->pan); - for (const auto& mod : region->panCC) { + forEachWithSmoother(Mod::pan, [&](const CCData& mod, Smoother& smoother) { linearModifier(resources, *tempSpan, mod, normalizePercents); + smoother.process(*tempSpan, *tempSpan); add(*tempSpan, *modulationSpan); - } + }); pan(*modulationSpan, leftBuffer, rightBuffer); } @@ -381,25 +410,28 @@ void sfz::Voice::panStageStereo(AudioSpan buffer) noexcept // Apply panning fill(*modulationSpan, region->pan); - for (const auto& mod : region->panCC) { + forEachWithSmoother(Mod::pan, [&](const CCData& mod, Smoother& smoother) { linearModifier(resources, *tempSpan, mod, normalizePercents); + smoother.process(*tempSpan, *tempSpan); add(*tempSpan, *modulationSpan); - } + }); pan(*modulationSpan, leftBuffer, rightBuffer); // Apply the width/position process fill(*modulationSpan, region->width); - for (const auto& mod : region->widthCC) { + forEachWithSmoother(Mod::width, [&](const CCData& mod, Smoother& smoother) { linearModifier(resources, *tempSpan, mod, normalizePercents); + smoother.process(*tempSpan, *tempSpan); add(*tempSpan, *modulationSpan); - } + }); width(*modulationSpan, leftBuffer, rightBuffer); fill(*modulationSpan, region->position); - for (const auto& mod : region->positionCC) { + forEachWithSmoother(Mod::position, [&](const CCData& mod, Smoother& smoother) { linearModifier(resources, *tempSpan, mod, normalizePercents); + smoother.process(*tempSpan, *tempSpan); add(*tempSpan, *modulationSpan); - } + }); pan(*modulationSpan, leftBuffer, rightBuffer); } @@ -452,30 +484,13 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept auto source = currentPromise->getData(); auto jumps = resources.bufferPool.getBuffer(numSamples); - auto bends = resources.bufferPool.getBuffer(numSamples); auto coeffs = resources.bufferPool.getBuffer(numSamples); auto indices = resources.bufferPool.getIndexBuffer(numSamples); - if (!jumps || !bends || !indices || !coeffs) + if (!jumps || !indices || !coeffs) return; fill(*jumps, pitchRatio * speedRatio); - - const auto events = resources.midiState.getPitchEvents(); - const auto bendLambda = [this](float bend) { - const auto bendInCents = bend > 0.0f ? bend * static_cast(region->bendUp) : -bend * static_cast(region->bendDown); - return centsFactor(bendInCents); - }; - - if (region->bendStep > 1) - pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor); - else - pitchBendEnvelope(events, *bends, bendLambda); - applyGain(*bends, *jumps); - - for (const auto& mod : region->tuneCC) { - multiplicativeModifier(resources, *bends, mod, [](float x) { return centsFactor(x); }); - applyGain(*bends, *jumps); - } + pitchEnvelope(*jumps); jumps->front() += floatPositionOffset; cumsum(*jumps, *jumps); @@ -584,30 +599,12 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept const auto numFrames = buffer.getNumFrames(); auto frequencies = resources.bufferPool.getBuffer(numFrames); - auto bends = resources.bufferPool.getBuffer(numFrames); - if (!frequencies || !bends) + if (!frequencies) return; float keycenterFrequency = midiNoteFrequency(region->pitchKeycenter); fill(*frequencies, pitchRatio * keycenterFrequency); - - const auto events = resources.midiState.getPitchEvents(); - const auto bendLambda = [this](float bend) { - const auto bendInCents = bend > 0.0f ? bend * static_cast(region->bendUp) : -bend * static_cast(region->bendDown); - return centsFactor(bendInCents); - }; - if (region->bendStep > 1) - pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor); - else - pitchBendEnvelope(events, *bends, bendLambda); - applyGain(*bends, *frequencies); - - for (const auto& mod : region->tuneCC) { - multiplicativeModifier(resources, *bends, mod, [](float x) { - return centsFactor(x); - }); - applyGain(*bends, *frequencies); - } + pitchEnvelope(*frequencies); if (waveUnisonSize == 1) { WavetableOscillator& osc = waveOscillators[0]; @@ -784,7 +781,6 @@ void sfz::Voice::setupOscillatorUnison() #endif } - void sfz::Voice::updateChannelPowers(AudioSpan buffer) { assert(smoothedChannelEnvelopes.size() == channelEnvelopeFilters.size()); @@ -809,3 +805,59 @@ void sfz::Voice::switchState(State s) stateListener->onVoiceStateChanged(id, 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(); + auto bends = resources.bufferPool.getBuffer(numFrames); + if (!bends) + return; + + const auto events = resources.midiState.getPitchEvents(); + const auto bendLambda = [this](float bend) { + return region->getBendInCents(bend); + }; + + if (region->bendStep > 1) + pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor); + else + pitchBendEnvelope(events, *bends, bendLambda); + bendSmoother.process(*bends, *bends); + applyGain(*bends, pitchSpan); + + forEachWithSmoother(Mod::pitch, [&](const CCData& mod, Smoother& smoother) { + multiplicativeModifier(resources, *bends, mod, [](float x) { + return centsFactor(x); + }); + smoother.process(*bends, *bends); + applyGain(*bends, pitchSpan); + }); +} + +void sfz::Voice::resetSmoothers() noexcept +{ + for (auto& mod : allModifiers) { + const auto resetValue = [mod] { + switch (mod) { + case Mod::volume: + return 1.0f; + 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 450f8c0c..adbe7d81 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -11,6 +11,7 @@ #include "Region.h" #include "AudioBuffer.h" #include "Resources.h" +#include "Smoothers.h" #include "AudioSpan.h" #include "LeakDetector.h" #include "OnePoleFilter.h" @@ -301,6 +302,8 @@ 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 @@ -337,12 +340,25 @@ private: void panStageStereo(AudioSpan buffer) noexcept; void filterStageMono(AudioSpan buffer) noexcept; void filterStageStereo(AudioSpan buffer) noexcept; + void pitchEnvelope(absl::Span pitchSpan) noexcept; /** * @brief Remove the voice from the sister ring * */ void removeVoiceFromRing() noexcept; + + template + void forEachWithSmoother(sfz::Mod modId, F&& lambda) + { + auto mod = region->modifiers[modId].begin(); + auto smoother = modifierSmoothers[modId].begin(); + while (mod < region->modifiers[modId].end()) { + lambda(*mod, *smoother); + incrementAll(mod, smoother); + } + } + /** * @brief Initialize frequency and gain coefficients for the oscillators. */ @@ -410,8 +426,15 @@ private: std::normal_distribution noiseDist { 0, config::noiseVariance }; + ModifierArray> modifierSmoothers; + Smoother gainSmoother; + Smoother bendSmoother; + void resetSmoothers() noexcept; + std::array, 2> channelEnvelopeFilters; std::array smoothedChannelEnvelopes; + + HistoricalBuffer powerHistory { config::powerHistoryLength }; LEAK_DETECTOR(Voice); }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index eb7303db..d3f32fe4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,6 +18,7 @@ set(SFIZZ_TEST_SOURCES MidiStateT.cpp OnePoleFilterT.cpp InterpolatorsT.cpp + SmoothersT.cpp RegionActivationT.cpp RegionValueComputationsT.cpp # If we're tweaking the curves this kind of tests does not make sense diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index b64264db..8d85b23a 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -13,10 +13,11 @@ #endif using namespace Catch::literals; using namespace sfz::literals; +using namespace sfz; TEST_CASE("[Files] Single region (regions_one.sfz)") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_one.sfz"); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); @@ -25,7 +26,7 @@ TEST_CASE("[Files] Single region (regions_one.sfz)") TEST_CASE("[Files] Multiple regions (regions_many.sfz)") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_many.sfz"); REQUIRE(synth.getNumRegions() == 3); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); @@ -35,15 +36,15 @@ TEST_CASE("[Files] Multiple regions (regions_many.sfz)") TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz"); REQUIRE(synth.getNumRegions() == 1); - REQUIRE(synth.getRegionView(0)->keyRange == sfz::Range(2, 14)); + REQUIRE(synth.getRegionView(0)->keyRange == Range(2, 14)); } TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz"); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->loopMode == SfzLoopMode::loop_sustain); @@ -51,7 +52,7 @@ TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)") TEST_CASE("[Files] (regions_bad.sfz)") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_bad.sfz"); REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); @@ -60,7 +61,7 @@ TEST_CASE("[Files] (regions_bad.sfz)") TEST_CASE("[Files] Local include") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_local.sfz"); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); @@ -68,7 +69,7 @@ TEST_CASE("[Files] Local include") TEST_CASE("[Files] Multiple includes") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz"); REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); @@ -77,7 +78,7 @@ TEST_CASE("[Files] Multiple includes") TEST_CASE("[Files] Multiple includes with comments") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz"); REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); @@ -86,7 +87,7 @@ TEST_CASE("[Files] Multiple includes with comments") TEST_CASE("[Files] Subdir include") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir.sfz"); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav"); @@ -94,7 +95,7 @@ TEST_CASE("[Files] Subdir include") TEST_CASE("[Files] Subdir include Win") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz"); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav"); @@ -102,8 +103,8 @@ TEST_CASE("[Files] Subdir include Win") TEST_CASE("[Files] Recursive include (with include guard)") { - sfz::Synth synth; - sfz::Parser& parser = synth.getParser(); + Synth synth; + Parser& parser = synth.getParser(); parser.setRecursiveIncludeGuardEnabled(true); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_recursive.sfz"); REQUIRE(synth.getNumRegions() == 2); @@ -113,8 +114,8 @@ TEST_CASE("[Files] Recursive include (with include guard)") TEST_CASE("[Files] Include loops (with include guard)") { - sfz::Synth synth; - sfz::Parser& parser = synth.getParser(); + Synth synth; + Parser& parser = synth.getParser(); parser.setRecursiveIncludeGuardEnabled(true); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_loop.sfz"); REQUIRE(synth.getNumRegions() == 2); @@ -124,34 +125,34 @@ TEST_CASE("[Files] Include loops (with include guard)") TEST_CASE("[Files] Define test") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/defines.sfz"); REQUIRE(synth.getNumRegions() == 4); - REQUIRE(synth.getRegionView(0)->keyRange == sfz::Range(36, 36)); - REQUIRE(synth.getRegionView(1)->keyRange == sfz::Range(38, 38)); - REQUIRE(synth.getRegionView(2)->keyRange == sfz::Range(42, 42)); + REQUIRE(synth.getRegionView(0)->keyRange == Range(36, 36)); + REQUIRE(synth.getRegionView(1)->keyRange == Range(38, 38)); + REQUIRE(synth.getRegionView(2)->keyRange == Range(42, 42)); REQUIRE(synth.getRegionView(3)->volume == -12.0f); } TEST_CASE("[Files] Group from AVL") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz"); REQUIRE(synth.getNumRegions() == 5); for (int i = 0; i < synth.getNumRegions(); ++i) { REQUIRE(synth.getRegionView(i)->volume == 6.0f); - REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range(36, 36)); + REQUIRE(synth.getRegionView(i)->keyRange == Range(36, 36)); } - 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)); + REQUIRE(synth.getRegionView(0)->velocityRange == Range(1_norm, 26_norm)); + REQUIRE(synth.getRegionView(1)->velocityRange == Range(27_norm, 52_norm)); + REQUIRE(synth.getRegionView(2)->velocityRange == Range(53_norm, 77_norm)); + REQUIRE(synth.getRegionView(3)->velocityRange == Range(78_norm, 102_norm)); + REQUIRE(synth.getRegionView(4)->velocityRange == Range(103_norm, 127_norm)); } TEST_CASE("[Files] Full hierarchy") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); REQUIRE(synth.getNumRegions() == 8); for (int i = 0; i < synth.getNumRegions(); ++i) { @@ -159,40 +160,40 @@ TEST_CASE("[Files] Full hierarchy") } REQUIRE(synth.getRegionView(0)->pan == 0.3_a); REQUIRE(synth.getRegionView(0)->delay == 67); - REQUIRE(synth.getRegionView(0)->keyRange == sfz::Range(60, 60)); + REQUIRE(synth.getRegionView(0)->keyRange == Range(60, 60)); REQUIRE(synth.getRegionView(1)->pan == 0.3_a); REQUIRE(synth.getRegionView(1)->delay == 67); - REQUIRE(synth.getRegionView(1)->keyRange == sfz::Range(61, 61)); + REQUIRE(synth.getRegionView(1)->keyRange == Range(61, 61)); REQUIRE(synth.getRegionView(2)->pan == 0.3_a); REQUIRE(synth.getRegionView(2)->delay == 56); - REQUIRE(synth.getRegionView(2)->keyRange == sfz::Range(50, 50)); + REQUIRE(synth.getRegionView(2)->keyRange == Range(50, 50)); REQUIRE(synth.getRegionView(3)->pan == 0.3_a); REQUIRE(synth.getRegionView(3)->delay == 56); - REQUIRE(synth.getRegionView(3)->keyRange == sfz::Range(51, 51)); + REQUIRE(synth.getRegionView(3)->keyRange == Range(51, 51)); REQUIRE(synth.getRegionView(4)->pan == -0.1_a); REQUIRE(synth.getRegionView(4)->delay == 47); - REQUIRE(synth.getRegionView(4)->keyRange == sfz::Range(40, 40)); + REQUIRE(synth.getRegionView(4)->keyRange == Range(40, 40)); REQUIRE(synth.getRegionView(5)->pan == -0.1_a); REQUIRE(synth.getRegionView(5)->delay == 47); - REQUIRE(synth.getRegionView(5)->keyRange == sfz::Range(41, 41)); + REQUIRE(synth.getRegionView(5)->keyRange == Range(41, 41)); REQUIRE(synth.getRegionView(6)->pan == -0.1_a); REQUIRE(synth.getRegionView(6)->delay == 36); - REQUIRE(synth.getRegionView(6)->keyRange == sfz::Range(30, 30)); + REQUIRE(synth.getRegionView(6)->keyRange == Range(30, 30)); REQUIRE(synth.getRegionView(7)->pan == -0.1_a); REQUIRE(synth.getRegionView(7)->delay == 36); - REQUIRE(synth.getRegionView(7)->keyRange == sfz::Range(31, 31)); + REQUIRE(synth.getRegionView(7)->keyRange == Range(31, 31)); } TEST_CASE("[Files] Reloading files") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); REQUIRE(synth.getNumRegions() == 8); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); @@ -202,7 +203,7 @@ TEST_CASE("[Files] Reloading files") TEST_CASE("[Files] Full hierarchy with antislashes") { { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); REQUIRE(synth.getNumRegions() == 8); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "Regions/dummy.wav"); @@ -216,7 +217,7 @@ TEST_CASE("[Files] Full hierarchy with antislashes") } { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz"); REQUIRE(synth.getNumRegions() == 8); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "Regions/dummy.wav"); @@ -232,19 +233,19 @@ TEST_CASE("[Files] Full hierarchy with antislashes") TEST_CASE("[Files] Pizz basic") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz"); 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_norm, 127_norm)); + REQUIRE(synth.getRegionView(i)->keyRange == Range(12, 22)); + REQUIRE(synth.getRegionView(i)->velocityRange == Range(97_norm, 127_norm)); REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21); - REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range(0_norm, 13_norm)); + REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == 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)); - REQUIRE(synth.getRegionView(2)->randRange == sfz::Range(0.5, 0.75)); - REQUIRE(synth.getRegionView(3)->randRange == sfz::Range(0.75, 1.0)); + REQUIRE(synth.getRegionView(0)->randRange == Range(0, 0.25)); + REQUIRE(synth.getRegionView(1)->randRange == Range(0.25, 0.5)); + REQUIRE(synth.getRegionView(2)->randRange == Range(0.5, 0.75)); + REQUIRE(synth.getRegionView(3)->randRange == Range(0.75, 1.0)); REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr1.wav)"); REQUIRE(synth.getRegionView(1)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr2.wav)"); REQUIRE(synth.getRegionView(2)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr3.wav)"); @@ -253,7 +254,7 @@ TEST_CASE("[Files] Pizz basic") TEST_CASE("[Files] Channels (channels.sfz)") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz"); REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "mono_sample.wav"); @@ -264,7 +265,7 @@ TEST_CASE("[Files] Channels (channels.sfz)") TEST_CASE("[Files] Channels (channels_multi.sfz)") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels_multi.sfz"); REQUIRE(synth.getNumRegions() == 6); @@ -301,7 +302,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)") TEST_CASE("[Files] sw_default") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz"); REQUIRE( synth.getNumRegions() == 4 ); REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); @@ -312,7 +313,7 @@ TEST_CASE("[Files] sw_default") TEST_CASE("[Files] sw_default and playing with switches") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz"); REQUIRE( synth.getNumRegions() == 4 ); REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); @@ -341,7 +342,7 @@ TEST_CASE("[Files] sw_default and playing with switches") TEST_CASE("[Files] wrong (overlapping) replacement for defines") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/wrong-replacements.sfz"); REQUIRE( synth.getNumRegions() == 3 ); @@ -355,14 +356,14 @@ 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)->amplitudeCC.empty()); - REQUIRE(synth.getRegionView(2)->amplitudeCC.contains(10)); - REQUIRE(synth.getRegionView(2)->amplitudeCC.getWithDefault(10).value == 34.0f); + 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); } TEST_CASE("[Files] Specific bug: relative path with backslashes") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/win_backslashes.sfz"); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(Xylo/Subfolder/closedhat.wav)"); @@ -370,7 +371,7 @@ TEST_CASE("[Files] Specific bug: relative path with backslashes") TEST_CASE("[Files] Default path") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz"); REQUIRE(synth.getNumRegions() == 4); REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(DefaultPath/SubPath1/sample1.wav)"); @@ -381,7 +382,7 @@ TEST_CASE("[Files] Default path") TEST_CASE("[Files] Default path reset when calling loadSfzFile again") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz"); REQUIRE(synth.getNumRegions() == 4); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_reset.sfz"); @@ -391,7 +392,7 @@ TEST_CASE("[Files] Default path reset when calling loadSfzFile again") TEST_CASE("[Files] Default path is ignored for generators") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_generator.sfz"); REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(*sine)"); @@ -399,7 +400,7 @@ TEST_CASE("[Files] Default path is ignored for generators") TEST_CASE("[Files] Set CC applies properly") { - sfz::Synth synth; + Synth synth; const auto& midiState = synth.getResources().midiState; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz"); REQUIRE(midiState.getCCValue(142) == 63_norm); @@ -426,26 +427,26 @@ TEST_CASE("[Files] Set RealCC applies properly") TEST_CASE("[Files] Note and octave offsets") { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/note_offset.sfz"); REQUIRE( synth.getNumRegions() == 7 ); - REQUIRE( synth.getRegionView(0)->keyRange == sfz::Range(64, 64) ); + REQUIRE(synth.getRegionView(0)->keyRange == Range(64, 64)); REQUIRE( synth.getRegionView(0)->pitchKeycenter == 64 ); - REQUIRE( synth.getRegionView(0)->keyswitchRange == sfz::Default::keyRange ); - REQUIRE( synth.getRegionView(0)->crossfadeKeyInRange == sfz::Default::crossfadeKeyInRange ); - REQUIRE( synth.getRegionView(0)->crossfadeKeyOutRange == sfz::Default::crossfadeKeyOutRange ); + REQUIRE(synth.getRegionView(0)->keyswitchRange == Default::keyRange); + REQUIRE(synth.getRegionView(0)->crossfadeKeyInRange == Default::crossfadeKeyInRange); + REQUIRE(synth.getRegionView(0)->crossfadeKeyOutRange == Default::crossfadeKeyOutRange); - REQUIRE( synth.getRegionView(1)->keyRange == sfz::Range(51, 56) ); + REQUIRE(synth.getRegionView(1)->keyRange == Range(51, 56)); REQUIRE( synth.getRegionView(1)->pitchKeycenter == 51 ); - REQUIRE( synth.getRegionView(2)->keyRange == sfz::Range(41, 45) ); + REQUIRE(synth.getRegionView(2)->keyRange == Range(41, 45)); REQUIRE( synth.getRegionView(2)->pitchKeycenter == 41 ); - REQUIRE( synth.getRegionView(2)->crossfadeKeyInRange == sfz::Range(37, 41) ); - REQUIRE( synth.getRegionView(2)->crossfadeKeyOutRange == sfz::Range(45, 49) ); + REQUIRE(synth.getRegionView(2)->crossfadeKeyInRange == Range(37, 41)); + REQUIRE(synth.getRegionView(2)->crossfadeKeyOutRange == Range(45, 49)); - REQUIRE( synth.getRegionView(3)->keyRange == sfz::Range(62, 62) ); - REQUIRE( synth.getRegionView(3)->keyswitchRange == sfz::Range(23, 27) ); + REQUIRE(synth.getRegionView(3)->keyRange == Range(62, 62)); + REQUIRE(synth.getRegionView(3)->keyswitchRange == Range(23, 27)); REQUIRE( synth.getRegionView(3)->keyswitch ); REQUIRE( *synth.getRegionView(3)->keyswitch == 24 ); REQUIRE( synth.getRegionView(3)->keyswitchUp ); @@ -455,21 +456,21 @@ TEST_CASE("[Files] Note and octave offsets") REQUIRE( synth.getRegionView(3)->previousNote ); REQUIRE( *synth.getRegionView(3)->previousNote == 61 ); - REQUIRE( synth.getRegionView(4)->keyRange == sfz::Range(76, 76) ); + REQUIRE(synth.getRegionView(4)->keyRange == Range(76, 76)); REQUIRE( synth.getRegionView(4)->pitchKeycenter == 76 ); - REQUIRE( synth.getRegionView(5)->keyRange == sfz::Range(50, 50) ); + REQUIRE(synth.getRegionView(5)->keyRange == Range(50, 50)); REQUIRE( synth.getRegionView(5)->pitchKeycenter == 50 ); - REQUIRE( synth.getRegionView(6)->keyRange == sfz::Range(50, 50) ); + REQUIRE(synth.getRegionView(6)->keyRange == Range(50, 50)); REQUIRE( synth.getRegionView(6)->pitchKeycenter == 50 ); } TEST_CASE("[Files] Off by with different delays") { - sfz::Synth synth; + Synth synth; synth.setSamplesPerBlock(256); - sfz::AudioBuffer buffer(2, 256); + AudioBuffer buffer(2, 256); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); REQUIRE( synth.getNumRegions() == 4 ); synth.noteOn(0, 63, 63); @@ -484,7 +485,7 @@ TEST_CASE("[Files] Off by with different delays") TEST_CASE("[Files] Off by with the same delays") { - sfz::Synth synth; + Synth synth; synth.setSamplesPerBlock(256); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); REQUIRE( synth.getNumRegions() == 4 ); @@ -499,7 +500,7 @@ TEST_CASE("[Files] Off by with the same delays") TEST_CASE("[Files] Off by with the same notes at the same time") { - sfz::Synth synth; + Synth synth; synth.setSamplesPerBlock(256); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); REQUIRE( synth.getNumRegions() == 4 ); @@ -507,7 +508,7 @@ TEST_CASE("[Files] Off by with the same notes at the same time") REQUIRE( synth.getNumActiveVoices() == 2 ); synth.noteOn(0, 65, 63); REQUIRE( synth.getNumActiveVoices() == 4 ); - sfz::AudioBuffer buffer { 2, 256 }; + AudioBuffer buffer { 2, 256 }; synth.renderBlock(buffer); synth.noteOn(0, 65, 63); synth.renderBlock(buffer); @@ -516,7 +517,7 @@ TEST_CASE("[Files] Off by with the same notes at the same time") TEST_CASE("[Files] Off modes") { - sfz::Synth synth; + Synth synth; synth.setSamplesPerBlock(256); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_mode.sfz"); REQUIRE( synth.getNumRegions() == 3 ); @@ -532,7 +533,7 @@ TEST_CASE("[Files] Off modes") synth.getVoiceView(0) ; synth.noteOn(100, 63, 63); REQUIRE( synth.getNumActiveVoices() == 3 ); - sfz::AudioBuffer buffer { 2, 256 }; + AudioBuffer buffer { 2, 256 }; synth.renderBlock(buffer); REQUIRE( synth.getNumActiveVoices() == 2 ); REQUIRE( fastVoice->isFree() ); @@ -541,7 +542,7 @@ TEST_CASE("[Files] Off modes") TEST_CASE("[Files] Looped regions taken from files and possibly overriden") { - sfz::Synth synth; + Synth synth; synth.setSamplesPerBlock(256); synth.setSampleRate(44100); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/looped_regions.sfz"); @@ -550,9 +551,9 @@ TEST_CASE("[Files] Looped regions taken from files and possibly overriden") REQUIRE( synth.getRegionView(1)->loopMode == SfzLoopMode::no_loop ); REQUIRE( synth.getRegionView(2)->loopMode == SfzLoopMode::loop_continuous ); - REQUIRE( synth.getRegionView(0)->loopRange == sfz::Range{ 77554, 186581 } ); - REQUIRE( synth.getRegionView(1)->loopRange == sfz::Range{ 77554, 186581 } ); - REQUIRE( synth.getRegionView(2)->loopRange == sfz::Range{ 4, 124 } ); + REQUIRE(synth.getRegionView(0)->loopRange == Range { 77554, 186581 }); + REQUIRE(synth.getRegionView(1)->loopRange == Range { 77554, 186581 }); + REQUIRE(synth.getRegionView(2)->loopRange == Range { 4, 124 }); } TEST_CASE("[Files] Case sentitiveness") @@ -568,7 +569,7 @@ TEST_CASE("[Files] Case sentitiveness") #endif if (caseSensitiveFs) { - sfz::Synth synth; + Synth synth; synth.loadSfzFile(sfzFilePath); REQUIRE(synth.getNumRegions() == 4); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy1.wav"); @@ -580,8 +581,8 @@ TEST_CASE("[Files] Case sentitiveness") TEST_CASE("[Files] Empty file") { - sfz::Synth synth; - sfz::Parser& parser = synth.getParser(); + Synth synth; + Parser& parser = synth.getParser(); REQUIRE(!synth.loadSfzFile("")); REQUIRE(parser.getIncludedFiles().empty()); REQUIRE(!synth.loadSfzFile({})); diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index c683cd96..994f0bd9 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -10,11 +10,12 @@ #include "catch2/catch.hpp" using namespace Catch::literals; using namespace sfz::literals; +using namespace sfz; TEST_CASE("[Region] Parsing opcodes") { - sfz::MidiState midiState; - sfz::Region region { 0, midiState }; + MidiState midiState; + Region region { 0, midiState }; SECTION("sample") { @@ -132,36 +133,36 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("loop_end") { - REQUIRE(region.loopRange == sfz::Range(0, 4294967295)); + REQUIRE(region.loopRange == Range(0, 4294967295)); region.parseOpcode({ "loop_end", "184" }); - REQUIRE(region.loopRange == sfz::Range(0, 184)); + REQUIRE(region.loopRange == Range(0, 184)); region.parseOpcode({ "loop_end", "-1" }); - REQUIRE(region.loopRange == sfz::Range(0, 0)); + REQUIRE(region.loopRange == Range(0, 0)); } SECTION("loop_start") { region.parseOpcode({ "loop_start", "184" }); - REQUIRE(region.loopRange == sfz::Range(184, 4294967295)); + REQUIRE(region.loopRange == Range(184, 4294967295)); region.parseOpcode({ "loop_start", "-1" }); - REQUIRE(region.loopRange == sfz::Range(0, 4294967295)); + REQUIRE(region.loopRange == Range(0, 4294967295)); } SECTION("loopend") { - REQUIRE(region.loopRange == sfz::Range(0, 4294967295)); + REQUIRE(region.loopRange == Range(0, 4294967295)); region.parseOpcode({ "loopend", "184" }); - REQUIRE(region.loopRange == sfz::Range(0, 184)); + REQUIRE(region.loopRange == Range(0, 184)); region.parseOpcode({ "loopend", "-1" }); - REQUIRE(region.loopRange == sfz::Range(0, 0)); + REQUIRE(region.loopRange == Range(0, 0)); } SECTION("loopstart") { region.parseOpcode({ "loopstart", "184" }); - REQUIRE(region.loopRange == sfz::Range(184, 4294967295)); + REQUIRE(region.loopRange == Range(184, 4294967295)); region.parseOpcode({ "loopstart", "-1" }); - REQUIRE(region.loopRange == sfz::Range(0, 4294967295)); + REQUIRE(region.loopRange == Range(0, 4294967295)); } SECTION("group") @@ -194,87 +195,87 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("lokey, hikey, and key") { - REQUIRE(region.keyRange == sfz::Range(0, 127)); + REQUIRE(region.keyRange == Range(0, 127)); region.parseOpcode({ "lokey", "37" }); - REQUIRE(region.keyRange == sfz::Range(37, 127)); + REQUIRE(region.keyRange == Range(37, 127)); region.parseOpcode({ "lokey", "c4" }); - REQUIRE(region.keyRange == sfz::Range(60, 127)); + REQUIRE(region.keyRange == Range(60, 127)); region.parseOpcode({ "lokey", "128" }); - REQUIRE(region.keyRange == sfz::Range(127, 127)); + REQUIRE(region.keyRange == Range(127, 127)); region.parseOpcode({ "lokey", "-3" }); - REQUIRE(region.keyRange == sfz::Range(0, 127)); + REQUIRE(region.keyRange == Range(0, 127)); region.parseOpcode({ "hikey", "65" }); - REQUIRE(region.keyRange == sfz::Range(0, 65)); + REQUIRE(region.keyRange == Range(0, 65)); region.parseOpcode({ "hikey", "c4" }); - REQUIRE(region.keyRange == sfz::Range(0, 60)); + REQUIRE(region.keyRange == Range(0, 60)); region.parseOpcode({ "hikey", "-1" }); - REQUIRE(region.keyRange == sfz::Range(0, 0)); + REQUIRE(region.keyRange == Range(0, 0)); region.parseOpcode({ "hikey", "128" }); - REQUIRE(region.keyRange == sfz::Range(0, 127)); + REQUIRE(region.keyRange == Range(0, 127)); region.parseOpcode({ "key", "26" }); - REQUIRE(region.keyRange == sfz::Range(26, 26)); + REQUIRE(region.keyRange == Range(26, 26)); REQUIRE(region.pitchKeycenter == 26); region.parseOpcode({ "key", "-26" }); - REQUIRE(region.keyRange == sfz::Range(0, 0)); + REQUIRE(region.keyRange == Range(0, 0)); REQUIRE(region.pitchKeycenter == 0); region.parseOpcode({ "key", "234" }); - REQUIRE(region.keyRange == sfz::Range(127, 127)); + REQUIRE(region.keyRange == Range(127, 127)); REQUIRE(region.pitchKeycenter == 127); region.parseOpcode({ "key", "c4" }); - REQUIRE(region.keyRange == sfz::Range(60, 60)); + REQUIRE(region.keyRange == Range(60, 60)); REQUIRE(region.pitchKeycenter == 60); } SECTION("lovel, hivel") { - REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.velocityRange == Range(0_norm, 127_norm)); region.parseOpcode({ "lovel", "37" }); - REQUIRE(region.velocityRange == sfz::Range(37_norm, 127_norm)); + REQUIRE(region.velocityRange == Range(37_norm, 127_norm)); region.parseOpcode({ "lovel", "128" }); - REQUIRE(region.velocityRange == sfz::Range(127_norm, 127_norm)); + REQUIRE(region.velocityRange == Range(127_norm, 127_norm)); region.parseOpcode({ "lovel", "-3" }); - REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.velocityRange == Range(0_norm, 127_norm)); region.parseOpcode({ "hivel", "65" }); - REQUIRE(region.velocityRange == sfz::Range(0_norm, 65_norm)); + REQUIRE(region.velocityRange == Range(0_norm, 65_norm)); region.parseOpcode({ "hivel", "-1" }); - REQUIRE(region.velocityRange == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.velocityRange == Range(0_norm, 0_norm)); region.parseOpcode({ "hivel", "128" }); - REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.velocityRange == Range(0_norm, 127_norm)); } SECTION("lobend, hibend") { - REQUIRE(region.bendRange == sfz::Range(-1.0f, 1.0f)); + REQUIRE(region.bendRange == Range(-1.0f, 1.0f)); region.parseOpcode({ "lobend", "400" }); - REQUIRE(region.bendRange.getStart() == Approx(sfz::normalizeBend(400))); + REQUIRE(region.bendRange.getStart() == Approx(normalizeBend(400))); REQUIRE(region.bendRange.getEnd() == 1.0_a); region.parseOpcode({ "lobend", "-128" }); - REQUIRE(region.bendRange.getStart() == Approx(sfz::normalizeBend(-128))); + REQUIRE(region.bendRange.getStart() == Approx(normalizeBend(-128))); REQUIRE(region.bendRange.getEnd() == 1.0_a); region.parseOpcode({ "lobend", "-10000" }); - REQUIRE(region.bendRange == sfz::Range(-1.0f, 1.0f)); + REQUIRE(region.bendRange == Range(-1.0f, 1.0f)); region.parseOpcode({ "hibend", "13" }); REQUIRE(region.bendRange.getStart() == -1.0_a); - REQUIRE(region.bendRange.getEnd() == Approx(sfz::normalizeBend(13))); + REQUIRE(region.bendRange.getEnd() == Approx(normalizeBend(13))); region.parseOpcode({ "hibend", "-1" }); REQUIRE(region.bendRange.getStart() == -1.0_a); - REQUIRE(region.bendRange.getEnd() == Approx(sfz::normalizeBend(-1))); + REQUIRE(region.bendRange.getEnd() == Approx(normalizeBend(-1))); region.parseOpcode({ "hibend", "10000" }); - REQUIRE(region.bendRange == sfz::Range(-1.0f, 1.0f)); + REQUIRE(region.bendRange == Range(-1.0f, 1.0f)); } SECTION("locc, hicc") { - REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range(0_norm, 127_norm)); - REQUIRE(region.ccConditions[127] == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.ccConditions.getWithDefault(0) == Range(0_norm, 127_norm)); + REQUIRE(region.ccConditions[127] == Range(0_norm, 127_norm)); region.parseOpcode({ "locc6", "4" }); - REQUIRE(region.ccConditions[6] == sfz::Range(4_norm, 127_norm)); + REQUIRE(region.ccConditions[6] == Range(4_norm, 127_norm)); region.parseOpcode({ "locc12", "-128" }); - REQUIRE(region.ccConditions[12] == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.ccConditions[12] == Range(0_norm, 127_norm)); region.parseOpcode({ "hicc65", "39" }); - REQUIRE(region.ccConditions[65] == sfz::Range(0_norm, 39_norm)); + REQUIRE(region.ccConditions[65] == Range(0_norm, 39_norm)); region.parseOpcode({ "hicc127", "135" }); - REQUIRE(region.ccConditions[127] == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.ccConditions[127] == Range(0_norm, 127_norm)); } SECTION("lohdcc, hihdcc") @@ -307,19 +308,19 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("sw_lokey, sw_hikey") { - REQUIRE(region.keyswitchRange == sfz::Range(0, 127)); + REQUIRE(region.keyswitchRange == Range(0, 127)); region.parseOpcode({ "sw_lokey", "4" }); - REQUIRE(region.keyswitchRange == sfz::Range(4, 127)); + REQUIRE(region.keyswitchRange == Range(4, 127)); region.parseOpcode({ "sw_lokey", "128" }); - REQUIRE(region.keyswitchRange == sfz::Range(127, 127)); + REQUIRE(region.keyswitchRange == Range(127, 127)); region.parseOpcode({ "sw_lokey", "0" }); - REQUIRE(region.keyswitchRange == sfz::Range(0, 127)); + REQUIRE(region.keyswitchRange == Range(0, 127)); region.parseOpcode({ "sw_hikey", "39" }); - REQUIRE(region.keyswitchRange == sfz::Range(0, 39)); + REQUIRE(region.keyswitchRange == Range(0, 39)); region.parseOpcode({ "sw_hikey", "135" }); - REQUIRE(region.keyswitchRange == sfz::Range(0, 127)); + REQUIRE(region.keyswitchRange == Range(0, 127)); region.parseOpcode({ "sw_hikey", "-1" }); - REQUIRE(region.keyswitchRange == sfz::Range(0, 0)); + REQUIRE(region.keyswitchRange == Range(0, 0)); } SECTION("sw_label") @@ -398,53 +399,53 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("lochanaft, hichanaft") { - REQUIRE(region.aftertouchRange == sfz::Range(0, 127)); + REQUIRE(region.aftertouchRange == Range(0, 127)); region.parseOpcode({ "lochanaft", "4" }); - REQUIRE(region.aftertouchRange == sfz::Range(4, 127)); + REQUIRE(region.aftertouchRange == Range(4, 127)); region.parseOpcode({ "lochanaft", "128" }); - REQUIRE(region.aftertouchRange == sfz::Range(127, 127)); + REQUIRE(region.aftertouchRange == Range(127, 127)); region.parseOpcode({ "lochanaft", "0" }); - REQUIRE(region.aftertouchRange == sfz::Range(0, 127)); + REQUIRE(region.aftertouchRange == Range(0, 127)); region.parseOpcode({ "hichanaft", "39" }); - REQUIRE(region.aftertouchRange == sfz::Range(0, 39)); + REQUIRE(region.aftertouchRange == Range(0, 39)); region.parseOpcode({ "hichanaft", "135" }); - REQUIRE(region.aftertouchRange == sfz::Range(0, 127)); + REQUIRE(region.aftertouchRange == Range(0, 127)); region.parseOpcode({ "hichanaft", "-1" }); - REQUIRE(region.aftertouchRange == sfz::Range(0, 0)); + REQUIRE(region.aftertouchRange == Range(0, 0)); } SECTION("lobpm, hibpm") { - REQUIRE(region.bpmRange == sfz::Range(0, 500)); + REQUIRE(region.bpmRange == Range(0, 500)); region.parseOpcode({ "lobpm", "47.5" }); - REQUIRE(region.bpmRange == sfz::Range(47.5, 500)); + REQUIRE(region.bpmRange == Range(47.5, 500)); region.parseOpcode({ "lobpm", "594" }); - REQUIRE(region.bpmRange == sfz::Range(500, 500)); + REQUIRE(region.bpmRange == Range(500, 500)); region.parseOpcode({ "lobpm", "0" }); - REQUIRE(region.bpmRange == sfz::Range(0, 500)); + REQUIRE(region.bpmRange == Range(0, 500)); region.parseOpcode({ "hibpm", "78" }); - REQUIRE(region.bpmRange == sfz::Range(0, 78)); + REQUIRE(region.bpmRange == Range(0, 78)); region.parseOpcode({ "hibpm", "895.4" }); - REQUIRE(region.bpmRange == sfz::Range(0, 500)); + REQUIRE(region.bpmRange == Range(0, 500)); region.parseOpcode({ "hibpm", "-1" }); - REQUIRE(region.bpmRange == sfz::Range(0, 0)); + REQUIRE(region.bpmRange == Range(0, 0)); } SECTION("lorand, hirand") { - REQUIRE(region.randRange == sfz::Range(0, 1)); + REQUIRE(region.randRange == Range(0, 1)); region.parseOpcode({ "lorand", "0.5" }); - REQUIRE(region.randRange == sfz::Range(0.5, 1)); + REQUIRE(region.randRange == Range(0.5, 1)); region.parseOpcode({ "lorand", "4" }); - REQUIRE(region.randRange == sfz::Range(1, 1)); + REQUIRE(region.randRange == Range(1, 1)); region.parseOpcode({ "lorand", "0" }); - REQUIRE(region.randRange == sfz::Range(0, 1)); + REQUIRE(region.randRange == Range(0, 1)); region.parseOpcode({ "hirand", "39" }); - REQUIRE(region.randRange == sfz::Range(0, 1)); + REQUIRE(region.randRange == Range(0, 1)); region.parseOpcode({ "hirand", "0.7" }); - REQUIRE(region.randRange == sfz::Range(0, 0.7f)); + REQUIRE(region.randRange == Range(0, 0.7f)); region.parseOpcode({ "hirand", "-1" }); - REQUIRE(region.randRange == sfz::Range(0, 0)); + REQUIRE(region.randRange == Range(0, 0)); } SECTION("seq_length") @@ -489,10 +490,10 @@ TEST_CASE("[Region] Parsing opcodes") } region.parseOpcode({ "on_locc45", "15" }); REQUIRE(region.ccTriggers.contains(45)); - REQUIRE(region.ccTriggers[45] == sfz::Range(15_norm, 127_norm)); + REQUIRE(region.ccTriggers[45] == Range(15_norm, 127_norm)); region.parseOpcode({ "on_hicc4", "47" }); REQUIRE(region.ccTriggers.contains(45)); - REQUIRE(region.ccTriggers[4] == sfz::Range(0_norm, 47_norm)); + REQUIRE(region.ccTriggers[4] == Range(0_norm, 47_norm)); } SECTION("on_lohdcc, on_hihdcc") @@ -540,28 +541,28 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("pan_oncc") { - REQUIRE(region.panCC.empty()); + REQUIRE(region.modifiers[Mod::pan].empty()); region.parseOpcode({ "pan_oncc45", "4.2" }); - REQUIRE(region.panCC.contains(45)); - REQUIRE(region.panCC[45].value == 4.2_a); + REQUIRE(region.modifiers[Mod::pan].contains(45)); + REQUIRE(region.modifiers[Mod::pan][45].value == 4.2_a); region.parseOpcode({ "pan_curvecc17", "18" }); - REQUIRE(region.panCC[17].curve == 18); + REQUIRE(region.modifiers[Mod::pan][17].curve == 18); region.parseOpcode({ "pan_curvecc17", "15482" }); - REQUIRE(region.panCC[17].curve == 255); + REQUIRE(region.modifiers[Mod::pan][17].curve == 255); region.parseOpcode({ "pan_curvecc17", "-2" }); - REQUIRE(region.panCC[17].curve == 0); + REQUIRE(region.modifiers[Mod::pan][17].curve == 0); region.parseOpcode({ "pan_smoothcc14", "85" }); - REQUIRE(region.panCC[14].smooth == 85); + REQUIRE(region.modifiers[Mod::pan][14].smooth == 85); region.parseOpcode({ "pan_smoothcc14", "15482" }); - REQUIRE(region.panCC[14].smooth == 127); + REQUIRE(region.modifiers[Mod::pan][14].smooth == 100); region.parseOpcode({ "pan_smoothcc14", "-2" }); - REQUIRE(region.panCC[14].smooth == 0); + REQUIRE(region.modifiers[Mod::pan][14].smooth == 0); region.parseOpcode({ "pan_stepcc120", "24" }); - REQUIRE(region.panCC[120].step == 24.0_a); + REQUIRE(region.modifiers[Mod::pan][120].step == 24.0_a); region.parseOpcode({ "pan_stepcc120", "15482" }); - REQUIRE(region.panCC[120].step == 200.0_a); + REQUIRE(region.modifiers[Mod::pan][120].step == 200.0_a); region.parseOpcode({ "pan_stepcc120", "-2" }); - REQUIRE(region.panCC[120].step == 0.0f); + REQUIRE(region.modifiers[Mod::pan][120].step == 0.0f); } SECTION("width") @@ -579,28 +580,28 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("width_oncc") { - REQUIRE(region.widthCC.empty()); + REQUIRE(region.modifiers[Mod::width].empty()); region.parseOpcode({ "width_oncc45", "4.2" }); - REQUIRE(region.widthCC.contains(45)); - REQUIRE(region.widthCC[45].value == 4.2_a); + REQUIRE(region.modifiers[Mod::width].contains(45)); + REQUIRE(region.modifiers[Mod::width][45].value == 4.2_a); region.parseOpcode({ "width_curvecc17", "18" }); - REQUIRE(region.widthCC[17].curve == 18); + REQUIRE(region.modifiers[Mod::width][17].curve == 18); region.parseOpcode({ "width_curvecc17", "15482" }); - REQUIRE(region.widthCC[17].curve == 255); + REQUIRE(region.modifiers[Mod::width][17].curve == 255); region.parseOpcode({ "width_curvecc17", "-2" }); - REQUIRE(region.widthCC[17].curve == 0); + REQUIRE(region.modifiers[Mod::width][17].curve == 0); region.parseOpcode({ "width_smoothcc14", "85" }); - REQUIRE(region.widthCC[14].smooth == 85); + REQUIRE(region.modifiers[Mod::width][14].smooth == 85); region.parseOpcode({ "width_smoothcc14", "15482" }); - REQUIRE(region.widthCC[14].smooth == 127); + REQUIRE(region.modifiers[Mod::width][14].smooth == 100); region.parseOpcode({ "width_smoothcc14", "-2" }); - REQUIRE(region.widthCC[14].smooth == 0); + REQUIRE(region.modifiers[Mod::width][14].smooth == 0); region.parseOpcode({ "width_stepcc120", "24" }); - REQUIRE(region.widthCC[120].step == 24.0_a); + REQUIRE(region.modifiers[Mod::width][120].step == 24.0_a); region.parseOpcode({ "width_stepcc120", "15482" }); - REQUIRE(region.widthCC[120].step == 200.0_a); + REQUIRE(region.modifiers[Mod::width][120].step == 200.0_a); region.parseOpcode({ "width_stepcc120", "-20" }); - REQUIRE(region.widthCC[120].step == 0.0f); + REQUIRE(region.modifiers[Mod::width][120].step == 0.0f); } SECTION("position") @@ -618,28 +619,28 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("position_oncc") { - REQUIRE(region.positionCC.empty()); + REQUIRE(region.modifiers[Mod::position].empty()); region.parseOpcode({ "position_oncc45", "4.2" }); - REQUIRE(region.positionCC.contains(45)); - REQUIRE(region.positionCC[45].value == 4.2_a); + REQUIRE(region.modifiers[Mod::position].contains(45)); + REQUIRE(region.modifiers[Mod::position][45].value == 4.2_a); region.parseOpcode({ "position_curvecc17", "18" }); - REQUIRE(region.positionCC[17].curve == 18); + REQUIRE(region.modifiers[Mod::position][17].curve == 18); region.parseOpcode({ "position_curvecc17", "15482" }); - REQUIRE(region.positionCC[17].curve == 255); + REQUIRE(region.modifiers[Mod::position][17].curve == 255); region.parseOpcode({ "position_curvecc17", "-2" }); - REQUIRE(region.positionCC[17].curve == 0); + REQUIRE(region.modifiers[Mod::position][17].curve == 0); region.parseOpcode({ "position_smoothcc14", "85" }); - REQUIRE(region.positionCC[14].smooth == 85); + REQUIRE(region.modifiers[Mod::position][14].smooth == 85); region.parseOpcode({ "position_smoothcc14", "15482" }); - REQUIRE(region.positionCC[14].smooth == 127); + REQUIRE(region.modifiers[Mod::position][14].smooth == 100); region.parseOpcode({ "position_smoothcc14", "-2" }); - REQUIRE(region.positionCC[14].smooth == 0); + REQUIRE(region.modifiers[Mod::position][14].smooth == 0); region.parseOpcode({ "position_stepcc120", "24" }); - REQUIRE(region.positionCC[120].step == 24.0_a); + REQUIRE(region.modifiers[Mod::position][120].step == 24.0_a); region.parseOpcode({ "position_stepcc120", "15482" }); - REQUIRE(region.positionCC[120].step == 200.0_a); + REQUIRE(region.modifiers[Mod::position][120].step == 200.0_a); region.parseOpcode({ "position_stepcc120", "-2" }); - REQUIRE(region.positionCC[120].step == 0.0f); + REQUIRE(region.modifiers[Mod::position][120].step == 0.0f); } SECTION("amp_keycenter") @@ -704,116 +705,116 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("xfin_lokey, xfin_hikey") { - REQUIRE(region.crossfadeKeyInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeKeyInRange == Range(0, 0)); region.parseOpcode({ "xfin_lokey", "4" }); - REQUIRE(region.crossfadeKeyInRange == sfz::Range(4, 4)); + REQUIRE(region.crossfadeKeyInRange == Range(4, 4)); region.parseOpcode({ "xfin_lokey", "128" }); - REQUIRE(region.crossfadeKeyInRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeKeyInRange == Range(127, 127)); region.parseOpcode({ "xfin_lokey", "59" }); - REQUIRE(region.crossfadeKeyInRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeKeyInRange == Range(59, 127)); region.parseOpcode({ "xfin_hikey", "59" }); - REQUIRE(region.crossfadeKeyInRange == sfz::Range(59, 59)); + REQUIRE(region.crossfadeKeyInRange == Range(59, 59)); region.parseOpcode({ "xfin_hikey", "128" }); - REQUIRE(region.crossfadeKeyInRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeKeyInRange == Range(59, 127)); region.parseOpcode({ "xfin_hikey", "0" }); - REQUIRE(region.crossfadeKeyInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeKeyInRange == Range(0, 0)); region.parseOpcode({ "xfin_hikey", "-1" }); - REQUIRE(region.crossfadeKeyInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeKeyInRange == Range(0, 0)); } SECTION("xfin_lovel, xfin_hivel") { - REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeVelInRange == Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_lovel", "4" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(4_norm, 4_norm)); + REQUIRE(region.crossfadeVelInRange == Range(4_norm, 4_norm)); region.parseOpcode({ "xfin_lovel", "128" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(127_norm, 127_norm)); + REQUIRE(region.crossfadeVelInRange == Range(127_norm, 127_norm)); region.parseOpcode({ "xfin_lovel", "59" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 127_norm)); + REQUIRE(region.crossfadeVelInRange == Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hivel", "59" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 59_norm)); + REQUIRE(region.crossfadeVelInRange == Range(59_norm, 59_norm)); region.parseOpcode({ "xfin_hivel", "128" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 127_norm)); + REQUIRE(region.crossfadeVelInRange == Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hivel", "0" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeVelInRange == Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_hivel", "-1" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeVelInRange == Range(0_norm, 0_norm)); } SECTION("xfout_lokey, xfout_hikey") { - REQUIRE(region.crossfadeKeyOutRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeKeyOutRange == Range(127, 127)); region.parseOpcode({ "xfout_lokey", "4" }); - REQUIRE(region.crossfadeKeyOutRange == sfz::Range(4, 127)); + REQUIRE(region.crossfadeKeyOutRange == Range(4, 127)); region.parseOpcode({ "xfout_lokey", "128" }); - REQUIRE(region.crossfadeKeyOutRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeKeyOutRange == Range(127, 127)); region.parseOpcode({ "xfout_lokey", "59" }); - REQUIRE(region.crossfadeKeyOutRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeKeyOutRange == Range(59, 127)); region.parseOpcode({ "xfout_hikey", "59" }); - REQUIRE(region.crossfadeKeyOutRange == sfz::Range(59, 59)); + REQUIRE(region.crossfadeKeyOutRange == Range(59, 59)); region.parseOpcode({ "xfout_hikey", "128" }); - REQUIRE(region.crossfadeKeyOutRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeKeyOutRange == Range(59, 127)); region.parseOpcode({ "xfout_hikey", "0" }); - REQUIRE(region.crossfadeKeyOutRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeKeyOutRange == Range(0, 0)); region.parseOpcode({ "xfout_hikey", "-1" }); - REQUIRE(region.crossfadeKeyOutRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeKeyOutRange == Range(0, 0)); } SECTION("xfout_lovel, xfout_hivel") { - REQUIRE(region.crossfadeVelOutRange == sfz::Range(127_norm, 127_norm)); + REQUIRE(region.crossfadeVelOutRange == Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "4" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(4_norm, 127_norm)); + REQUIRE(region.crossfadeVelOutRange == Range(4_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "128" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(127_norm, 127_norm)); + REQUIRE(region.crossfadeVelOutRange == Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "59" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 127_norm)); + REQUIRE(region.crossfadeVelOutRange == Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hivel", "59" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 59_norm)); + REQUIRE(region.crossfadeVelOutRange == Range(59_norm, 59_norm)); region.parseOpcode({ "xfout_hivel", "128" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 127_norm)); + REQUIRE(region.crossfadeVelOutRange == Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hivel", "0" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeVelOutRange == Range(0_norm, 0_norm)); region.parseOpcode({ "xfout_hivel", "-1" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeVelOutRange == Range(0_norm, 0_norm)); } SECTION("xfin_locc, xfin_hicc") { REQUIRE(!region.crossfadeCCInRange.contains(4)); region.parseOpcode({ "xfin_locc4", "4" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(4_norm, 4_norm)); + REQUIRE(region.crossfadeCCInRange[4] == Range(4_norm, 4_norm)); region.parseOpcode({ "xfin_locc4", "128" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(127_norm, 127_norm)); + REQUIRE(region.crossfadeCCInRange[4] == Range(127_norm, 127_norm)); region.parseOpcode({ "xfin_locc4", "59" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 127_norm)); + REQUIRE(region.crossfadeCCInRange[4] == Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hicc4", "59" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 59_norm)); + REQUIRE(region.crossfadeCCInRange[4] == Range(59_norm, 59_norm)); region.parseOpcode({ "xfin_hicc4", "128" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 127_norm)); + REQUIRE(region.crossfadeCCInRange[4] == Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hicc4", "0" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeCCInRange[4] == Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_hicc4", "-1" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeCCInRange[4] == 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_norm, 127_norm)); + REQUIRE(region.crossfadeCCOutRange[4] == Range(4_norm, 127_norm)); region.parseOpcode({ "xfout_locc4", "128" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(127_norm, 127_norm)); + REQUIRE(region.crossfadeCCOutRange[4] == Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_locc4", "59" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 127_norm)); + REQUIRE(region.crossfadeCCOutRange[4] == Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hicc4", "59" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 59_norm)); + REQUIRE(region.crossfadeCCOutRange[4] == Range(59_norm, 59_norm)); region.parseOpcode({ "xfout_hicc4", "128" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 127_norm)); + REQUIRE(region.crossfadeCCOutRange[4] == Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hicc4", "0" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeCCOutRange[4] == Range(0_norm, 0_norm)); region.parseOpcode({ "xfout_hicc4", "-1" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0_norm, 0_norm)); + REQUIRE(region.crossfadeCCOutRange[4] == Range(0_norm, 0_norm)); } SECTION("xf_keycurve") @@ -932,7 +933,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.tune == -9600); } - SECTION("bend_up, bend_down, bend_step") + SECTION("bend_up, bend_down, bend_step, bend_smooth") { REQUIRE(region.bendUp == 200); REQUIRE(region.bendDown == -200); @@ -959,6 +960,12 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.bendStep == 1); region.parseOpcode({ "bend_step", "9700" }); REQUIRE(region.bendStep == 1200); + region.parseOpcode({ "bend_smooth", "10" }); + REQUIRE(region.bendSmooth == 10); + region.parseOpcode({ "bend_smooth", "120" }); + REQUIRE(region.bendSmooth == 100); + region.parseOpcode({ "bend_smooth", "-2" }); + REQUIRE(region.bendSmooth == 0); } SECTION("ampeg") @@ -1173,7 +1180,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.filters[0].cutoff == 500.0f); // Check filter defaults REQUIRE(region.filters[0].keycenter == 60); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2p); + REQUIRE(region.filters[0].type == FilterType::kFilterLpf2p); REQUIRE(region.filters[0].keytrack == 0); REQUIRE(region.filters[0].gain == 0); REQUIRE(region.filters[0].veltrack == 0); @@ -1187,7 +1194,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.filters[1].cutoff == 5000.0f); // Check filter defaults REQUIRE(region.filters[1].keycenter == 60); - REQUIRE(region.filters[1].type == sfz::FilterType::kFilterLpf2p); + REQUIRE(region.filters[1].type == FilterType::kFilterLpf2p); REQUIRE(region.filters[1].keytrack == 0); REQUIRE(region.filters[1].gain == 0); REQUIRE(region.filters[1].veltrack == 0); @@ -1202,7 +1209,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.filters[3].cutoff == 50.0f); // Check filter defaults REQUIRE(region.filters[2].keycenter == 60); - REQUIRE(region.filters[2].type == sfz::FilterType::kFilterLpf2p); + REQUIRE(region.filters[2].type == FilterType::kFilterLpf2p); REQUIRE(region.filters[2].keytrack == 0); REQUIRE(region.filters[2].gain == 0); REQUIRE(region.filters[2].veltrack == 0); @@ -1211,7 +1218,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.filters[2].gainCC.empty()); REQUIRE(region.filters[2].resonanceCC.empty()); REQUIRE(region.filters[3].keycenter == 60); - REQUIRE(region.filters[3].type == sfz::FilterType::kFilterLpf2p); + REQUIRE(region.filters[3].type == FilterType::kFilterLpf2p); REQUIRE(region.filters[3].keytrack == 0); REQUIRE(region.filters[3].gain == 0); REQUIRE(region.filters[3].veltrack == 0); @@ -1313,55 +1320,55 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "fil_type", "lpf_1p" }); REQUIRE(region.filters.size() == 1); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf1p); + REQUIRE(region.filters[0].type == FilterType::kFilterLpf1p); region.parseOpcode({ "fil_type", "lpf_2p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2p); + REQUIRE(region.filters[0].type == FilterType::kFilterLpf2p); region.parseOpcode({ "fil_type", "hpf_1p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf1p); + REQUIRE(region.filters[0].type == FilterType::kFilterHpf1p); region.parseOpcode({ "fil_type", "hpf_2p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf2p); + REQUIRE(region.filters[0].type == FilterType::kFilterHpf2p); region.parseOpcode({ "fil_type", "bpf_2p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf2p); + REQUIRE(region.filters[0].type == FilterType::kFilterBpf2p); region.parseOpcode({ "fil_type", "brf_2p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf2p); + REQUIRE(region.filters[0].type == FilterType::kFilterBrf2p); region.parseOpcode({ "fil_type", "bpf_1p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf1p); + REQUIRE(region.filters[0].type == FilterType::kFilterBpf1p); region.parseOpcode({ "fil_type", "brf_1p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf1p); + REQUIRE(region.filters[0].type == FilterType::kFilterBrf1p); region.parseOpcode({ "fil_type", "apf_1p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterApf1p); + REQUIRE(region.filters[0].type == FilterType::kFilterApf1p); region.parseOpcode({ "fil_type", "lpf_2p_sv" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2pSv); + REQUIRE(region.filters[0].type == FilterType::kFilterLpf2pSv); region.parseOpcode({ "fil_type", "hpf_2p_sv" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf2pSv); + REQUIRE(region.filters[0].type == FilterType::kFilterHpf2pSv); region.parseOpcode({ "fil_type", "bpf_2p_sv" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf2pSv); + REQUIRE(region.filters[0].type == FilterType::kFilterBpf2pSv); region.parseOpcode({ "fil_type", "brf_2p_sv" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf2pSv); + REQUIRE(region.filters[0].type == FilterType::kFilterBrf2pSv); region.parseOpcode({ "fil_type", "lpf_4p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf4p); + REQUIRE(region.filters[0].type == FilterType::kFilterLpf4p); region.parseOpcode({ "fil_type", "hpf_4p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf4p); + REQUIRE(region.filters[0].type == FilterType::kFilterHpf4p); region.parseOpcode({ "fil_type", "lpf_6p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf6p); + REQUIRE(region.filters[0].type == FilterType::kFilterLpf6p); region.parseOpcode({ "fil_type", "hpf_6p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf6p); + REQUIRE(region.filters[0].type == FilterType::kFilterHpf6p); region.parseOpcode({ "fil_type", "pink" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPink); + REQUIRE(region.filters[0].type == FilterType::kFilterPink); region.parseOpcode({ "fil_type", "lsh" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLsh); + REQUIRE(region.filters[0].type == FilterType::kFilterLsh); region.parseOpcode({ "fil_type", "hsh" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHsh); + REQUIRE(region.filters[0].type == FilterType::kFilterHsh); region.parseOpcode({ "fil_type", "peq" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPeq); + REQUIRE(region.filters[0].type == FilterType::kFilterPeq); region.parseOpcode({ "fil_type", "lpf_1p" }); region.parseOpcode({ "fil_type", "pkf_2p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPeq); + REQUIRE(region.filters[0].type == FilterType::kFilterPeq); region.parseOpcode({ "fil_type", "lpf_1p" }); region.parseOpcode({ "fil_type", "bpk_2p" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPeq); + REQUIRE(region.filters[0].type == FilterType::kFilterPeq); region.parseOpcode({ "fil_type", "unknown" }); - REQUIRE(region.filters[0].type == sfz::FilterType::kFilterNone); + REQUIRE(region.filters[0].type == FilterType::kFilterNone); } SECTION("EQ stacking and gains") @@ -1372,7 +1379,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.equalizers.size() == 1); REQUIRE(region.equalizers[0].gain == 6.0f); // Check defaults - REQUIRE(region.equalizers[0].type == sfz::EqType::kEqPeak); + REQUIRE(region.equalizers[0].type == EqType::kEqPeak); REQUIRE(region.equalizers[0].bandwidth == 1.0f); REQUIRE(region.equalizers[0].frequency == 0.0f); REQUIRE(region.equalizers[0].vel2frequency == 0); @@ -1385,7 +1392,7 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.equalizers.size() == 2); REQUIRE(region.equalizers[1].gain == -96.0f); // Check defaults - REQUIRE(region.equalizers[1].type == sfz::EqType::kEqPeak); + REQUIRE(region.equalizers[1].type == EqType::kEqPeak); REQUIRE(region.equalizers[1].bandwidth == 1.0f); REQUIRE(region.equalizers[1].frequency == 0.0f); REQUIRE(region.equalizers[1].vel2frequency == 0); @@ -1397,7 +1404,7 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "eq4_gain", "500" }); REQUIRE(region.equalizers.size() == 4); REQUIRE(region.equalizers[2].gain == 0.0f); - REQUIRE(region.equalizers[3].type == sfz::EqType::kEqPeak); + REQUIRE(region.equalizers[3].type == EqType::kEqPeak); REQUIRE(region.equalizers[3].gain == 96.0f); // Check defaults REQUIRE(region.equalizers[2].bandwidth == 1.0f); @@ -1419,13 +1426,13 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("EQ types") { region.parseOpcode({ "eq1_type", "hshelf" }); - REQUIRE(region.equalizers[0].type == sfz::EqType::kEqHighShelf); + REQUIRE(region.equalizers[0].type == EqType::kEqHighShelf); region.parseOpcode({ "eq1_type", "somethingsomething" }); - REQUIRE(region.equalizers[0].type == sfz::EqType::kEqNone); + REQUIRE(region.equalizers[0].type == EqType::kEqNone); region.parseOpcode({ "eq1_type", "lshelf" }); - REQUIRE(region.equalizers[0].type == sfz::EqType::kEqLowShelf); + REQUIRE(region.equalizers[0].type == EqType::kEqLowShelf); region.parseOpcode({ "eq1_type", "peak" }); - REQUIRE(region.equalizers[0].type == sfz::EqType::kEqPeak); + REQUIRE(region.equalizers[0].type == EqType::kEqPeak); } SECTION("EQ parameter dispatch") @@ -1438,7 +1445,7 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "eq2_freq", "300" }); REQUIRE(region.equalizers[1].frequency == 300.0f); region.parseOpcode({ "eq3_type", "lshelf" }); - REQUIRE(region.equalizers[2].type == sfz::EqType::kEqLowShelf); + REQUIRE(region.equalizers[2].type == EqType::kEqLowShelf); region.parseOpcode({ "eq3_vel2gain", "10" }); REQUIRE(region.equalizers[2].vel2gain == 10.0f); region.parseOpcode({ "eq1_vel2freq", "100" }); @@ -1455,7 +1462,7 @@ TEST_CASE("[Region] Parsing opcodes") region.parseOpcode({ "eq3_freq_oncc15", "20" }); REQUIRE(region.equalizers[2].frequencyCC[15] == 20.0f); region.parseOpcode({ "eq1_type", "hshelf" }); - REQUIRE(region.equalizers[0].type == sfz::EqType::kEqHighShelf); + REQUIRE(region.equalizers[0].type == EqType::kEqHighShelf); region.parseOpcode({ "eq2_gaincc123", "2" }); REQUIRE(region.equalizers[1].gainCC.contains(123)); REQUIRE(region.equalizers[1].gainCC[123] == 2.0f); @@ -1576,103 +1583,103 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("amplitude_cc") { - REQUIRE(region.amplitudeCC.empty()); + REQUIRE(region.modifiers[Mod::amplitude].empty()); region.parseOpcode({ "amplitude_cc1", "40" }); - REQUIRE(region.amplitudeCC.contains(1)); - REQUIRE(region.amplitudeCC[1].value == 40.0_a); + REQUIRE(region.modifiers[Mod::amplitude].contains(1)); + REQUIRE(region.modifiers[Mod::amplitude][1].value == 40.0_a); region.parseOpcode({ "amplitude_oncc2", "30" }); - REQUIRE(region.amplitudeCC.contains(2)); - REQUIRE(region.amplitudeCC[2].value == 30.0_a); + REQUIRE(region.modifiers[Mod::amplitude].contains(2)); + REQUIRE(region.modifiers[Mod::amplitude][2].value == 30.0_a); region.parseOpcode({ "amplitude_curvecc17", "18" }); - REQUIRE(region.amplitudeCC[17].curve == 18); + REQUIRE(region.modifiers[Mod::amplitude][17].curve == 18); region.parseOpcode({ "amplitude_curvecc17", "15482" }); - REQUIRE(region.amplitudeCC[17].curve == 255); + REQUIRE(region.modifiers[Mod::amplitude][17].curve == 255); region.parseOpcode({ "amplitude_curvecc17", "-2" }); - REQUIRE(region.amplitudeCC[17].curve == 0); + REQUIRE(region.modifiers[Mod::amplitude][17].curve == 0); region.parseOpcode({ "amplitude_smoothcc14", "85" }); - REQUIRE(region.amplitudeCC[14].smooth == 85); + REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 85); region.parseOpcode({ "amplitude_smoothcc14", "15482" }); - REQUIRE(region.amplitudeCC[14].smooth == 127); + REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 100); region.parseOpcode({ "amplitude_smoothcc14", "-2" }); - REQUIRE(region.amplitudeCC[14].smooth == 0); + REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 0); region.parseOpcode({ "amplitude_stepcc120", "24" }); - REQUIRE(region.amplitudeCC[120].step == 24.0_a); + REQUIRE(region.[Mod::amplitude][120].step == 24.0_a); region.parseOpcode({ "amplitude_stepcc120", "15482" }); - REQUIRE(region.amplitudeCC[120].step == 100.0_a); + REQUIRE(region.[Mod::amplitude][120].step == 100.0_a); region.parseOpcode({ "amplitude_stepcc120", "-2" }); - REQUIRE(region.amplitudeCC[120].step == 0.0f); + REQUIRE(region.modifiers[Mod::amplitude][120].step == 0.0f); } SECTION("volume_oncc/gain_cc") { - REQUIRE(region.volumeCC.empty()); + REQUIRE(region.modifiers[Mod::volume].empty()); region.parseOpcode({ "gain_cc1", "40" }); - REQUIRE(region.volumeCC.contains(1)); - REQUIRE(region.volumeCC[1].value == 40_a); + REQUIRE(region.modifiers[Mod::volume].contains(1)); + REQUIRE(region.modifiers[Mod::volume][1].value == 40_a); region.parseOpcode({ "volume_oncc2", "-76" }); - REQUIRE(region.volumeCC.contains(2)); - REQUIRE(region.volumeCC[2].value == -76.0_a); + REQUIRE(region.modifiers[Mod::volume].contains(2)); + REQUIRE(region.modifiers[Mod::volume][2].value == -76.0_a); region.parseOpcode({ "gain_oncc4", "-1" }); - REQUIRE(region.volumeCC.contains(4)); - REQUIRE(region.volumeCC[4].value == -1.0_a); + REQUIRE(region.modifiers[Mod::volume].contains(4)); + REQUIRE(region.modifiers[Mod::volume][4].value == -1.0_a); region.parseOpcode({ "volume_curvecc17", "18" }); - REQUIRE(region.volumeCC[17].curve == 18); + REQUIRE(region.modifiers[Mod::volume][17].curve == 18); region.parseOpcode({ "volume_curvecc17", "15482" }); - REQUIRE(region.volumeCC[17].curve == 255); + REQUIRE(region.modifiers[Mod::volume][17].curve == 255); region.parseOpcode({ "volume_curvecc17", "-2" }); - REQUIRE(region.volumeCC[17].curve == 0); + REQUIRE(region.modifiers[Mod::volume][17].curve == 0); region.parseOpcode({ "volume_smoothcc14", "85" }); - REQUIRE(region.volumeCC[14].smooth == 85); + REQUIRE(region.modifiers[Mod::volume][14].smooth == 85); region.parseOpcode({ "volume_smoothcc14", "15482" }); - REQUIRE(region.volumeCC[14].smooth == 127); + REQUIRE(region.modifiers[Mod::volume][14].smooth == 100); region.parseOpcode({ "volume_smoothcc14", "-2" }); - REQUIRE(region.volumeCC[14].smooth == 0); + REQUIRE(region.modifiers[Mod::volume][14].smooth == 0); region.parseOpcode({ "volume_stepcc120", "24" }); - REQUIRE(region.volumeCC[120].step == 24.0f); + REQUIRE(region.modifiers[Mod::volume][120].step == 24.0f); region.parseOpcode({ "volume_stepcc120", "15482" }); - REQUIRE(region.volumeCC[120].step == 144.0f); + REQUIRE(region.modifiers[Mod::volume][120].step == 144.0f); region.parseOpcode({ "volume_stepcc120", "-2" }); - REQUIRE(region.volumeCC[120].step == 0.0f); + REQUIRE(region.modifiers[Mod::volume][120].step == 0.0f); } SECTION("tune_cc/pitch_cc") { - REQUIRE(region.tuneCC.empty()); + REQUIRE(region.modifiers[Mod::pitch].empty()); region.parseOpcode({ "pitch_cc1", "40" }); - REQUIRE(region.tuneCC.contains(1)); - REQUIRE(region.tuneCC[1].value == 40.0); + REQUIRE(region.modifiers[Mod::pitch].contains(1)); + REQUIRE(region.modifiers[Mod::pitch][1].value == 40.0); region.parseOpcode({ "tune_oncc2", "-76" }); - REQUIRE(region.tuneCC.contains(2)); - REQUIRE(region.tuneCC[2].value == -76.0); + REQUIRE(region.modifiers[Mod::pitch].contains(2)); + REQUIRE(region.modifiers[Mod::pitch][2].value == -76.0); region.parseOpcode({ "pitch_oncc4", "-1" }); - REQUIRE(region.tuneCC.contains(4)); - REQUIRE(region.tuneCC[4].value == -1.0); + REQUIRE(region.modifiers[Mod::pitch].contains(4)); + REQUIRE(region.modifiers[Mod::pitch][4].value == -1.0); region.parseOpcode({ "tune_curvecc17", "18" }); - REQUIRE(region.tuneCC[17].curve == 18); + REQUIRE(region.modifiers[Mod::pitch][17].curve == 18); region.parseOpcode({ "pitch_curvecc17", "15482" }); - REQUIRE(region.tuneCC[17].curve == 255); + REQUIRE(region.modifiers[Mod::pitch][17].curve == 255); region.parseOpcode({ "tune_curvecc17", "-2" }); - REQUIRE(region.tuneCC[17].curve == 0); + REQUIRE(region.modifiers[Mod::pitch][17].curve == 0); region.parseOpcode({ "pitch_smoothcc14", "85" }); - REQUIRE(region.tuneCC[14].smooth == 85); + REQUIRE(region.modifiers[Mod::pitch][14].smooth == 85); region.parseOpcode({ "tune_smoothcc14", "15482" }); - REQUIRE(region.tuneCC[14].smooth == 127); + REQUIRE(region.modifiers[Mod::pitch][14].smooth == 100); region.parseOpcode({ "pitch_smoothcc14", "-2" }); - REQUIRE(region.tuneCC[14].smooth == 0); + REQUIRE(region.modifiers[Mod::pitch][14].smooth == 0); region.parseOpcode({ "tune_stepcc120", "24" }); - REQUIRE(region.tuneCC[120].step == 24.0f); + REQUIRE(region.modifiers[Mod::pitch][120].step == 24.0f); region.parseOpcode({ "pitch_stepcc120", "15482" }); - REQUIRE(region.tuneCC[120].step == 9600.0f); + REQUIRE(region.modifiers[Mod::pitch][120].step == 9600.0f); region.parseOpcode({ "tune_stepcc120", "-2" }); - REQUIRE(region.tuneCC[120].step == 0.0f); + REQUIRE(region.modifiers[Mod::pitch][120].step == 0.0f); } } // Specific region bugs TEST_CASE("[Region] Non-conforming floating point values in integer opcodes") { - sfz::MidiState midiState; - sfz::Region region { 0, midiState }; + MidiState midiState; + Region region { 0, midiState }; region.parseOpcode({ "offset", "2014.5" }); REQUIRE(region.offset == 2014); region.parseOpcode({ "pitch_keytrack", "-2.1" }); diff --git a/tests/OnePoleFilterT.cpp b/tests/SmoothersT.cpp similarity index 97% rename from tests/OnePoleFilterT.cpp rename to tests/SmoothersT.cpp index f44e65cc..63c05141 100644 --- a/tests/OnePoleFilterT.cpp +++ b/tests/SmoothersT.cpp @@ -5,6 +5,7 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/OnePoleFilter.h" +#include "sfizz/Smoothers.h" #include "catch2/catch.hpp" // #include "cnpy.h" // #include "ghc/fs_std.hpp" @@ -16,14 +17,13 @@ using namespace Catch::literals; template -inline bool approxEqual(const std::array &lhs, const std::array &rhs) +inline bool approxEqual(const std::array& lhs, const std::array& rhs, Type epsilon = 1e-3) { if (lhs.size() != rhs.size()) return false; for (size_t i = 0; i < rhs.size(); ++i) - if (lhs[i] != Approx(rhs[i]).epsilon(1e-3)) - { + if (lhs[i] != Approx(rhs[i]).epsilon(epsilon)) { std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n'; return false; } @@ -74,7 +74,8 @@ constexpr std::array floatInput01 = { 1.1666758495956464f, -0.04382336233428379f, 0.43819763320865857f, 0.1740610608056625f, 0.30473671729007396f, -0.4065881153810866f, -0.9784770671900811f, -0.17674381857142665f, 0.3493213284003123f, -1.2540491577273034f, 1.2597719140599792f, 0.4198847510851298f, - 0.9612865621570132f, -1.5614809857797225f, -0.31416474166626646f, -1.4741449502960542f }; + 0.9612865621570132f, -1.5614809857797225f, -0.31416474166626646f, -1.4741449502960542f +}; constexpr std::array floatOutputLow01 = { 0.06567783171600353f, 0.18655945645590016f, 0.15368937959051f, 0.05054483866245487f, @@ -92,7 +93,8 @@ constexpr std::array floatOutputLow01 = { 0.09460636244101792f, 0.17948270447550216f, 0.18270169192308128f, 0.20514308375655024f, 0.21137141199133533f, 0.16368102816645502f, 0.008005824629720673f, -0.0984698603721838f, -0.06487738486552441f, -0.13532948119242824f, -0.11020387039992532f, 0.06252925741325283f, - 0.1767213299964926f, 0.0900270496677931f, -0.09685475276689556f, -0.24181840607858007f }; + 0.1767213299964926f, 0.0900270496677931f, -0.09685475276689556f, -0.24181840607858007f +}; constexpr std::array floatOutputHigh01 = { 0.6567783171600353f, 0.5520379302389311f, -0.8807386988928331f, -0.1507067103877182f, @@ -110,7 +112,8 @@ constexpr std::array floatOutputHigh01 = { 1.0720694871546286f, -0.22330606680978596f, 0.2554959412855773f, -0.031082022950887744f, 0.09336530529873863f, -0.5702691435475415f, -0.9864828918198018f, -0.07827395819924285f, 0.41419871326583674f, -1.1187196765348753f, 1.3699757844599045f, 0.35735549367187697f, - 0.7845652321605207f, -1.6515080354475156f, -0.2173099888993709f, -1.2323265442174742f }; + 0.7845652321605207f, -1.6515080354475156f, -0.2173099888993709f, -1.2323265442174742f +}; constexpr std::array floatInput05 = { -0.8247415510202276f, -1.0299159073255513f, 0.7689727513393745f, -0.023063681797826918f, @@ -128,7 +131,8 @@ constexpr std::array floatInput05 = { -0.6137810912250902f, -0.7039958189789415f, -1.33840097232278f, 0.4786946763969468f, -0.46464793721558995f, -1.7121509287301122f, 0.7887828546774234f, -0.902172963851904f, -0.2591368523894675f, -0.9510361177022718f, 0.5739217219088085f, -0.25730420306720403f, - 0.41740839680521646f, -2.0979181310103074f, 1.1494564006889283f, 0.5059893282486726f }; + 0.41740839680521646f, -2.0979181310103074f, 1.1494564006889283f, 0.5059893282486726f +}; constexpr std::array floatOutputLow05 = { -0.27491385034007587f, -0.7098571028952849f, -0.32360008629382064f, 0.14076966108257558f, @@ -146,7 +150,8 @@ constexpr std::array floatOutputLow05 = { 0.02703177262292994f, -0.4302483791937005f, -0.8242150568318073f, -0.5613071175858801f, -0.18242012613484115f, -0.7864063306935144f, -0.5699248015820677f, -0.22777163691884944f, -0.46302715105340697f, -0.5577333737150487f, -0.311615923169504f, 0.001667198557366828f, - 0.05392379743179307f, -0.5421953122577658f, -0.49688568085971485f, 0.3861866826926287f }; + 0.05392379743179307f, -0.5421953122577658f, -0.49688568085971485f, 0.3861866826926287f +}; constexpr std::array floatOutputHigh05 = { -0.5498277006801517f, -0.32005880443026635f, 1.0925728376331951f, -0.1638333428804025f, @@ -164,7 +169,8 @@ constexpr std::array floatOutputHigh05 = { -0.6408128638480202f, -0.27374743978524096f, -0.5141859154909728f, 1.040001793982827f, -0.2822278110807488f, -0.9257445980365978f, 1.358707656259491f, -0.6744013269330547f, 0.20389029866393948f, -0.3933027439872231f, 0.8855376450783126f, -0.2589714016245709f, - 0.36348459937342337f, -1.5557228187525416f, 1.6463420815486431f, 0.11980264555604392f }; + 0.36348459937342337f, -1.5557228187525416f, 1.6463420815486431f, 0.11980264555604392f +}; constexpr std::array floatInput09 = { -0.9629663717342508f, 1.054078826032172f, -1.0644939081323097f, -0.05328934531304567f, @@ -182,7 +188,8 @@ constexpr std::array floatInput09 = { 0.7517403248613556f, 1.1194691465807607f, -1.1160170942539855f, -1.0010374555669668f, 2.1609909686692763f, -0.07213993925443297f, -0.5083174992310037f, -0.7489925703250175f, 0.5119124853257149f, -0.33950253799120345f, -0.26764774112191847f, 0.10271208568438035f, - -0.09893862035889031f, -0.4154625911342657f, 0.11272544601558693f, -0.6895075000870634f }; + -0.09893862035889031f, -0.4154625911342657f, 0.11272544601558693f, -0.6895075000870634f +}; constexpr std::array floatOutputLow09 = { -0.45614196555832937f, 0.01915105911173487f, -0.003925509462605503f, -0.5296828837089897f, @@ -200,7 +207,8 @@ constexpr std::array floatOutputLow09 = { 1.3518864464016498f, 0.9575142994410892f, 0.0520306721253716f, -1.0000768566454319f, 0.496816040067124f, 1.015603963410564f, -0.22150068331359823f, -0.6072258583851468f, -0.14426034859888792f, 0.07407521986377441f, -0.2836988048502276f, -0.09305893177831953f, - -0.003110407570995219f, -0.24382743742154736f, -0.15623482860471877f, -0.28143543764463197f }; + -0.003110407570995219f, -0.24382743742154736f, -0.15623482860471877f, -0.28143543764463197f +}; constexpr std::array floatOutputHigh09 = { -0.5068244061759215f, 1.034927766920437f, -1.0605683986697043f, 0.47639353839594406f, @@ -218,7 +226,8 @@ constexpr std::array floatOutputHigh09 = { -0.6001461215402942f, 0.16195484713967145f, -1.1680477663793571f, -0.0009605989215348831f, 1.6641749286021523f, -1.087743902664997f, -0.28681681591740543f, -0.14176671193987078f, 0.6561728339246028f, -0.41357775785497786f, 0.016051063728309112f, 0.19577101746269987f, - -0.09582821278789509f, -0.17163515371271834f, 0.2689602746203057f, -0.40807206244243144f }; + -0.09582821278789509f, -0.17163515371271834f, 0.2689602746203057f, -0.40807206244243144f +}; constexpr std::array doubleInput01 = { 0.7224561488760388, 0.7385973866948313, -0.7270493193023231, -0.10016187172526334, @@ -236,7 +245,8 @@ constexpr std::array doubleInput01 = { 1.1666758495956464, -0.04382336233428379, 0.43819763320865857, 0.1740610608056625, 0.30473671729007396, -0.4065881153810866, -0.9784770671900811, -0.17674381857142665, 0.3493213284003123, -1.2540491577273034, 1.2597719140599792, 0.4198847510851298, - 0.9612865621570132, -1.5614809857797225, -0.31416474166626646, -1.4741449502960542 }; + 0.9612865621570132, -1.5614809857797225, -0.31416474166626646, -1.4741449502960542 +}; constexpr std::array doubleOutputLow01 = { 0.06567783171600353, 0.18655945645590016, 0.15368937959051, 0.05054483866245487, @@ -254,7 +264,8 @@ constexpr std::array doubleOutputLow01 = { 0.09460636244101792, 0.17948270447550216, 0.18270169192308128, 0.20514308375655024, 0.21137141199133533, 0.16368102816645502, 0.008005824629720673, -0.0984698603721838, -0.06487738486552441, -0.13532948119242824, -0.11020387039992532, 0.06252925741325283, - 0.1767213299964926, 0.0900270496677931, -0.09685475276689556, -0.24181840607858007 }; + 0.1767213299964926, 0.0900270496677931, -0.09685475276689556, -0.24181840607858007 +}; constexpr std::array doubleOutputHigh01 = { 0.6567783171600353, 0.5520379302389311, -0.8807386988928331, -0.1507067103877182, @@ -272,7 +283,8 @@ constexpr std::array doubleOutputHigh01 = { 1.0720694871546286, -0.22330606680978596, 0.2554959412855773, -0.031082022950887744, 0.09336530529873863, -0.5702691435475415, -0.9864828918198018, -0.07827395819924285, 0.41419871326583674, -1.1187196765348753, 1.3699757844599045, 0.35735549367187697, - 0.7845652321605207, -1.6515080354475156, -0.2173099888993709, -1.2323265442174742 }; + 0.7845652321605207, -1.6515080354475156, -0.2173099888993709, -1.2323265442174742 +}; constexpr std::array doubleInput05 = { -0.8247415510202276, -1.0299159073255513, 0.7689727513393745, -0.023063681797826918, @@ -290,7 +302,8 @@ constexpr std::array doubleInput05 = { -0.6137810912250902, -0.7039958189789415, -1.33840097232278, 0.4786946763969468, -0.46464793721558995, -1.7121509287301122, 0.7887828546774234, -0.902172963851904, -0.2591368523894675, -0.9510361177022718, 0.5739217219088085, -0.25730420306720403, - 0.41740839680521646, -2.0979181310103074, 1.1494564006889283, 0.5059893282486726 }; + 0.41740839680521646, -2.0979181310103074, 1.1494564006889283, 0.5059893282486726 +}; constexpr std::array doubleOutputLow05 = { -0.27491385034007587, -0.7098571028952849, -0.32360008629382064, 0.14076966108257558, @@ -308,7 +321,8 @@ constexpr std::array doubleOutputLow05 = { 0.02703177262292994, -0.4302483791937005, -0.8242150568318073, -0.5613071175858801, -0.18242012613484115, -0.7864063306935144, -0.5699248015820677, -0.22777163691884944, -0.46302715105340697, -0.5577333737150487, -0.311615923169504, 0.001667198557366828, - 0.05392379743179307, -0.5421953122577658, -0.49688568085971485, 0.3861866826926287 }; + 0.05392379743179307, -0.5421953122577658, -0.49688568085971485, 0.3861866826926287 +}; constexpr std::array doubleOutputHigh05 = { -0.5498277006801517, -0.32005880443026635, 1.0925728376331951, -0.1638333428804025, @@ -326,7 +340,8 @@ constexpr std::array doubleOutputHigh05 = { -0.6408128638480202, -0.27374743978524096, -0.5141859154909728, 1.040001793982827, -0.2822278110807488, -0.9257445980365978, 1.358707656259491, -0.6744013269330547, 0.20389029866393948, -0.3933027439872231, 0.8855376450783126, -0.2589714016245709, - 0.36348459937342337, -1.5557228187525416, 1.6463420815486431, 0.11980264555604392 }; + 0.36348459937342337, -1.5557228187525416, 1.6463420815486431, 0.11980264555604392 +}; constexpr std::array doubleInput09 = { -0.9629663717342508, 1.054078826032172, -1.0644939081323097, -0.05328934531304567, @@ -344,7 +359,8 @@ constexpr std::array doubleInput09 = { 0.7517403248613556, 1.1194691465807607, -1.1160170942539855, -1.0010374555669668, 2.1609909686692763, -0.07213993925443297, -0.5083174992310037, -0.7489925703250175, 0.5119124853257149, -0.33950253799120345, -0.26764774112191847, 0.10271208568438035, - -0.09893862035889031, -0.4154625911342657, 0.11272544601558693, -0.6895075000870634 }; + -0.09893862035889031, -0.4154625911342657, 0.11272544601558693, -0.6895075000870634 +}; constexpr std::array doubleOutputLow09 = { -0.45614196555832937, 0.01915105911173487, -0.003925509462605503, -0.5296828837089897, @@ -362,7 +378,8 @@ constexpr std::array doubleOutputLow09 = { 1.3518864464016498, 0.9575142994410892, 0.0520306721253716, -1.0000768566454319, 0.496816040067124, 1.015603963410564, -0.22150068331359823, -0.6072258583851468, -0.14426034859888792, 0.07407521986377441, -0.2836988048502276, -0.09305893177831953, - -0.003110407570995219, -0.24382743742154736, -0.15623482860471877, -0.28143543764463197 }; + -0.003110407570995219, -0.24382743742154736, -0.15623482860471877, -0.28143543764463197 +}; constexpr std::array doubleOutputHigh09 = { -0.5068244061759215, 1.034927766920437, -1.0605683986697043, 0.47639353839594406, @@ -380,7 +397,8 @@ constexpr std::array doubleOutputHigh09 = { -0.6001461215402942, 0.16195484713967145, -1.1680477663793571, -0.0009605989215348831, 1.6641749286021523, -1.087743902664997, -0.28681681591740543, -0.14176671193987078, 0.6561728339246028, -0.41357775785497786, 0.016051063728309112, 0.19577101746269987, - -0.09582821278789509, -0.17163515371271834, 0.2689602746203057, -0.40807206244243144 }; + -0.09582821278789509, -0.17163515371271834, 0.2689602746203057, -0.40807206244243144 +}; TEST_CASE("[OnePoleFilter] Tests") {