Remove unused code, update tests

This commit is contained in:
Jean Pierre Cimalando 2020-07-27 17:32:25 +02:00
parent 0ebe24939d
commit 82ebb639a7
16 changed files with 182 additions and 374 deletions

View file

@ -73,7 +73,6 @@ set (SFIZZ_HEADERS
sfizz/MathHelpers.h
sfizz/MidiState.h
sfizz/ModifierHelpers.h
sfizz/Modifiers.h
sfizz/NumericId.h
sfizz/OnePoleFilter.h
sfizz/Oversampler.h

View file

@ -8,8 +8,7 @@
#include "Range.h"
#include "Defaults.h"
#include "Modifiers.h"
#include "Resources.h"
#include "SfzHelpers.h"
#include "absl/types/span.h"
namespace sfz {
@ -257,77 +256,4 @@ void pitchBendEnvelope(const EventVector& events, absl::Span<float> envelope, F&
multiplicativeEnvelope<F>(events, envelope, std::forward<F>(lambda));
}
/**
* @brief Builds a linear envelope, possibly quantized, based on the events fetched
* from a midi state and the modifier data. This is a helper function for recurrent
* code in the voice logic.
*
* @tparam F
* @param resources
* @param span
* @param ccData
* @param lambda
*/
template <class F>
void linearModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& ccData, F&& lambda)
{
const auto& events = resources.midiState.getCCEvents(ccData.cc);
const auto& curve = resources.curves.getCurve(ccData.data.curve);
if (ccData.data.step == 0.0f) {
linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) {
return lambda(curve.evalNormalized(x) * ccData.data.value);
});
} else {
const float stepSize { lambda(ccData.data.step) };
linearEnvelope(
events, span, [&ccData, &curve, &lambda](float x) {
return lambda(curve.evalNormalized(x) * ccData.data.value);
},
stepSize);
}
}
/**
* @brief Builds a multiplicative envelope, possibly quantized, based on the events fetched
* from a midi state and the modifier data. This is a helper function for recurrent
* code in the voice logic.
*
* @tparam F
* @param resources
* @param span
* @param ccData
* @param lambda
*/
template <class F>
void multiplicativeModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& ccData, F&& lambda)
{
const auto& events = resources.midiState.getCCEvents(ccData.cc);
const auto& curve = resources.curves.getCurve(ccData.data.curve);
if (ccData.data.step == 0.0f) {
multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) {
return lambda(curve.evalNormalized(x) * ccData.data.value);
});
} else {
const float stepSize { lambda(ccData.data.step) };
multiplicativeEnvelope(
events, span, [&ccData, &curve, &lambda](float x) {
return lambda(curve.evalNormalized(x) * ccData.data.value);
},
stepSize);
}
}
/**
* @brief Alias for a simple linear modifier with no lambda
*
* @tparam F
* @param resources
* @param span
* @param ccData
* @param lambda
*/
inline void linearModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& ccData)
{
linearModifier(resources, span, ccData, [](float x) { return x; });
}
}
} // namespace sfz

View file

@ -1,92 +0,0 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include "Config.h"
#include <numeric>
#include <array>
#include <vector>
#include <limits>
#include <cstdint>
namespace sfz {
/**
* @brief Base modifier class
*
*/
struct Modifier {
float value { 0.0f };
float step { 0.0f };
uint8_t curve { 0 };
uint8_t smooth { 0 };
static_assert(config::maxCurves - 1 <= std::numeric_limits<decltype(curve)>::max(), "The curve type in the Modifier struct cannot support the required number of curves");
};
enum class Mod : size_t {
amplitude = 0,
pan,
width,
position,
pitch,
volume,
sentinel
};
/**
* @brief Vectors of elements indexed on modifiers with casting and iterators
*
* @tparam T
*/
template <class T>
class ModifierVector : public std::vector<T> {
public:
T& operator[](sfz::Mod idx) { return this->std::vector<T>::operator[](static_cast<size_t>(idx)); }
const T& operator[](sfz::Mod idx) const { return this->std::vector<T>::operator[](static_cast<size_t>(idx)); }
};
/**
* @brief Array of elements indexed on modifiers with casting and iterators
*
* @tparam T
*/
template <class T>
class ModifierArray {
public:
using ContainerType = typename std::array<T, (size_t)Mod::sentinel>;
using iterator = typename ContainerType::iterator;
using const_iterator = typename ContainerType::const_iterator;
ModifierArray() = default;
ModifierArray(T val)
{
std::fill(underlying.begin(), underlying.end(), val);
}
ModifierArray(std::array<T, (size_t)Mod::sentinel>&& array) : underlying(array) {}
T& operator[](sfz::Mod idx) { return underlying.operator[](static_cast<size_t>(idx)); }
const T& operator[](sfz::Mod idx) const { return underlying.operator[](static_cast<size_t>(idx)); }
iterator begin() { return underlying.begin(); }
iterator end() { return underlying.end(); }
const_iterator begin() const { return underlying.begin(); }
const_iterator end() const { return underlying.end(); }
private:
ContainerType underlying {};
};
/**
* @brief Helper for iterating over all possible modifiers.
* Should fail at compile time if you update the modifiers but not this.
*
*/
static const ModifierArray<Mod> allModifiers {{
Mod::amplitude,
Mod::pan,
Mod::width,
Mod::position,
Mod::pitch,
Mod::volume
}};
}

View file

@ -13,7 +13,7 @@
#include "absl/types/optional.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/ascii.h"
#include <string_view>
#include "absl/strings/string_view.h"
#include <vector>
#include <type_traits>
#include <iosfwd>

View file

@ -379,35 +379,35 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
setValueFromOpcode(opcode, volume, Default::volumeRange);
break;
case_any_ccN("volume"): // also gain
processGenericCc(opcode, Default::volumeCCRange, &modifiers[Mod::volume], ModKey::createNXYZ(ModId::Volume, id));
processGenericCc(opcode, Default::volumeCCRange, ModKey::createNXYZ(ModId::Volume, id));
break;
case hash("amplitude"):
if (auto value = readOpcode(opcode.value, Default::amplitudeRange))
amplitude = normalizePercents(*value);
break;
case_any_ccN("amplitude"):
processGenericCc(opcode, Default::amplitudeRange, &modifiers[Mod::amplitude], ModKey::createNXYZ(ModId::Amplitude, id));
processGenericCc(opcode, Default::amplitudeRange, ModKey::createNXYZ(ModId::Amplitude, id));
break;
case hash("pan"):
if (auto value = readOpcode(opcode.value, Default::panRange))
pan = normalizePercents(*value);
break;
case_any_ccN("pan"):
processGenericCc(opcode, Default::panCCRange, &modifiers[Mod::pan], ModKey::createNXYZ(ModId::Pan, id));
processGenericCc(opcode, Default::panCCRange, ModKey::createNXYZ(ModId::Pan, id));
break;
case hash("position"):
if (auto value = readOpcode(opcode.value, Default::positionRange))
position = normalizePercents(*value);
break;
case_any_ccN("position"):
processGenericCc(opcode, Default::positionCCRange, &modifiers[Mod::position], ModKey::createNXYZ(ModId::Position, id));
processGenericCc(opcode, Default::positionCCRange, ModKey::createNXYZ(ModId::Position, id));
break;
case hash("width"):
if (auto value = readOpcode(opcode.value, Default::widthRange))
width = normalizePercents(*value);
break;
case_any_ccN("width"):
processGenericCc(opcode, Default::widthCCRange, &modifiers[Mod::width], ModKey::createNXYZ(ModId::Width, id));
processGenericCc(opcode, Default::widthCCRange, ModKey::createNXYZ(ModId::Width, id));
break;
case hash("amp_keycenter"):
setValueFromOpcode(opcode, ampKeycenter, Default::keyRange);
@ -771,7 +771,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
setValueFromOpcode(opcode, tune, Default::tuneRange);
break;
case_any_ccN("pitch"): // also tune
processGenericCc(opcode, Default::tuneCCRange, &modifiers[Mod::pitch], ModKey::createNXYZ(ModId::Pitch, id));
processGenericCc(opcode, Default::tuneCCRange, ModKey::createNXYZ(ModId::Pitch, id));
break;
case hash("bend_up"): // also bendup
setValueFromOpcode(opcode, bendUp, Default::bendBoundRange);
@ -925,7 +925,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
return true;
}
bool sfz::Region::processGenericCc(const Opcode& opcode, Range<float> range, CCMap<Modifier> *ccMap, const ModKey& target)
bool sfz::Region::processGenericCc(const Opcode& opcode, Range<float> range, const ModKey& target)
{
if (!opcode.isAnyCcN())
return false;
@ -934,31 +934,6 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, Range<float> range, CCM
if (ccNumber >= config::numCCs)
return false;
// TODO obsolete after implementing mod matrix
if (ccMap) {
Modifier& modifier = (*ccMap)[ccNumber];
switch (opcode.category) {
case kOpcodeOnCcN:
setValueFromOpcode(opcode, modifier.value, range);
break;
case kOpcodeCurveCcN:
setValueFromOpcode(opcode, modifier.curve, Default::curveCCRange);
break;
case kOpcodeStepCcN:
{
const Range<float> stepCCRange { 0.0f, std::max(std::abs(range.getStart()), std::abs(range.getEnd())) };
setValueFromOpcode(opcode, modifier.step, stepCCRange);
}
break;
case kOpcodeSmoothCcN:
setValueFromOpcode(opcode, modifier.smooth, Default::smoothCCRange);
break;
default:
assert(false);
break;
}
}
if (target) {
// search an existing connection of same CC number and target
// if it exists, modify, otherwise create

View file

@ -17,9 +17,9 @@
#include "MidiState.h"
#include "FileId.h"
#include "NumericId.h"
#include "Modifiers.h"
#include "modulations/ModKey.h"
#include "absl/types/optional.h"
#include "absl/strings/string_view.h"
#include <bitset>
#include <string>
#include <vector>
@ -241,12 +241,11 @@ struct Region {
*
* @param opcode
* @param range
* @param ccMap
* @param target
* @return true if the opcode was properly read and stored.
* @return false
*/
bool processGenericCc(const Opcode& opcode, Range<float> range, CCMap<Modifier> *ccMap, const ModKey& target);
bool processGenericCc(const Opcode& opcode, Range<float> range, const ModKey& target);
void offsetAllKeys(int offset) noexcept;
@ -370,9 +369,6 @@ struct Region {
// Effects
std::vector<float> gainToEffect;
// Modifiers
ModifierArray<CCMap<Modifier>> modifiers;
bool triggerOnCC { false }; // whether the region triggers on CC events or note events
bool triggerOnNote { true };

View file

@ -452,7 +452,6 @@ void sfz::Synth::finalizeSfzLoad()
size_t maxFilters { 0 };
size_t maxEQs { 0 };
ModifierArray<size_t> maxModifiers { 0 };
while (currentRegionIndex < currentRegionCount) {
auto region = regions[currentRegionIndex].get();
@ -563,8 +562,6 @@ void sfz::Synth::finalizeSfzLoad()
region->registerTempo(2.0f);
maxFilters = max(maxFilters, region->filters.size());
maxEQs = max(maxEQs, region->equalizers.size());
for (const auto& mod : allModifiers)
maxModifiers[mod] = max(maxModifiers[mod], region->modifiers[mod].size());
++currentRegionIndex;
}
@ -574,7 +571,6 @@ void sfz::Synth::finalizeSfzLoad()
settingsPerVoice.maxFilters = maxFilters;
settingsPerVoice.maxEQs = maxEQs;
settingsPerVoice.maxModifiers = maxModifiers;
applySettingsPerVoice();
@ -1346,7 +1342,6 @@ void sfz::Synth::applySettingsPerVoice()
for (auto& voice : voices) {
voice->setMaxFiltersPerVoice(settingsPerVoice.maxFilters);
voice->setMaxEQsPerVoice(settingsPerVoice.maxEQs);
voice->prepareSmoothers(settingsPerVoice.maxModifiers);
}
}

View file

@ -18,11 +18,11 @@
#include "parser/Parser.h"
#include "VoiceStealing.h"
#include "utility/SpinMutex.h"
#include "absl/types/span.h"
#include <absl/types/span.h>
#include <absl/types/optional.h>
#include <absl/strings/string_view.h>
#include <random>
#include <set>
#include <string_view>
#include <vector>
namespace sfz {
@ -772,7 +772,6 @@ private:
struct SettingsPerVoice {
size_t maxFilters { 0 };
size_t maxEQs { 0 };
ModifierArray<size_t> maxModifiers { 0 };
};
SettingsPerVoice settingsPerVoice;

View file

@ -141,33 +141,6 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value,
bendSmoother.reset(centsFactor(region->getBendInCents(resources.midiState.getPitchBend())));
egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, value, sampleRate);
for (auto& modId : allModifiers) {
ASSERT(modifierSmoothers[modId].size() >= region->modifiers[modId].size());
forEachWithSmoother(modId, [modId, this](const CCData<Modifier>& mod, Smoother& smoother) {
const auto ccValue = resources.midiState.getCCValue(mod.cc);
const auto& curve = resources.curves.getCurve(mod.data.curve);
const auto finalValue = curve.evalNormalized(ccValue) * mod.data.value;
switch (modId) {
case Mod::volume:
smoother.reset(db2mag(finalValue));
break;
case Mod::pitch:
smoother.reset(centsFactor(finalValue));
break;
case Mod::amplitude:
case Mod::pan:
case Mod::width:
case Mod::position:
smoother.reset(normalizePercents(finalValue));
break;
default:
smoother.reset(finalValue);
break;
}
smoother.setSmoothing(mod.data.smooth, sampleRate);
});
}
resources.modMatrix.initVoice(id);
}
@ -882,12 +855,6 @@ void sfz::Voice::switchState(State s)
}
}
void sfz::Voice::prepareSmoothers(const ModifierArray<size_t>& numModifiers)
{
for (auto& mod : allModifiers)
modifierSmoothers[mod].resize(numModifiers[mod]);
}
void sfz::Voice::pitchEnvelope(absl::Span<float> pitchSpan) noexcept
{
const auto numFrames = pitchSpan.size();
@ -918,21 +885,6 @@ void sfz::Voice::pitchEnvelope(absl::Span<float> pitchSpan) noexcept
void sfz::Voice::resetSmoothers() noexcept
{
for (auto& mod : allModifiers) {
const auto resetValue = [mod] {
switch (mod) {
case Mod::volume: // fallthrough
case Mod::pitch:
return 1.0f;
default:
return 0.0f;
}
}();
for (auto& smoother : modifierSmoothers[mod]) {
smoother.reset(resetValue);
}
}
bendSmoother.reset(1.0f);
gainSmoother.reset(0.0f);
}

View file

@ -302,8 +302,6 @@ public:
Duration getLastFilterDuration() const noexcept { return filterDuration; }
Duration getLastPanningDuration() const noexcept { return panningDuration; }
void prepareSmoothers(const ModifierArray<size_t>& numModifiers);
private:
/**
* @brief Fill a span with data from a file source. This is the first step
@ -390,27 +388,6 @@ private:
*/
void removeVoiceFromRing() noexcept;
/**
* @brief Helper function to iterate jointly on modifiers and smoothers
* for a given modulation target of type sfz::Mod
*
* @tparam F
* @param modId
* @param lambda
*/
template <class F>
void forEachWithSmoother(sfz::Mod modId, F&& lambda)
{
size_t count = region->modifiers[modId].size();
ASSERT(modifierSmoothers[modId].size() >= count);
auto mod = region->modifiers[modId].begin();
auto smoother = modifierSmoothers[modId].begin();
for (size_t i = 0; i < count; ++i) {
lambda(*mod, *smoother);
incrementAll(mod, smoother);
}
}
/**
* @brief Initialize frequency and gain coefficients for the oscillators.
*/
@ -479,7 +456,6 @@ private:
fast_real_distribution<float> uniformNoiseDist { -config::uniformNoiseBounds, config::uniformNoiseBounds };
fast_gaussian_generator<float> gaussianNoiseDist { 0.0f, config::noiseVariance };
ModifierArray<std::vector<Smoother>> modifierSmoothers;
Smoother gainSmoother;
Smoother bendSmoother;
Smoother xfadeSmoother;

View file

@ -5,6 +5,8 @@ project(sfizz)
set(SFIZZ_TEST_SOURCES
RegionT.cpp
RegionTHelpers.h
RegionTHelpers.cpp
ParsingT.cpp
HelpersT.cpp
HelpersT.cpp

View file

@ -261,6 +261,7 @@ TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps")
REQUIRE(approxEqual<float>(output, expected));
}
#if 0
TEST_CASE("[linearModifiers] Compare with envelopes")
{
sfz::Resources resources;
@ -360,4 +361,4 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes")
});
REQUIRE(approxEqual<float>(output, envelope));
}
#endif

View file

@ -4,8 +4,11 @@
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "RegionTHelpers.h"
#include "sfizz/Synth.h"
#include "sfizz/SfzHelpers.h"
#include "sfizz/modulations/ModId.h"
#include "sfizz/modulations/ModKey.h"
#include "catch2/catch.hpp"
#include "ghc/fs_std.hpp"
#if defined(__APPLE__)
@ -356,9 +359,11 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines")
REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 );
REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 );
REQUIRE(!synth.getRegionView(2)->modifiers[Mod::amplitude].empty());
REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].contains(10));
REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].getWithDefault(10).value == 34.0f);
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, synth.getRegionView(2)->getId());
const RegionCCView view(*synth.getRegionView(2), target);
REQUIRE(!view.empty());
REQUIRE(view.at(10).value == 34.0f);
}
TEST_CASE("[Files] Specific bug: relative path with backslashes")

View file

@ -4,10 +4,14 @@
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "RegionTHelpers.h"
#include "sfizz/MidiState.h"
#include "sfizz/Region.h"
#include "sfizz/SfzHelpers.h"
#include "sfizz/modulations/ModId.h"
#include "sfizz/modulations/ModKey.h"
#include "catch2/catch.hpp"
#include <stdexcept>
using namespace Catch::literals;
using namespace sfz::literals;
using namespace sfz;
@ -541,28 +545,29 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("pan_oncc")
{
REQUIRE(region.modifiers[Mod::pan].empty());
const ModKey target = ModKey::createNXYZ(ModId::Pan, region.getId());
const RegionCCView view(region, target);
REQUIRE(view.empty());
region.parseOpcode({ "pan_oncc45", "4.2" });
REQUIRE(region.modifiers[Mod::pan].contains(45));
REQUIRE(region.modifiers[Mod::pan][45].value == 4.2_a);
REQUIRE(view.at(45).value == 4.2_a);
region.parseOpcode({ "pan_curvecc17", "18" });
REQUIRE(region.modifiers[Mod::pan][17].curve == 18);
REQUIRE(view.at(17).curve == 18);
region.parseOpcode({ "pan_curvecc17", "15482" });
REQUIRE(region.modifiers[Mod::pan][17].curve == 255);
REQUIRE(view.at(17).curve == 255);
region.parseOpcode({ "pan_curvecc17", "-2" });
REQUIRE(region.modifiers[Mod::pan][17].curve == 0);
REQUIRE(view.at(17).curve == 0);
region.parseOpcode({ "pan_smoothcc14", "85" });
REQUIRE(region.modifiers[Mod::pan][14].smooth == 85);
REQUIRE(view.at(14).smooth == 85);
region.parseOpcode({ "pan_smoothcc14", "15482" });
REQUIRE(region.modifiers[Mod::pan][14].smooth == 100);
REQUIRE(view.at(14).smooth == 100);
region.parseOpcode({ "pan_smoothcc14", "-2" });
REQUIRE(region.modifiers[Mod::pan][14].smooth == 0);
REQUIRE(view.at(14).smooth == 0);
region.parseOpcode({ "pan_stepcc120", "24" });
REQUIRE(region.modifiers[Mod::pan][120].step == 24.0_a);
REQUIRE(view.at(120).step == 24.0_a);
region.parseOpcode({ "pan_stepcc120", "15482" });
REQUIRE(region.modifiers[Mod::pan][120].step == 200.0_a);
REQUIRE(view.at(120).step == 200.0_a);
region.parseOpcode({ "pan_stepcc120", "-2" });
REQUIRE(region.modifiers[Mod::pan][120].step == 0.0f);
REQUIRE(view.at(120).step == 0.0f);
}
SECTION("width")
@ -580,28 +585,29 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("width_oncc")
{
REQUIRE(region.modifiers[Mod::width].empty());
const ModKey target = ModKey::createNXYZ(ModId::Width, region.getId());
const RegionCCView view(region, target);
REQUIRE(view.empty());
region.parseOpcode({ "width_oncc45", "4.2" });
REQUIRE(region.modifiers[Mod::width].contains(45));
REQUIRE(region.modifiers[Mod::width][45].value == 4.2_a);
REQUIRE(view.at(45).value == 4.2_a);
region.parseOpcode({ "width_curvecc17", "18" });
REQUIRE(region.modifiers[Mod::width][17].curve == 18);
REQUIRE(view.at(17).curve == 18);
region.parseOpcode({ "width_curvecc17", "15482" });
REQUIRE(region.modifiers[Mod::width][17].curve == 255);
REQUIRE(view.at(17).curve == 255);
region.parseOpcode({ "width_curvecc17", "-2" });
REQUIRE(region.modifiers[Mod::width][17].curve == 0);
REQUIRE(view.at(17).curve == 0);
region.parseOpcode({ "width_smoothcc14", "85" });
REQUIRE(region.modifiers[Mod::width][14].smooth == 85);
REQUIRE(view.at(14).smooth == 85);
region.parseOpcode({ "width_smoothcc14", "15482" });
REQUIRE(region.modifiers[Mod::width][14].smooth == 100);
REQUIRE(view.at(14).smooth == 100);
region.parseOpcode({ "width_smoothcc14", "-2" });
REQUIRE(region.modifiers[Mod::width][14].smooth == 0);
REQUIRE(view.at(14).smooth == 0);
region.parseOpcode({ "width_stepcc120", "24" });
REQUIRE(region.modifiers[Mod::width][120].step == 24.0_a);
REQUIRE(view.at(120).step == 24.0_a);
region.parseOpcode({ "width_stepcc120", "15482" });
REQUIRE(region.modifiers[Mod::width][120].step == 200.0_a);
REQUIRE(view.at(120).step == 200.0_a);
region.parseOpcode({ "width_stepcc120", "-20" });
REQUIRE(region.modifiers[Mod::width][120].step == 0.0f);
REQUIRE(view.at(120).step == 0.0f);
}
SECTION("position")
@ -619,28 +625,29 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("position_oncc")
{
REQUIRE(region.modifiers[Mod::position].empty());
const ModKey target = ModKey::createNXYZ(ModId::Position, region.getId());
const RegionCCView view(region, target);
REQUIRE(view.empty());
region.parseOpcode({ "position_oncc45", "4.2" });
REQUIRE(region.modifiers[Mod::position].contains(45));
REQUIRE(region.modifiers[Mod::position][45].value == 4.2_a);
REQUIRE(view.at(45).value == 4.2_a);
region.parseOpcode({ "position_curvecc17", "18" });
REQUIRE(region.modifiers[Mod::position][17].curve == 18);
REQUIRE(view.at(17).curve == 18);
region.parseOpcode({ "position_curvecc17", "15482" });
REQUIRE(region.modifiers[Mod::position][17].curve == 255);
REQUIRE(view.at(17).curve == 255);
region.parseOpcode({ "position_curvecc17", "-2" });
REQUIRE(region.modifiers[Mod::position][17].curve == 0);
REQUIRE(view.at(17).curve == 0);
region.parseOpcode({ "position_smoothcc14", "85" });
REQUIRE(region.modifiers[Mod::position][14].smooth == 85);
REQUIRE(view.at(14).smooth == 85);
region.parseOpcode({ "position_smoothcc14", "15482" });
REQUIRE(region.modifiers[Mod::position][14].smooth == 100);
REQUIRE(view.at(14).smooth == 100);
region.parseOpcode({ "position_smoothcc14", "-2" });
REQUIRE(region.modifiers[Mod::position][14].smooth == 0);
REQUIRE(view.at(14).smooth == 0);
region.parseOpcode({ "position_stepcc120", "24" });
REQUIRE(region.modifiers[Mod::position][120].step == 24.0_a);
REQUIRE(view.at(120).step == 24.0_a);
region.parseOpcode({ "position_stepcc120", "15482" });
REQUIRE(region.modifiers[Mod::position][120].step == 200.0_a);
REQUIRE(view.at(120).step == 200.0_a);
region.parseOpcode({ "position_stepcc120", "-2" });
REQUIRE(region.modifiers[Mod::position][120].step == 0.0f);
REQUIRE(view.at(120).step == 0.0f);
}
SECTION("amp_keycenter")
@ -1641,95 +1648,93 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("amplitude_cc")
{
REQUIRE(region.modifiers[Mod::amplitude].empty());
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, region.getId());
const RegionCCView view(region, target);
REQUIRE(view.empty());
region.parseOpcode({ "amplitude_cc1", "40" });
REQUIRE(region.modifiers[Mod::amplitude].contains(1));
REQUIRE(region.modifiers[Mod::amplitude][1].value == 40.0_a);
REQUIRE(view.at(1).value == 40.0_a);
region.parseOpcode({ "amplitude_oncc2", "30" });
REQUIRE(region.modifiers[Mod::amplitude].contains(2));
REQUIRE(region.modifiers[Mod::amplitude][2].value == 30.0_a);
REQUIRE(view.at(2).value == 30.0_a);
region.parseOpcode({ "amplitude_curvecc17", "18" });
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 18);
REQUIRE(view.at(17).curve == 18);
region.parseOpcode({ "amplitude_curvecc17", "15482" });
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 255);
REQUIRE(view.at(17).curve == 255);
region.parseOpcode({ "amplitude_curvecc17", "-2" });
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 0);
REQUIRE(view.at(17).curve == 0);
region.parseOpcode({ "amplitude_smoothcc14", "85" });
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 85);
REQUIRE(view.at(14).smooth == 85);
region.parseOpcode({ "amplitude_smoothcc14", "15482" });
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 100);
REQUIRE(view.at(14).smooth == 100);
region.parseOpcode({ "amplitude_smoothcc14", "-2" });
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 0);
REQUIRE(view.at(14).smooth == 0);
region.parseOpcode({ "amplitude_stepcc120", "24" });
REQUIRE(region.modifiers[Mod::amplitude][120].step == 24.0_a);
REQUIRE(view.at(120).step == 24.0_a);
region.parseOpcode({ "amplitude_stepcc120", "15482" });
REQUIRE(region.modifiers[Mod::amplitude][120].step == 100.0_a);
REQUIRE(view.at(120).step == 100.0_a);
region.parseOpcode({ "amplitude_stepcc120", "-2" });
REQUIRE(region.modifiers[Mod::amplitude][120].step == 0.0f);
REQUIRE(view.at(120).step == 0.0f);
}
SECTION("volume_oncc/gain_cc")
{
REQUIRE(region.modifiers[Mod::volume].empty());
const ModKey target = ModKey::createNXYZ(ModId::Volume, region.getId());
const RegionCCView view(region, target);
REQUIRE(view.empty());
region.parseOpcode({ "gain_cc1", "40" });
REQUIRE(region.modifiers[Mod::volume].contains(1));
REQUIRE(region.modifiers[Mod::volume][1].value == 40_a);
REQUIRE(view.at(1).value == 40_a);
region.parseOpcode({ "volume_oncc2", "-76" });
REQUIRE(region.modifiers[Mod::volume].contains(2));
REQUIRE(region.modifiers[Mod::volume][2].value == -76.0_a);
REQUIRE(view.at(2).value == -76.0_a);
region.parseOpcode({ "gain_oncc4", "-1" });
REQUIRE(region.modifiers[Mod::volume].contains(4));
REQUIRE(region.modifiers[Mod::volume][4].value == -1.0_a);
REQUIRE(view.at(4).value == -1.0_a);
region.parseOpcode({ "volume_curvecc17", "18" });
REQUIRE(region.modifiers[Mod::volume][17].curve == 18);
REQUIRE(view.at(17).curve == 18);
region.parseOpcode({ "volume_curvecc17", "15482" });
REQUIRE(region.modifiers[Mod::volume][17].curve == 255);
REQUIRE(view.at(17).curve == 255);
region.parseOpcode({ "volume_curvecc17", "-2" });
REQUIRE(region.modifiers[Mod::volume][17].curve == 0);
REQUIRE(view.at(17).curve == 0);
region.parseOpcode({ "volume_smoothcc14", "85" });
REQUIRE(region.modifiers[Mod::volume][14].smooth == 85);
REQUIRE(view.at(14).smooth == 85);
region.parseOpcode({ "volume_smoothcc14", "15482" });
REQUIRE(region.modifiers[Mod::volume][14].smooth == 100);
REQUIRE(view.at(14).smooth == 100);
region.parseOpcode({ "volume_smoothcc14", "-2" });
REQUIRE(region.modifiers[Mod::volume][14].smooth == 0);
REQUIRE(view.at(14).smooth == 0);
region.parseOpcode({ "volume_stepcc120", "24" });
REQUIRE(region.modifiers[Mod::volume][120].step == 24.0f);
REQUIRE(view.at(120).step == 24.0f);
region.parseOpcode({ "volume_stepcc120", "15482" });
REQUIRE(region.modifiers[Mod::volume][120].step == 144.0f);
REQUIRE(view.at(120).step == 144.0f);
region.parseOpcode({ "volume_stepcc120", "-2" });
REQUIRE(region.modifiers[Mod::volume][120].step == 0.0f);
REQUIRE(view.at(120).step == 0.0f);
}
SECTION("tune_cc/pitch_cc")
{
REQUIRE(region.modifiers[Mod::pitch].empty());
const ModKey target = ModKey::createNXYZ(ModId::Pitch, region.getId());
const RegionCCView view(region, target);
REQUIRE(view.empty());
region.parseOpcode({ "pitch_cc1", "40" });
REQUIRE(region.modifiers[Mod::pitch].contains(1));
REQUIRE(region.modifiers[Mod::pitch][1].value == 40.0);
REQUIRE(view.at(1).value == 40.0);
region.parseOpcode({ "tune_oncc2", "-76" });
REQUIRE(region.modifiers[Mod::pitch].contains(2));
REQUIRE(region.modifiers[Mod::pitch][2].value == -76.0);
REQUIRE(view.at(2).value == -76.0);
region.parseOpcode({ "pitch_oncc4", "-1" });
REQUIRE(region.modifiers[Mod::pitch].contains(4));
REQUIRE(region.modifiers[Mod::pitch][4].value == -1.0);
REQUIRE(view.at(4).value == -1.0);
region.parseOpcode({ "tune_curvecc17", "18" });
REQUIRE(region.modifiers[Mod::pitch][17].curve == 18);
REQUIRE(view.at(17).curve == 18);
region.parseOpcode({ "pitch_curvecc17", "15482" });
REQUIRE(region.modifiers[Mod::pitch][17].curve == 255);
REQUIRE(view.at(17).curve == 255);
region.parseOpcode({ "tune_curvecc17", "-2" });
REQUIRE(region.modifiers[Mod::pitch][17].curve == 0);
REQUIRE(view.at(17).curve == 0);
region.parseOpcode({ "pitch_smoothcc14", "85" });
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 85);
REQUIRE(view.at(14).smooth == 85);
region.parseOpcode({ "tune_smoothcc14", "15482" });
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 100);
REQUIRE(view.at(14).smooth == 100);
region.parseOpcode({ "pitch_smoothcc14", "-2" });
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 0);
REQUIRE(view.at(14).smooth == 0);
region.parseOpcode({ "tune_stepcc120", "24" });
REQUIRE(region.modifiers[Mod::pitch][120].step == 24.0f);
REQUIRE(view.at(120).step == 24.0f);
region.parseOpcode({ "pitch_stepcc120", "15482" });
REQUIRE(region.modifiers[Mod::pitch][120].step == 9600.0f);
REQUIRE(view.at(120).step == 9600.0f);
region.parseOpcode({ "tune_stepcc120", "-2" });
REQUIRE(region.modifiers[Mod::pitch][120].step == 0.0f);
REQUIRE(view.at(120).step == 0.0f);
}
}

