Clean up the va OPF and wrap it as a linear smoother

Move modifiers and their helpers in their own files
Add an enum class for modifiers and facilities to iterate over all possible targets
Add smoothers to the voices and preallocate them
Iterate over smoothers and modifiers jointly in the voices for each target
This commit is contained in:
Paul Ferrand 2020-03-31 20:13:13 +02:00 committed by Jean Pierre Cimalando
parent cb3f53496f
commit 0ed1f4cbeb
23 changed files with 798 additions and 445 deletions

View file

@ -46,9 +46,8 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, Linear)(benchmark::State& state) {
} }
} }
BENCHMARK_DEFINE_F(EnvelopeFixture, LinearNoEvent)(benchmark::State& state) { BENCHMARK_DEFINE_F(EnvelopeFixture, LinearNoEvent) (benchmark::State& state) {
for (auto _ : state) for (auto _ : state) {
{
sfz::EventVector events { sfz::EventVector events {
{ 0, dist(gen) } { 0, dist(gen) }
}; };
@ -79,9 +78,8 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, Multiplicative)(benchmark::State& state) {
} }
} }
BENCHMARK_DEFINE_F(EnvelopeFixture, MultiplicativeNoEvent)(benchmark::State& state) { BENCHMARK_DEFINE_F(EnvelopeFixture, MultiplicativeNoEvent) (benchmark::State& state) {
for (auto _ : state) for (auto _ : state) {
{
sfz::EventVector events { sfz::EventVector events {
{ 0, dist(gen) } { 0, dist(gen) }
}; };

View file

@ -7,6 +7,7 @@
#include "SIMDHelpers.h" #include "SIMDHelpers.h"
#include "OnePoleFilter.h" #include "OnePoleFilter.h"
#include "SfzFilter.h" #include "SfzFilter.h"
#include "SfzHelpers.h"
#include "ScopedFTZ.h" #include "ScopedFTZ.h"
#include "SfzHelpers.h" #include "SfzHelpers.h"
#include <benchmark/benchmark.h> #include <benchmark/benchmark.h>

View file

@ -0,0 +1,57 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
#include "ModifierHelpers.h"
#include "Smoothers.h"
#include "absl/types/span.h"
class SmootherFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state)
{
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
sfz::cumsum<float, false>(input, absl::MakeSpan(input));
}
void TearDown(const ::benchmark::State& /* state */)
{
}
std::random_device rd {};
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.5, 4 };
std::vector<float> input;
std::vector<float> output;
};
BENCHMARK_DEFINE_F(SmootherFixture, Linear) (benchmark::State& state)
{
sfz::Smoother smoother;
smoother.setSmoothing(10, sfz::config::defaultSampleRate);
for (auto _ : state) {
smoother.process(input, absl::MakeSpan(output));
}
}
// BENCHMARK_DEFINE_F(SmootherFixture, Multiplicative)(benchmark::State& state) {
// sfz::MultiplicativeSmoother smoother;
// smoother.setSmoothing(10, sfz::config::defaultSampleRate);
// for (auto _ : state)
// {
// smoother.process(input, absl::MakeSpan(output));
// }
// }
BENCHMARK_REGISTER_F(SmootherFixture, Linear)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
// BENCHMARK_REGISTER_F(SmootherFixture, Multiplicative)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -0,0 +1,59 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SIMDHelpers.h"
#include "OnePoleFilter.h"
#include "SfzFilter.h"
#include "ScopedFTZ.h"
#include <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
constexpr int blockSize { 256 };
constexpr float sampleRate { 48000.0f };
class FilterFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State&)
{
input = std::vector<float>(blockSize);
output = std::vector<float>(blockSize);
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */)
{
}
std::random_device rd {};
std::mt19937 gen { rd() };
std::normal_distribution<float> dist { 1, 0.2 };
std::vector<float> input;
std::vector<float> output;
};
BENCHMARK_DEFINE_F(FilterFixture, OnePole_VA) (benchmark::State& state)
{
ScopedFTZ ftz;
sfz::OnePoleFilter<float> filter;
for (auto _ : state) {
filter.processLowpass(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(FilterFixture, OnePoleMul_VA) (benchmark::State& state)
{
ScopedFTZ ftz;
sfz::OnePoleFilterMul<float> filter;
for (auto _ : state) {
filter.processLowpass(input, absl::MakeSpan(output));
}
}
BENCHMARK_REGISTER_F(FilterFixture, OnePole_VA);
BENCHMARK_REGISTER_F(FilterFixture, OnePoleMul_VA);
BENCHMARK_MAIN();

View file

@ -62,6 +62,8 @@ sfizz_add_benchmark(bm_random BM_random.cpp)
sfizz_add_benchmark(bm_logger BM_logger.cpp) sfizz_add_benchmark(bm_logger BM_logger.cpp)
target_link_libraries(bm_logger PRIVATE sfizz::sfizz) target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
sfizz_add_benchmark(bm_smoothers BM_smoothers.cpp)
target_link_libraries(bm_smoothers PRIVATE sfizz::sfizz)
if (TARGET sfizz-samplerate) if (TARGET sfizz-samplerate)
sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES}) sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES})

View file

@ -20,6 +20,7 @@ set (SFIZZ_SOURCES
sfizz/Logger.cpp sfizz/Logger.cpp
sfizz/SfzFilter.cpp sfizz/SfzFilter.cpp
sfizz/Curve.cpp sfizz/Curve.cpp
sfizz/Smoothers.cpp
sfizz/Wavetables.cpp sfizz/Wavetables.cpp
sfizz/Tuning.cpp sfizz/Tuning.cpp
sfizz/RTSemaphore.cpp sfizz/RTSemaphore.cpp

View file

@ -26,6 +26,7 @@ enum class Oversampling: int {
namespace config { namespace config {
constexpr float defaultSampleRate { 48000 }; constexpr float defaultSampleRate { 48000 };
constexpr float maxSampleRate { 192000 };
constexpr int defaultSamplesPerBlock { 1024 }; constexpr int defaultSamplesPerBlock { 1024 };
constexpr int maxBlockSize { 8192 }; constexpr int maxBlockSize { 8192 };
constexpr int bufferPoolSize { 6 }; constexpr int bufferPoolSize { 6 };
@ -39,6 +40,9 @@ namespace config {
constexpr int numBackgroundThreads { 4 }; constexpr int numBackgroundThreads { 4 };
constexpr int numVoices { 64 }; constexpr int numVoices { 64 };
constexpr unsigned maxVoices { 256 }; constexpr unsigned maxVoices { 256 };
constexpr unsigned smoothingSteps { 512 };
constexpr uint8_t gainSmoothing { 10 };
constexpr unsigned powerTableSizeExponent { 11 };
constexpr int maxFilePromises { maxVoices }; constexpr int maxFilePromises { maxVoices };
constexpr int sustainCC { 64 }; constexpr int sustainCC { 64 };
constexpr int allSoundOffCC { 120 }; constexpr int allSoundOffCC { 120 };

View file

@ -82,8 +82,9 @@ namespace Default
// Region logic: MIDI conditions // Region logic: MIDI conditions
constexpr Range<uint8_t> channelRange { 1, 16 }; constexpr Range<uint8_t> channelRange { 1, 16 };
constexpr Range<uint8_t> midiChannelRange { 0, 15 }; constexpr Range<uint8_t> midiChannelRange { 0, 15 };
constexpr Range<float> stepCCRange { 0, 127 }; constexpr Range<uint8_t> stepCCRange { 0, 127 };
constexpr Range<uint8_t> smoothCCRange { 0, 127 }; constexpr Range<uint8_t> smoothCCRange { 0, 127 };
constexpr float smoothTauPerStep { 3e-3 };
constexpr Range<uint8_t> curveCCRange { 0, 255 }; constexpr Range<uint8_t> curveCCRange { 0, 255 };
constexpr Range<uint16_t> ccNumberRange { 0, config::numCCs }; constexpr Range<uint16_t> ccNumberRange { 0, config::numCCs };
constexpr auto ccValueRange = normalizedRange; constexpr auto ccValueRange = normalizedRange;
@ -202,6 +203,7 @@ namespace Default
constexpr int bendUp { 200 }; // No range here because the bounds can be inverted constexpr int bendUp { 200 }; // No range here because the bounds can be inverted
constexpr int bendDown { -200 }; constexpr int bendDown { -200 };
constexpr int bendStep { 1 }; constexpr int bendStep { 1 };
constexpr uint8_t bendSmooth { 0 };
// Envelope generators // Envelope generators
constexpr float attack { 0 }; constexpr float attack { 0 };

View file

@ -254,6 +254,11 @@ constexpr Type sqrtTwo() { return static_cast<Type>(1.41421356237309504880168872
template <class Type> template <class Type>
constexpr Type sqrtTwoInv() { return static_cast<Type>(0.707106781186547524400844362104849039284835937688474036588); }; constexpr Type sqrtTwoInv() { return static_cast<Type>(0.707106781186547524400844362104849039284835937688474036588); };
constexpr unsigned int mask(int x)
{
return (1U << x) - 1;
}
/** /**
* @brief lround for positive values * @brief lround for positive values
* This optimizes a bit better by ignoring the negative code path * This optimizes a bit better by ignoring the negative code path
@ -493,7 +498,6 @@ constexpr bool checkSpanSizes(const absl::Span<T>& span1, Others... others)
#define CHECK_SPAN_SIZES(...) SFIZZ_CHECK(checkSpanSizes(__VA_ARGS__)) #define CHECK_SPAN_SIZES(...) SFIZZ_CHECK(checkSpanSizes(__VA_ARGS__))
class ScopedRoundingMode { class ScopedRoundingMode {
public: public:
ScopedRoundingMode() = delete; ScopedRoundingMode() = delete;

View file

@ -2,12 +2,11 @@
#include "Range.h" #include "Range.h"
#include "Defaults.h" #include "Defaults.h"
#include "SfzHelpers.h" #include "Modifiers.h"
#include "Resources.h" #include "Resources.h"
#include "absl/types/span.h" #include "absl/types/span.h"
namespace sfz { namespace sfz {
/** /**
* @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...) * @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...)
*/ */

54
src/sfizz/Modifiers.h Normal file
View file

@ -0,0 +1,54 @@
#pragma once
#include <numeric>
#include <array>
#include <vector>
namespace sfz {
struct Modifier {
float value { 0.0f };
float step { 0.0f };
uint8_t curve { 0 };
uint8_t smooth { 0 };
static_assert(config::maxCurves - 1 <= std::numeric_limits<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
};
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)); }
};
template <class T>
class ModifierArray : public std::array<T, (size_t)Mod::sentinel> {
public:
T& operator[](sfz::Mod idx) { return this->std::array<T, (size_t)Mod::sentinel>::operator[](static_cast<size_t>(idx)); }
const T& operator[](sfz::Mod idx) const { return this->std::array<T, (size_t)Mod::sentinel>::operator[](static_cast<size_t>(idx)); }
};
/**
* @brief Helper for iterating over all possible modifiers.
* Should fail at compile time if you update the modifiers but not this.
*
*/
static const ModifierArray<Mod> allModifiers = {{
Mod::amplitude,
Mod::pan,
Mod::width,
Mod::position,
Mod::pitch,
Mod::volume
}};
}

View file

@ -351,35 +351,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, &volumeCC); processGenericCc(opcode, Default::volumeCCRange, &modifiers[Mod::volume]);
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, &amplitudeCC); processGenericCc(opcode, Default::amplitudeRange, &modifiers[Mod::amplitude]);
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, &panCC); processGenericCc(opcode, Default::panCCRange, &modifiers[Mod::pan]);
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, &positionCC); processGenericCc(opcode, Default::positionCCRange, &modifiers[Mod::position]);
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, &widthCC); processGenericCc(opcode, Default::widthCCRange, &modifiers[Mod::width]);
break; break;
case hash("amp_keycenter"): case hash("amp_keycenter"):
setValueFromOpcode(opcode, ampKeycenter, Default::keyRange); setValueFromOpcode(opcode, ampKeycenter, Default::keyRange);
@ -742,7 +742,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, &tuneCC); processGenericCc(opcode, Default::tuneCCRange, &modifiers[Mod::pitch]);
break; break;
case hash("bend_up"): // also bendup case hash("bend_up"): // also bendup
setValueFromOpcode(opcode, bendUp, Default::bendBoundRange); setValueFromOpcode(opcode, bendUp, Default::bendBoundRange);
@ -753,6 +753,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
case hash("bend_step"): case hash("bend_step"):
setValueFromOpcode(opcode, bendStep, Default::bendStepRange); setValueFromOpcode(opcode, bendStep, Default::bendStepRange);
break; break;
case hash("bend_smooth"):
setValueFromOpcode(opcode, bendSmooth, Default::smoothCCRange);
break;
// Amplitude Envelope // Amplitude Envelope
case hash("ampeg_attack"): case hash("ampeg_attack"):
@ -1219,3 +1222,9 @@ float sfz::Region::getGainToEffectBus(unsigned number) const noexcept
return gainToEffect[number]; return gainToEffect[number];
} }
float sfz::Region::getBendInCents(float bend) const noexcept
{
const auto bendInCents = bend > 0.0f ? bend * static_cast<float>(bendUp) : -bend * static_cast<float>(bendDown);
return centsFactor(bendInCents);
}

View file

