Remove unused code, update tests
This commit is contained in:
parent
0ebe24939d
commit
82ebb639a7
16 changed files with 182 additions and 374 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}};
|
||||
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ project(sfizz)
|
|||
|
||||
set(SFIZZ_TEST_SOURCES
|
||||
RegionT.cpp
|
||||
RegionTHelpers.h
|
||||
RegionTHelpers.cpp
|
||||
ParsingT.cpp
|
||||
HelpersT.cpp
|
||||
HelpersT.cpp
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
41
tests/RegionTHelpers.cpp
Normal 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
28
tests/RegionTHelpers.h
Normal 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_;
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue