// 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 "sfizz/ModifierHelpers.h" #include "sfizz/Buffer.h" #include "catch2/catch.hpp" #include "TestHelpers.h" #include #include #include #include #include using namespace Catch::literals; const auto idModifier = [](float x) { return x; }; const auto twiceModifier = [](float x) { return 2 * x; }; const auto expModifier = [](float x) { return std::exp(x); }; TEST_CASE("[Envelopes] Empty") { sfz::EventVector events { { 0, 0.0f } }; std::array output; std::array expected { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array expectedMul { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); REQUIRE(approxEqual(output, expected)); linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); REQUIRE(approxEqual(output, expected)); multiplicativeEnvelope(events, absl::MakeSpan(output), expModifier); REQUIRE(approxEqual(output, expectedMul)); multiplicativeEnvelope(events, absl::MakeSpan(output), expModifier, 2.0f); REQUIRE(approxEqual(output, expectedMul)); } TEST_CASE("[Envelopes] Linear basic") { sfz::EventVector events { { 0, 0.0f }, { 4, 1.0f } }; std::array output; std::array expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, close") { sfz::EventVector events { { 0, 0.0f }, { 4, 1.0f }, { 5, 2.0f } }; std::array output; std::array expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, far") { sfz::EventVector events { { 0, 0.0f }, { 2, 1.0f }, { 6, 2.0f } }; std::array output; std::array expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 3 events, out of block") { sfz::EventVector events { { 0, 0.0f }, { 2, 1.0f }, { 6, 2.0f }, { 10, 3.0f } }; std::array output; std::array expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.5f, 3.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, function") { sfz::EventVector events { { 0, 0.0f }, { 2, 1.0f }, { 6, 2.0f } }; std::array output; std::array expected { 0.0f, 1.0f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.0f, 4.0f }; linearEnvelope(events, absl::MakeSpan(output), twiceModifier); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized") { sfz::EventVector events { { 0, 0.0f }, { 2, 1.0f }, { 6, 2.0f } }; std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets") { sfz::EventVector events { { 0, 0.0f }, { 2, 1.1f }, { 6, 1.9f } }; std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with 2 steps") { sfz::EventVector events { { 0, 0.0f }, { 2, 1.0f }, { 6, 3.0f } }; std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and an unquantized out of block step") { sfz::EventVector events { { 0, 0.0f }, { 2, 1.0f }, { 6, 3.0f }, { 10, 4.2f }, }; std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 4.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Going down quantized with 2 steps") { sfz::EventVector events { { 0, 3.0f }, { 2, 2.0f }, { 6, 0.0f } }; std::array output; std::array expected { 3.0f, 3.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.0f, 0.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Basic event") { sfz::EventVector events { { 0, 1.0f }, { 4, 2.0f } }; std::array output; std::array expected { 1.0f, 1.1892f, 1.4142f, 1.68176f, 2.0f, 2.0f, 2.0f, 2.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] 2 events") { sfz::EventVector events { { 0, 1.0f }, { 4, 2.0f }, { 5, 4.0f } }; std::array output; std::array expected { 1.0f, 1.1892f, 1.4142f, 1.68176f, 2.0f, 4.0f, 4.0f, 4.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] 2 events, far") { sfz::EventVector events { { 0, 1.0f }, { 2, 2.0f }, { 6, 4.0f } }; std::array output; std::array expected { 1.0f, 1.4142f, 2.0f, 2.37841f, 2.82843f, 3.36358f, 4.0f, 4.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps") { sfz::EventVector events { { 0, 1.3f }, { 2, 2.1f }, { 6, 4.2f } }; std::array output; std::array expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 4.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of range step") { sfz::EventVector events { { 0, 1.3f }, { 2, 2.1f }, { 6, 4.1f }, { 10, 8.2f } }; std::array output; std::array expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 8.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") { sfz::EventVector events { { 0, 4.1f }, { 2, 2.2f }, { 6, 0.4f } }; std::array output; std::array expected { 4.0f, 4.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.5f, 0.5f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); REQUIRE(approxEqual(output, expected)); } #if 0 TEST_CASE("[linearModifiers] Compare with envelopes") { sfz::Resources resources; resources.curves = sfz::CurveSet::createPredefined(); sfz::CCData ccData; ccData.cc = 20; ccData.data.value = 100.0f; resources.midiState.ccEvent(5, 20, 0.1); resources.midiState.ccEvent(10, 20, 0.8); std::array output; std::array envelope; linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return ccData.data.value * x; }); linearModifier(resources, absl::MakeSpan(output), ccData); REQUIRE(approxEqual(output, envelope)); ccData.data.curve = 1; linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return ccData.data.value * (2 * x - 1); }); linearModifier(resources, absl::MakeSpan(output), ccData); REQUIRE(approxEqual(output, envelope)); ccData.data.curve = 3; ccData.data.value = 10.0f; linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return ccData.data.value * (1 - 2 * x); }); linearModifier(resources, absl::MakeSpan(output), ccData); REQUIRE(approxEqual(output, envelope)); ccData.data.curve = 2; ccData.data.value = 20.0f; ccData.data.step = 2.5f; linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return ccData.data.value * (1 - x); }, ccData.data.step); linearModifier(resources, absl::MakeSpan(output), ccData); REQUIRE(approxEqual(output, envelope)); } TEST_CASE("[multiplicativeModifiers] Compare with envelopes") { sfz::Resources resources; resources.curves = sfz::CurveSet::createPredefined(); sfz::CCData ccData; ccData.cc = 20; ccData.data.value = 100.0f; resources.midiState.ccEvent(5, 20, 0.1); resources.midiState.ccEvent(15, 20, 0.8); std::array output; std::array envelope; multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return db2mag(ccData.data.value * x); }); multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { return db2mag(x); }); REQUIRE(approxEqual(output, envelope)); ccData.data.curve = 1; multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return db2mag(ccData.data.value * (2 * x - 1)); }); multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { return db2mag(x); }); REQUIRE(approxEqual(output, envelope)); ccData.data.curve = 3; ccData.data.value = 10.0f; multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return db2mag(ccData.data.value * (1 - 2 * x)); }); multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { return db2mag(x); }); REQUIRE(approxEqual(output, envelope)); ccData.data.curve = 2; ccData.data.value = 20.0f; ccData.data.step = 2.5f; multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return db2mag(ccData.data.value * (1 - x)); }, db2mag(ccData.data.step) ); multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) { return db2mag(x); }); REQUIRE(approxEqual(output, envelope)); } #endif