41
tests/RegionTHelpers.cpp Normal file
View file

@ -0,0 +1,41 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "RegionTHelpers.h"
#include "sfizz/modulations/ModId.h"
size_t RegionCCView::size() const
{
size_t count = 0;
for (const sfz::Region::Connection& conn : region_.connections)
count += match(conn);
return count;
}
bool RegionCCView::empty() const
{
for (const sfz::Region::Connection& conn : region_.connections)
if (match(conn))
return false;
return true;
}
sfz::ModKey::Parameters RegionCCView::at(int cc) const
{
for (const sfz::Region::Connection& conn : region_.connections) {
if (match(conn)) {
const sfz::ModKey::Parameters p = conn.first.parameters();
if (p.cc == cc)
return p;
}
}
throw std::out_of_range("Region CC");
}
bool RegionCCView::match(const sfz::Region::Connection& conn) const
{
return conn.first.id() == sfz::ModId::Controller && conn.second == target_;
}

28
tests/RegionTHelpers.h Normal file
View file

@ -0,0 +1,28 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include "sfizz/Region.h"
#include "sfizz/modulations/ModKey.h"
class RegionCCView {
public:
RegionCCView(const sfz::Region& region, sfz::ModKey target)
: region_(region), target_(target)
{
}
size_t size() const;
bool empty() const;
sfz::ModKey::Parameters at(int cc) const;
private:
bool match(const sfz::Region::Connection& conn) const;
private:
const sfz::Region& region_;
sfz::ModKey target_;
};