@ -17,6 +17,7 @@
#include "MidiState.h" #include "MidiState.h"
#include "FileId.h" #include "FileId.h"
#include "NumericId.h" #include "NumericId.h"
#include "Modifiers.h"
#include "absl/types/optional.h" #include "absl/types/optional.h"
#include <bitset> #include <bitset>
#include <string> #include <string>
@ -195,6 +196,14 @@ struct Region {
* @return float * @return float
*/ */
float velocityCurve(float velocity) const noexcept; float velocityCurve(float velocity) const noexcept;
/**
* @brief Get the cents factor for a given bend value between -1 and 1
*
* @param bend
* @return float
*/
float getBendInCents(float bend) const noexcept;
/** /**
* @brief Get the region offset in samples * @brief Get the region offset in samples
* *
@ -309,11 +318,6 @@ struct Region {
float pan { normalizePercents(Default::pan) }; // pan float pan { normalizePercents(Default::pan) }; // pan
float width { normalizePercents(Default::width) }; // width float width { normalizePercents(Default::width) }; // width
float position { normalizePercents(Default::position) }; // position float position { normalizePercents(Default::position) }; // position
CCMap<Modifier> volumeCC { {} }; // volume_oncc
CCMap<Modifier> amplitudeCC { {} }; // amplitude_oncc
CCMap<Modifier> panCC { {} }; // pan_oncc
CCMap<Modifier> widthCC { {} }; // width_oncc
CCMap<Modifier> positionCC { {} }; // position_oncc
uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter
float ampKeytrack { Default::ampKeytrack }; // amp_keytrack float ampKeytrack { Default::ampKeytrack }; // amp_keytrack
float ampVeltrack { Default::ampVeltrack }; // amp_keytrack float ampVeltrack { Default::ampVeltrack }; // amp_keytrack
@ -342,10 +346,10 @@ struct Region {
int pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack int pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack
int transpose { Default::transpose }; // transpose int transpose { Default::transpose }; // transpose
int tune { Default::tune }; // tune int tune { Default::tune }; // tune
CCMap<Modifier> tuneCC { {} };
int bendUp { Default::bendUp }; int bendUp { Default::bendUp };
int bendDown { Default::bendDown }; int bendDown { Default::bendDown };
int bendStep { Default::bendStep }; int bendStep { Default::bendStep };
uint8_t bendSmooth { Default::bendSmooth };
// Envelopes // Envelopes
EGDescription amplitudeEG; EGDescription amplitudeEG;
@ -357,6 +361,9 @@ struct Region {
// Effects // Effects
std::vector<float> gainToEffect; std::vector<float> gainToEffect;
// Modifiers
ModifierArray<CCMap<Modifier>> modifiers;
private: private:
const MidiState& midiState; const MidiState& midiState;
bool keySwitched { true }; bool keySwitched { true };

View file

@ -17,29 +17,20 @@
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "Defaults.h" #include "Defaults.h"
namespace sfz namespace sfz {
{
using CCNamePair = std::pair<uint16_t, std::string>; using CCNamePair = std::pair<uint16_t, std::string>;
using NoteNamePair = std::pair<uint8_t, std::string>; using NoteNamePair = std::pair<uint8_t, std::string>;
template <class T> template <class T>
using MidiNoteArray = std::array<T, 128>; using MidiNoteArray = std::array<T, 128>;
template<class ValueType> template <class ValueType>
struct CCData { struct CCData {
int cc; int cc;
ValueType data; ValueType data;
static_assert(config::numCCs - 1 < std::numeric_limits<decltype(cc)>::max(), "The cc type in the CCData struct cannot support the required number of CCs"); static_assert(config::numCCs - 1 < std::numeric_limits<decltype(cc)>::max(), "The cc type in the CCData struct cannot support the required number of CCs");
}; };
struct Modifier {
float value { 0.0f };
float step { 0.0f };
uint8_t curve { 0 };
uint8_t smooth { 0 };
static_assert(config::maxCurves - 1 <= std::numeric_limits<decltype(curve)>::max(), "The curve type in the Modifier struct cannot support the required number of curves");
};
template <class ValueType> template <class ValueType>
struct CCDataComparator { struct CCDataComparator {
bool operator()(const CCData<ValueType>& ccData, const int& cc) bool operator()(const CCData<ValueType>& ccData, const int& cc)
@ -106,13 +97,13 @@ struct MidiEventValueComparator {
* @param centsPerOctave * @param centsPerOctave
* @return constexpr float * @return constexpr float
*/ */
template<class T> template <class T>
constexpr float centsFactor(T cents, T centsPerOctave = 1200) constexpr float centsFactor(T cents, T centsPerOctave = 1200)
{ {
return std::pow(2.0f, static_cast<float>(cents) / centsPerOctave); return std::pow(2.0f, static_cast<float>(cents) / centsPerOctave);
} }
template<class T, absl::enable_if_t<std::is_integral<T>::value, int> = 0> template <class T, absl::enable_if_t<std::is_integral<T>::value, int> = 0>
constexpr T denormalize7Bits(float value) constexpr T denormalize7Bits(float value)
{ {
return static_cast<T>(value * 127.0f); return static_cast<T>(value * 127.0f);
@ -128,10 +119,10 @@ constexpr uint8_t denormalizeVelocity(float value)
return denormalize7Bits<uint8_t>(value); return denormalize7Bits<uint8_t>(value);
} }
template<class T> template <class T>
constexpr float normalize7Bits(T value) constexpr float normalize7Bits(T value)
{ {
return static_cast<float>(min(max(value, T{ 0 }), T{ 127 })) / 127.0f; return static_cast<float>(min(max(value, T { 0 }), T { 127 })) / 127.0f;
} }
/** /**
@ -141,7 +132,7 @@ constexpr float normalize7Bits(T value)
* @param ccValue * @param ccValue
* @return constexpr float * @return constexpr float
*/ */
template<class T> template <class T>
constexpr float normalizeCC(T ccValue) constexpr float normalizeCC(T ccValue)
{ {
return normalize7Bits(ccValue); return normalize7Bits(ccValue);
@ -154,13 +145,12 @@ constexpr float normalizeCC(T ccValue)
* @param ccValue * @param ccValue
* @return constexpr float * @return constexpr float
*/ */
template<class T> template <class T>
constexpr float normalizeVelocity(T velocity) constexpr float normalizeVelocity(T velocity)
{ {
return normalize7Bits(velocity); return normalize7Bits(velocity);
} }
/** /**
* @brief Normalize a percentage between 0 and 1 * @brief Normalize a percentage between 0 and 1
* *
@ -168,7 +158,7 @@ constexpr float normalizeVelocity(T velocity)
* @param percentValue * @param percentValue
* @return constexpr float * @return constexpr float
*/ */
template<class T> template <class T>
constexpr float normalizePercents(T percentValue) constexpr float normalizePercents(T percentValue)
{ {
return percentValue * 0.01f; return percentValue * 0.01f;
@ -212,6 +202,12 @@ inline CXX14_CONSTEXPR Type vaGain(Type cutoff, Type sampleRate)
return std::tan(cutoff / sampleRate * pi<Type>()); return std::tan(cutoff / sampleRate * pi<Type>());
} }
template <class Type>
inline CXX14_CONSTEXPR Type vaGain(Type cutoff, Type sampleRate)
{
return std::tan(cutoff / sampleRate * pi<Type>());
}
/** /**
* @brief From a source view, find the next sfz header and its members and * @brief From a source view, find the next sfz header and its members and
* return them, while updating the source by removing this header * return them, while updating the source by removing this header
@ -275,4 +271,3 @@ bool findDefine(absl::string_view line, absl::string_view& variable, absl::strin
bool findInclude(absl::string_view line, std::string& path); bool findInclude(absl::string_view line, std::string& path);
} // namespace sfz } // namespace sfz

30
src/sfizz/Smoothers.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "Smoothers.h"
namespace sfz {
Smoother::Smoother()
{
}
void Smoother::setSmoothing(uint8_t smoothValue, float sampleRate)
{
smoothing = (smoothValue > 0);
if (smoothing) {
filter.setGain(std::tan(1.0f / (2 * Default::smoothTauPerStep * smoothValue * sampleRate)));
}
}
void Smoother::reset(float value)
{
filter.reset(value);
}
void Smoother::process(absl::Span<const float> input, absl::Span<float> output)
{
if (smoothing)
filter.processLowpass(input, output);
else
copy<float>(input, output);
}
}

24
src/sfizz/Smoothers.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include "Config.h"
#include "Defaults.h"
#include "MathHelpers.h"
#include "SfzHelpers.h"
#include "OnePoleFilter.h"
#include "SIMDHelpers.h"
#include <array>
namespace sfz {
class Smoother {
public:
Smoother();
void setSmoothing(uint8_t smoothValue, float sampleRate);
void reset(float value = 0.0f);
void process(absl::Span<const float> input, absl::Span<float> output);
private:
bool smoothing { false };
OnePoleFilter<float> filter {};
};
}

View file

@ -9,6 +9,7 @@
#include "Debug.h" #include "Debug.h"
#include "Macros.h" #include "Macros.h"
#include "MidiState.h" #include "MidiState.h"
#include "ModifierHelpers.h"
#include "ScopedFTZ.h" #include "ScopedFTZ.h"
#include "StringViewHelpers.h" #include "StringViewHelpers.h"
#include "pugixml.hpp" #include "pugixml.hpp"
@ -374,6 +375,7 @@ 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();
@ -480,6 +482,8 @@ 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;
} }
@ -490,6 +494,7 @@ void sfz::Synth::finalizeSfzLoad()
for (auto& voice : voices) { for (auto& voice : voices) {
voice->setMaxFiltersPerVoice(maxFilters); voice->setMaxFiltersPerVoice(maxFilters);
voice->setMaxEQsPerVoice(maxEQs); voice->setMaxEQsPerVoice(maxEQs);
voice->prepareSmoothers(maxModifiers);
} }
} }

View file

@ -24,6 +24,8 @@ sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources)
for (WavetableOscillator& osc : waveOscillators) for (WavetableOscillator& osc : waveOscillators)
osc.init(sampleRate); osc.init(sampleRate);
gainSmoother.setSmoothing(config::gainSmoothing, sampleRate);
for (auto & filter : channelEnvelopeFilters) for (auto & filter : channelEnvelopeFilters)
filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate));
} }
@ -97,6 +99,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value,
baseGain = region->getBaseGain(); baseGain = region->getBaseGain();
if (triggerType != TriggerType::CC) if (triggerType != TriggerType::CC)
baseGain *= region->getNoteGain(number, value); baseGain *= region->getNoteGain(number, value);
gainSmoother.reset();
// Check that we can handle the number of filters; filters should be cleared here // Check that we can handle the number of filters; filters should be cleared here
ASSERT((filters.capacity() - filters.size()) >= region->filters.size()); ASSERT((filters.capacity() - filters.size()) >= region->filters.size());
@ -120,7 +123,26 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value,
initialDelay = delay + static_cast<int>(region->getDelay() * sampleRate); initialDelay = delay + static_cast<int>(region->getDelay() * sampleRate);
baseFrequency = resources.tuning.getFrequencyOfKey(number); baseFrequency = resources.tuning.getFrequencyOfKey(number);
bendStepFactor = centsFactor(region->bendStep); bendStepFactor = centsFactor(region->bendStep);
bendSmoother.setSmoothing(region->bendSmooth, sampleRate);
bendSmoother.reset(region->getBendInCents(resources.midiState.getPitchBend()));
egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, value, sampleRate); 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) {
switch (modId) {
case Mod::volume:
smoother.reset(db2mag(resources.midiState.getCCValue(mod.cc) * mod.data.value));
break;
case Mod::pitch:
smoother.reset(centsFactor(resources.midiState.getCCValue(mod.cc) * mod.data.value));
break;
default:
smoother.reset(resources.midiState.getCCValue(mod.cc) * mod.data.value);
}
smoother.setSmoothing(mod.data.smooth, sampleRate);
});
}
} }
int sfz::Voice::getCurrentSampleQuality() const noexcept int sfz::Voice::getCurrentSampleQuality() const noexcept
@ -206,6 +228,7 @@ void sfz::Voice::registerTempo(int delay, float secondsPerQuarter) noexcept
void sfz::Voice::setSampleRate(float sampleRate) noexcept void sfz::Voice::setSampleRate(float sampleRate) noexcept
{ {
this->sampleRate = sampleRate; this->sampleRate = sampleRate;
gainSmoother.setSmoothing(config::gainSmoothing, sampleRate);
for (auto & filter : channelEnvelopeFilters) for (auto & filter : channelEnvelopeFilters)
filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate));
@ -283,10 +306,11 @@ void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
// Amplitude envelope // Amplitude envelope
applyGain1<float>(baseGain, modulationSpan); applyGain1<float>(baseGain, modulationSpan);
for (const auto& mod : region->amplitudeCC) { forEachWithSmoother(Mod::amplitude, [&](const CCData<Modifier>& mod, Smoother& smoother) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>); linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
smoother.process(*tempSpan, *tempSpan);
applyGain<float>(*tempSpan, modulationSpan); applyGain<float>(*tempSpan, modulationSpan);
} });
// Crossfade envelopes // Crossfade envelopes
for (const auto& mod : region->crossfadeCCInRange) { for (const auto& mod : region->crossfadeCCInRange) {
@ -306,12 +330,16 @@ void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
// Volume envelope // Volume envelope
applyGain1<float>(db2mag(baseVolumedB), modulationSpan); applyGain1<float>(db2mag(baseVolumedB), modulationSpan);
for (const auto& mod : region->volumeCC) { forEachWithSmoother(Mod::volume, [&](const CCData<Modifier>& mod, Smoother& smoother) {
multiplicativeModifier(resources, *tempSpan, mod, [](float x) { multiplicativeModifier(resources, *tempSpan, mod, [](float x) {
return db2mag(x); return db2mag(x);
}); });
smoother.process(*tempSpan, *tempSpan);
applyGain<float>(*tempSpan, modulationSpan); applyGain<float>(*tempSpan, modulationSpan);
} });
// Smooth the gain transitions
gainSmoother.process(modulationSpan, modulationSpan);
} }
void sfz::Voice::ampStageMono(AudioSpan<float> buffer) noexcept void sfz::Voice::ampStageMono(AudioSpan<float> buffer) noexcept
@ -360,10 +388,11 @@ void sfz::Voice::panStageMono(AudioSpan<float> buffer) noexcept
// Apply panning // Apply panning
fill(*modulationSpan, region->pan); fill(*modulationSpan, region->pan);
for (const auto& mod : region->panCC) { forEachWithSmoother(Mod::pan, [&](const CCData<Modifier>& mod, Smoother& smoother) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>); linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
smoother.process(*tempSpan, *tempSpan);
add<float>(*tempSpan, *modulationSpan); add<float>(*tempSpan, *modulationSpan);
} });
pan(*modulationSpan, leftBuffer, rightBuffer); pan(*modulationSpan, leftBuffer, rightBuffer);
} }
@ -381,25 +410,28 @@ void sfz::Voice::panStageStereo(AudioSpan<float> buffer) noexcept
// Apply panning // Apply panning
fill(*modulationSpan, region->pan); fill(*modulationSpan, region->pan);
for (const auto& mod : region->panCC) { forEachWithSmoother(Mod::pan, [&](const CCData<Modifier>& mod, Smoother& smoother) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>); linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
smoother.process(*tempSpan, *tempSpan);
add<float>(*tempSpan, *modulationSpan); add<float>(*tempSpan, *modulationSpan);
} });
pan(*modulationSpan, leftBuffer, rightBuffer); pan(*modulationSpan, leftBuffer, rightBuffer);
// Apply the width/position process // Apply the width/position process
fill(*modulationSpan, region->width); fill(*modulationSpan, region->width);
for (const auto& mod : region->widthCC) { forEachWithSmoother(Mod::width, [&](const CCData<Modifier>& mod, Smoother& smoother) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>); linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
smoother.process(*tempSpan, *tempSpan);
add<float>(*tempSpan, *modulationSpan); add<float>(*tempSpan, *modulationSpan);
} });
width(*modulationSpan, leftBuffer, rightBuffer); width(*modulationSpan, leftBuffer, rightBuffer);
fill(*modulationSpan, region->position); fill(*modulationSpan, region->position);
for (const auto& mod : region->positionCC) { forEachWithSmoother(Mod::position, [&](const CCData<Modifier>& mod, Smoother& smoother) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>); linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
smoother.process(*tempSpan, *tempSpan);
add<float>(*tempSpan, *modulationSpan); add<float>(*tempSpan, *modulationSpan);
} });
pan(*modulationSpan, leftBuffer, rightBuffer); pan(*modulationSpan, leftBuffer, rightBuffer);
} }
@ -452,30 +484,13 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
auto source = currentPromise->getData(); auto source = currentPromise->getData();
auto jumps = resources.bufferPool.getBuffer(numSamples); auto jumps = resources.bufferPool.getBuffer(numSamples);
auto bends = resources.bufferPool.getBuffer(numSamples);
auto coeffs = resources.bufferPool.getBuffer(numSamples); auto coeffs = resources.bufferPool.getBuffer(numSamples);
auto indices = resources.bufferPool.getIndexBuffer(numSamples); auto indices = resources.bufferPool.getIndexBuffer(numSamples);
if (!jumps || !bends || !indices || !coeffs) if (!jumps || !indices || !coeffs)
return; return;
fill(*jumps, pitchRatio * speedRatio); fill(*jumps, pitchRatio * speedRatio);
pitchEnvelope(*jumps);
const auto events = resources.midiState.getPitchEvents();
const auto bendLambda = [this](float bend) {
const auto bendInCents = bend > 0.0f ? bend * static_cast<float>(region->bendUp) : -bend * static_cast<float>(region->bendDown);
return centsFactor(bendInCents);
};
if (region->bendStep > 1)
pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor);
else
pitchBendEnvelope(events, *bends, bendLambda);
applyGain<float>(*bends, *jumps);
for (const auto& mod : region->tuneCC) {
multiplicativeModifier(resources, *bends, mod, [](float x) { return centsFactor(x); });
applyGain<float>(*bends, *jumps);
}
jumps->front() += floatPositionOffset; jumps->front() += floatPositionOffset;
cumsum<float>(*jumps, *jumps); cumsum<float>(*jumps, *jumps);
@ -584,30 +599,12 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
const auto numFrames = buffer.getNumFrames(); const auto numFrames = buffer.getNumFrames();
auto frequencies = resources.bufferPool.getBuffer(numFrames); auto frequencies = resources.bufferPool.getBuffer(numFrames);
auto bends = resources.bufferPool.getBuffer(numFrames); if (!frequencies)
if (!frequencies || !bends)
return; return;
float keycenterFrequency = midiNoteFrequency(region->pitchKeycenter); float keycenterFrequency = midiNoteFrequency(region->pitchKeycenter);
fill(*frequencies, pitchRatio * keycenterFrequency); fill(*frequencies, pitchRatio * keycenterFrequency);
pitchEnvelope(*frequencies);
const auto events = resources.midiState.getPitchEvents();
const auto bendLambda = [this](float bend) {
const auto bendInCents = bend > 0.0f ? bend * static_cast<float>(region->bendUp) : -bend * static_cast<float>(region->bendDown);
return centsFactor(bendInCents);
};
if (region->bendStep > 1)
pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor);
else
pitchBendEnvelope(events, *bends, bendLambda);
applyGain<float>(*bends, *frequencies);
for (const auto& mod : region->tuneCC) {
multiplicativeModifier(resources, *bends, mod, [](float x) {
return centsFactor(x);
});
applyGain<float>(*bends, *frequencies);
}
if (waveUnisonSize == 1) { if (waveUnisonSize == 1) {
WavetableOscillator& osc = waveOscillators[0]; WavetableOscillator& osc = waveOscillators[0];
@ -784,7 +781,6 @@ void sfz::Voice::setupOscillatorUnison()
#endif #endif
} }
void sfz::Voice::updateChannelPowers(AudioSpan<float> buffer) void sfz::Voice::updateChannelPowers(AudioSpan<float> buffer)
{ {
assert(smoothedChannelEnvelopes.size() == channelEnvelopeFilters.size()); assert(smoothedChannelEnvelopes.size() == channelEnvelopeFilters.size());
@ -809,3 +805,59 @@ void sfz::Voice::switchState(State s)
stateListener->onVoiceStateChanged(id, s); stateListener->onVoiceStateChanged(id, 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();
auto bends = resources.bufferPool.getBuffer(numFrames);
if (!bends)
return;
const auto events = resources.midiState.getPitchEvents();
const auto bendLambda = [this](float bend) {
return region->getBendInCents(bend);
};
if (region->bendStep > 1)
pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor);
else
pitchBendEnvelope(events, *bends, bendLambda);
bendSmoother.process(*bends, *bends);
applyGain<float>(*bends, pitchSpan);
forEachWithSmoother(Mod::pitch, [&](const CCData<Modifier>& mod, Smoother& smoother) {
multiplicativeModifier(resources, *bends, mod, [](float x) {
return centsFactor(x);
});
smoother.process(*bends, *bends);
applyGain<float>(*bends, pitchSpan);
});
}
void sfz::Voice::resetSmoothers() noexcept
{
for (auto& mod : allModifiers) {
const auto resetValue = [mod] {
switch (mod) {
case Mod::volume:
return 1.0f;
case Mod::pitch:
return 1.0f;
default:
return 0.0f;
}
}();
for (auto& smoother : modifierSmoothers[mod]) {
smoother.reset(resetValue);
}
}
bendSmoother.reset(1.0f);
gainSmoother.reset(0.0f);
}

View file

@ -11,6 +11,7 @@
#include "Region.h" #include "Region.h"
#include "AudioBuffer.h" #include "AudioBuffer.h"
#include "Resources.h" #include "Resources.h"
#include "Smoothers.h"
#include "AudioSpan.h" #include "AudioSpan.h"
#include "LeakDetector.h" #include "LeakDetector.h"
#include "OnePoleFilter.h" #include "OnePoleFilter.h"
@ -301,6 +302,8 @@ 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
@ -337,12 +340,25 @@ private:
void panStageStereo(AudioSpan<float> buffer) noexcept; void panStageStereo(AudioSpan<float> buffer) noexcept;
void filterStageMono(AudioSpan<float> buffer) noexcept; void filterStageMono(AudioSpan<float> buffer) noexcept;
void filterStageStereo(AudioSpan<float> buffer) noexcept; void filterStageStereo(AudioSpan<float> buffer) noexcept;
void pitchEnvelope(absl::Span<float> pitchSpan) noexcept;
/** /**
* @brief Remove the voice from the sister ring * @brief Remove the voice from the sister ring
* *
*/ */
void removeVoiceFromRing() noexcept; void removeVoiceFromRing() noexcept;
template <class F>
void forEachWithSmoother(sfz::Mod modId, F&& lambda)
{
auto mod = region->modifiers[modId].begin();
auto smoother = modifierSmoothers[modId].begin();
while (mod < region->modifiers[modId].end()) {
lambda(*mod, *smoother);
incrementAll(mod, smoother);
}
}
/** /**
* @brief Initialize frequency and gain coefficients for the oscillators. * @brief Initialize frequency and gain coefficients for the oscillators.
*/ */
@ -410,8 +426,15 @@ private:
std::normal_distribution<float> noiseDist { 0, config::noiseVariance }; std::normal_distribution<float> noiseDist { 0, config::noiseVariance };
ModifierArray<std::vector<Smoother>> modifierSmoothers;
Smoother gainSmoother;
Smoother bendSmoother;
void resetSmoothers() noexcept;
std::array<OnePoleFilter<float>, 2> channelEnvelopeFilters; std::array<OnePoleFilter<float>, 2> channelEnvelopeFilters;
std::array<float, 2> smoothedChannelEnvelopes; std::array<float, 2> smoothedChannelEnvelopes;
HistoricalBuffer<float> powerHistory { config::powerHistoryLength };
LEAK_DETECTOR(Voice); LEAK_DETECTOR(Voice);
}; };

View file

@ -18,6 +18,7 @@ set(SFIZZ_TEST_SOURCES
MidiStateT.cpp MidiStateT.cpp
OnePoleFilterT.cpp OnePoleFilterT.cpp
InterpolatorsT.cpp InterpolatorsT.cpp
SmoothersT.cpp
RegionActivationT.cpp RegionActivationT.cpp
RegionValueComputationsT.cpp RegionValueComputationsT.cpp
# If we're tweaking the curves this kind of tests does not make sense # If we're tweaking the curves this kind of tests does not make sense

View file

@ -13,10 +13,11 @@
#endif #endif
using namespace Catch::literals; using namespace Catch::literals;
using namespace sfz::literals; using namespace sfz::literals;
using namespace sfz;
TEST_CASE("[Files] Single region (regions_one.sfz)") TEST_CASE("[Files] Single region (regions_one.sfz)")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_one.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_one.sfz");
REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -25,7 +26,7 @@ TEST_CASE("[Files] Single region (regions_one.sfz)")
TEST_CASE("[Files] Multiple regions (regions_many.sfz)") TEST_CASE("[Files] Multiple regions (regions_many.sfz)")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_many.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_many.sfz");
REQUIRE(synth.getNumRegions() == 3); REQUIRE(synth.getNumRegions() == 3);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -35,15 +36,15 @@ TEST_CASE("[Files] Multiple regions (regions_many.sfz)")
TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)") TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz");
REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->keyRange == sfz::Range<uint8_t>(2, 14)); REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(2, 14));
} }
TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)") TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz");
REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->loopMode == SfzLoopMode::loop_sustain); REQUIRE(synth.getRegionView(0)->loopMode == SfzLoopMode::loop_sustain);
@ -51,7 +52,7 @@ TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
TEST_CASE("[Files] (regions_bad.sfz)") TEST_CASE("[Files] (regions_bad.sfz)")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_bad.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_bad.sfz");
REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getNumRegions() == 2);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -60,7 +61,7 @@ TEST_CASE("[Files] (regions_bad.sfz)")
TEST_CASE("[Files] Local include") TEST_CASE("[Files] Local include")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_local.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_local.sfz");
REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -68,7 +69,7 @@ TEST_CASE("[Files] Local include")
TEST_CASE("[Files] Multiple includes") TEST_CASE("[Files] Multiple includes")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz");
REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getNumRegions() == 2);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -77,7 +78,7 @@ TEST_CASE("[Files] Multiple includes")
TEST_CASE("[Files] Multiple includes with comments") TEST_CASE("[Files] Multiple includes with comments")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz");
REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getNumRegions() == 2);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -86,7 +87,7 @@ TEST_CASE("[Files] Multiple includes with comments")
TEST_CASE("[Files] Subdir include") TEST_CASE("[Files] Subdir include")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir.sfz");
REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav");
@ -94,7 +95,7 @@ TEST_CASE("[Files] Subdir include")
TEST_CASE("[Files] Subdir include Win") TEST_CASE("[Files] Subdir include Win")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz");
REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav");
@ -102,8 +103,8 @@ TEST_CASE("[Files] Subdir include Win")
TEST_CASE("[Files] Recursive include (with include guard)") TEST_CASE("[Files] Recursive include (with include guard)")
{ {
sfz::Synth synth; Synth synth;
sfz::Parser& parser = synth.getParser(); Parser& parser = synth.getParser();
parser.setRecursiveIncludeGuardEnabled(true); parser.setRecursiveIncludeGuardEnabled(true);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_recursive.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_recursive.sfz");
REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getNumRegions() == 2);
@ -113,8 +114,8 @@ TEST_CASE("[Files] Recursive include (with include guard)")
TEST_CASE("[Files] Include loops (with include guard)") TEST_CASE("[Files] Include loops (with include guard)")
{ {
sfz::Synth synth; Synth synth;
sfz::Parser& parser = synth.getParser(); Parser& parser = synth.getParser();
parser.setRecursiveIncludeGuardEnabled(true); parser.setRecursiveIncludeGuardEnabled(true);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_loop.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_loop.sfz");
REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getNumRegions() == 2);
@ -124,34 +125,34 @@ TEST_CASE("[Files] Include loops (with include guard)")
TEST_CASE("[Files] Define test") TEST_CASE("[Files] Define test")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/defines.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/defines.sfz");
REQUIRE(synth.getNumRegions() == 4); REQUIRE(synth.getNumRegions() == 4);
REQUIRE(synth.getRegionView(0)->keyRange == sfz::Range<uint8_t>(36, 36)); REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(36, 36));
REQUIRE(synth.getRegionView(1)->keyRange == sfz::Range<uint8_t>(38, 38)); REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(38, 38));
REQUIRE(synth.getRegionView(2)->keyRange == sfz::Range<uint8_t>(42, 42)); REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(42, 42));
REQUIRE(synth.getRegionView(3)->volume == -12.0f); REQUIRE(synth.getRegionView(3)->volume == -12.0f);
} }
TEST_CASE("[Files] Group from AVL") TEST_CASE("[Files] Group from AVL")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
REQUIRE(synth.getNumRegions() == 5); REQUIRE(synth.getNumRegions() == 5);
for (int i = 0; i < synth.getNumRegions(); ++i) { for (int i = 0; i < synth.getNumRegions(); ++i) {
REQUIRE(synth.getRegionView(i)->volume == 6.0f); REQUIRE(synth.getRegionView(i)->volume == 6.0f);
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(36, 36)); REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(36, 36));
} }
REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range<float>(1_norm, 26_norm)); REQUIRE(synth.getRegionView(0)->velocityRange == Range<float>(1_norm, 26_norm));
REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range<float>(27_norm, 52_norm)); REQUIRE(synth.getRegionView(1)->velocityRange == Range<float>(27_norm, 52_norm));
REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range<float>(53_norm, 77_norm)); REQUIRE(synth.getRegionView(2)->velocityRange == Range<float>(53_norm, 77_norm));
REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range<float>(78_norm, 102_norm)); REQUIRE(synth.getRegionView(3)->velocityRange == Range<float>(78_norm, 102_norm));
REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range<float>(103_norm, 127_norm)); REQUIRE(synth.getRegionView(4)->velocityRange == Range<float>(103_norm, 127_norm));
} }
TEST_CASE("[Files] Full hierarchy") TEST_CASE("[Files] Full hierarchy")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE(synth.getNumRegions() == 8); REQUIRE(synth.getNumRegions() == 8);
for (int i = 0; i < synth.getNumRegions(); ++i) { for (int i = 0; i < synth.getNumRegions(); ++i) {
@ -159,40 +160,40 @@ TEST_CASE("[Files] Full hierarchy")
} }
REQUIRE(synth.getRegionView(0)->pan == 0.3_a); REQUIRE(synth.getRegionView(0)->pan == 0.3_a);
REQUIRE(synth.getRegionView(0)->delay == 67); REQUIRE(synth.getRegionView(0)->delay == 67);
REQUIRE(synth.getRegionView(0)->keyRange == sfz::Range<uint8_t>(60, 60)); REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(60, 60));
REQUIRE(synth.getRegionView(1)->pan == 0.3_a); REQUIRE(synth.getRegionView(1)->pan == 0.3_a);
REQUIRE(synth.getRegionView(1)->delay == 67); REQUIRE(synth.getRegionView(1)->delay == 67);
REQUIRE(synth.getRegionView(1)->keyRange == sfz::Range<uint8_t>(61, 61)); REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(61, 61));
REQUIRE(synth.getRegionView(2)->pan == 0.3_a); REQUIRE(synth.getRegionView(2)->pan == 0.3_a);
REQUIRE(synth.getRegionView(2)->delay == 56); REQUIRE(synth.getRegionView(2)->delay == 56);
REQUIRE(synth.getRegionView(2)->keyRange == sfz::Range<uint8_t>(50, 50)); REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(50, 50));
REQUIRE(synth.getRegionView(3)->pan == 0.3_a); REQUIRE(synth.getRegionView(3)->pan == 0.3_a);
REQUIRE(synth.getRegionView(3)->delay == 56); REQUIRE(synth.getRegionView(3)->delay == 56);
REQUIRE(synth.getRegionView(3)->keyRange == sfz::Range<uint8_t>(51, 51)); REQUIRE(synth.getRegionView(3)->keyRange == Range<uint8_t>(51, 51));
REQUIRE(synth.getRegionView(4)->pan == -0.1_a); REQUIRE(synth.getRegionView(4)->pan == -0.1_a);
REQUIRE(synth.getRegionView(4)->delay == 47); REQUIRE(synth.getRegionView(4)->delay == 47);
REQUIRE(synth.getRegionView(4)->keyRange == sfz::Range<uint8_t>(40, 40)); REQUIRE(synth.getRegionView(4)->keyRange == Range<uint8_t>(40, 40));
REQUIRE(synth.getRegionView(5)->pan == -0.1_a); REQUIRE(synth.getRegionView(5)->pan == -0.1_a);
REQUIRE(synth.getRegionView(5)->delay == 47); REQUIRE(synth.getRegionView(5)->delay == 47);
REQUIRE(synth.getRegionView(5)->keyRange == sfz::Range<uint8_t>(41, 41)); REQUIRE(synth.getRegionView(5)->keyRange == Range<uint8_t>(41, 41));
REQUIRE(synth.getRegionView(6)->pan == -0.1_a); REQUIRE(synth.getRegionView(6)->pan == -0.1_a);
REQUIRE(synth.getRegionView(6)->delay == 36); REQUIRE(synth.getRegionView(6)->delay == 36);
REQUIRE(synth.getRegionView(6)->keyRange == sfz::Range<uint8_t>(30, 30)); REQUIRE(synth.getRegionView(6)->keyRange == Range<uint8_t>(30, 30));
REQUIRE(synth.getRegionView(7)->pan == -0.1_a); REQUIRE(synth.getRegionView(7)->pan == -0.1_a);
REQUIRE(synth.getRegionView(7)->delay == 36); REQUIRE(synth.getRegionView(7)->delay == 36);
REQUIRE(synth.getRegionView(7)->keyRange == sfz::Range<uint8_t>(31, 31)); REQUIRE(synth.getRegionView(7)->keyRange == Range<uint8_t>(31, 31));
} }
TEST_CASE("[Files] Reloading files") TEST_CASE("[Files] Reloading files")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE(synth.getNumRegions() == 8); REQUIRE(synth.getNumRegions() == 8);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
@ -202,7 +203,7 @@ TEST_CASE("[Files] Reloading files")
TEST_CASE("[Files] Full hierarchy with antislashes") TEST_CASE("[Files] Full hierarchy with antislashes")
{ {
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE(synth.getNumRegions() == 8); REQUIRE(synth.getNumRegions() == 8);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "Regions/dummy.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "Regions/dummy.wav");
@ -216,7 +217,7 @@ TEST_CASE("[Files] Full hierarchy with antislashes")
} }
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz");
REQUIRE(synth.getNumRegions() == 8); REQUIRE(synth.getNumRegions() == 8);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "Regions/dummy.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "Regions/dummy.wav");
@ -232,19 +233,19 @@ TEST_CASE("[Files] Full hierarchy with antislashes")
TEST_CASE("[Files] Pizz basic") TEST_CASE("[Files] Pizz basic")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz");
REQUIRE(synth.getNumRegions() == 4); REQUIRE(synth.getNumRegions() == 4);
for (int i = 0; i < synth.getNumRegions(); ++i) { for (int i = 0; i < synth.getNumRegions(); ++i) {
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(12, 22)); REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(12, 22));
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<float>(97_norm, 127_norm)); REQUIRE(synth.getRegionView(i)->velocityRange == Range<float>(97_norm, 127_norm));
REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21); REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21);
REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range<float>(0_norm, 13_norm)); REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == Range<float>(0_norm, 13_norm));
} }
REQUIRE(synth.getRegionView(0)->randRange == sfz::Range<float>(0, 0.25)); REQUIRE(synth.getRegionView(0)->randRange == Range<float>(0, 0.25));
REQUIRE(synth.getRegionView(1)->randRange == sfz::Range<float>(0.25, 0.5)); REQUIRE(synth.getRegionView(1)->randRange == Range<float>(0.25, 0.5));
REQUIRE(synth.getRegionView(2)->randRange == sfz::Range<float>(0.5, 0.75)); REQUIRE(synth.getRegionView(2)->randRange == Range<float>(0.5, 0.75));
REQUIRE(synth.getRegionView(3)->randRange == sfz::Range<float>(0.75, 1.0)); REQUIRE(synth.getRegionView(3)->randRange == Range<float>(0.75, 1.0));
REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr1.wav)"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr1.wav)");
REQUIRE(synth.getRegionView(1)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr2.wav)"); REQUIRE(synth.getRegionView(1)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr2.wav)");
REQUIRE(synth.getRegionView(2)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr3.wav)"); REQUIRE(synth.getRegionView(2)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr3.wav)");
@ -253,7 +254,7 @@ TEST_CASE("[Files] Pizz basic")
TEST_CASE("[Files] Channels (channels.sfz)") TEST_CASE("[Files] Channels (channels.sfz)")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz");
REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getNumRegions() == 2);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "mono_sample.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "mono_sample.wav");
@ -264,7 +265,7 @@ TEST_CASE("[Files] Channels (channels.sfz)")
TEST_CASE("[Files] Channels (channels_multi.sfz)") TEST_CASE("[Files] Channels (channels_multi.sfz)")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels_multi.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels_multi.sfz");
REQUIRE(synth.getNumRegions() == 6); REQUIRE(synth.getNumRegions() == 6);
@ -301,7 +302,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
TEST_CASE("[Files] sw_default") TEST_CASE("[Files] sw_default")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz");
REQUIRE( synth.getNumRegions() == 4 ); REQUIRE( synth.getNumRegions() == 4 );
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
@ -312,7 +313,7 @@ TEST_CASE("[Files] sw_default")
TEST_CASE("[Files] sw_default and playing with switches") TEST_CASE("[Files] sw_default and playing with switches")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz");
REQUIRE( synth.getNumRegions() == 4 ); REQUIRE( synth.getNumRegions() == 4 );
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
@ -341,7 +342,7 @@ TEST_CASE("[Files] sw_default and playing with switches")
TEST_CASE("[Files] wrong (overlapping) replacement for defines") TEST_CASE("[Files] wrong (overlapping) replacement for defines")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/wrong-replacements.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/wrong-replacements.sfz");
REQUIRE( synth.getNumRegions() == 3 ); REQUIRE( synth.getNumRegions() == 3 );
@ -355,14 +356,14 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines")
REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 ); REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 );
REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 ); REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 );
REQUIRE(!synth.getRegionView(2)->amplitudeCC.empty()); REQUIRE(!synth.getRegionView(2)->modifiers[Mod::amplitude].empty());
REQUIRE(synth.getRegionView(2)->amplitudeCC.contains(10)); REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].contains(10));
REQUIRE(synth.getRegionView(2)->amplitudeCC.getWithDefault(10).value == 34.0f); REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].getWithDefault(10).value == 34.0f);
} }
TEST_CASE("[Files] Specific bug: relative path with backslashes") TEST_CASE("[Files] Specific bug: relative path with backslashes")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/win_backslashes.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/win_backslashes.sfz");
REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(Xylo/Subfolder/closedhat.wav)"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(Xylo/Subfolder/closedhat.wav)");
@ -370,7 +371,7 @@ TEST_CASE("[Files] Specific bug: relative path with backslashes")
TEST_CASE("[Files] Default path") TEST_CASE("[Files] Default path")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz");
REQUIRE(synth.getNumRegions() == 4); REQUIRE(synth.getNumRegions() == 4);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(DefaultPath/SubPath1/sample1.wav)"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(DefaultPath/SubPath1/sample1.wav)");
@ -381,7 +382,7 @@ TEST_CASE("[Files] Default path")
TEST_CASE("[Files] Default path reset when calling loadSfzFile again") TEST_CASE("[Files] Default path reset when calling loadSfzFile again")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz");
REQUIRE(synth.getNumRegions() == 4); REQUIRE(synth.getNumRegions() == 4);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_reset.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_reset.sfz");
@ -391,7 +392,7 @@ TEST_CASE("[Files] Default path reset when calling loadSfzFile again")
TEST_CASE("[Files] Default path is ignored for generators") TEST_CASE("[Files] Default path is ignored for generators")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_generator.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_generator.sfz");
REQUIRE(synth.getNumRegions() == 1); REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(*sine)"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == R"(*sine)");
@ -399,7 +400,7 @@ TEST_CASE("[Files] Default path is ignored for generators")
TEST_CASE("[Files] Set CC applies properly") TEST_CASE("[Files] Set CC applies properly")
{ {
sfz::Synth synth; Synth synth;
const auto& midiState = synth.getResources().midiState; const auto& midiState = synth.getResources().midiState;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz");
REQUIRE(midiState.getCCValue(142) == 63_norm); REQUIRE(midiState.getCCValue(142) == 63_norm);
@ -426,26 +427,26 @@ TEST_CASE("[Files] Set RealCC applies properly")
TEST_CASE("[Files] Note and octave offsets") TEST_CASE("[Files] Note and octave offsets")
{ {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/note_offset.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/note_offset.sfz");
REQUIRE( synth.getNumRegions() == 7 ); REQUIRE( synth.getNumRegions() == 7 );
REQUIRE( synth.getRegionView(0)->keyRange == sfz::Range<uint8_t>(64, 64) ); REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(64, 64));
REQUIRE( synth.getRegionView(0)->pitchKeycenter == 64 ); REQUIRE( synth.getRegionView(0)->pitchKeycenter == 64 );
REQUIRE( synth.getRegionView(0)->keyswitchRange == sfz::Default::keyRange ); REQUIRE(synth.getRegionView(0)->keyswitchRange == Default::keyRange);
REQUIRE( synth.getRegionView(0)->crossfadeKeyInRange == sfz::Default::crossfadeKeyInRange ); REQUIRE(synth.getRegionView(0)->crossfadeKeyInRange == Default::crossfadeKeyInRange);
REQUIRE( synth.getRegionView(0)->crossfadeKeyOutRange == sfz::Default::crossfadeKeyOutRange ); REQUIRE(synth.getRegionView(0)->crossfadeKeyOutRange == Default::crossfadeKeyOutRange);
REQUIRE( synth.getRegionView(1)->keyRange == sfz::Range<uint8_t>(51, 56) ); REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(51, 56));
REQUIRE( synth.getRegionView(1)->pitchKeycenter == 51 ); REQUIRE( synth.getRegionView(1)->pitchKeycenter == 51 );
REQUIRE( synth.getRegionView(2)->keyRange == sfz::Range<uint8_t>(41, 45) ); REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(41, 45));
REQUIRE( synth.getRegionView(2)->pitchKeycenter == 41 ); REQUIRE( synth.getRegionView(2)->pitchKeycenter == 41 );
REQUIRE( synth.getRegionView(2)->crossfadeKeyInRange == sfz::Range<uint8_t>(37, 41) ); REQUIRE(synth.getRegionView(2)->crossfadeKeyInRange == Range<uint8_t>(37, 41));
REQUIRE( synth.getRegionView(2)->crossfadeKeyOutRange == sfz::Range<uint8_t>(45, 49) ); REQUIRE(synth.getRegionView(2)->crossfadeKeyOutRange == Range<uint8_t>(45, 49));
REQUIRE( synth.getRegionView(3)->keyRange == sfz::Range<uint8_t>(62, 62) ); REQUIRE(synth.getRegionView(3)->keyRange == Range<uint8_t>(62, 62));
REQUIRE( synth.getRegionView(3)->keyswitchRange == sfz::Range<uint8_t>(23, 27) ); REQUIRE(synth.getRegionView(3)->keyswitchRange == Range<uint8_t>(23, 27));
REQUIRE( synth.getRegionView(3)->keyswitch ); REQUIRE( synth.getRegionView(3)->keyswitch );
REQUIRE( *synth.getRegionView(3)->keyswitch == 24 ); REQUIRE( *synth.getRegionView(3)->keyswitch == 24 );
REQUIRE( synth.getRegionView(3)->keyswitchUp ); REQUIRE( synth.getRegionView(3)->keyswitchUp );
@ -455,21 +456,21 @@ TEST_CASE("[Files] Note and octave offsets")
REQUIRE( synth.getRegionView(3)->previousNote ); REQUIRE( synth.getRegionView(3)->previousNote );
REQUIRE( *synth.getRegionView(3)->previousNote == 61 ); REQUIRE( *synth.getRegionView(3)->previousNote == 61 );
REQUIRE( synth.getRegionView(4)->keyRange == sfz::Range<uint8_t>(76, 76) ); REQUIRE(synth.getRegionView(4)->keyRange == Range<uint8_t>(76, 76));
REQUIRE( synth.getRegionView(4)->pitchKeycenter == 76 ); REQUIRE( synth.getRegionView(4)->pitchKeycenter == 76 );
REQUIRE( synth.getRegionView(5)->keyRange == sfz::Range<uint8_t>(50, 50) ); REQUIRE(synth.getRegionView(5)->keyRange == Range<uint8_t>(50, 50));
REQUIRE( synth.getRegionView(5)->pitchKeycenter == 50 ); REQUIRE( synth.getRegionView(5)->pitchKeycenter == 50 );
REQUIRE( synth.getRegionView(6)->keyRange == sfz::Range<uint8_t>(50, 50) ); REQUIRE(synth.getRegionView(6)->keyRange == Range<uint8_t>(50, 50));
REQUIRE( synth.getRegionView(6)->pitchKeycenter == 50 ); REQUIRE( synth.getRegionView(6)->pitchKeycenter == 50 );
} }
TEST_CASE("[Files] Off by with different delays") TEST_CASE("[Files] Off by with different delays")
{ {
sfz::Synth synth; Synth synth;
synth.setSamplesPerBlock(256); synth.setSamplesPerBlock(256);
sfz::AudioBuffer<float> buffer(2, 256); AudioBuffer<float> buffer(2, 256);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz");
REQUIRE( synth.getNumRegions() == 4 ); REQUIRE( synth.getNumRegions() == 4 );
synth.noteOn(0, 63, 63); synth.noteOn(0, 63, 63);
@ -484,7 +485,7 @@ TEST_CASE("[Files] Off by with different delays")
TEST_CASE("[Files] Off by with the same delays") TEST_CASE("[Files] Off by with the same delays")
{ {
sfz::Synth synth; Synth synth;
synth.setSamplesPerBlock(256); synth.setSamplesPerBlock(256);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz");
REQUIRE( synth.getNumRegions() == 4 ); REQUIRE( synth.getNumRegions() == 4 );
@ -499,7 +500,7 @@ TEST_CASE("[Files] Off by with the same delays")
TEST_CASE("[Files] Off by with the same notes at the same time") TEST_CASE("[Files] Off by with the same notes at the same time")
{ {
sfz::Synth synth; Synth synth;
synth.setSamplesPerBlock(256); synth.setSamplesPerBlock(256);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz");
REQUIRE( synth.getNumRegions() == 4 ); REQUIRE( synth.getNumRegions() == 4 );
@ -507,7 +508,7 @@ TEST_CASE("[Files] Off by with the same notes at the same time")
REQUIRE( synth.getNumActiveVoices() == 2 ); REQUIRE( synth.getNumActiveVoices() == 2 );
synth.noteOn(0, 65, 63); synth.noteOn(0, 65, 63);
REQUIRE( synth.getNumActiveVoices() == 4 ); REQUIRE( synth.getNumActiveVoices() == 4 );
sfz::AudioBuffer<float> buffer { 2, 256 }; AudioBuffer<float> buffer { 2, 256 };
synth.renderBlock(buffer); synth.renderBlock(buffer);
synth.noteOn(0, 65, 63); synth.noteOn(0, 65, 63);
synth.renderBlock(buffer); synth.renderBlock(buffer);
@ -516,7 +517,7 @@ TEST_CASE("[Files] Off by with the same notes at the same time")
TEST_CASE("[Files] Off modes") TEST_CASE("[Files] Off modes")
{ {
sfz::Synth synth; Synth synth;
synth.setSamplesPerBlock(256); synth.setSamplesPerBlock(256);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_mode.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_mode.sfz");
REQUIRE( synth.getNumRegions() == 3 ); REQUIRE( synth.getNumRegions() == 3 );
@ -532,7 +533,7 @@ TEST_CASE("[Files] Off modes")
synth.getVoiceView(0) ; synth.getVoiceView(0) ;
synth.noteOn(100, 63, 63); synth.noteOn(100, 63, 63);
REQUIRE( synth.getNumActiveVoices() == 3 ); REQUIRE( synth.getNumActiveVoices() == 3 );
sfz::AudioBuffer<float> buffer { 2, 256 }; AudioBuffer<float> buffer { 2, 256 };
synth.renderBlock(buffer); synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices() == 2 ); REQUIRE( synth.getNumActiveVoices() == 2 );
REQUIRE( fastVoice->isFree() ); REQUIRE( fastVoice->isFree() );
@ -541,7 +542,7 @@ TEST_CASE("[Files] Off modes")
TEST_CASE("[Files] Looped regions taken from files and possibly overriden") TEST_CASE("[Files] Looped regions taken from files and possibly overriden")
{ {
sfz::Synth synth; Synth synth;
synth.setSamplesPerBlock(256); synth.setSamplesPerBlock(256);
synth.setSampleRate(44100); synth.setSampleRate(44100);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/looped_regions.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/looped_regions.sfz");
@ -550,9 +551,9 @@ TEST_CASE("[Files] Looped regions taken from files and possibly overriden")
REQUIRE( synth.getRegionView(1)->loopMode == SfzLoopMode::no_loop ); REQUIRE( synth.getRegionView(1)->loopMode == SfzLoopMode::no_loop );
REQUIRE( synth.getRegionView(2)->loopMode == SfzLoopMode::loop_continuous ); REQUIRE( synth.getRegionView(2)->loopMode == SfzLoopMode::loop_continuous );
REQUIRE( synth.getRegionView(0)->loopRange == sfz::Range<uint32_t>{ 77554, 186581 } ); REQUIRE(synth.getRegionView(0)->loopRange == Range<uint32_t> { 77554, 186581 });
REQUIRE( synth.getRegionView(1)->loopRange == sfz::Range<uint32_t>{ 77554, 186581 } ); REQUIRE(synth.getRegionView(1)->loopRange == Range<uint32_t> { 77554, 186581 });
REQUIRE( synth.getRegionView(2)->loopRange == sfz::Range<uint32_t>{ 4, 124 } ); REQUIRE(synth.getRegionView(2)->loopRange == Range<uint32_t> { 4, 124 });
} }
TEST_CASE("[Files] Case sentitiveness") TEST_CASE("[Files] Case sentitiveness")
@ -568,7 +569,7 @@ TEST_CASE("[Files] Case sentitiveness")
#endif #endif
if (caseSensitiveFs) { if (caseSensitiveFs) {
sfz::Synth synth; Synth synth;
synth.loadSfzFile(sfzFilePath); synth.loadSfzFile(sfzFilePath);
REQUIRE(synth.getNumRegions() == 4); REQUIRE(synth.getNumRegions() == 4);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy1.wav"); REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy1.wav");
@ -580,8 +581,8 @@ TEST_CASE("[Files] Case sentitiveness")
TEST_CASE("[Files] Empty file") TEST_CASE("[Files] Empty file")
{ {
sfz::Synth synth; Synth synth;
sfz::Parser& parser = synth.getParser(); Parser& parser = synth.getParser();
REQUIRE(!synth.loadSfzFile("")); REQUIRE(!synth.loadSfzFile(""));
REQUIRE(parser.getIncludedFiles().empty()); REQUIRE(parser.getIncludedFiles().empty());
REQUIRE(!synth.loadSfzFile({})); REQUIRE(!synth.loadSfzFile({}));

View file

@ -10,11 +10,12 @@
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
using namespace Catch::literals; using namespace Catch::literals;
using namespace sfz::literals; using namespace sfz::literals;
using namespace sfz;
TEST_CASE("[Region] Parsing opcodes") TEST_CASE("[Region] Parsing opcodes")
{ {
sfz::MidiState midiState; MidiState midiState;
sfz::Region region { 0, midiState }; Region region { 0, midiState };
SECTION("sample") SECTION("sample")
{ {
@ -132,36 +133,36 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("loop_end") SECTION("loop_end")
{ {
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 4294967295)); REQUIRE(region.loopRange == Range<uint32_t>(0, 4294967295));
region.parseOpcode({ "loop_end", "184" }); region.parseOpcode({ "loop_end", "184" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 184)); REQUIRE(region.loopRange == Range<uint32_t>(0, 184));
region.parseOpcode({ "loop_end", "-1" }); region.parseOpcode({ "loop_end", "-1" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 0)); REQUIRE(region.loopRange == Range<uint32_t>(0, 0));
} }
SECTION("loop_start") SECTION("loop_start")
{ {
region.parseOpcode({ "loop_start", "184" }); region.parseOpcode({ "loop_start", "184" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(184, 4294967295)); REQUIRE(region.loopRange == Range<uint32_t>(184, 4294967295));
region.parseOpcode({ "loop_start", "-1" }); region.parseOpcode({ "loop_start", "-1" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 4294967295)); REQUIRE(region.loopRange == Range<uint32_t>(0, 4294967295));
} }
SECTION("loopend") SECTION("loopend")
{ {
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 4294967295)); REQUIRE(region.loopRange == Range<uint32_t>(0, 4294967295));
region.parseOpcode({ "loopend", "184" }); region.parseOpcode({ "loopend", "184" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 184)); REQUIRE(region.loopRange == Range<uint32_t>(0, 184));
region.parseOpcode({ "loopend", "-1" }); region.parseOpcode({ "loopend", "-1" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 0)); REQUIRE(region.loopRange == Range<uint32_t>(0, 0));
} }
SECTION("loopstart") SECTION("loopstart")
{ {
region.parseOpcode({ "loopstart", "184" }); region.parseOpcode({ "loopstart", "184" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(184, 4294967295)); REQUIRE(region.loopRange == Range<uint32_t>(184, 4294967295));
region.parseOpcode({ "loopstart", "-1" }); region.parseOpcode({ "loopstart", "-1" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 4294967295)); REQUIRE(region.loopRange == Range<uint32_t>(0, 4294967295));
} }
SECTION("group") SECTION("group")
@ -194,87 +195,87 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("lokey, hikey, and key") SECTION("lokey, hikey, and key")
{ {
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.keyRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "lokey", "37" }); region.parseOpcode({ "lokey", "37" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(37, 127)); REQUIRE(region.keyRange == Range<uint8_t>(37, 127));
region.parseOpcode({ "lokey", "c4" }); region.parseOpcode({ "lokey", "c4" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(60, 127)); REQUIRE(region.keyRange == Range<uint8_t>(60, 127));
region.parseOpcode({ "lokey", "128" }); region.parseOpcode({ "lokey", "128" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(127, 127)); REQUIRE(region.keyRange == Range<uint8_t>(127, 127));
region.parseOpcode({ "lokey", "-3" }); region.parseOpcode({ "lokey", "-3" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.keyRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "hikey", "65" }); region.parseOpcode({ "hikey", "65" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 65)); REQUIRE(region.keyRange == Range<uint8_t>(0, 65));
region.parseOpcode({ "hikey", "c4" }); region.parseOpcode({ "hikey", "c4" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 60)); REQUIRE(region.keyRange == Range<uint8_t>(0, 60));
region.parseOpcode({ "hikey", "-1" }); region.parseOpcode({ "hikey", "-1" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.keyRange == Range<uint8_t>(0, 0));
region.parseOpcode({ "hikey", "128" }); region.parseOpcode({ "hikey", "128" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.keyRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "key", "26" }); region.parseOpcode({ "key", "26" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(26, 26)); REQUIRE(region.keyRange == Range<uint8_t>(26, 26));
REQUIRE(region.pitchKeycenter == 26); REQUIRE(region.pitchKeycenter == 26);
region.parseOpcode({ "key", "-26" }); region.parseOpcode({ "key", "-26" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.keyRange == Range<uint8_t>(0, 0));
REQUIRE(region.pitchKeycenter == 0); REQUIRE(region.pitchKeycenter == 0);
region.parseOpcode({ "key", "234" }); region.parseOpcode({ "key", "234" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(127, 127)); REQUIRE(region.keyRange == Range<uint8_t>(127, 127));
REQUIRE(region.pitchKeycenter == 127); REQUIRE(region.pitchKeycenter == 127);
region.parseOpcode({ "key", "c4" }); region.parseOpcode({ "key", "c4" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(60, 60)); REQUIRE(region.keyRange == Range<uint8_t>(60, 60));
REQUIRE(region.pitchKeycenter == 60); REQUIRE(region.pitchKeycenter == 60);
} }
SECTION("lovel, hivel") SECTION("lovel, hivel")
{ {
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm)); REQUIRE(region.velocityRange == Range<float>(0_norm, 127_norm));
region.parseOpcode({ "lovel", "37" }); region.parseOpcode({ "lovel", "37" });
REQUIRE(region.velocityRange == sfz::Range<float>(37_norm, 127_norm)); REQUIRE(region.velocityRange == Range<float>(37_norm, 127_norm));
region.parseOpcode({ "lovel", "128" }); region.parseOpcode({ "lovel", "128" });
REQUIRE(region.velocityRange == sfz::Range<float>(127_norm, 127_norm)); REQUIRE(region.velocityRange == Range<float>(127_norm, 127_norm));
region.parseOpcode({ "lovel", "-3" }); region.parseOpcode({ "lovel", "-3" });
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm)); REQUIRE(region.velocityRange == Range<float>(0_norm, 127_norm));
region.parseOpcode({ "hivel", "65" }); region.parseOpcode({ "hivel", "65" });
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 65_norm)); REQUIRE(region.velocityRange == Range<float>(0_norm, 65_norm));
region.parseOpcode({ "hivel", "-1" }); region.parseOpcode({ "hivel", "-1" });
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.velocityRange == Range<float>(0_norm, 0_norm));
region.parseOpcode({ "hivel", "128" }); region.parseOpcode({ "hivel", "128" });
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm)); REQUIRE(region.velocityRange == Range<float>(0_norm, 127_norm));
} }
SECTION("lobend, hibend") SECTION("lobend, hibend")
{ {
REQUIRE(region.bendRange == sfz::Range<float>(-1.0f, 1.0f)); REQUIRE(region.bendRange == Range<float>(-1.0f, 1.0f));
region.parseOpcode({ "lobend", "400" }); region.parseOpcode({ "lobend", "400" });
REQUIRE(region.bendRange.getStart() == Approx(sfz::normalizeBend(400))); REQUIRE(region.bendRange.getStart() == Approx(normalizeBend(400)));
REQUIRE(region.bendRange.getEnd() == 1.0_a); REQUIRE(region.bendRange.getEnd() == 1.0_a);
region.parseOpcode({ "lobend", "-128" }); region.parseOpcode({ "lobend", "-128" });
REQUIRE(region.bendRange.getStart() == Approx(sfz::normalizeBend(-128))); REQUIRE(region.bendRange.getStart() == Approx(normalizeBend(-128)));
REQUIRE(region.bendRange.getEnd() == 1.0_a); REQUIRE(region.bendRange.getEnd() == 1.0_a);
region.parseOpcode({ "lobend", "-10000" }); region.parseOpcode({ "lobend", "-10000" });
REQUIRE(region.bendRange == sfz::Range<float>(-1.0f, 1.0f)); REQUIRE(region.bendRange == Range<float>(-1.0f, 1.0f));
region.parseOpcode({ "hibend", "13" }); region.parseOpcode({ "hibend", "13" });
REQUIRE(region.bendRange.getStart() == -1.0_a); REQUIRE(region.bendRange.getStart() == -1.0_a);
REQUIRE(region.bendRange.getEnd() == Approx(sfz::normalizeBend(13))); REQUIRE(region.bendRange.getEnd() == Approx(normalizeBend(13)));
region.parseOpcode({ "hibend", "-1" }); region.parseOpcode({ "hibend", "-1" });
REQUIRE(region.bendRange.getStart() == -1.0_a); REQUIRE(region.bendRange.getStart() == -1.0_a);
REQUIRE(region.bendRange.getEnd() == Approx(sfz::normalizeBend(-1))); REQUIRE(region.bendRange.getEnd() == Approx(normalizeBend(-1)));
region.parseOpcode({ "hibend", "10000" }); region.parseOpcode({ "hibend", "10000" });
REQUIRE(region.bendRange == sfz::Range<float>(-1.0f, 1.0f)); REQUIRE(region.bendRange == Range<float>(-1.0f, 1.0f));
} }
SECTION("locc, hicc") SECTION("locc, hicc")
{ {
REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range<float>(0_norm, 127_norm)); REQUIRE(region.ccConditions.getWithDefault(0) == Range<float>(0_norm, 127_norm));
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm)); REQUIRE(region.ccConditions[127] == Range<float>(0_norm, 127_norm));
region.parseOpcode({ "locc6", "4" }); region.parseOpcode({ "locc6", "4" });
REQUIRE(region.ccConditions[6] == sfz::Range<float>(4_norm, 127_norm)); REQUIRE(region.ccConditions[6] == Range<float>(4_norm, 127_norm));
region.parseOpcode({ "locc12", "-128" }); region.parseOpcode({ "locc12", "-128" });
REQUIRE(region.ccConditions[12] == sfz::Range<float>(0_norm, 127_norm)); REQUIRE(region.ccConditions[12] == Range<float>(0_norm, 127_norm));
region.parseOpcode({ "hicc65", "39" }); region.parseOpcode({ "hicc65", "39" });
REQUIRE(region.ccConditions[65] == sfz::Range<float>(0_norm, 39_norm)); REQUIRE(region.ccConditions[65] == Range<float>(0_norm, 39_norm));
region.parseOpcode({ "hicc127", "135" }); region.parseOpcode({ "hicc127", "135" });
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm)); REQUIRE(region.ccConditions[127] == Range<float>(0_norm, 127_norm));
} }
SECTION("lohdcc, hihdcc") SECTION("lohdcc, hihdcc")
@ -307,19 +308,19 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("sw_lokey, sw_hikey") SECTION("sw_lokey, sw_hikey")
{ {
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "sw_lokey", "4" }); region.parseOpcode({ "sw_lokey", "4" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(4, 127)); REQUIRE(region.keyswitchRange == Range<uint8_t>(4, 127));
region.parseOpcode({ "sw_lokey", "128" }); region.parseOpcode({ "sw_lokey", "128" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(127, 127)); REQUIRE(region.keyswitchRange == Range<uint8_t>(127, 127));
region.parseOpcode({ "sw_lokey", "0" }); region.parseOpcode({ "sw_lokey", "0" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "sw_hikey", "39" }); region.parseOpcode({ "sw_hikey", "39" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 39)); REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 39));
region.parseOpcode({ "sw_hikey", "135" }); region.parseOpcode({ "sw_hikey", "135" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "sw_hikey", "-1" }); region.parseOpcode({ "sw_hikey", "-1" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 0));
} }
SECTION("sw_label") SECTION("sw_label")
@ -398,53 +399,53 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("lochanaft, hichanaft") SECTION("lochanaft, hichanaft")
{ {
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.aftertouchRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "lochanaft", "4" }); region.parseOpcode({ "lochanaft", "4" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(4, 127)); REQUIRE(region.aftertouchRange == Range<uint8_t>(4, 127));
region.parseOpcode({ "lochanaft", "128" }); region.parseOpcode({ "lochanaft", "128" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(127, 127)); REQUIRE(region.aftertouchRange == Range<uint8_t>(127, 127));
region.parseOpcode({ "lochanaft", "0" }); region.parseOpcode({ "lochanaft", "0" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.aftertouchRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "hichanaft", "39" }); region.parseOpcode({ "hichanaft", "39" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(0, 39)); REQUIRE(region.aftertouchRange == Range<uint8_t>(0, 39));
region.parseOpcode({ "hichanaft", "135" }); region.parseOpcode({ "hichanaft", "135" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.aftertouchRange == Range<uint8_t>(0, 127));
region.parseOpcode({ "hichanaft", "-1" }); region.parseOpcode({ "hichanaft", "-1" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.aftertouchRange == Range<uint8_t>(0, 0));
} }
SECTION("lobpm, hibpm") SECTION("lobpm, hibpm")
{ {
REQUIRE(region.bpmRange == sfz::Range<float>(0, 500)); REQUIRE(region.bpmRange == Range<float>(0, 500));
region.parseOpcode({ "lobpm", "47.5" }); region.parseOpcode({ "lobpm", "47.5" });
REQUIRE(region.bpmRange == sfz::Range<float>(47.5, 500)); REQUIRE(region.bpmRange == Range<float>(47.5, 500));
region.parseOpcode({ "lobpm", "594" }); region.parseOpcode({ "lobpm", "594" });
REQUIRE(region.bpmRange == sfz::Range<float>(500, 500)); REQUIRE(region.bpmRange == Range<float>(500, 500));
region.parseOpcode({ "lobpm", "0" }); region.parseOpcode({ "lobpm", "0" });
REQUIRE(region.bpmRange == sfz::Range<float>(0, 500)); REQUIRE(region.bpmRange == Range<float>(0, 500));
region.parseOpcode({ "hibpm", "78" }); region.parseOpcode({ "hibpm", "78" });
REQUIRE(region.bpmRange == sfz::Range<float>(0, 78)); REQUIRE(region.bpmRange == Range<float>(0, 78));
region.parseOpcode({ "hibpm", "895.4" }); region.parseOpcode({ "hibpm", "895.4" });
REQUIRE(region.bpmRange == sfz::Range<float>(0, 500)); REQUIRE(region.bpmRange == Range<float>(0, 500));
region.parseOpcode({ "hibpm", "-1" }); region.parseOpcode({ "hibpm", "-1" });
REQUIRE(region.bpmRange == sfz::Range<float>(0, 0)); REQUIRE(region.bpmRange == Range<float>(0, 0));
} }
SECTION("lorand, hirand") SECTION("lorand, hirand")
{ {
REQUIRE(region.randRange == sfz::Range<float>(0, 1)); REQUIRE(region.randRange == Range<float>(0, 1));
region.parseOpcode({ "lorand", "0.5" }); region.parseOpcode({ "lorand", "0.5" });
REQUIRE(region.randRange == sfz::Range<float>(0.5, 1)); REQUIRE(region.randRange == Range<float>(0.5, 1));
region.parseOpcode({ "lorand", "4" }); region.parseOpcode({ "lorand", "4" });
REQUIRE(region.randRange == sfz::Range<float>(1, 1)); REQUIRE(region.randRange == Range<float>(1, 1));
region.parseOpcode({ "lorand", "0" }); region.parseOpcode({ "lorand", "0" });
REQUIRE(region.randRange == sfz::Range<float>(0, 1)); REQUIRE(region.randRange == Range<float>(0, 1));
region.parseOpcode({ "hirand", "39" }); region.parseOpcode({ "hirand", "39" });
REQUIRE(region.randRange == sfz::Range<float>(0, 1)); REQUIRE(region.randRange == Range<float>(0, 1));
region.parseOpcode({ "hirand", "0.7" }); region.parseOpcode({ "hirand", "0.7" });
REQUIRE(region.randRange == sfz::Range<float>(0, 0.7f)); REQUIRE(region.randRange == Range<float>(0, 0.7f));
region.parseOpcode({ "hirand", "-1" }); region.parseOpcode({ "hirand", "-1" });
REQUIRE(region.randRange == sfz::Range<float>(0, 0)); REQUIRE(region.randRange == Range<float>(0, 0));
} }
SECTION("seq_length") SECTION("seq_length")
@ -489,10 +490,10 @@ TEST_CASE("[Region] Parsing opcodes")
} }
region.parseOpcode({ "on_locc45", "15" }); region.parseOpcode({ "on_locc45", "15" });
REQUIRE(region.ccTriggers.contains(45)); REQUIRE(region.ccTriggers.contains(45));
REQUIRE(region.ccTriggers[45] == sfz::Range<float>(15_norm, 127_norm)); REQUIRE(region.ccTriggers[45] == Range<float>(15_norm, 127_norm));
region.parseOpcode({ "on_hicc4", "47" }); region.parseOpcode({ "on_hicc4", "47" });
REQUIRE(region.ccTriggers.contains(45)); REQUIRE(region.ccTriggers.contains(45));
REQUIRE(region.ccTriggers[4] == sfz::Range<float>(0_norm, 47_norm)); REQUIRE(region.ccTriggers[4] == Range<float>(0_norm, 47_norm));
} }
SECTION("on_lohdcc, on_hihdcc") SECTION("on_lohdcc, on_hihdcc")
@ -540,28 +541,28 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("pan_oncc") SECTION("pan_oncc")
{ {
REQUIRE(region.panCC.empty()); REQUIRE(region.modifiers[Mod::pan].empty());
region.parseOpcode({ "pan_oncc45", "4.2" }); region.parseOpcode({ "pan_oncc45", "4.2" });
REQUIRE(region.panCC.contains(45)); REQUIRE(region.modifiers[Mod::pan].contains(45));
REQUIRE(region.panCC[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.panCC[17].curve == 18); REQUIRE(region.modifiers[Mod::pan][17].curve == 18);
region.parseOpcode({ "pan_curvecc17", "15482" }); region.parseOpcode({ "pan_curvecc17", "15482" });
REQUIRE(region.panCC[17].curve == 255); REQUIRE(region.modifiers[Mod::pan][17].curve == 255);
region.parseOpcode({ "pan_curvecc17", "-2" }); region.parseOpcode({ "pan_curvecc17", "-2" });
REQUIRE(region.panCC[17].curve == 0); REQUIRE(region.modifiers[Mod::pan][17].curve == 0);
region.parseOpcode({ "pan_smoothcc14", "85" }); region.parseOpcode({ "pan_smoothcc14", "85" });
REQUIRE(region.panCC[14].smooth == 85); REQUIRE(region.modifiers[Mod::pan][14].smooth == 85);
region.parseOpcode({ "pan_smoothcc14", "15482" }); region.parseOpcode({ "pan_smoothcc14", "15482" });
REQUIRE(region.panCC[14].smooth == 127); REQUIRE(region.modifiers[Mod::pan][14].smooth == 100);
region.parseOpcode({ "pan_smoothcc14", "-2" }); region.parseOpcode({ "pan_smoothcc14", "-2" });
REQUIRE(region.panCC[14].smooth == 0); REQUIRE(region.modifiers[Mod::pan][14].smooth == 0);
region.parseOpcode({ "pan_stepcc120", "24" }); region.parseOpcode({ "pan_stepcc120", "24" });
REQUIRE(region.panCC[120].step == 24.0_a); REQUIRE(region.modifiers[Mod::pan][120].step == 24.0_a);
region.parseOpcode({ "pan_stepcc120", "15482" }); region.parseOpcode({ "pan_stepcc120", "15482" });
REQUIRE(region.panCC[120].step == 200.0_a); REQUIRE(region.modifiers[Mod::pan][120].step == 200.0_a);
region.parseOpcode({ "pan_stepcc120", "-2" }); region.parseOpcode({ "pan_stepcc120", "-2" });
REQUIRE(region.panCC[120].step == 0.0f); REQUIRE(region.modifiers[Mod::pan][120].step == 0.0f);
} }
SECTION("width") SECTION("width")
@ -579,28 +580,28 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("width_oncc") SECTION("width_oncc")
{ {
REQUIRE(region.widthCC.empty()); REQUIRE(region.modifiers[Mod::width].empty());
region.parseOpcode({ "width_oncc45", "4.2" }); region.parseOpcode({ "width_oncc45", "4.2" });
REQUIRE(region.widthCC.contains(45)); REQUIRE(region.modifiers[Mod::width].contains(45));
REQUIRE(region.widthCC[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.widthCC[17].curve == 18); REQUIRE(region.modifiers[Mod::width][17].curve == 18);
region.parseOpcode({ "width_curvecc17", "15482" }); region.parseOpcode({ "width_curvecc17", "15482" });
REQUIRE(region.widthCC[17].curve == 255); REQUIRE(region.modifiers[Mod::width][17].curve == 255);
region.parseOpcode({ "width_curvecc17", "-2" }); region.parseOpcode({ "width_curvecc17", "-2" });
REQUIRE(region.widthCC[17].curve == 0); REQUIRE(region.modifiers[Mod::width][17].curve == 0);
region.parseOpcode({ "width_smoothcc14", "85" }); region.parseOpcode({ "width_smoothcc14", "85" });
REQUIRE(region.widthCC[14].smooth == 85); REQUIRE(region.modifiers[Mod::width][14].smooth == 85);
region.parseOpcode({ "width_smoothcc14", "15482" }); region.parseOpcode({ "width_smoothcc14", "15482" });
REQUIRE(region.widthCC[14].smooth == 127); REQUIRE(region.modifiers[Mod::width][14].smooth == 100);
region.parseOpcode({ "width_smoothcc14", "-2" }); region.parseOpcode({ "width_smoothcc14", "-2" });
REQUIRE(region.widthCC[14].smooth == 0); REQUIRE(region.modifiers[Mod::width][14].smooth == 0);
region.parseOpcode({ "width_stepcc120", "24" }); region.parseOpcode({ "width_stepcc120", "24" });
REQUIRE(region.widthCC[120].step == 24.0_a); REQUIRE(region.modifiers[Mod::width][120].step == 24.0_a);
region.parseOpcode({ "width_stepcc120", "15482" }); region.parseOpcode({ "width_stepcc120", "15482" });
REQUIRE(region.widthCC[120].step == 200.0_a); REQUIRE(region.modifiers[Mod::width][120].step == 200.0_a);
region.parseOpcode({ "width_stepcc120", "-20" }); region.parseOpcode({ "width_stepcc120", "-20" });
REQUIRE(region.widthCC[120].step == 0.0f); REQUIRE(region.modifiers[Mod::width][120].step == 0.0f);
} }
SECTION("position") SECTION("position")
@ -618,28 +619,28 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("position_oncc") SECTION("position_oncc")
{ {
REQUIRE(region.positionCC.empty()); REQUIRE(region.modifiers[Mod::position].empty());
region.parseOpcode({ "position_oncc45", "4.2" }); region.parseOpcode({ "position_oncc45", "4.2" });
REQUIRE(region.positionCC.contains(45)); REQUIRE(region.modifiers[Mod::position].contains(45));
REQUIRE(region.positionCC[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.positionCC[17].curve == 18); REQUIRE(region.modifiers[Mod::position][17].curve == 18);
region.parseOpcode({ "position_curvecc17", "15482" }); region.parseOpcode({ "position_curvecc17", "15482" });
REQUIRE(region.positionCC[17].curve == 255); REQUIRE(region.modifiers[Mod::position][17].curve == 255);
region.parseOpcode({ "position_curvecc17", "-2" }); region.parseOpcode({ "position_curvecc17", "-2" });
REQUIRE(region.positionCC[17].curve == 0); REQUIRE(region.modifiers[Mod::position][17].curve == 0);
region.parseOpcode({ "position_smoothcc14", "85" }); region.parseOpcode({ "position_smoothcc14", "85" });
REQUIRE(region.positionCC[14].smooth == 85); REQUIRE(region.modifiers[Mod::position][14].smooth == 85);
region.parseOpcode({ "position_smoothcc14", "15482" }); region.parseOpcode({ "position_smoothcc14", "15482" });
REQUIRE(region.positionCC[14].smooth == 127); REQUIRE(region.modifiers[Mod::position][14].smooth == 100);
region.parseOpcode({ "position_smoothcc14", "-2" }); region.parseOpcode({ "position_smoothcc14", "-2" });
REQUIRE(region.positionCC[14].smooth == 0); REQUIRE(region.modifiers[Mod::position][14].smooth == 0);
region.parseOpcode({ "position_stepcc120", "24" }); region.parseOpcode({ "position_stepcc120", "24" });
REQUIRE(region.positionCC[120].step == 24.0_a); REQUIRE(region.modifiers[Mod::position][120].step == 24.0_a);
region.parseOpcode({ "position_stepcc120", "15482" }); region.parseOpcode({ "position_stepcc120", "15482" });
REQUIRE(region.positionCC[120].step == 200.0_a); REQUIRE(region.modifiers[Mod::position][120].step == 200.0_a);
region.parseOpcode({ "position_stepcc120", "-2" }); region.parseOpcode({ "position_stepcc120", "-2" });
REQUIRE(region.positionCC[120].step == 0.0f); REQUIRE(region.modifiers[Mod::position][120].step == 0.0f);
} }
SECTION("amp_keycenter") SECTION("amp_keycenter")
@ -704,116 +705,116 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("xfin_lokey, xfin_hikey") SECTION("xfin_lokey, xfin_hikey")
{ {
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(0, 0));
region.parseOpcode({ "xfin_lokey", "4" }); region.parseOpcode({ "xfin_lokey", "4" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(4, 4)); REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(4, 4));
region.parseOpcode({ "xfin_lokey", "128" }); region.parseOpcode({ "xfin_lokey", "128" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(127, 127)); REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(127, 127));
region.parseOpcode({ "xfin_lokey", "59" }); region.parseOpcode({ "xfin_lokey", "59" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(59, 127)); REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(59, 127));
region.parseOpcode({ "xfin_hikey", "59" }); region.parseOpcode({ "xfin_hikey", "59" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(59, 59)); REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(59, 59));
region.parseOpcode({ "xfin_hikey", "128" }); region.parseOpcode({ "xfin_hikey", "128" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(59, 127)); REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(59, 127));
region.parseOpcode({ "xfin_hikey", "0" }); region.parseOpcode({ "xfin_hikey", "0" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(0, 0));
region.parseOpcode({ "xfin_hikey", "-1" }); region.parseOpcode({ "xfin_hikey", "-1" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(0, 0));
} }
SECTION("xfin_lovel, xfin_hivel") SECTION("xfin_lovel, xfin_hivel")
{ {
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeVelInRange == Range<float>(0_norm, 0_norm));
region.parseOpcode({ "xfin_lovel", "4" }); region.parseOpcode({ "xfin_lovel", "4" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(4_norm, 4_norm)); REQUIRE(region.crossfadeVelInRange == Range<float>(4_norm, 4_norm));
region.parseOpcode({ "xfin_lovel", "128" }); region.parseOpcode({ "xfin_lovel", "128" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(127_norm, 127_norm)); REQUIRE(region.crossfadeVelInRange == Range<float>(127_norm, 127_norm));
region.parseOpcode({ "xfin_lovel", "59" }); region.parseOpcode({ "xfin_lovel", "59" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm)); REQUIRE(region.crossfadeVelInRange == Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfin_hivel", "59" }); region.parseOpcode({ "xfin_hivel", "59" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 59_norm)); REQUIRE(region.crossfadeVelInRange == Range<float>(59_norm, 59_norm));
region.parseOpcode({ "xfin_hivel", "128" }); region.parseOpcode({ "xfin_hivel", "128" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm)); REQUIRE(region.crossfadeVelInRange == Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfin_hivel", "0" }); region.parseOpcode({ "xfin_hivel", "0" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeVelInRange == Range<float>(0_norm, 0_norm));
region.parseOpcode({ "xfin_hivel", "-1" }); region.parseOpcode({ "xfin_hivel", "-1" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeVelInRange == Range<float>(0_norm, 0_norm));
} }
SECTION("xfout_lokey, xfout_hikey") SECTION("xfout_lokey, xfout_hikey")
{ {
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(127, 127)); REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(127, 127));
region.parseOpcode({ "xfout_lokey", "4" }); region.parseOpcode({ "xfout_lokey", "4" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(4, 127)); REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(4, 127));
region.parseOpcode({ "xfout_lokey", "128" }); region.parseOpcode({ "xfout_lokey", "128" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(127, 127)); REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(127, 127));
region.parseOpcode({ "xfout_lokey", "59" }); region.parseOpcode({ "xfout_lokey", "59" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(59, 127)); REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(59, 127));
region.parseOpcode({ "xfout_hikey", "59" }); region.parseOpcode({ "xfout_hikey", "59" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(59, 59)); REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(59, 59));
region.parseOpcode({ "xfout_hikey", "128" }); region.parseOpcode({ "xfout_hikey", "128" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(59, 127)); REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(59, 127));
region.parseOpcode({ "xfout_hikey", "0" }); region.parseOpcode({ "xfout_hikey", "0" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(0, 0));
region.parseOpcode({ "xfout_hikey", "-1" }); region.parseOpcode({ "xfout_hikey", "-1" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(0, 0)); REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(0, 0));
} }
SECTION("xfout_lovel, xfout_hivel") SECTION("xfout_lovel, xfout_hivel")
{ {
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(127_norm, 127_norm)); REQUIRE(region.crossfadeVelOutRange == Range<float>(127_norm, 127_norm));
region.parseOpcode({ "xfout_lovel", "4" }); region.parseOpcode({ "xfout_lovel", "4" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(4_norm, 127_norm)); REQUIRE(region.crossfadeVelOutRange == Range<float>(4_norm, 127_norm));
region.parseOpcode({ "xfout_lovel", "128" }); region.parseOpcode({ "xfout_lovel", "128" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(127_norm, 127_norm)); REQUIRE(region.crossfadeVelOutRange == Range<float>(127_norm, 127_norm));
region.parseOpcode({ "xfout_lovel", "59" }); region.parseOpcode({ "xfout_lovel", "59" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm)); REQUIRE(region.crossfadeVelOutRange == Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfout_hivel", "59" }); region.parseOpcode({ "xfout_hivel", "59" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 59_norm)); REQUIRE(region.crossfadeVelOutRange == Range<float>(59_norm, 59_norm));
region.parseOpcode({ "xfout_hivel", "128" }); region.parseOpcode({ "xfout_hivel", "128" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm)); REQUIRE(region.crossfadeVelOutRange == Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfout_hivel", "0" }); region.parseOpcode({ "xfout_hivel", "0" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeVelOutRange == Range<float>(0_norm, 0_norm));
region.parseOpcode({ "xfout_hivel", "-1" }); region.parseOpcode({ "xfout_hivel", "-1" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeVelOutRange == Range<float>(0_norm, 0_norm));
} }
SECTION("xfin_locc, xfin_hicc") SECTION("xfin_locc, xfin_hicc")
{ {
REQUIRE(!region.crossfadeCCInRange.contains(4)); REQUIRE(!region.crossfadeCCInRange.contains(4));
region.parseOpcode({ "xfin_locc4", "4" }); region.parseOpcode({ "xfin_locc4", "4" });
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(4_norm, 4_norm)); REQUIRE(region.crossfadeCCInRange[4] == Range<float>(4_norm, 4_norm));
region.parseOpcode({ "xfin_locc4", "128" }); region.parseOpcode({ "xfin_locc4", "128" });
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(127_norm, 127_norm)); REQUIRE(region.crossfadeCCInRange[4] == Range<float>(127_norm, 127_norm));
region.parseOpcode({ "xfin_locc4", "59" }); region.parseOpcode({ "xfin_locc4", "59" });
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 127_norm)); REQUIRE(region.crossfadeCCInRange[4] == Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfin_hicc4", "59" }); region.parseOpcode({ "xfin_hicc4", "59" });
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 59_norm)); REQUIRE(region.crossfadeCCInRange[4] == Range<float>(59_norm, 59_norm));
region.parseOpcode({ "xfin_hicc4", "128" }); region.parseOpcode({ "xfin_hicc4", "128" });
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 127_norm)); REQUIRE(region.crossfadeCCInRange[4] == Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfin_hicc4", "0" }); region.parseOpcode({ "xfin_hicc4", "0" });
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeCCInRange[4] == Range<float>(0_norm, 0_norm));
region.parseOpcode({ "xfin_hicc4", "-1" }); region.parseOpcode({ "xfin_hicc4", "-1" });
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeCCInRange[4] == Range<float>(0_norm, 0_norm));
} }
SECTION("xfout_locc, xfout_hicc") SECTION("xfout_locc, xfout_hicc")
{ {
REQUIRE(!region.crossfadeCCOutRange.contains(4)); REQUIRE(!region.crossfadeCCOutRange.contains(4));
region.parseOpcode({ "xfout_locc4", "4" }); region.parseOpcode({ "xfout_locc4", "4" });
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(4_norm, 127_norm)); REQUIRE(region.crossfadeCCOutRange[4] == Range<float>(4_norm, 127_norm));
region.parseOpcode({ "xfout_locc4", "128" }); region.parseOpcode({ "xfout_locc4", "128" });
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(127_norm, 127_norm)); REQUIRE(region.crossfadeCCOutRange[4] == Range<float>(127_norm, 127_norm));
region.parseOpcode({ "xfout_locc4", "59" }); region.parseOpcode({ "xfout_locc4", "59" });
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 127_norm)); REQUIRE(region.crossfadeCCOutRange[4] == Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfout_hicc4", "59" }); region.parseOpcode({ "xfout_hicc4", "59" });
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 59_norm)); REQUIRE(region.crossfadeCCOutRange[4] == Range<float>(59_norm, 59_norm));
region.parseOpcode({ "xfout_hicc4", "128" }); region.parseOpcode({ "xfout_hicc4", "128" });
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 127_norm)); REQUIRE(region.crossfadeCCOutRange[4] == Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfout_hicc4", "0" }); region.parseOpcode({ "xfout_hicc4", "0" });
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeCCOutRange[4] == Range<float>(0_norm, 0_norm));
region.parseOpcode({ "xfout_hicc4", "-1" }); region.parseOpcode({ "xfout_hicc4", "-1" });
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(0_norm, 0_norm)); REQUIRE(region.crossfadeCCOutRange[4] == Range<float>(0_norm, 0_norm));
} }
SECTION("xf_keycurve") SECTION("xf_keycurve")
@ -932,7 +933,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.tune == -9600); REQUIRE(region.tune == -9600);
} }
SECTION("bend_up, bend_down, bend_step") SECTION("bend_up, bend_down, bend_step, bend_smooth")
{ {
REQUIRE(region.bendUp == 200); REQUIRE(region.bendUp == 200);
REQUIRE(region.bendDown == -200); REQUIRE(region.bendDown == -200);
@ -959,6 +960,12 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.bendStep == 1); REQUIRE(region.bendStep == 1);
region.parseOpcode({ "bend_step", "9700" }); region.parseOpcode({ "bend_step", "9700" });
REQUIRE(region.bendStep == 1200); REQUIRE(region.bendStep == 1200);
region.parseOpcode({ "bend_smooth", "10" });
REQUIRE(region.bendSmooth == 10);
region.parseOpcode({ "bend_smooth", "120" });
REQUIRE(region.bendSmooth == 100);
region.parseOpcode({ "bend_smooth", "-2" });
REQUIRE(region.bendSmooth == 0);
} }
SECTION("ampeg") SECTION("ampeg")
@ -1173,7 +1180,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.filters[0].cutoff == 500.0f); REQUIRE(region.filters[0].cutoff == 500.0f);
// Check filter defaults // Check filter defaults
REQUIRE(region.filters[0].keycenter == 60); REQUIRE(region.filters[0].keycenter == 60);
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2p); REQUIRE(region.filters[0].type == FilterType::kFilterLpf2p);
REQUIRE(region.filters[0].keytrack == 0); REQUIRE(region.filters[0].keytrack == 0);
REQUIRE(region.filters[0].gain == 0); REQUIRE(region.filters[0].gain == 0);
REQUIRE(region.filters[0].veltrack == 0); REQUIRE(region.filters[0].veltrack == 0);
@ -1187,7 +1194,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.filters[1].cutoff == 5000.0f); REQUIRE(region.filters[1].cutoff == 5000.0f);
// Check filter defaults // Check filter defaults
REQUIRE(region.filters[1].keycenter == 60); REQUIRE(region.filters[1].keycenter == 60);
REQUIRE(region.filters[1].type == sfz::FilterType::kFilterLpf2p); REQUIRE(region.filters[1].type == FilterType::kFilterLpf2p);
REQUIRE(region.filters[1].keytrack == 0); REQUIRE(region.filters[1].keytrack == 0);
REQUIRE(region.filters[1].gain == 0); REQUIRE(region.filters[1].gain == 0);
REQUIRE(region.filters[1].veltrack == 0); REQUIRE(region.filters[1].veltrack == 0);
@ -1202,7 +1209,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.filters[3].cutoff == 50.0f); REQUIRE(region.filters[3].cutoff == 50.0f);
// Check filter defaults // Check filter defaults
REQUIRE(region.filters[2].keycenter == 60); REQUIRE(region.filters[2].keycenter == 60);
REQUIRE(region.filters[2].type == sfz::FilterType::kFilterLpf2p); REQUIRE(region.filters[2].type == FilterType::kFilterLpf2p);
REQUIRE(region.filters[2].keytrack == 0); REQUIRE(region.filters[2].keytrack == 0);
REQUIRE(region.filters[2].gain == 0); REQUIRE(region.filters[2].gain == 0);
REQUIRE(region.filters[2].veltrack == 0); REQUIRE(region.filters[2].veltrack == 0);
@ -1211,7 +1218,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.filters[2].gainCC.empty()); REQUIRE(region.filters[2].gainCC.empty());
REQUIRE(region.filters[2].resonanceCC.empty()); REQUIRE(region.filters[2].resonanceCC.empty());
REQUIRE(region.filters[3].keycenter == 60); REQUIRE(region.filters[3].keycenter == 60);
REQUIRE(region.filters[3].type == sfz::FilterType::kFilterLpf2p); REQUIRE(region.filters[3].type == FilterType::kFilterLpf2p);
REQUIRE(region.filters[3].keytrack == 0); REQUIRE(region.filters[3].keytrack == 0);
REQUIRE(region.filters[3].gain == 0); REQUIRE(region.filters[3].gain == 0);
REQUIRE(region.filters[3].veltrack == 0); REQUIRE(region.filters[3].veltrack == 0);
@ -1313,55 +1320,55 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "fil_type", "lpf_1p" }); region.parseOpcode({ "fil_type", "lpf_1p" });
REQUIRE(region.filters.size() == 1); REQUIRE(region.filters.size() == 1);
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf1p); REQUIRE(region.filters[0].type == FilterType::kFilterLpf1p);
region.parseOpcode({ "fil_type", "lpf_2p" }); region.parseOpcode({ "fil_type", "lpf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2p); REQUIRE(region.filters[0].type == FilterType::kFilterLpf2p);
region.parseOpcode({ "fil_type", "hpf_1p" }); region.parseOpcode({ "fil_type", "hpf_1p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf1p); REQUIRE(region.filters[0].type == FilterType::kFilterHpf1p);
region.parseOpcode({ "fil_type", "hpf_2p" }); region.parseOpcode({ "fil_type", "hpf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf2p); REQUIRE(region.filters[0].type == FilterType::kFilterHpf2p);
region.parseOpcode({ "fil_type", "bpf_2p" }); region.parseOpcode({ "fil_type", "bpf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf2p); REQUIRE(region.filters[0].type == FilterType::kFilterBpf2p);
region.parseOpcode({ "fil_type", "brf_2p" }); region.parseOpcode({ "fil_type", "brf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf2p); REQUIRE(region.filters[0].type == FilterType::kFilterBrf2p);
region.parseOpcode({ "fil_type", "bpf_1p" }); region.parseOpcode({ "fil_type", "bpf_1p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf1p); REQUIRE(region.filters[0].type == FilterType::kFilterBpf1p);
region.parseOpcode({ "fil_type", "brf_1p" }); region.parseOpcode({ "fil_type", "brf_1p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf1p); REQUIRE(region.filters[0].type == FilterType::kFilterBrf1p);
region.parseOpcode({ "fil_type", "apf_1p" }); region.parseOpcode({ "fil_type", "apf_1p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterApf1p); REQUIRE(region.filters[0].type == FilterType::kFilterApf1p);
region.parseOpcode({ "fil_type", "lpf_2p_sv" }); region.parseOpcode({ "fil_type", "lpf_2p_sv" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2pSv); REQUIRE(region.filters[0].type == FilterType::kFilterLpf2pSv);
region.parseOpcode({ "fil_type", "hpf_2p_sv" }); region.parseOpcode({ "fil_type", "hpf_2p_sv" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf2pSv); REQUIRE(region.filters[0].type == FilterType::kFilterHpf2pSv);
region.parseOpcode({ "fil_type", "bpf_2p_sv" }); region.parseOpcode({ "fil_type", "bpf_2p_sv" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf2pSv); REQUIRE(region.filters[0].type == FilterType::kFilterBpf2pSv);
region.parseOpcode({ "fil_type", "brf_2p_sv" }); region.parseOpcode({ "fil_type", "brf_2p_sv" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf2pSv); REQUIRE(region.filters[0].type == FilterType::kFilterBrf2pSv);
region.parseOpcode({ "fil_type", "lpf_4p" }); region.parseOpcode({ "fil_type", "lpf_4p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf4p); REQUIRE(region.filters[0].type == FilterType::kFilterLpf4p);
region.parseOpcode({ "fil_type", "hpf_4p" }); region.parseOpcode({ "fil_type", "hpf_4p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf4p); REQUIRE(region.filters[0].type == FilterType::kFilterHpf4p);
region.parseOpcode({ "fil_type", "lpf_6p" }); region.parseOpcode({ "fil_type", "lpf_6p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf6p); REQUIRE(region.filters[0].type == FilterType::kFilterLpf6p);
region.parseOpcode({ "fil_type", "hpf_6p" }); region.parseOpcode({ "fil_type", "hpf_6p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf6p); REQUIRE(region.filters[0].type == FilterType::kFilterHpf6p);
region.parseOpcode({ "fil_type", "pink" }); region.parseOpcode({ "fil_type", "pink" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPink); REQUIRE(region.filters[0].type == FilterType::kFilterPink);
region.parseOpcode({ "fil_type", "lsh" }); region.parseOpcode({ "fil_type", "lsh" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLsh); REQUIRE(region.filters[0].type == FilterType::kFilterLsh);
region.parseOpcode({ "fil_type", "hsh" }); region.parseOpcode({ "fil_type", "hsh" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHsh); REQUIRE(region.filters[0].type == FilterType::kFilterHsh);
region.parseOpcode({ "fil_type", "peq" }); region.parseOpcode({ "fil_type", "peq" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPeq); REQUIRE(region.filters[0].type == FilterType::kFilterPeq);
region.parseOpcode({ "fil_type", "lpf_1p" }); region.parseOpcode({ "fil_type", "lpf_1p" });
region.parseOpcode({ "fil_type", "pkf_2p" }); region.parseOpcode({ "fil_type", "pkf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPeq); REQUIRE(region.filters[0].type == FilterType::kFilterPeq);
region.parseOpcode({ "fil_type", "lpf_1p" }); region.parseOpcode({ "fil_type", "lpf_1p" });
region.parseOpcode({ "fil_type", "bpk_2p" }); region.parseOpcode({ "fil_type", "bpk_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPeq); REQUIRE(region.filters[0].type == FilterType::kFilterPeq);
region.parseOpcode({ "fil_type", "unknown" }); region.parseOpcode({ "fil_type", "unknown" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterNone); REQUIRE(region.filters[0].type == FilterType::kFilterNone);
} }
SECTION("EQ stacking and gains") SECTION("EQ stacking and gains")
@ -1372,7 +1379,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.equalizers.size() == 1); REQUIRE(region.equalizers.size() == 1);
REQUIRE(region.equalizers[0].gain == 6.0f); REQUIRE(region.equalizers[0].gain == 6.0f);
// Check defaults // Check defaults
REQUIRE(region.equalizers[0].type == sfz::EqType::kEqPeak); REQUIRE(region.equalizers[0].type == EqType::kEqPeak);
REQUIRE(region.equalizers[0].bandwidth == 1.0f); REQUIRE(region.equalizers[0].bandwidth == 1.0f);
REQUIRE(region.equalizers[0].frequency == 0.0f); REQUIRE(region.equalizers[0].frequency == 0.0f);
REQUIRE(region.equalizers[0].vel2frequency == 0); REQUIRE(region.equalizers[0].vel2frequency == 0);
@ -1385,7 +1392,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.equalizers.size() == 2); REQUIRE(region.equalizers.size() == 2);
REQUIRE(region.equalizers[1].gain == -96.0f); REQUIRE(region.equalizers[1].gain == -96.0f);
// Check defaults // Check defaults
REQUIRE(region.equalizers[1].type == sfz::EqType::kEqPeak); REQUIRE(region.equalizers[1].type == EqType::kEqPeak);
REQUIRE(region.equalizers[1].bandwidth == 1.0f); REQUIRE(region.equalizers[1].bandwidth == 1.0f);
REQUIRE(region.equalizers[1].frequency == 0.0f); REQUIRE(region.equalizers[1].frequency == 0.0f);
REQUIRE(region.equalizers[1].vel2frequency == 0); REQUIRE(region.equalizers[1].vel2frequency == 0);
@ -1397,7 +1404,7 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "eq4_gain", "500" }); region.parseOpcode({ "eq4_gain", "500" });
REQUIRE(region.equalizers.size() == 4); REQUIRE(region.equalizers.size() == 4);
REQUIRE(region.equalizers[2].gain == 0.0f); REQUIRE(region.equalizers[2].gain == 0.0f);
REQUIRE(region.equalizers[3].type == sfz::EqType::kEqPeak); REQUIRE(region.equalizers[3].type == EqType::kEqPeak);
REQUIRE(region.equalizers[3].gain == 96.0f); REQUIRE(region.equalizers[3].gain == 96.0f);
// Check defaults // Check defaults
REQUIRE(region.equalizers[2].bandwidth == 1.0f); REQUIRE(region.equalizers[2].bandwidth == 1.0f);
@ -1419,13 +1426,13 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("EQ types") SECTION("EQ types")
{ {
region.parseOpcode({ "eq1_type", "hshelf" }); region.parseOpcode({ "eq1_type", "hshelf" });
REQUIRE(region.equalizers[0].type == sfz::EqType::kEqHighShelf); REQUIRE(region.equalizers[0].type == EqType::kEqHighShelf);
region.parseOpcode({ "eq1_type", "somethingsomething" }); region.parseOpcode({ "eq1_type", "somethingsomething" });
REQUIRE(region.equalizers[0].type == sfz::EqType::kEqNone); REQUIRE(region.equalizers[0].type == EqType::kEqNone);
region.parseOpcode({ "eq1_type", "lshelf" }); region.parseOpcode({ "eq1_type", "lshelf" });
REQUIRE(region.equalizers[0].type == sfz::EqType::kEqLowShelf); REQUIRE(region.equalizers[0].type == EqType::kEqLowShelf);
region.parseOpcode({ "eq1_type", "peak" }); region.parseOpcode({ "eq1_type", "peak" });
REQUIRE(region.equalizers[0].type == sfz::EqType::kEqPeak); REQUIRE(region.equalizers[0].type == EqType::kEqPeak);
} }
SECTION("EQ parameter dispatch") SECTION("EQ parameter dispatch")
@ -1438,7 +1445,7 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "eq2_freq", "300" }); region.parseOpcode({ "eq2_freq", "300" });
REQUIRE(region.equalizers[1].frequency == 300.0f); REQUIRE(region.equalizers[1].frequency == 300.0f);
region.parseOpcode({ "eq3_type", "lshelf" }); region.parseOpcode({ "eq3_type", "lshelf" });
REQUIRE(region.equalizers[2].type == sfz::EqType::kEqLowShelf); REQUIRE(region.equalizers[2].type == EqType::kEqLowShelf);
region.parseOpcode({ "eq3_vel2gain", "10" }); region.parseOpcode({ "eq3_vel2gain", "10" });
REQUIRE(region.equalizers[2].vel2gain == 10.0f); REQUIRE(region.equalizers[2].vel2gain == 10.0f);
region.parseOpcode({ "eq1_vel2freq", "100" }); region.parseOpcode({ "eq1_vel2freq", "100" });
@ -1455,7 +1462,7 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "eq3_freq_oncc15", "20" }); region.parseOpcode({ "eq3_freq_oncc15", "20" });
REQUIRE(region.equalizers[2].frequencyCC[15] == 20.0f); REQUIRE(region.equalizers[2].frequencyCC[15] == 20.0f);
region.parseOpcode({ "eq1_type", "hshelf" }); region.parseOpcode({ "eq1_type", "hshelf" });
REQUIRE(region.equalizers[0].type == sfz::EqType::kEqHighShelf); REQUIRE(region.equalizers[0].type == EqType::kEqHighShelf);
region.parseOpcode({ "eq2_gaincc123", "2" }); region.parseOpcode({ "eq2_gaincc123", "2" });
REQUIRE(region.equalizers[1].gainCC.contains(123)); REQUIRE(region.equalizers[1].gainCC.contains(123));
REQUIRE(region.equalizers[1].gainCC[123] == 2.0f); REQUIRE(region.equalizers[1].gainCC[123] == 2.0f);
@ -1576,103 +1583,103 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("amplitude_cc") SECTION("amplitude_cc")
{ {
REQUIRE(region.amplitudeCC.empty()); REQUIRE(region.modifiers[Mod::amplitude].empty());
region.parseOpcode({ "amplitude_cc1", "40" }); region.parseOpcode({ "amplitude_cc1", "40" });
REQUIRE(region.amplitudeCC.contains(1)); REQUIRE(region.modifiers[Mod::amplitude].contains(1));
REQUIRE(region.amplitudeCC[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.amplitudeCC.contains(2)); REQUIRE(region.modifiers[Mod::amplitude].contains(2));
REQUIRE(region.amplitudeCC[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.amplitudeCC[17].curve == 18); REQUIRE(region.modifiers[Mod::amplitude][17].curve == 18);
region.parseOpcode({ "amplitude_curvecc17", "15482" }); region.parseOpcode({ "amplitude_curvecc17", "15482" });
REQUIRE(region.amplitudeCC[17].curve == 255); REQUIRE(region.modifiers[Mod::amplitude][17].curve == 255);
region.parseOpcode({ "amplitude_curvecc17", "-2" }); region.parseOpcode({ "amplitude_curvecc17", "-2" });
REQUIRE(region.amplitudeCC[17].curve == 0); REQUIRE(region.modifiers[Mod::amplitude][17].curve == 0);
region.parseOpcode({ "amplitude_smoothcc14", "85" }); region.parseOpcode({ "amplitude_smoothcc14", "85" });
REQUIRE(region.amplitudeCC[14].smooth == 85); REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 85);
region.parseOpcode({ "amplitude_smoothcc14", "15482" }); region.parseOpcode({ "amplitude_smoothcc14", "15482" });
REQUIRE(region.amplitudeCC[14].smooth == 127); REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 100);
region.parseOpcode({ "amplitude_smoothcc14", "-2" }); region.parseOpcode({ "amplitude_smoothcc14", "-2" });
REQUIRE(region.amplitudeCC[14].smooth == 0); REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 0);
region.parseOpcode({ "amplitude_stepcc120", "24" }); region.parseOpcode({ "amplitude_stepcc120", "24" });
REQUIRE(region.amplitudeCC[120].step == 24.0_a); REQUIRE(region.[Mod::amplitude][120].step == 24.0_a);
region.parseOpcode({ "amplitude_stepcc120", "15482" }); region.parseOpcode({ "amplitude_stepcc120", "15482" });
REQUIRE(region.amplitudeCC[120].step == 100.0_a); REQUIRE(region.[Mod::amplitude][120].step == 100.0_a);
region.parseOpcode({ "amplitude_stepcc120", "-2" }); region.parseOpcode({ "amplitude_stepcc120", "-2" });
REQUIRE(region.amplitudeCC[120].step == 0.0f); REQUIRE(region.modifiers[Mod::amplitude][120].step == 0.0f);
} }
SECTION("volume_oncc/gain_cc") SECTION("volume_oncc/gain_cc")
{ {
REQUIRE(region.volumeCC.empty()); REQUIRE(region.modifiers[Mod::volume].empty());
region.parseOpcode({ "gain_cc1", "40" }); region.parseOpcode({ "gain_cc1", "40" });
REQUIRE(region.volumeCC.contains(1)); REQUIRE(region.modifiers[Mod::volume].contains(1));
REQUIRE(region.volumeCC[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.volumeCC.contains(2)); REQUIRE(region.modifiers[Mod::volume].contains(2));
REQUIRE(region.volumeCC[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.volumeCC.contains(4)); REQUIRE(region.modifiers[Mod::volume].contains(4));
REQUIRE(region.volumeCC[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.volumeCC[17].curve == 18); REQUIRE(region.modifiers[Mod::volume][17].curve == 18);
region.parseOpcode({ "volume_curvecc17", "15482" }); region.parseOpcode({ "volume_curvecc17", "15482" });
REQUIRE(region.volumeCC[17].curve == 255); REQUIRE(region.modifiers[Mod::volume][17].curve == 255);
region.parseOpcode({ "volume_curvecc17", "-2" }); region.parseOpcode({ "volume_curvecc17", "-2" });
REQUIRE(region.volumeCC[17].curve == 0); REQUIRE(region.modifiers[Mod::volume][17].curve == 0);
region.parseOpcode({ "volume_smoothcc14", "85" }); region.parseOpcode({ "volume_smoothcc14", "85" });
REQUIRE(region.volumeCC[14].smooth == 85); REQUIRE(region.modifiers[Mod::volume][14].smooth == 85);
region.parseOpcode({ "volume_smoothcc14", "15482" }); region.parseOpcode({ "volume_smoothcc14", "15482" });
REQUIRE(region.volumeCC[14].smooth == 127); REQUIRE(region.modifiers[Mod::volume][14].smooth == 100);
region.parseOpcode({ "volume_smoothcc14", "-2" }); region.parseOpcode({ "volume_smoothcc14", "-2" });
REQUIRE(region.volumeCC[14].smooth == 0); REQUIRE(region.modifiers[Mod::volume][14].smooth == 0);
region.parseOpcode({ "volume_stepcc120", "24" }); region.parseOpcode({ "volume_stepcc120", "24" });
REQUIRE(region.volumeCC[120].step == 24.0f); REQUIRE(region.modifiers[Mod::volume][120].step == 24.0f);
region.parseOpcode({ "volume_stepcc120", "15482" }); region.parseOpcode({ "volume_stepcc120", "15482" });
REQUIRE(region.volumeCC[120].step == 144.0f); REQUIRE(region.modifiers[Mod::volume][120].step == 144.0f);
region.parseOpcode({ "volume_stepcc120", "-2" }); region.parseOpcode({ "volume_stepcc120", "-2" });
REQUIRE(region.volumeCC[120].step == 0.0f); REQUIRE(region.modifiers[Mod::volume][120].step == 0.0f);
} }
SECTION("tune_cc/pitch_cc") SECTION("tune_cc/pitch_cc")
{ {
REQUIRE(region.tuneCC.empty()); REQUIRE(region.modifiers[Mod::pitch].empty());
region.parseOpcode({ "pitch_cc1", "40" }); region.parseOpcode({ "pitch_cc1", "40" });
REQUIRE(region.tuneCC.contains(1)); REQUIRE(region.modifiers[Mod::pitch].contains(1));
REQUIRE(region.tuneCC[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.tuneCC.contains(2)); REQUIRE(region.modifiers[Mod::pitch].contains(2));
REQUIRE(region.tuneCC[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.tuneCC.contains(4)); REQUIRE(region.modifiers[Mod::pitch].contains(4));
REQUIRE(region.tuneCC[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.tuneCC[17].curve == 18); REQUIRE(region.modifiers[Mod::pitch][17].curve == 18);
region.parseOpcode({ "pitch_curvecc17", "15482" }); region.parseOpcode({ "pitch_curvecc17", "15482" });
REQUIRE(region.tuneCC[17].curve == 255); REQUIRE(region.modifiers[Mod::pitch][17].curve == 255);
region.parseOpcode({ "tune_curvecc17", "-2" }); region.parseOpcode({ "tune_curvecc17", "-2" });
REQUIRE(region.tuneCC[17].curve == 0); REQUIRE(region.modifiers[Mod::pitch][17].curve == 0);
region.parseOpcode({ "pitch_smoothcc14", "85" }); region.parseOpcode({ "pitch_smoothcc14", "85" });
REQUIRE(region.tuneCC[14].smooth == 85); REQUIRE(region.modifiers[Mod::pitch][14].smooth == 85);
region.parseOpcode({ "tune_smoothcc14", "15482" }); region.parseOpcode({ "tune_smoothcc14", "15482" });
REQUIRE(region.tuneCC[14].smooth == 127); REQUIRE(region.modifiers[Mod::pitch][14].smooth == 100);
region.parseOpcode({ "pitch_smoothcc14", "-2" }); region.parseOpcode({ "pitch_smoothcc14", "-2" });
REQUIRE(region.tuneCC[14].smooth == 0); REQUIRE(region.modifiers[Mod::pitch][14].smooth == 0);
region.parseOpcode({ "tune_stepcc120", "24" }); region.parseOpcode({ "tune_stepcc120", "24" });
REQUIRE(region.tuneCC[120].step == 24.0f); REQUIRE(region.modifiers[Mod::pitch][120].step == 24.0f);
region.parseOpcode({ "pitch_stepcc120", "15482" }); region.parseOpcode({ "pitch_stepcc120", "15482" });
REQUIRE(region.tuneCC[120].step == 9600.0f); REQUIRE(region.modifiers[Mod::pitch][120].step == 9600.0f);
region.parseOpcode({ "tune_stepcc120", "-2" }); region.parseOpcode({ "tune_stepcc120", "-2" });
REQUIRE(region.tuneCC[120].step == 0.0f); REQUIRE(region.modifiers[Mod::pitch][120].step == 0.0f);
} }
} }
// Specific region bugs // Specific region bugs
TEST_CASE("[Region] Non-conforming floating point values in integer opcodes") TEST_CASE("[Region] Non-conforming floating point values in integer opcodes")
{ {
sfz::MidiState midiState; MidiState midiState;
sfz::Region region { 0, midiState }; Region region { 0, midiState };
region.parseOpcode({ "offset", "2014.5" }); region.parseOpcode({ "offset", "2014.5" });
REQUIRE(region.offset == 2014); REQUIRE(region.offset == 2014);
region.parseOpcode({ "pitch_keytrack", "-2.1" }); region.parseOpcode({ "pitch_keytrack", "-2.1" });

View file

@ -5,6 +5,7 @@
// 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 "sfizz/OnePoleFilter.h" #include "sfizz/OnePoleFilter.h"
#include "sfizz/Smoothers.h"
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
// #include "cnpy.h" // #include "cnpy.h"
// #include "ghc/fs_std.hpp" // #include "ghc/fs_std.hpp"
@ -16,14 +17,13 @@
using namespace Catch::literals; using namespace Catch::literals;
template <class Type, size_t N> template <class Type, size_t N>
inline bool approxEqual(const std::array<Type, N> &lhs, const std::array<Type, N> &rhs) inline bool approxEqual(const std::array<Type, N>& lhs, const std::array<Type, N>& rhs, Type epsilon = 1e-3)
{ {
if (lhs.size() != rhs.size()) if (lhs.size() != rhs.size())
return false; return false;
for (size_t i = 0; i < rhs.size(); ++i) for (size_t i = 0; i < rhs.size(); ++i)
if (lhs[i] != Approx(rhs[i]).epsilon(1e-3)) if (lhs[i] != Approx(rhs[i]).epsilon(epsilon)) {
{
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n'; std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
return false; return false;
} }
@ -74,7 +74,8 @@ constexpr std::array<float, 64> floatInput01 = {
1.1666758495956464f, -0.04382336233428379f, 0.43819763320865857f, 0.1740610608056625f, 1.1666758495956464f, -0.04382336233428379f, 0.43819763320865857f, 0.1740610608056625f,
0.30473671729007396f, -0.4065881153810866f, -0.9784770671900811f, -0.17674381857142665f, 0.30473671729007396f, -0.4065881153810866f, -0.9784770671900811f, -0.17674381857142665f,
0.3493213284003123f, -1.2540491577273034f, 1.2597719140599792f, 0.4198847510851298f, 0.3493213284003123f, -1.2540491577273034f, 1.2597719140599792f, 0.4198847510851298f,
0.9612865621570132f, -1.5614809857797225f, -0.31416474166626646f, -1.4741449502960542f }; 0.9612865621570132f, -1.5614809857797225f, -0.31416474166626646f, -1.4741449502960542f
};
constexpr std::array<float, 64> floatOutputLow01 = { constexpr std::array<float, 64> floatOutputLow01 = {
0.06567783171600353f, 0.18655945645590016f, 0.15368937959051f, 0.05054483866245487f, 0.06567783171600353f, 0.18655945645590016f, 0.15368937959051f, 0.05054483866245487f,
@ -92,7 +93,8 @@ constexpr std::array<float, 64> floatOutputLow01 = {
0.09460636244101792f, 0.17948270447550216f, 0.18270169192308128f, 0.20514308375655024f, 0.09460636244101792f, 0.17948270447550216f, 0.18270169192308128f, 0.20514308375655024f,
0.21137141199133533f, 0.16368102816645502f, 0.008005824629720673f, -0.0984698603721838f, 0.21137141199133533f, 0.16368102816645502f, 0.008005824629720673f, -0.0984698603721838f,
-0.06487738486552441f, -0.13532948119242824f, -0.11020387039992532f, 0.06252925741325283f, -0.06487738486552441f, -0.13532948119242824f, -0.11020387039992532f, 0.06252925741325283f,
0.1767213299964926f, 0.0900270496677931f, -0.09685475276689556f, -0.24181840607858007f }; 0.1767213299964926f, 0.0900270496677931f, -0.09685475276689556f, -0.24181840607858007f
};
constexpr std::array<float, 64> floatOutputHigh01 = { constexpr std::array<float, 64> floatOutputHigh01 = {
0.6567783171600353f, 0.5520379302389311f, -0.8807386988928331f, -0.1507067103877182f, 0.6567783171600353f, 0.5520379302389311f, -0.8807386988928331f, -0.1507067103877182f,
@ -110,7 +112,8 @@ constexpr std::array<float, 64> floatOutputHigh01 = {
1.0720694871546286f, -0.22330606680978596f, 0.2554959412855773f, -0.031082022950887744f, 1.0720694871546286f, -0.22330606680978596f, 0.2554959412855773f, -0.031082022950887744f,
0.09336530529873863f, -0.5702691435475415f, -0.9864828918198018f, -0.07827395819924285f, 0.09336530529873863f, -0.5702691435475415f, -0.9864828918198018f, -0.07827395819924285f,
0.41419871326583674f, -1.1187196765348753f, 1.3699757844599045f, 0.35735549367187697f, 0.41419871326583674f, -1.1187196765348753f, 1.3699757844599045f, 0.35735549367187697f,
0.7845652321605207f, -1.6515080354475156f, -0.2173099888993709f, -1.2323265442174742f }; 0.7845652321605207f, -1.6515080354475156f, -0.2173099888993709f, -1.2323265442174742f
};
constexpr std::array<float, 64> floatInput05 = { constexpr std::array<float, 64> floatInput05 = {
-0.8247415510202276f, -1.0299159073255513f, 0.7689727513393745f, -0.023063681797826918f, -0.8247415510202276f, -1.0299159073255513f, 0.7689727513393745f, -0.023063681797826918f,
@ -128,7 +131,8 @@ constexpr std::array<float, 64> floatInput05 = {
-0.6137810912250902f, -0.7039958189789415f, -1.33840097232278f, 0.4786946763969468f, -0.6137810912250902f, -0.7039958189789415f, -1.33840097232278f, 0.4786946763969468f,
-0.46464793721558995f, -1.7121509287301122f, 0.7887828546774234f, -0.902172963851904f, -0.46464793721558995f, -1.7121509287301122f, 0.7887828546774234f, -0.902172963851904f,
-0.2591368523894675f, -0.9510361177022718f, 0.5739217219088085f, -0.25730420306720403f, -0.2591368523894675f, -0.9510361177022718f, 0.5739217219088085f, -0.25730420306720403f,
0.41740839680521646f, -2.0979181310103074f, 1.1494564006889283f, 0.5059893282486726f }; 0.41740839680521646f, -2.0979181310103074f, 1.1494564006889283f, 0.5059893282486726f
};
constexpr std::array<float, 64> floatOutputLow05 = { constexpr std::array<float, 64> floatOutputLow05 = {
-0.27491385034007587f, -0.7098571028952849f, -0.32360008629382064f, 0.14076966108257558f, -0.27491385034007587f, -0.7098571028952849f, -0.32360008629382064f, 0.14076966108257558f,
@ -146,7 +150,8 @@ constexpr std::array<float, 64> floatOutputLow05 = {
0.02703177262292994f, -0.4302483791937005f, -0.8242150568318073f, -0.5613071175858801f, 0.02703177262292994f, -0.4302483791937005f, -0.8242150568318073f, -0.5613071175858801f,
-0.18242012613484115f, -0.7864063306935144f, -0.5699248015820677f, -0.22777163691884944f, -0.18242012613484115f, -0.7864063306935144f, -0.5699248015820677f, -0.22777163691884944f,
-0.46302715105340697f, -0.5577333737150487f, -0.311615923169504f, 0.001667198557366828f, -0.46302715105340697f, -0.5577333737150487f, -0.311615923169504f, 0.001667198557366828f,
0.05392379743179307f, -0.5421953122577658f, -0.49688568085971485f, 0.3861866826926287f }; 0.05392379743179307f, -0.5421953122577658f, -0.49688568085971485f, 0.3861866826926287f
};
constexpr std::array<float, 64> floatOutputHigh05 = { constexpr std::array<float, 64> floatOutputHigh05 = {
-0.5498277006801517f, -0.32005880443026635f, 1.0925728376331951f, -0.1638333428804025f, -0.5498277006801517f, -0.32005880443026635f, 1.0925728376331951f, -0.1638333428804025f,
@ -164,7 +169,8 @@ constexpr std::array<float, 64> floatOutputHigh05 = {
-0.6408128638480202f, -0.27374743978524096f, -0.5141859154909728f, 1.040001793982827f, -0.6408128638480202f, -0.27374743978524096f, -0.5141859154909728f, 1.040001793982827f,
-0.2822278110807488f, -0.9257445980365978f, 1.358707656259491f, -0.6744013269330547f, -0.2822278110807488f, -0.9257445980365978f, 1.358707656259491f, -0.6744013269330547f,
0.20389029866393948f, -0.3933027439872231f, 0.8855376450783126f, -0.2589714016245709f, 0.20389029866393948f, -0.3933027439872231f, 0.8855376450783126f, -0.2589714016245709f,
0.36348459937342337f, -1.5557228187525416f, 1.6463420815486431f, 0.11980264555604392f }; 0.36348459937342337f, -1.5557228187525416f, 1.6463420815486431f, 0.11980264555604392f
};
constexpr std::array<float, 64> floatInput09 = { constexpr std::array<float, 64> floatInput09 = {
-0.9629663717342508f, 1.054078826032172f, -1.0644939081323097f, -0.05328934531304567f, -0.9629663717342508f, 1.054078826032172f, -1.0644939081323097f, -0.05328934531304567f,
@ -182,7 +188,8 @@ constexpr std::array<float, 64> floatInput09 = {
0.7517403248613556f, 1.1194691465807607f, -1.1160170942539855f, -1.0010374555669668f, 0.7517403248613556f, 1.1194691465807607f, -1.1160170942539855f, -1.0010374555669668f,
2.1609909686692763f, -0.07213993925443297f, -0.5083174992310037f, -0.7489925703250175f, 2.1609909686692763f, -0.07213993925443297f, -0.5083174992310037f, -0.7489925703250175f,
0.5119124853257149f, -0.33950253799120345f, -0.26764774112191847f, 0.10271208568438035f, 0.5119124853257149f, -0.33950253799120345f, -0.26764774112191847f, 0.10271208568438035f,
-0.09893862035889031f, -0.4154625911342657f, 0.11272544601558693f, -0.6895075000870634f }; -0.09893862035889031f, -0.4154625911342657f, 0.11272544601558693f, -0.6895075000870634f
};
constexpr std::array<float, 64> floatOutputLow09 = { constexpr std::array<float, 64> floatOutputLow09 = {
-0.45614196555832937f, 0.01915105911173487f, -0.003925509462605503f, -0.5296828837089897f, -0.45614196555832937f, 0.01915105911173487f, -0.003925509462605503f, -0.5296828837089897f,
@ -200,7 +207,8 @@ constexpr std::array<float, 64> floatOutputLow09 = {
1.3518864464016498f, 0.9575142994410892f, 0.0520306721253716f, -1.0000768566454319f, 1.3518864464016498f, 0.9575142994410892f, 0.0520306721253716f, -1.0000768566454319f,
0.496816040067124f, 1.015603963410564f, -0.22150068331359823f, -0.6072258583851468f, 0.496816040067124f, 1.015603963410564f, -0.22150068331359823f, -0.6072258583851468f,
-0.14426034859888792f, 0.07407521986377441f, -0.2836988048502276f, -0.09305893177831953f, -0.14426034859888792f, 0.07407521986377441f, -0.2836988048502276f, -0.09305893177831953f,
-0.003110407570995219f, -0.24382743742154736f, -0.15623482860471877f, -0.28143543764463197f }; -0.003110407570995219f, -0.24382743742154736f, -0.15623482860471877f, -0.28143543764463197f
};
constexpr std::array<float, 64> floatOutputHigh09 = { constexpr std::array<float, 64> floatOutputHigh09 = {
-0.5068244061759215f, 1.034927766920437f, -1.0605683986697043f, 0.47639353839594406f, -0.5068244061759215f, 1.034927766920437f, -1.0605683986697043f, 0.47639353839594406f,
@ -218,7 +226,8 @@ constexpr std::array<float, 64> floatOutputHigh09 = {
-0.6001461215402942f, 0.16195484713967145f, -1.1680477663793571f, -0.0009605989215348831f, -0.6001461215402942f, 0.16195484713967145f, -1.1680477663793571f, -0.0009605989215348831f,
1.6641749286021523f, -1.087743902664997f, -0.28681681591740543f, -0.14176671193987078f, 1.6641749286021523f, -1.087743902664997f, -0.28681681591740543f, -0.14176671193987078f,
0.6561728339246028f, -0.41357775785497786f, 0.016051063728309112f, 0.19577101746269987f, 0.6561728339246028f, -0.41357775785497786f, 0.016051063728309112f, 0.19577101746269987f,
-0.09582821278789509f, -0.17163515371271834f, 0.2689602746203057f, -0.40807206244243144f }; -0.09582821278789509f, -0.17163515371271834f, 0.2689602746203057f, -0.40807206244243144f
};
constexpr std::array<double, 64> doubleInput01 = { constexpr std::array<double, 64> doubleInput01 = {
0.7224561488760388, 0.7385973866948313, -0.7270493193023231, -0.10016187172526334, 0.7224561488760388, 0.7385973866948313, -0.7270493193023231, -0.10016187172526334,
@ -236,7 +245,8 @@ constexpr std::array<double, 64> doubleInput01 = {
1.1666758495956464, -0.04382336233428379, 0.43819763320865857, 0.1740610608056625, 1.1666758495956464, -0.04382336233428379, 0.43819763320865857, 0.1740610608056625,
0.30473671729007396, -0.4065881153810866, -0.9784770671900811, -0.17674381857142665, 0.30473671729007396, -0.4065881153810866, -0.9784770671900811, -0.17674381857142665,
0.3493213284003123, -1.2540491577273034, 1.2597719140599792, 0.4198847510851298, 0.3493213284003123, -1.2540491577273034, 1.2597719140599792, 0.4198847510851298,
0.9612865621570132, -1.5614809857797225, -0.31416474166626646, -1.4741449502960542 }; 0.9612865621570132, -1.5614809857797225, -0.31416474166626646, -1.4741449502960542
};
constexpr std::array<double, 64> doubleOutputLow01 = { constexpr std::array<double, 64> doubleOutputLow01 = {
0.06567783171600353, 0.18655945645590016, 0.15368937959051, 0.05054483866245487, 0.06567783171600353, 0.18655945645590016, 0.15368937959051, 0.05054483866245487,
@ -254,7 +264,8 @@ constexpr std::array<double, 64> doubleOutputLow01 = {
0.09460636244101792, 0.17948270447550216, 0.18270169192308128, 0.20514308375655024, 0.09460636244101792, 0.17948270447550216, 0.18270169192308128, 0.20514308375655024,
0.21137141199133533, 0.16368102816645502, 0.008005824629720673, -0.0984698603721838, 0.21137141199133533, 0.16368102816645502, 0.008005824629720673, -0.0984698603721838,
-0.06487738486552441, -0.13532948119242824, -0.11020387039992532, 0.06252925741325283, -0.06487738486552441, -0.13532948119242824, -0.11020387039992532, 0.06252925741325283,
0.1767213299964926, 0.0900270496677931, -0.09685475276689556, -0.24181840607858007 }; 0.1767213299964926, 0.0900270496677931, -0.09685475276689556, -0.24181840607858007
};
constexpr std::array<double, 64> doubleOutputHigh01 = { constexpr std::array<double, 64> doubleOutputHigh01 = {
0.6567783171600353, 0.5520379302389311, -0.8807386988928331, -0.1507067103877182, 0.6567783171600353, 0.5520379302389311, -0.8807386988928331, -0.1507067103877182,
@ -272,7 +283,8 @@ constexpr std::array<double, 64> doubleOutputHigh01 = {
1.0720694871546286, -0.22330606680978596, 0.2554959412855773, -0.031082022950887744, 1.0720694871546286, -0.22330606680978596, 0.2554959412855773, -0.031082022950887744,
0.09336530529873863, -0.5702691435475415, -0.9864828918198018, -0.07827395819924285, 0.09336530529873863, -0.5702691435475415, -0.9864828918198018, -0.07827395819924285,
0.41419871326583674, -1.1187196765348753, 1.3699757844599045, 0.35735549367187697, 0.41419871326583674, -1.1187196765348753, 1.3699757844599045, 0.35735549367187697,
0.7845652321605207, -1.6515080354475156, -0.2173099888993709, -1.2323265442174742 }; 0.7845652321605207, -1.6515080354475156, -0.2173099888993709, -1.2323265442174742
};
constexpr std::array<double, 64> doubleInput05 = { constexpr std::array<double, 64> doubleInput05 = {
-0.8247415510202276, -1.0299159073255513, 0.7689727513393745, -0.023063681797826918, -0.8247415510202276, -1.0299159073255513, 0.7689727513393745, -0.023063681797826918,
@ -290,7 +302,8 @@ constexpr std::array<double, 64> doubleInput05 = {
-0.6137810912250902, -0.7039958189789415, -1.33840097232278, 0.4786946763969468, -0.6137810912250902, -0.7039958189789415, -1.33840097232278, 0.4786946763969468,
-0.46464793721558995, -1.7121509287301122, 0.7887828546774234, -0.902172963851904, -0.46464793721558995, -1.7121509287301122, 0.7887828546774234, -0.902172963851904,
-0.2591368523894675, -0.9510361177022718, 0.5739217219088085, -0.25730420306720403, -0.2591368523894675, -0.9510361177022718, 0.5739217219088085, -0.25730420306720403,
0.41740839680521646, -2.0979181310103074, 1.1494564006889283, 0.5059893282486726 }; 0.41740839680521646, -2.0979181310103074, 1.1494564006889283, 0.5059893282486726
};
constexpr std::array<double, 64> doubleOutputLow05 = { constexpr std::array<double, 64> doubleOutputLow05 = {
-0.27491385034007587, -0.7098571028952849, -0.32360008629382064, 0.14076966108257558, -0.27491385034007587, -0.7098571028952849, -0.32360008629382064, 0.14076966108257558,
@ -308,7 +321,8 @@ constexpr std::array<double, 64> doubleOutputLow05 = {
0.02703177262292994, -0.4302483791937005, -0.8242150568318073, -0.5613071175858801, 0.02703177262292994, -0.4302483791937005, -0.8242150568318073, -0.5613071175858801,
-0.18242012613484115, -0.7864063306935144, -0.5699248015820677, -0.22777163691884944, -0.18242012613484115, -0.7864063306935144, -0.5699248015820677, -0.22777163691884944,
-0.46302715105340697, -0.5577333737150487, -0.311615923169504, 0.001667198557366828, -0.46302715105340697, -0.5577333737150487, -0.311615923169504, 0.001667198557366828,
0.05392379743179307, -0.5421953122577658, -0.49688568085971485, 0.3861866826926287 }; 0.05392379743179307, -0.5421953122577658, -0.49688568085971485, 0.3861866826926287
};
constexpr std::array<double, 64> doubleOutputHigh05 = { constexpr std::array<double, 64> doubleOutputHigh05 = {
-0.5498277006801517, -0.32005880443026635, 1.0925728376331951, -0.1638333428804025, -0.5498277006801517, -0.32005880443026635, 1.0925728376331951, -0.1638333428804025,
@ -326,7 +340,8 @@ constexpr std::array<double, 64> doubleOutputHigh05 = {
-0.6408128638480202, -0.27374743978524096, -0.5141859154909728, 1.040001793982827, -0.6408128638480202, -0.27374743978524096, -0.5141859154909728, 1.040001793982827,
-0.2822278110807488, -0.9257445980365978, 1.358707656259491, -0.6744013269330547, -0.2822278110807488, -0.9257445980365978, 1.358707656259491, -0.6744013269330547,
0.20389029866393948, -0.3933027439872231, 0.8855376450783126, -0.2589714016245709, 0.20389029866393948, -0.3933027439872231, 0.8855376450783126, -0.2589714016245709,
0.36348459937342337, -1.5557228187525416, 1.6463420815486431, 0.11980264555604392 }; 0.36348459937342337, -1.5557228187525416, 1.6463420815486431, 0.11980264555604392
};
constexpr std::array<double, 64> doubleInput09 = { constexpr std::array<double, 64> doubleInput09 = {
-0.9629663717342508, 1.054078826032172, -1.0644939081323097, -0.05328934531304567, -0.9629663717342508, 1.054078826032172, -1.0644939081323097, -0.05328934531304567,
@ -344,7 +359,8 @@ constexpr std::array<double, 64> doubleInput09 = {
0.7517403248613556, 1.1194691465807607, -1.1160170942539855, -1.0010374555669668, 0.7517403248613556, 1.1194691465807607, -1.1160170942539855, -1.0010374555669668,
2.1609909686692763, -0.07213993925443297, -0.5083174992310037, -0.7489925703250175, 2.1609909686692763, -0.07213993925443297, -0.5083174992310037, -0.7489925703250175,
0.5119124853257149, -0.33950253799120345, -0.26764774112191847, 0.10271208568438035, 0.5119124853257149, -0.33950253799120345, -0.26764774112191847, 0.10271208568438035,
-0.09893862035889031, -0.4154625911342657, 0.11272544601558693, -0.6895075000870634 }; -0.09893862035889031, -0.4154625911342657, 0.11272544601558693, -0.6895075000870634
};
constexpr std::array<double, 64> doubleOutputLow09 = { constexpr std::array<double, 64> doubleOutputLow09 = {
-0.45614196555832937, 0.01915105911173487, -0.003925509462605503, -0.5296828837089897, -0.45614196555832937, 0.01915105911173487, -0.003925509462605503, -0.5296828837089897,
@ -362,7 +378,8 @@ constexpr std::array<double, 64> doubleOutputLow09 = {
1.3518864464016498, 0.9575142994410892, 0.0520306721253716, -1.0000768566454319, 1.3518864464016498, 0.9575142994410892, 0.0520306721253716, -1.0000768566454319,
0.496816040067124, 1.015603963410564, -0.22150068331359823, -0.6072258583851468, 0.496816040067124, 1.015603963410564, -0.22150068331359823, -0.6072258583851468,
-0.14426034859888792, 0.07407521986377441, -0.2836988048502276, -0.09305893177831953, -0.14426034859888792, 0.07407521986377441, -0.2836988048502276, -0.09305893177831953,
-0.003110407570995219, -0.24382743742154736, -0.15623482860471877, -0.28143543764463197 }; -0.003110407570995219, -0.24382743742154736, -0.15623482860471877, -0.28143543764463197
};
constexpr std::array<double, 64> doubleOutputHigh09 = { constexpr std::array<double, 64> doubleOutputHigh09 = {
-0.5068244061759215, 1.034927766920437, -1.0605683986697043, 0.47639353839594406, -0.5068244061759215, 1.034927766920437, -1.0605683986697043, 0.47639353839594406,
@ -380,7 +397,8 @@ constexpr std::array<double, 64> doubleOutputHigh09 = {
-0.6001461215402942, 0.16195484713967145, -1.1680477663793571, -0.0009605989215348831, -0.6001461215402942, 0.16195484713967145, -1.1680477663793571, -0.0009605989215348831,
1.6641749286021523, -1.087743902664997, -0.28681681591740543, -0.14176671193987078, 1.6641749286021523, -1.087743902664997, -0.28681681591740543, -0.14176671193987078,
0.6561728339246028, -0.41357775785497786, 0.016051063728309112, 0.19577101746269987, 0.6561728339246028, -0.41357775785497786, 0.016051063728309112, 0.19577101746269987,
-0.09582821278789509, -0.17163515371271834, 0.2689602746203057, -0.40807206244243144 }; -0.09582821278789509, -0.17163515371271834, 0.2689602746203057, -0.40807206244243144
};
TEST_CASE("[OnePoleFilter] Tests") TEST_CASE("[OnePoleFilter] Tests")
{ {