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/MathHelpers.h
|
||||||
sfizz/MidiState.h
|
sfizz/MidiState.h
|
||||||
sfizz/ModifierHelpers.h
|
sfizz/ModifierHelpers.h
|
||||||
sfizz/Modifiers.h
|
|
||||||
sfizz/NumericId.h
|
sfizz/NumericId.h
|
||||||
sfizz/OnePoleFilter.h
|
sfizz/OnePoleFilter.h
|
||||||
sfizz/Oversampler.h
|
sfizz/Oversampler.h
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@
|
||||||
|
|
||||||
#include "Range.h"
|
#include "Range.h"
|
||||||
#include "Defaults.h"
|
#include "Defaults.h"
|
||||||
#include "Modifiers.h"
|
#include "SfzHelpers.h"
|
||||||
#include "Resources.h"
|
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
|
|
@ -257,77 +256,4 @@ void pitchBendEnvelope(const EventVector& events, absl::Span<float> envelope, F&
|
||||||
multiplicativeEnvelope<F>(events, envelope, std::forward<F>(lambda));
|
multiplicativeEnvelope<F>(events, envelope, std::forward<F>(lambda));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
} // namespace sfz
|
||||||
* @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; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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/types/optional.h"
|
||||||
#include "absl/meta/type_traits.h"
|
#include "absl/meta/type_traits.h"
|
||||||
#include "absl/strings/ascii.h"
|
#include "absl/strings/ascii.h"
|
||||||
#include <string_view>
|
#include "absl/strings/string_view.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
|
|
||||||
|
|
@ -379,35 +379,35 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
setValueFromOpcode(opcode, volume, Default::volumeRange);
|
setValueFromOpcode(opcode, volume, Default::volumeRange);
|
||||||
break;
|
break;
|
||||||
case_any_ccN("volume"): // also gain
|
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;
|
break;
|
||||||
case hash("amplitude"):
|
case hash("amplitude"):
|
||||||
if (auto value = readOpcode(opcode.value, Default::amplitudeRange))
|
if (auto value = readOpcode(opcode.value, Default::amplitudeRange))
|
||||||
amplitude = normalizePercents(*value);
|
amplitude = normalizePercents(*value);
|
||||||
break;
|
break;
|
||||||
case_any_ccN("amplitude"):
|
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;
|
break;
|
||||||
case hash("pan"):
|
case hash("pan"):
|
||||||
if (auto value = readOpcode(opcode.value, Default::panRange))
|
if (auto value = readOpcode(opcode.value, Default::panRange))
|
||||||
pan = normalizePercents(*value);
|
pan = normalizePercents(*value);
|
||||||
break;
|
break;
|
||||||
case_any_ccN("pan"):
|
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;
|
break;
|
||||||
case hash("position"):
|
case hash("position"):
|
||||||
if (auto value = readOpcode(opcode.value, Default::positionRange))
|
if (auto value = readOpcode(opcode.value, Default::positionRange))
|
||||||
position = normalizePercents(*value);
|
position = normalizePercents(*value);
|
||||||
break;
|
break;
|
||||||
case_any_ccN("position"):
|
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;
|
break;
|
||||||
case hash("width"):
|
case hash("width"):
|
||||||
if (auto value = readOpcode(opcode.value, Default::widthRange))
|
if (auto value = readOpcode(opcode.value, Default::widthRange))
|
||||||
width = normalizePercents(*value);
|
width = normalizePercents(*value);
|
||||||
break;
|
break;
|
||||||
case_any_ccN("width"):
|
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;
|
break;
|
||||||
case hash("amp_keycenter"):
|
case hash("amp_keycenter"):
|
||||||
setValueFromOpcode(opcode, ampKeycenter, Default::keyRange);
|
setValueFromOpcode(opcode, ampKeycenter, Default::keyRange);
|
||||||
|
|
@ -771,7 +771,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
setValueFromOpcode(opcode, tune, Default::tuneRange);
|
setValueFromOpcode(opcode, tune, Default::tuneRange);
|
||||||
break;
|
break;
|
||||||
case_any_ccN("pitch"): // also tune
|
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;
|
break;
|
||||||
case hash("bend_up"): // also bendup
|
case hash("bend_up"): // also bendup
|
||||||
setValueFromOpcode(opcode, bendUp, Default::bendBoundRange);
|
setValueFromOpcode(opcode, bendUp, Default::bendBoundRange);
|
||||||
|
|
@ -925,7 +925,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
return true;
|
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())
|
if (!opcode.isAnyCcN())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -934,31 +934,6 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, Range<float> range, CCM
|
||||||
if (ccNumber >= config::numCCs)
|
if (ccNumber >= config::numCCs)
|
||||||
return false;
|
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) {
|
if (target) {
|
||||||
// search an existing connection of same CC number and target
|
// search an existing connection of same CC number and target
|
||||||
// if it exists, modify, otherwise create
|
// if it exists, modify, otherwise create
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
#include "MidiState.h"
|
#include "MidiState.h"
|
||||||
#include "FileId.h"
|
#include "FileId.h"
|
||||||
#include "NumericId.h"
|
#include "NumericId.h"
|
||||||
#include "Modifiers.h"
|
|
||||||
#include "modulations/ModKey.h"
|
#include "modulations/ModKey.h"
|
||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
|
#include "absl/strings/string_view.h"
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -241,12 +241,11 @@ struct Region {
|
||||||
*
|
*
|
||||||
* @param opcode
|
* @param opcode
|
||||||
* @param range
|
* @param range
|
||||||
* @param ccMap
|
|
||||||
* @param target
|
* @param target
|
||||||
* @return true if the opcode was properly read and stored.
|
* @return true if the opcode was properly read and stored.
|
||||||
* @return false
|
* @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;
|
void offsetAllKeys(int offset) noexcept;
|
||||||
|
|
||||||
|
|
@ -370,9 +369,6 @@ struct Region {
|
||||||
// Effects
|
// Effects
|
||||||
std::vector<float> gainToEffect;
|
std::vector<float> gainToEffect;
|
||||||
|
|
||||||
// Modifiers
|
|
||||||
ModifierArray<CCMap<Modifier>> modifiers;
|
|
||||||
|
|
||||||
bool triggerOnCC { false }; // whether the region triggers on CC events or note events
|
bool triggerOnCC { false }; // whether the region triggers on CC events or note events
|
||||||
bool triggerOnNote { true };
|
bool triggerOnNote { true };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -452,7 +452,6 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
|
|
||||||
size_t maxFilters { 0 };
|
size_t maxFilters { 0 };
|
||||||
size_t maxEQs { 0 };
|
size_t maxEQs { 0 };
|
||||||
ModifierArray<size_t> maxModifiers { 0 };
|
|
||||||
|
|
||||||
while (currentRegionIndex < currentRegionCount) {
|
while (currentRegionIndex < currentRegionCount) {
|
||||||
auto region = regions[currentRegionIndex].get();
|
auto region = regions[currentRegionIndex].get();
|
||||||
|
|
@ -563,8 +562,6 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
region->registerTempo(2.0f);
|
region->registerTempo(2.0f);
|
||||||
maxFilters = max(maxFilters, region->filters.size());
|
maxFilters = max(maxFilters, region->filters.size());
|
||||||
maxEQs = max(maxEQs, region->equalizers.size());
|
maxEQs = max(maxEQs, region->equalizers.size());
|
||||||
for (const auto& mod : allModifiers)
|
|
||||||
maxModifiers[mod] = max(maxModifiers[mod], region->modifiers[mod].size());
|
|
||||||
|
|
||||||
++currentRegionIndex;
|
++currentRegionIndex;
|
||||||
}
|
}
|
||||||
|
|
@ -574,7 +571,6 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
|
|
||||||
settingsPerVoice.maxFilters = maxFilters;
|
settingsPerVoice.maxFilters = maxFilters;
|
||||||
settingsPerVoice.maxEQs = maxEQs;
|
settingsPerVoice.maxEQs = maxEQs;
|
||||||
settingsPerVoice.maxModifiers = maxModifiers;
|
|
||||||
|
|
||||||
applySettingsPerVoice();
|
applySettingsPerVoice();
|
||||||
|
|
||||||
|
|
@ -1346,7 +1342,6 @@ void sfz::Synth::applySettingsPerVoice()
|
||||||
for (auto& voice : voices) {
|
for (auto& voice : voices) {
|
||||||
voice->setMaxFiltersPerVoice(settingsPerVoice.maxFilters);
|
voice->setMaxFiltersPerVoice(settingsPerVoice.maxFilters);
|
||||||
voice->setMaxEQsPerVoice(settingsPerVoice.maxEQs);
|
voice->setMaxEQsPerVoice(settingsPerVoice.maxEQs);
|
||||||
voice->prepareSmoothers(settingsPerVoice.maxModifiers);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@
|
||||||
#include "parser/Parser.h"
|
#include "parser/Parser.h"
|
||||||
#include "VoiceStealing.h"
|
#include "VoiceStealing.h"
|
||||||
#include "utility/SpinMutex.h"
|
#include "utility/SpinMutex.h"
|
||||||
#include "absl/types/span.h"
|
#include <absl/types/span.h>
|
||||||
#include <absl/types/optional.h>
|
#include <absl/types/optional.h>
|
||||||
|
#include <absl/strings/string_view.h>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
|
|
@ -772,7 +772,6 @@ private:
|
||||||
struct SettingsPerVoice {
|
struct SettingsPerVoice {
|
||||||
size_t maxFilters { 0 };
|
size_t maxFilters { 0 };
|
||||||
size_t maxEQs { 0 };
|
size_t maxEQs { 0 };
|
||||||
ModifierArray<size_t> maxModifiers { 0 };
|
|
||||||
};
|
};
|
||||||
SettingsPerVoice settingsPerVoice;
|
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())));
|
bendSmoother.reset(centsFactor(region->getBendInCents(resources.midiState.getPitchBend())));
|
||||||
egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, value, sampleRate);
|
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);
|
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
|
void sfz::Voice::pitchEnvelope(absl::Span<float> pitchSpan) noexcept
|
||||||
{
|
{
|
||||||
const auto numFrames = pitchSpan.size();
|
const auto numFrames = pitchSpan.size();
|
||||||
|
|
@ -918,21 +885,6 @@ void sfz::Voice::pitchEnvelope(absl::Span<float> pitchSpan) noexcept
|
||||||
|
|
||||||
void sfz::Voice::resetSmoothers() 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);
|
bendSmoother.reset(1.0f);
|
||||||
gainSmoother.reset(0.0f);
|
gainSmoother.reset(0.0f);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -302,8 +302,6 @@ public:
|
||||||
Duration getLastFilterDuration() const noexcept { return filterDuration; }
|
Duration getLastFilterDuration() const noexcept { return filterDuration; }
|
||||||
Duration getLastPanningDuration() const noexcept { return panningDuration; }
|
Duration getLastPanningDuration() const noexcept { return panningDuration; }
|
||||||
|
|
||||||
void prepareSmoothers(const ModifierArray<size_t>& numModifiers);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Fill a span with data from a file source. This is the first step
|
* @brief Fill a span with data from a file source. This is the first step
|
||||||
|
|
@ -390,27 +388,6 @@ private:
|
||||||
*/
|
*/
|
||||||
void removeVoiceFromRing() noexcept;
|
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.
|
* @brief Initialize frequency and gain coefficients for the oscillators.
|
||||||
*/
|
*/
|
||||||
|
|
@ -479,7 +456,6 @@ private:
|
||||||
fast_real_distribution<float> uniformNoiseDist { -config::uniformNoiseBounds, config::uniformNoiseBounds };
|
fast_real_distribution<float> uniformNoiseDist { -config::uniformNoiseBounds, config::uniformNoiseBounds };
|
||||||
fast_gaussian_generator<float> gaussianNoiseDist { 0.0f, config::noiseVariance };
|
fast_gaussian_generator<float> gaussianNoiseDist { 0.0f, config::noiseVariance };
|
||||||
|
|
||||||
ModifierArray<std::vector<Smoother>> modifierSmoothers;
|
|
||||||
Smoother gainSmoother;
|
Smoother gainSmoother;
|
||||||
Smoother bendSmoother;
|
Smoother bendSmoother;
|
||||||
Smoother xfadeSmoother;
|
Smoother xfadeSmoother;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ project(sfizz)
|
||||||
|
|
||||||
set(SFIZZ_TEST_SOURCES
|
set(SFIZZ_TEST_SOURCES
|
||||||
RegionT.cpp
|
RegionT.cpp
|
||||||
|
RegionTHelpers.h
|
||||||
|
RegionTHelpers.cpp
|
||||||
ParsingT.cpp
|
ParsingT.cpp
|
||||||
HelpersT.cpp
|
HelpersT.cpp
|
||||||
HelpersT.cpp
|
HelpersT.cpp
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,7 @@ TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps")
|
||||||
REQUIRE(approxEqual<float>(output, expected));
|
REQUIRE(approxEqual<float>(output, expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
TEST_CASE("[linearModifiers] Compare with envelopes")
|
TEST_CASE("[linearModifiers] Compare with envelopes")
|
||||||
{
|
{
|
||||||
sfz::Resources resources;
|
sfz::Resources resources;
|
||||||
|
|
@ -360,4 +361,4 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes")
|
||||||
});
|
});
|
||||||
REQUIRE(approxEqual<float>(output, envelope));
|
REQUIRE(approxEqual<float>(output, envelope));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@
|
||||||
// license. You should have receive a LICENSE.md file along with the code.
|
// 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
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#include "RegionTHelpers.h"
|
||||||
#include "sfizz/Synth.h"
|
#include "sfizz/Synth.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
|
#include "sfizz/modulations/ModId.h"
|
||||||
|
#include "sfizz/modulations/ModKey.h"
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
#include "ghc/fs_std.hpp"
|
#include "ghc/fs_std.hpp"
|
||||||
#if defined(__APPLE__)
|
#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.getStart() == 57 );
|
||||||
REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 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));
|
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, synth.getRegionView(2)->getId());
|
||||||
REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].getWithDefault(10).value == 34.0f);
|
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")
|
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.
|
// 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
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#include "RegionTHelpers.h"
|
||||||
#include "sfizz/MidiState.h"
|
#include "sfizz/MidiState.h"
|
||||||
#include "sfizz/Region.h"
|
#include "sfizz/Region.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
|
#include "sfizz/modulations/ModId.h"
|
||||||
|
#include "sfizz/modulations/ModKey.h"
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
using namespace sfz::literals;
|
using namespace sfz::literals;
|
||||||
using namespace sfz;
|
using namespace sfz;
|
||||||
|
|
@ -541,28 +545,29 @@ TEST_CASE("[Region] Parsing opcodes")
|
||||||
|
|
||||||
SECTION("pan_oncc")
|
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" });
|
region.parseOpcode({ "pan_oncc45", "4.2" });
|
||||||
REQUIRE(region.modifiers[Mod::pan].contains(45));
|
REQUIRE(view.at(45).value == 4.2_a);
|
||||||
REQUIRE(region.modifiers[Mod::pan][45].value == 4.2_a);
|
|
||||||
region.parseOpcode({ "pan_curvecc17", "18" });
|
region.parseOpcode({ "pan_curvecc17", "18" });
|
||||||
REQUIRE(region.modifiers[Mod::pan][17].curve == 18);
|
REQUIRE(view.at(17).curve == 18);
|
||||||
region.parseOpcode({ "pan_curvecc17", "15482" });
|
region.parseOpcode({ "pan_curvecc17", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::pan][17].curve == 255);
|
REQUIRE(view.at(17).curve == 255);
|
||||||
region.parseOpcode({ "pan_curvecc17", "-2" });
|
region.parseOpcode({ "pan_curvecc17", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::pan][17].curve == 0);
|
REQUIRE(view.at(17).curve == 0);
|
||||||
region.parseOpcode({ "pan_smoothcc14", "85" });
|
region.parseOpcode({ "pan_smoothcc14", "85" });
|
||||||
REQUIRE(region.modifiers[Mod::pan][14].smooth == 85);
|
REQUIRE(view.at(14).smooth == 85);
|
||||||
region.parseOpcode({ "pan_smoothcc14", "15482" });
|
region.parseOpcode({ "pan_smoothcc14", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::pan][14].smooth == 100);
|
REQUIRE(view.at(14).smooth == 100);
|
||||||
region.parseOpcode({ "pan_smoothcc14", "-2" });
|
region.parseOpcode({ "pan_smoothcc14", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::pan][14].smooth == 0);
|
REQUIRE(view.at(14).smooth == 0);
|
||||||
region.parseOpcode({ "pan_stepcc120", "24" });
|
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" });
|
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" });
|
region.parseOpcode({ "pan_stepcc120", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::pan][120].step == 0.0f);
|
REQUIRE(view.at(120).step == 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("width")
|
SECTION("width")
|
||||||
|
|
@ -580,28 +585,29 @@ TEST_CASE("[Region] Parsing opcodes")
|
||||||
|
|
||||||
SECTION("width_oncc")
|
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" });
|
region.parseOpcode({ "width_oncc45", "4.2" });
|
||||||
REQUIRE(region.modifiers[Mod::width].contains(45));
|
REQUIRE(view.at(45).value == 4.2_a);
|
||||||
REQUIRE(region.modifiers[Mod::width][45].value == 4.2_a);
|
|
||||||
region.parseOpcode({ "width_curvecc17", "18" });
|
region.parseOpcode({ "width_curvecc17", "18" });
|
||||||
REQUIRE(region.modifiers[Mod::width][17].curve == 18);
|
REQUIRE(view.at(17).curve == 18);
|
||||||
region.parseOpcode({ "width_curvecc17", "15482" });
|
region.parseOpcode({ "width_curvecc17", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::width][17].curve == 255);
|
REQUIRE(view.at(17).curve == 255);
|
||||||
region.parseOpcode({ "width_curvecc17", "-2" });
|
region.parseOpcode({ "width_curvecc17", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::width][17].curve == 0);
|
REQUIRE(view.at(17).curve == 0);
|
||||||
region.parseOpcode({ "width_smoothcc14", "85" });
|
region.parseOpcode({ "width_smoothcc14", "85" });
|
||||||
REQUIRE(region.modifiers[Mod::width][14].smooth == 85);
|
REQUIRE(view.at(14).smooth == 85);
|
||||||
region.parseOpcode({ "width_smoothcc14", "15482" });
|
region.parseOpcode({ "width_smoothcc14", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::width][14].smooth == 100);
|
REQUIRE(view.at(14).smooth == 100);
|
||||||
region.parseOpcode({ "width_smoothcc14", "-2" });
|
region.parseOpcode({ "width_smoothcc14", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::width][14].smooth == 0);
|
REQUIRE(view.at(14).smooth == 0);
|
||||||
region.parseOpcode({ "width_stepcc120", "24" });
|
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" });
|
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" });
|
region.parseOpcode({ "width_stepcc120", "-20" });
|
||||||
REQUIRE(region.modifiers[Mod::width][120].step == 0.0f);
|
REQUIRE(view.at(120).step == 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("position")
|
SECTION("position")
|
||||||
|
|
@ -619,28 +625,29 @@ TEST_CASE("[Region] Parsing opcodes")
|
||||||
|
|
||||||
SECTION("position_oncc")
|
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" });
|
region.parseOpcode({ "position_oncc45", "4.2" });
|
||||||
REQUIRE(region.modifiers[Mod::position].contains(45));
|
REQUIRE(view.at(45).value == 4.2_a);
|
||||||
REQUIRE(region.modifiers[Mod::position][45].value == 4.2_a);
|
|
||||||
region.parseOpcode({ "position_curvecc17", "18" });
|
region.parseOpcode({ "position_curvecc17", "18" });
|
||||||
REQUIRE(region.modifiers[Mod::position][17].curve == 18);
|
REQUIRE(view.at(17).curve == 18);
|
||||||
region.parseOpcode({ "position_curvecc17", "15482" });
|
region.parseOpcode({ "position_curvecc17", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::position][17].curve == 255);
|
REQUIRE(view.at(17).curve == 255);
|
||||||
region.parseOpcode({ "position_curvecc17", "-2" });
|
region.parseOpcode({ "position_curvecc17", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::position][17].curve == 0);
|
REQUIRE(view.at(17).curve == 0);
|
||||||
region.parseOpcode({ "position_smoothcc14", "85" });
|
region.parseOpcode({ "position_smoothcc14", "85" });
|
||||||
REQUIRE(region.modifiers[Mod::position][14].smooth == 85);
|
REQUIRE(view.at(14).smooth == 85);
|
||||||
region.parseOpcode({ "position_smoothcc14", "15482" });
|
region.parseOpcode({ "position_smoothcc14", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::position][14].smooth == 100);
|
REQUIRE(view.at(14).smooth == 100);
|
||||||
region.parseOpcode({ "position_smoothcc14", "-2" });
|
region.parseOpcode({ "position_smoothcc14", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::position][14].smooth == 0);
|
REQUIRE(view.at(14).smooth == 0);
|
||||||
region.parseOpcode({ "position_stepcc120", "24" });
|
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" });
|
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" });
|
region.parseOpcode({ "position_stepcc120", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::position][120].step == 0.0f);
|
REQUIRE(view.at(120).step == 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("amp_keycenter")
|
SECTION("amp_keycenter")
|
||||||
|
|
@ -1641,95 +1648,93 @@ TEST_CASE("[Region] Parsing opcodes")
|
||||||
|
|
||||||
SECTION("amplitude_cc")
|
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" });
|
region.parseOpcode({ "amplitude_cc1", "40" });
|
||||||
REQUIRE(region.modifiers[Mod::amplitude].contains(1));
|
REQUIRE(view.at(1).value == 40.0_a);
|
||||||
REQUIRE(region.modifiers[Mod::amplitude][1].value == 40.0_a);
|
|
||||||
region.parseOpcode({ "amplitude_oncc2", "30" });
|
region.parseOpcode({ "amplitude_oncc2", "30" });
|
||||||
REQUIRE(region.modifiers[Mod::amplitude].contains(2));
|
REQUIRE(view.at(2).value == 30.0_a);
|
||||||
REQUIRE(region.modifiers[Mod::amplitude][2].value == 30.0_a);
|
|
||||||
region.parseOpcode({ "amplitude_curvecc17", "18" });
|
region.parseOpcode({ "amplitude_curvecc17", "18" });
|
||||||
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 18);
|
REQUIRE(view.at(17).curve == 18);
|
||||||
region.parseOpcode({ "amplitude_curvecc17", "15482" });
|
region.parseOpcode({ "amplitude_curvecc17", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 255);
|
REQUIRE(view.at(17).curve == 255);
|
||||||
region.parseOpcode({ "amplitude_curvecc17", "-2" });
|
region.parseOpcode({ "amplitude_curvecc17", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 0);
|
REQUIRE(view.at(17).curve == 0);
|
||||||
region.parseOpcode({ "amplitude_smoothcc14", "85" });
|
region.parseOpcode({ "amplitude_smoothcc14", "85" });
|
||||||
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 85);
|
REQUIRE(view.at(14).smooth == 85);
|
||||||
region.parseOpcode({ "amplitude_smoothcc14", "15482" });
|
region.parseOpcode({ "amplitude_smoothcc14", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 100);
|
REQUIRE(view.at(14).smooth == 100);
|
||||||
region.parseOpcode({ "amplitude_smoothcc14", "-2" });
|
region.parseOpcode({ "amplitude_smoothcc14", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 0);
|
REQUIRE(view.at(14).smooth == 0);
|
||||||
region.parseOpcode({ "amplitude_stepcc120", "24" });
|
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" });
|
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" });
|
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")
|
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" });
|
region.parseOpcode({ "gain_cc1", "40" });
|
||||||
REQUIRE(region.modifiers[Mod::volume].contains(1));
|
REQUIRE(view.at(1).value == 40_a);
|
||||||
REQUIRE(region.modifiers[Mod::volume][1].value == 40_a);
|
|
||||||
region.parseOpcode({ "volume_oncc2", "-76" });
|
region.parseOpcode({ "volume_oncc2", "-76" });
|
||||||
REQUIRE(region.modifiers[Mod::volume].contains(2));
|
REQUIRE(view.at(2).value == -76.0_a);
|
||||||
REQUIRE(region.modifiers[Mod::volume][2].value == -76.0_a);
|
|
||||||
region.parseOpcode({ "gain_oncc4", "-1" });
|
region.parseOpcode({ "gain_oncc4", "-1" });
|
||||||
REQUIRE(region.modifiers[Mod::volume].contains(4));
|
REQUIRE(view.at(4).value == -1.0_a);
|
||||||
REQUIRE(region.modifiers[Mod::volume][4].value == -1.0_a);
|
|
||||||
region.parseOpcode({ "volume_curvecc17", "18" });
|
region.parseOpcode({ "volume_curvecc17", "18" });
|
||||||
REQUIRE(region.modifiers[Mod::volume][17].curve == 18);
|
REQUIRE(view.at(17).curve == 18);
|
||||||
region.parseOpcode({ "volume_curvecc17", "15482" });
|
region.parseOpcode({ "volume_curvecc17", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::volume][17].curve == 255);
|
REQUIRE(view.at(17).curve == 255);
|
||||||
region.parseOpcode({ "volume_curvecc17", "-2" });
|
region.parseOpcode({ "volume_curvecc17", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::volume][17].curve == 0);
|
REQUIRE(view.at(17).curve == 0);
|
||||||
region.parseOpcode({ "volume_smoothcc14", "85" });
|
region.parseOpcode({ "volume_smoothcc14", "85" });
|
||||||
REQUIRE(region.modifiers[Mod::volume][14].smooth == 85);
|
REQUIRE(view.at(14).smooth == 85);
|
||||||
region.parseOpcode({ "volume_smoothcc14", "15482" });
|
region.parseOpcode({ "volume_smoothcc14", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::volume][14].smooth == 100);
|
REQUIRE(view.at(14).smooth == 100);
|
||||||
region.parseOpcode({ "volume_smoothcc14", "-2" });
|
region.parseOpcode({ "volume_smoothcc14", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::volume][14].smooth == 0);
|
REQUIRE(view.at(14).smooth == 0);
|
||||||
region.parseOpcode({ "volume_stepcc120", "24" });
|
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" });
|
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" });
|
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")
|
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" });
|
region.parseOpcode({ "pitch_cc1", "40" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch].contains(1));
|
REQUIRE(view.at(1).value == 40.0);
|
||||||
REQUIRE(region.modifiers[Mod::pitch][1].value == 40.0);
|
|
||||||
region.parseOpcode({ "tune_oncc2", "-76" });
|
region.parseOpcode({ "tune_oncc2", "-76" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch].contains(2));
|
REQUIRE(view.at(2).value == -76.0);
|
||||||
REQUIRE(region.modifiers[Mod::pitch][2].value == -76.0);
|
|
||||||
region.parseOpcode({ "pitch_oncc4", "-1" });
|
region.parseOpcode({ "pitch_oncc4", "-1" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch].contains(4));
|
REQUIRE(view.at(4).value == -1.0);
|
||||||
REQUIRE(region.modifiers[Mod::pitch][4].value == -1.0);
|
|
||||||
region.parseOpcode({ "tune_curvecc17", "18" });
|
region.parseOpcode({ "tune_curvecc17", "18" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch][17].curve == 18);
|
REQUIRE(view.at(17).curve == 18);
|
||||||
region.parseOpcode({ "pitch_curvecc17", "15482" });
|
region.parseOpcode({ "pitch_curvecc17", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch][17].curve == 255);
|
REQUIRE(view.at(17).curve == 255);
|
||||||
region.parseOpcode({ "tune_curvecc17", "-2" });
|
region.parseOpcode({ "tune_curvecc17", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch][17].curve == 0);
|
REQUIRE(view.at(17).curve == 0);
|
||||||
region.parseOpcode({ "pitch_smoothcc14", "85" });
|
region.parseOpcode({ "pitch_smoothcc14", "85" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 85);
|
REQUIRE(view.at(14).smooth == 85);
|
||||||
region.parseOpcode({ "tune_smoothcc14", "15482" });
|
region.parseOpcode({ "tune_smoothcc14", "15482" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 100);
|
REQUIRE(view.at(14).smooth == 100);
|
||||||
region.parseOpcode({ "pitch_smoothcc14", "-2" });
|
region.parseOpcode({ "pitch_smoothcc14", "-2" });
|
||||||
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 0);
|
REQUIRE(view.at(14).smooth == 0);
|
||||||
region.parseOpcode({ "tune_stepcc120", "24" });
|
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" });
|
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" });
|
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