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

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

View file

@ -7,6 +7,7 @@
#include "SIMDHelpers.h"
#include "OnePoleFilter.h"
#include "SfzFilter.h"
#include "SfzHelpers.h"
#include "ScopedFTZ.h"
#include "SfzHelpers.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)
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)
sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES})

View file

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

View file

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

View file

@ -82,8 +82,9 @@ namespace Default
// Region logic: MIDI conditions
constexpr Range<uint8_t> channelRange { 1, 16 };
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 float smoothTauPerStep { 3e-3 };
constexpr Range<uint8_t> curveCCRange { 0, 255 };
constexpr Range<uint16_t> ccNumberRange { 0, config::numCCs };
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 bendDown { -200 };
constexpr int bendStep { 1 };
constexpr uint8_t bendSmooth { 0 };
// Envelope generators
constexpr float attack { 0 };

View file

@ -254,6 +254,11 @@ constexpr Type sqrtTwo() { return static_cast<Type>(1.41421356237309504880168872
template <class Type>
constexpr Type sqrtTwoInv() { return static_cast<Type>(0.707106781186547524400844362104849039284835937688474036588); };
constexpr unsigned int mask(int x)
{
return (1U << x) - 1;
}
/**
* @brief lround for positive values
* 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__))
class ScopedRoundingMode {
public:
ScopedRoundingMode() = delete;

View file

@ -2,12 +2,11 @@
#include "Range.h"
#include "Defaults.h"
#include "SfzHelpers.h"
#include "Modifiers.h"
#include "Resources.h"
#include "absl/types/span.h"
namespace sfz {
/**
* @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);
break;
case_any_ccN("volume"): // also gain
processGenericCc(opcode, Default::volumeCCRange, &volumeCC);
processGenericCc(opcode, Default::volumeCCRange, &modifiers[Mod::volume]);
break;
case hash("amplitude"):
if (auto value = readOpcode(opcode.value, Default::amplitudeRange))
amplitude = normalizePercents(*value);
break;
case_any_ccN("amplitude"):
processGenericCc(opcode, Default::amplitudeRange, &amplitudeCC);
processGenericCc(opcode, Default::amplitudeRange, &modifiers[Mod::amplitude]);
break;
case hash("pan"):
if (auto value = readOpcode(opcode.value, Default::panRange))
pan = normalizePercents(*value);
break;
case_any_ccN("pan"):
processGenericCc(opcode, Default::panCCRange, &panCC);
processGenericCc(opcode, Default::panCCRange, &modifiers[Mod::pan]);
break;
case hash("position"):
if (auto value = readOpcode(opcode.value, Default::positionRange))
position = normalizePercents(*value);
break;
case_any_ccN("position"):
processGenericCc(opcode, Default::positionCCRange, &positionCC);
processGenericCc(opcode, Default::positionCCRange, &modifiers[Mod::position]);
break;
case hash("width"):
if (auto value = readOpcode(opcode.value, Default::widthRange))
width = normalizePercents(*value);
break;
case_any_ccN("width"):
processGenericCc(opcode, Default::widthCCRange, &widthCC);
processGenericCc(opcode, Default::widthCCRange, &modifiers[Mod::width]);
break;
case hash("amp_keycenter"):
setValueFromOpcode(opcode, ampKeycenter, Default::keyRange);
@ -742,7 +742,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
setValueFromOpcode(opcode, tune, Default::tuneRange);
break;
case_any_ccN("pitch"): // also tune
processGenericCc(opcode, Default::tuneCCRange, &tuneCC);
processGenericCc(opcode, Default::tuneCCRange, &modifiers[Mod::pitch]);
break;
case hash("bend_up"): // also bendup
setValueFromOpcode(opcode, bendUp, Default::bendBoundRange);
@ -753,6 +753,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
case hash("bend_step"):
setValueFromOpcode(opcode, bendStep, Default::bendStepRange);
break;
case hash("bend_smooth"):
setValueFromOpcode(opcode, bendSmooth, Default::smoothCCRange);
break;
// Amplitude Envelope
case hash("ampeg_attack"):
@ -1219,3 +1222,9 @@ float sfz::Region::getGainToEffectBus(unsigned number) const noexcept
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 "FileId.h"
#include "NumericId.h"
#include "Modifiers.h"
#include "absl/types/optional.h"
#include <bitset>
#include <string>
@ -195,6 +196,14 @@ struct Region {
* @return float
*/
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
*
@ -309,11 +318,6 @@ struct Region {
float pan { normalizePercents(Default::pan) }; // pan
float width { normalizePercents(Default::width) }; // width
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
float ampKeytrack { Default::ampKeytrack }; // amp_keytrack
float ampVeltrack { Default::ampVeltrack }; // amp_keytrack
@ -342,10 +346,10 @@ struct Region {
int pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack
int transpose { Default::transpose }; // transpose
int tune { Default::tune }; // tune
CCMap<Modifier> tuneCC { {} };
int bendUp { Default::bendUp };
int bendDown { Default::bendDown };
int bendStep { Default::bendStep };
uint8_t bendSmooth { Default::bendSmooth };
// Envelopes
EGDescription amplitudeEG;
@ -357,6 +361,9 @@ struct Region {
// Effects
std::vector<float> gainToEffect;
// Modifiers
ModifierArray<CCMap<Modifier>> modifiers;
private:
const MidiState& midiState;
bool keySwitched { true };

View file

@ -17,8 +17,7 @@
#include "absl/meta/type_traits.h"
#include "Defaults.h"
namespace sfz
{
namespace sfz {
using CCNamePair = std::pair<uint16_t, std::string>;
using NoteNamePair = std::pair<uint8_t, std::string>;
@ -32,14 +31,6 @@ struct CCData {
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>
struct CCDataComparator {
bool operator()(const CCData<ValueType>& ccData, const int& cc)
@ -160,7 +151,6 @@ constexpr float normalizeVelocity(T velocity)
return normalize7Bits(velocity);
}
/**
* @brief Normalize a percentage between 0 and 1
*
@ -212,6 +202,12 @@ inline CXX14_CONSTEXPR Type vaGain(Type cutoff, Type sampleRate)
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
* 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);
} // 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 "Macros.h"
#include "MidiState.h"
#include "ModifierHelpers.h"
#include "ScopedFTZ.h"
#include "StringViewHelpers.h"
#include "pugixml.hpp"
@ -374,6 +375,7 @@ void sfz::Synth::finalizeSfzLoad()
size_t maxFilters { 0 };
size_t maxEQs { 0 };
ModifierArray<size_t> maxModifiers { 0 };
while (currentRegionIndex < currentRegionCount) {
auto region = regions[currentRegionIndex].get();
@ -480,6 +482,8 @@ void sfz::Synth::finalizeSfzLoad()
region->registerTempo(2.0f);
maxFilters = max(maxFilters, region->filters.size());
maxEQs = max(maxEQs, region->equalizers.size());
for (const auto& mod : allModifiers)
maxModifiers[mod] = max(maxModifiers[mod], region->modifiers[mod].size());
++currentRegionIndex;
}
@ -490,6 +494,7 @@ void sfz::Synth::finalizeSfzLoad()
for (auto& voice : voices) {
voice->setMaxFiltersPerVoice(maxFilters);
voice->setMaxEQsPerVoice(maxEQs);
voice->prepareSmoothers(maxModifiers);
}
}

View file

@ -24,6 +24,8 @@ sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources)
for (WavetableOscillator& osc : waveOscillators)
osc.init(sampleRate);
gainSmoother.setSmoothing(config::gainSmoothing, sampleRate);
for (auto & filter : channelEnvelopeFilters)
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();
if (triggerType != TriggerType::CC)
baseGain *= region->getNoteGain(number, value);
gainSmoother.reset();
// Check that we can handle the number of filters; filters should be cleared here
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);
baseFrequency = resources.tuning.getFrequencyOfKey(number);
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);
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
@ -206,6 +228,7 @@ void sfz::Voice::registerTempo(int delay, float secondsPerQuarter) noexcept
void sfz::Voice::setSampleRate(float sampleRate) noexcept
{
this->sampleRate = sampleRate;
gainSmoother.setSmoothing(config::gainSmoothing, sampleRate);
for (auto & filter : channelEnvelopeFilters)
filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate));
@ -283,10 +306,11 @@ void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
// Amplitude envelope
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>);
smoother.process(*tempSpan, *tempSpan);
applyGain<float>(*tempSpan, modulationSpan);
}
});
// Crossfade envelopes
for (const auto& mod : region->crossfadeCCInRange) {
@ -306,12 +330,16 @@ void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
// Volume envelope
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) {
return db2mag(x);
});
smoother.process(*tempSpan, *tempSpan);
applyGain<float>(*tempSpan, modulationSpan);
}
});
// Smooth the gain transitions
gainSmoother.process(modulationSpan, modulationSpan);
}
void sfz::Voice::ampStageMono(AudioSpan<float> buffer) noexcept
@ -360,10 +388,11 @@ void sfz::Voice::panStageMono(AudioSpan<float> buffer) noexcept
// Apply panning
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>);
smoother.process(*tempSpan, *tempSpan);
add<float>(*tempSpan, *modulationSpan);
}
});
pan(*modulationSpan, leftBuffer, rightBuffer);
}
@ -381,25 +410,28 @@ void sfz::Voice::panStageStereo(AudioSpan<float> buffer) noexcept
// Apply panning
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>);
smoother.process(*tempSpan, *tempSpan);
add<float>(*tempSpan, *modulationSpan);
}
});
pan(*modulationSpan, leftBuffer, rightBuffer);
// Apply the width/position process
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>);
smoother.process(*tempSpan, *tempSpan);
add<float>(*tempSpan, *modulationSpan);
}
});
width(*modulationSpan, leftBuffer, rightBuffer);
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>);
smoother.process(*tempSpan, *tempSpan);
add<float>(*tempSpan, *modulationSpan);
}
});
pan(*modulationSpan, leftBuffer, rightBuffer);
}
@ -452,30 +484,13 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
auto source = currentPromise->getData();
auto jumps = resources.bufferPool.getBuffer(numSamples);
auto bends = resources.bufferPool.getBuffer(numSamples);
auto coeffs = resources.bufferPool.getBuffer(numSamples);
auto indices = resources.bufferPool.getIndexBuffer(numSamples);
if (!jumps || !bends || !indices || !coeffs)
if (!jumps || !indices || !coeffs)
return;
fill(*jumps, pitchRatio * speedRatio);
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);
}
pitchEnvelope(*jumps);
jumps->front() += floatPositionOffset;
cumsum<float>(*jumps, *jumps);
@ -584,30 +599,12 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
const auto numFrames = buffer.getNumFrames();
auto frequencies = resources.bufferPool.getBuffer(numFrames);
auto bends = resources.bufferPool.getBuffer(numFrames);
if (!frequencies || !bends)
if (!frequencies)
return;
float keycenterFrequency = midiNoteFrequency(region->pitchKeycenter);
fill(*frequencies, pitchRatio * keycenterFrequency);
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);
}
pitchEnvelope(*frequencies);
if (waveUnisonSize == 1) {
WavetableOscillator& osc = waveOscillators[0];
@ -784,7 +781,6 @@ void sfz::Voice::setupOscillatorUnison()
#endif
}
void sfz::Voice::updateChannelPowers(AudioSpan<float> buffer)
{
assert(smoothedChannelEnvelopes.size() == channelEnvelopeFilters.size());
@ -809,3 +805,59 @@ void sfz::Voice::switchState(State 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 "AudioBuffer.h"
#include "Resources.h"
#include "Smoothers.h"
#include "AudioSpan.h"
#include "LeakDetector.h"
#include "OnePoleFilter.h"
@ -301,6 +302,8 @@ public:
Duration getLastFilterDuration() const noexcept { return filterDuration; }
Duration getLastPanningDuration() const noexcept { return panningDuration; }
void prepareSmoothers(const ModifierArray<size_t>& numModifiers);
private:
/**
* @brief Fill a span with data from a file source. This is the first step
@ -337,12 +340,25 @@ private:
void panStageStereo(AudioSpan<float> buffer) noexcept;
void filterStageMono(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
*
*/
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.
*/
@ -410,8 +426,15 @@ private:
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<float, 2> smoothedChannelEnvelopes;
HistoricalBuffer<float> powerHistory { config::powerHistoryLength };
LEAK_DETECTOR(Voice);
};

View file

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

View file

@ -13,10 +13,11 @@
#endif
using namespace Catch::literals;
using namespace sfz::literals;
using namespace 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");
REQUIRE(synth.getNumRegions() == 1);
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)")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_many.sfz");
REQUIRE(synth.getNumRegions() == 3);
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)")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz");
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)")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz");
REQUIRE(synth.getNumRegions() == 1);
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)")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_bad.sfz");
REQUIRE(synth.getNumRegions() == 2);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -60,7 +61,7 @@ TEST_CASE("[Files] (regions_bad.sfz)")
TEST_CASE("[Files] Local include")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_local.sfz");
REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -68,7 +69,7 @@ TEST_CASE("[Files] Local include")
TEST_CASE("[Files] Multiple includes")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz");
REQUIRE(synth.getNumRegions() == 2);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -77,7 +78,7 @@ TEST_CASE("[Files] Multiple includes")
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");
REQUIRE(synth.getNumRegions() == 2);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy.wav");
@ -86,7 +87,7 @@ TEST_CASE("[Files] Multiple includes with comments")
TEST_CASE("[Files] Subdir include")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir.sfz");
REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy_subdir.wav");
@ -94,7 +95,7 @@ TEST_CASE("[Files] Subdir include")
TEST_CASE("[Files] Subdir include Win")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz");
REQUIRE(synth.getNumRegions() == 1);
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)")
{
sfz::Synth synth;
sfz::Parser& parser = synth.getParser();
Synth synth;
Parser& parser = synth.getParser();
parser.setRecursiveIncludeGuardEnabled(true);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_recursive.sfz");
REQUIRE(synth.getNumRegions() == 2);
@ -113,8 +114,8 @@ TEST_CASE("[Files] Recursive include (with include guard)")
TEST_CASE("[Files] Include loops (with include guard)")
{
sfz::Synth synth;
sfz::Parser& parser = synth.getParser();
Synth synth;
Parser& parser = synth.getParser();
parser.setRecursiveIncludeGuardEnabled(true);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_loop.sfz");
REQUIRE(synth.getNumRegions() == 2);
@ -124,34 +125,34 @@ TEST_CASE("[Files] Include loops (with include guard)")
TEST_CASE("[Files] Define test")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/defines.sfz");
REQUIRE(synth.getNumRegions() == 4);
REQUIRE(synth.getRegionView(0)->keyRange == sfz::Range<uint8_t>(36, 36));
REQUIRE(synth.getRegionView(1)->keyRange == sfz::Range<uint8_t>(38, 38));
REQUIRE(synth.getRegionView(2)->keyRange == sfz::Range<uint8_t>(42, 42));
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(36, 36));
REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(38, 38));
REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(42, 42));
REQUIRE(synth.getRegionView(3)->volume == -12.0f);
}
TEST_CASE("[Files] Group from AVL")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
REQUIRE(synth.getNumRegions() == 5);
for (int i = 0; i < synth.getNumRegions(); ++i) {
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(1)->velocityRange == sfz::Range<float>(27_norm, 52_norm));
REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range<float>(53_norm, 77_norm));
REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range<float>(78_norm, 102_norm));
REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range<float>(103_norm, 127_norm));
REQUIRE(synth.getRegionView(0)->velocityRange == Range<float>(1_norm, 26_norm));
REQUIRE(synth.getRegionView(1)->velocityRange == Range<float>(27_norm, 52_norm));
REQUIRE(synth.getRegionView(2)->velocityRange == Range<float>(53_norm, 77_norm));
REQUIRE(synth.getRegionView(3)->velocityRange == Range<float>(78_norm, 102_norm));
REQUIRE(synth.getRegionView(4)->velocityRange == Range<float>(103_norm, 127_norm));
}
TEST_CASE("[Files] Full hierarchy")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE(synth.getNumRegions() == 8);
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)->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)->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)->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)->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)->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)->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)->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)->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")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE(synth.getNumRegions() == 8);
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")
{
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE(synth.getNumRegions() == 8);
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");
REQUIRE(synth.getNumRegions() == 8);
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")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz");
REQUIRE(synth.getNumRegions() == 4);
for (int i = 0; i < synth.getNumRegions(); ++i) {
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(12, 22));
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<float>(97_norm, 127_norm));
REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(12, 22));
REQUIRE(synth.getRegionView(i)->velocityRange == Range<float>(97_norm, 127_norm));
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(1)->randRange == sfz::Range<float>(0.25, 0.5));
REQUIRE(synth.getRegionView(2)->randRange == sfz::Range<float>(0.5, 0.75));
REQUIRE(synth.getRegionView(3)->randRange == sfz::Range<float>(0.75, 1.0));
REQUIRE(synth.getRegionView(0)->randRange == Range<float>(0, 0.25));
REQUIRE(synth.getRegionView(1)->randRange == Range<float>(0.25, 0.5));
REQUIRE(synth.getRegionView(2)->randRange == Range<float>(0.5, 0.75));
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(1)->sampleId.filename() == R"(../Samples/pizz/a0_vl4_rr2.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)")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz");
REQUIRE(synth.getNumRegions() == 2);
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)")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels_multi.sfz");
REQUIRE(synth.getNumRegions() == 6);
@ -301,7 +302,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
TEST_CASE("[Files] sw_default")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz");
REQUIRE( synth.getNumRegions() == 4 );
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
@ -312,7 +313,7 @@ TEST_CASE("[Files] sw_default")
TEST_CASE("[Files] sw_default and playing with switches")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz");
REQUIRE( synth.getNumRegions() == 4 );
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")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/wrong-replacements.sfz");
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.getEnd() == 57 );
REQUIRE(!synth.getRegionView(2)->amplitudeCC.empty());
REQUIRE(synth.getRegionView(2)->amplitudeCC.contains(10));
REQUIRE(synth.getRegionView(2)->amplitudeCC.getWithDefault(10).value == 34.0f);
REQUIRE(!synth.getRegionView(2)->modifiers[Mod::amplitude].empty());
REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].contains(10));
REQUIRE(synth.getRegionView(2)->modifiers[Mod::amplitude].getWithDefault(10).value == 34.0f);
}
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");
REQUIRE(synth.getNumRegions() == 1);
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")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz");
REQUIRE(synth.getNumRegions() == 4);
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")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz");
REQUIRE(synth.getNumRegions() == 4);
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")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_generator.sfz");
REQUIRE(synth.getNumRegions() == 1);
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")
{
sfz::Synth synth;
Synth synth;
const auto& midiState = synth.getResources().midiState;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz");
REQUIRE(midiState.getCCValue(142) == 63_norm);
@ -426,26 +427,26 @@ TEST_CASE("[Files] Set RealCC applies properly")
TEST_CASE("[Files] Note and octave offsets")
{
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/note_offset.sfz");
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)->keyswitchRange == sfz::Default::keyRange );
REQUIRE( synth.getRegionView(0)->crossfadeKeyInRange == sfz::Default::crossfadeKeyInRange );
REQUIRE( synth.getRegionView(0)->crossfadeKeyOutRange == sfz::Default::crossfadeKeyOutRange );
REQUIRE(synth.getRegionView(0)->keyswitchRange == Default::keyRange);
REQUIRE(synth.getRegionView(0)->crossfadeKeyInRange == Default::crossfadeKeyInRange);
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(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)->crossfadeKeyInRange == sfz::Range<uint8_t>(37, 41) );
REQUIRE( synth.getRegionView(2)->crossfadeKeyOutRange == sfz::Range<uint8_t>(45, 49) );
REQUIRE(synth.getRegionView(2)->crossfadeKeyInRange == Range<uint8_t>(37, 41));
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)->keyswitchRange == sfz::Range<uint8_t>(23, 27) );
REQUIRE(synth.getRegionView(3)->keyRange == Range<uint8_t>(62, 62));
REQUIRE(synth.getRegionView(3)->keyswitchRange == Range<uint8_t>(23, 27));
REQUIRE( synth.getRegionView(3)->keyswitch );
REQUIRE( *synth.getRegionView(3)->keyswitch == 24 );
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 == 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(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(6)->keyRange == sfz::Range<uint8_t>(50, 50) );
REQUIRE(synth.getRegionView(6)->keyRange == Range<uint8_t>(50, 50));
REQUIRE( synth.getRegionView(6)->pitchKeycenter == 50 );
}
TEST_CASE("[Files] Off by with different delays")
{
sfz::Synth synth;
Synth synth;
synth.setSamplesPerBlock(256);
sfz::AudioBuffer<float> buffer(2, 256);
AudioBuffer<float> buffer(2, 256);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz");
REQUIRE( synth.getNumRegions() == 4 );
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")
{
sfz::Synth synth;
Synth synth;
synth.setSamplesPerBlock(256);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz");
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")
{
sfz::Synth synth;
Synth synth;
synth.setSamplesPerBlock(256);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_by.sfz");
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 );
synth.noteOn(0, 65, 63);
REQUIRE( synth.getNumActiveVoices() == 4 );
sfz::AudioBuffer<float> buffer { 2, 256 };
AudioBuffer<float> buffer { 2, 256 };
synth.renderBlock(buffer);
synth.noteOn(0, 65, 63);
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")
{
sfz::Synth synth;
Synth synth;
synth.setSamplesPerBlock(256);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_mode.sfz");
REQUIRE( synth.getNumRegions() == 3 );
@ -532,7 +533,7 @@ TEST_CASE("[Files] Off modes")
synth.getVoiceView(0) ;
synth.noteOn(100, 63, 63);
REQUIRE( synth.getNumActiveVoices() == 3 );
sfz::AudioBuffer<float> buffer { 2, 256 };
AudioBuffer<float> buffer { 2, 256 };
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices() == 2 );
REQUIRE( fastVoice->isFree() );
@ -541,7 +542,7 @@ TEST_CASE("[Files] Off modes")
TEST_CASE("[Files] Looped regions taken from files and possibly overriden")
{
sfz::Synth synth;
Synth synth;
synth.setSamplesPerBlock(256);
synth.setSampleRate(44100);
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(2)->loopMode == SfzLoopMode::loop_continuous );
REQUIRE( synth.getRegionView(0)->loopRange == sfz::Range<uint32_t>{ 77554, 186581 } );
REQUIRE( synth.getRegionView(1)->loopRange == sfz::Range<uint32_t>{ 77554, 186581 } );
REQUIRE( synth.getRegionView(2)->loopRange == sfz::Range<uint32_t>{ 4, 124 } );
REQUIRE(synth.getRegionView(0)->loopRange == Range<uint32_t> { 77554, 186581 });
REQUIRE(synth.getRegionView(1)->loopRange == Range<uint32_t> { 77554, 186581 });
REQUIRE(synth.getRegionView(2)->loopRange == Range<uint32_t> { 4, 124 });
}
TEST_CASE("[Files] Case sentitiveness")
@ -568,7 +569,7 @@ TEST_CASE("[Files] Case sentitiveness")
#endif
if (caseSensitiveFs) {
sfz::Synth synth;
Synth synth;
synth.loadSfzFile(sfzFilePath);
REQUIRE(synth.getNumRegions() == 4);
REQUIRE(synth.getRegionView(0)->sampleId.filename() == "dummy1.wav");
@ -580,8 +581,8 @@ TEST_CASE("[Files] Case sentitiveness")
TEST_CASE("[Files] Empty file")
{
sfz::Synth synth;
sfz::Parser& parser = synth.getParser();
Synth synth;
Parser& parser = synth.getParser();
REQUIRE(!synth.loadSfzFile(""));
REQUIRE(parser.getIncludedFiles().empty());
REQUIRE(!synth.loadSfzFile({}));

View file

@ -10,11 +10,12 @@
#include "catch2/catch.hpp"
using namespace Catch::literals;
using namespace sfz::literals;
using namespace sfz;
TEST_CASE("[Region] Parsing opcodes")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
SECTION("sample")
{
@ -132,36 +133,36 @@ TEST_CASE("[Region] Parsing opcodes")
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" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 184));
REQUIRE(region.loopRange == Range<uint32_t>(0, 184));
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")
{
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" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 4294967295));
REQUIRE(region.loopRange == Range<uint32_t>(0, 4294967295));
}
SECTION("loopend")
{
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 4294967295));
REQUIRE(region.loopRange == Range<uint32_t>(0, 4294967295));
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" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 0));
REQUIRE(region.loopRange == Range<uint32_t>(0, 0));
}
SECTION("loopstart")
{
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" });
REQUIRE(region.loopRange == sfz::Range<uint32_t>(0, 4294967295));
REQUIRE(region.loopRange == Range<uint32_t>(0, 4294967295));
}
SECTION("group")
@ -194,87 +195,87 @@ TEST_CASE("[Region] Parsing opcodes")
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" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(37, 127));
REQUIRE(region.keyRange == Range<uint8_t>(37, 127));
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" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(127, 127));
REQUIRE(region.keyRange == Range<uint8_t>(127, 127));
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" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 65));
REQUIRE(region.keyRange == Range<uint8_t>(0, 65));
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" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.keyRange == Range<uint8_t>(0, 0));
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" });
REQUIRE(region.keyRange == sfz::Range<uint8_t>(26, 26));
REQUIRE(region.keyRange == Range<uint8_t>(26, 26));
REQUIRE(region.pitchKeycenter == 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);
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);
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);
}
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" });
REQUIRE(region.velocityRange == sfz::Range<float>(37_norm, 127_norm));
REQUIRE(region.velocityRange == Range<float>(37_norm, 127_norm));
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" });
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
REQUIRE(region.velocityRange == Range<float>(0_norm, 127_norm));
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" });
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 0_norm));
REQUIRE(region.velocityRange == Range<float>(0_norm, 0_norm));
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")
{
REQUIRE(region.bendRange == sfz::Range<float>(-1.0f, 1.0f));
REQUIRE(region.bendRange == Range<float>(-1.0f, 1.0f));
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);
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);
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" });
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" });
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" });
REQUIRE(region.bendRange == sfz::Range<float>(-1.0f, 1.0f));
REQUIRE(region.bendRange == Range<float>(-1.0f, 1.0f));
}
SECTION("locc, hicc")
{
REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range<float>(0_norm, 127_norm));
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm));
REQUIRE(region.ccConditions.getWithDefault(0) == Range<float>(0_norm, 127_norm));
REQUIRE(region.ccConditions[127] == Range<float>(0_norm, 127_norm));
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" });
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" });
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" });
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm));
REQUIRE(region.ccConditions[127] == Range<float>(0_norm, 127_norm));
}
SECTION("lohdcc, hihdcc")
@ -307,19 +308,19 @@ TEST_CASE("[Region] Parsing opcodes")
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" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(4, 127));
REQUIRE(region.keyswitchRange == Range<uint8_t>(4, 127));
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" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 127));
REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 127));
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" });
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 127));
REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 127));
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")
@ -398,53 +399,53 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("lochanaft, hichanaft")
{
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(0, 127));
REQUIRE(region.aftertouchRange == Range<uint8_t>(0, 127));
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" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(127, 127));
REQUIRE(region.aftertouchRange == Range<uint8_t>(127, 127));
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" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(0, 39));
REQUIRE(region.aftertouchRange == Range<uint8_t>(0, 39));
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" });
REQUIRE(region.aftertouchRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.aftertouchRange == Range<uint8_t>(0, 0));
}
SECTION("lobpm, hibpm")
{
REQUIRE(region.bpmRange == sfz::Range<float>(0, 500));
REQUIRE(region.bpmRange == Range<float>(0, 500));
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" });
REQUIRE(region.bpmRange == sfz::Range<float>(500, 500));
REQUIRE(region.bpmRange == Range<float>(500, 500));
region.parseOpcode({ "lobpm", "0" });
REQUIRE(region.bpmRange == sfz::Range<float>(0, 500));
REQUIRE(region.bpmRange == Range<float>(0, 500));
region.parseOpcode({ "hibpm", "78" });
REQUIRE(region.bpmRange == sfz::Range<float>(0, 78));
REQUIRE(region.bpmRange == Range<float>(0, 78));
region.parseOpcode({ "hibpm", "895.4" });
REQUIRE(region.bpmRange == sfz::Range<float>(0, 500));
REQUIRE(region.bpmRange == Range<float>(0, 500));
region.parseOpcode({ "hibpm", "-1" });
REQUIRE(region.bpmRange == sfz::Range<float>(0, 0));
REQUIRE(region.bpmRange == Range<float>(0, 0));
}
SECTION("lorand, hirand")
{
REQUIRE(region.randRange == sfz::Range<float>(0, 1));
REQUIRE(region.randRange == Range<float>(0, 1));
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" });
REQUIRE(region.randRange == sfz::Range<float>(1, 1));
REQUIRE(region.randRange == Range<float>(1, 1));
region.parseOpcode({ "lorand", "0" });
REQUIRE(region.randRange == sfz::Range<float>(0, 1));
REQUIRE(region.randRange == Range<float>(0, 1));
region.parseOpcode({ "hirand", "39" });
REQUIRE(region.randRange == sfz::Range<float>(0, 1));
REQUIRE(region.randRange == Range<float>(0, 1));
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" });
REQUIRE(region.randRange == sfz::Range<float>(0, 0));
REQUIRE(region.randRange == Range<float>(0, 0));
}
SECTION("seq_length")
@ -489,10 +490,10 @@ TEST_CASE("[Region] Parsing opcodes")
}
region.parseOpcode({ "on_locc45", "15" });
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" });
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")
@ -540,28 +541,28 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("pan_oncc")
{
REQUIRE(region.panCC.empty());
REQUIRE(region.modifiers[Mod::pan].empty());
region.parseOpcode({ "pan_oncc45", "4.2" });
REQUIRE(region.panCC.contains(45));
REQUIRE(region.panCC[45].value == 4.2_a);
REQUIRE(region.modifiers[Mod::pan].contains(45));
REQUIRE(region.modifiers[Mod::pan][45].value == 4.2_a);
region.parseOpcode({ "pan_curvecc17", "18" });
REQUIRE(region.panCC[17].curve == 18);
REQUIRE(region.modifiers[Mod::pan][17].curve == 18);
region.parseOpcode({ "pan_curvecc17", "15482" });
REQUIRE(region.panCC[17].curve == 255);
REQUIRE(region.modifiers[Mod::pan][17].curve == 255);
region.parseOpcode({ "pan_curvecc17", "-2" });
REQUIRE(region.panCC[17].curve == 0);
REQUIRE(region.modifiers[Mod::pan][17].curve == 0);
region.parseOpcode({ "pan_smoothcc14", "85" });
REQUIRE(region.panCC[14].smooth == 85);
REQUIRE(region.modifiers[Mod::pan][14].smooth == 85);
region.parseOpcode({ "pan_smoothcc14", "15482" });
REQUIRE(region.panCC[14].smooth == 127);
REQUIRE(region.modifiers[Mod::pan][14].smooth == 100);
region.parseOpcode({ "pan_smoothcc14", "-2" });
REQUIRE(region.panCC[14].smooth == 0);
REQUIRE(region.modifiers[Mod::pan][14].smooth == 0);
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" });
REQUIRE(region.panCC[120].step == 200.0_a);
REQUIRE(region.modifiers[Mod::pan][120].step == 200.0_a);
region.parseOpcode({ "pan_stepcc120", "-2" });
REQUIRE(region.panCC[120].step == 0.0f);
REQUIRE(region.modifiers[Mod::pan][120].step == 0.0f);
}
SECTION("width")
@ -579,28 +580,28 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("width_oncc")
{
REQUIRE(region.widthCC.empty());
REQUIRE(region.modifiers[Mod::width].empty());
region.parseOpcode({ "width_oncc45", "4.2" });
REQUIRE(region.widthCC.contains(45));
REQUIRE(region.widthCC[45].value == 4.2_a);
REQUIRE(region.modifiers[Mod::width].contains(45));
REQUIRE(region.modifiers[Mod::width][45].value == 4.2_a);
region.parseOpcode({ "width_curvecc17", "18" });
REQUIRE(region.widthCC[17].curve == 18);
REQUIRE(region.modifiers[Mod::width][17].curve == 18);
region.parseOpcode({ "width_curvecc17", "15482" });
REQUIRE(region.widthCC[17].curve == 255);
REQUIRE(region.modifiers[Mod::width][17].curve == 255);
region.parseOpcode({ "width_curvecc17", "-2" });
REQUIRE(region.widthCC[17].curve == 0);
REQUIRE(region.modifiers[Mod::width][17].curve == 0);
region.parseOpcode({ "width_smoothcc14", "85" });
REQUIRE(region.widthCC[14].smooth == 85);
REQUIRE(region.modifiers[Mod::width][14].smooth == 85);
region.parseOpcode({ "width_smoothcc14", "15482" });
REQUIRE(region.widthCC[14].smooth == 127);
REQUIRE(region.modifiers[Mod::width][14].smooth == 100);
region.parseOpcode({ "width_smoothcc14", "-2" });
REQUIRE(region.widthCC[14].smooth == 0);
REQUIRE(region.modifiers[Mod::width][14].smooth == 0);
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" });
REQUIRE(region.widthCC[120].step == 200.0_a);
REQUIRE(region.modifiers[Mod::width][120].step == 200.0_a);
region.parseOpcode({ "width_stepcc120", "-20" });
REQUIRE(region.widthCC[120].step == 0.0f);
REQUIRE(region.modifiers[Mod::width][120].step == 0.0f);
}
SECTION("position")
@ -618,28 +619,28 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("position_oncc")
{
REQUIRE(region.positionCC.empty());
REQUIRE(region.modifiers[Mod::position].empty());
region.parseOpcode({ "position_oncc45", "4.2" });
REQUIRE(region.positionCC.contains(45));
REQUIRE(region.positionCC[45].value == 4.2_a);
REQUIRE(region.modifiers[Mod::position].contains(45));
REQUIRE(region.modifiers[Mod::position][45].value == 4.2_a);
region.parseOpcode({ "position_curvecc17", "18" });
REQUIRE(region.positionCC[17].curve == 18);
REQUIRE(region.modifiers[Mod::position][17].curve == 18);
region.parseOpcode({ "position_curvecc17", "15482" });
REQUIRE(region.positionCC[17].curve == 255);
REQUIRE(region.modifiers[Mod::position][17].curve == 255);
region.parseOpcode({ "position_curvecc17", "-2" });
REQUIRE(region.positionCC[17].curve == 0);
REQUIRE(region.modifiers[Mod::position][17].curve == 0);
region.parseOpcode({ "position_smoothcc14", "85" });
REQUIRE(region.positionCC[14].smooth == 85);
REQUIRE(region.modifiers[Mod::position][14].smooth == 85);
region.parseOpcode({ "position_smoothcc14", "15482" });
REQUIRE(region.positionCC[14].smooth == 127);
REQUIRE(region.modifiers[Mod::position][14].smooth == 100);
region.parseOpcode({ "position_smoothcc14", "-2" });
REQUIRE(region.positionCC[14].smooth == 0);
REQUIRE(region.modifiers[Mod::position][14].smooth == 0);
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" });
REQUIRE(region.positionCC[120].step == 200.0_a);
REQUIRE(region.modifiers[Mod::position][120].step == 200.0_a);
region.parseOpcode({ "position_stepcc120", "-2" });
REQUIRE(region.positionCC[120].step == 0.0f);
REQUIRE(region.modifiers[Mod::position][120].step == 0.0f);
}
SECTION("amp_keycenter")
@ -704,116 +705,116 @@ TEST_CASE("[Region] Parsing opcodes")
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" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(4, 4));
REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(4, 4));
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" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(59, 127));
REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(59, 127));
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" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(59, 127));
REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(59, 127));
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" });
REQUIRE(region.crossfadeKeyInRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.crossfadeKeyInRange == Range<uint8_t>(0, 0));
}
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" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(4_norm, 4_norm));
REQUIRE(region.crossfadeVelInRange == Range<float>(4_norm, 4_norm));
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" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm));
REQUIRE(region.crossfadeVelInRange == Range<float>(59_norm, 127_norm));
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" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm));
REQUIRE(region.crossfadeVelInRange == Range<float>(59_norm, 127_norm));
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" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
REQUIRE(region.crossfadeVelInRange == Range<float>(0_norm, 0_norm));
}
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" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(4, 127));
REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(4, 127));
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" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(59, 127));
REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(59, 127));
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" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(59, 127));
REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(59, 127));
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" });
REQUIRE(region.crossfadeKeyOutRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.crossfadeKeyOutRange == Range<uint8_t>(0, 0));
}
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" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(4_norm, 127_norm));
REQUIRE(region.crossfadeVelOutRange == Range<float>(4_norm, 127_norm));
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" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm));
REQUIRE(region.crossfadeVelOutRange == Range<float>(59_norm, 127_norm));
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" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm));
REQUIRE(region.crossfadeVelOutRange == Range<float>(59_norm, 127_norm));
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" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm));
REQUIRE(region.crossfadeVelOutRange == Range<float>(0_norm, 0_norm));
}
SECTION("xfin_locc, xfin_hicc")
{
REQUIRE(!region.crossfadeCCInRange.contains(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" });
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" });
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" });
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" });
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" });
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" });
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")
{
REQUIRE(!region.crossfadeCCOutRange.contains(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" });
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" });
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" });
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" });
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" });
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" });
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(0_norm, 0_norm));
REQUIRE(region.crossfadeCCOutRange[4] == Range<float>(0_norm, 0_norm));
}
SECTION("xf_keycurve")
@ -932,7 +933,7 @@ TEST_CASE("[Region] Parsing opcodes")
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.bendDown == -200);
@ -959,6 +960,12 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.bendStep == 1);
region.parseOpcode({ "bend_step", "9700" });
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")
@ -1173,7 +1180,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.filters[0].cutoff == 500.0f);
// Check filter defaults
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].gain == 0);
REQUIRE(region.filters[0].veltrack == 0);
@ -1187,7 +1194,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.filters[1].cutoff == 5000.0f);
// Check filter defaults
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].gain == 0);
REQUIRE(region.filters[1].veltrack == 0);
@ -1202,7 +1209,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.filters[3].cutoff == 50.0f);
// Check filter defaults
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].gain == 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].resonanceCC.empty());
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].gain == 0);
REQUIRE(region.filters[3].veltrack == 0);
@ -1313,55 +1320,55 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "fil_type", "lpf_1p" });
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2p);
REQUIRE(region.filters[0].type == FilterType::kFilterLpf2p);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf2p);
REQUIRE(region.filters[0].type == FilterType::kFilterHpf2p);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf2p);
REQUIRE(region.filters[0].type == FilterType::kFilterBrf2p);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf1p);
REQUIRE(region.filters[0].type == FilterType::kFilterBrf1p);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2pSv);
REQUIRE(region.filters[0].type == FilterType::kFilterLpf2pSv);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf2pSv);
REQUIRE(region.filters[0].type == FilterType::kFilterBpf2pSv);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf4p);
REQUIRE(region.filters[0].type == FilterType::kFilterLpf4p);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf6p);
REQUIRE(region.filters[0].type == FilterType::kFilterLpf6p);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPink);
REQUIRE(region.filters[0].type == FilterType::kFilterPink);
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" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHsh);
REQUIRE(region.filters[0].type == FilterType::kFilterHsh);
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", "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", "bpk_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPeq);
REQUIRE(region.filters[0].type == FilterType::kFilterPeq);
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")
@ -1372,7 +1379,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.equalizers.size() == 1);
REQUIRE(region.equalizers[0].gain == 6.0f);
// 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].frequency == 0.0f);
REQUIRE(region.equalizers[0].vel2frequency == 0);
@ -1385,7 +1392,7 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.equalizers.size() == 2);
REQUIRE(region.equalizers[1].gain == -96.0f);
// 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].frequency == 0.0f);
REQUIRE(region.equalizers[1].vel2frequency == 0);
@ -1397,7 +1404,7 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "eq4_gain", "500" });
REQUIRE(region.equalizers.size() == 4);
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);
// Check defaults
REQUIRE(region.equalizers[2].bandwidth == 1.0f);
@ -1419,13 +1426,13 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("EQ types")
{
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" });
REQUIRE(region.equalizers[0].type == sfz::EqType::kEqNone);
REQUIRE(region.equalizers[0].type == EqType::kEqNone);
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" });
REQUIRE(region.equalizers[0].type == sfz::EqType::kEqPeak);
REQUIRE(region.equalizers[0].type == EqType::kEqPeak);
}
SECTION("EQ parameter dispatch")
@ -1438,7 +1445,7 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "eq2_freq", "300" });
REQUIRE(region.equalizers[1].frequency == 300.0f);
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" });
REQUIRE(region.equalizers[2].vel2gain == 10.0f);
region.parseOpcode({ "eq1_vel2freq", "100" });
@ -1455,7 +1462,7 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "eq3_freq_oncc15", "20" });
REQUIRE(region.equalizers[2].frequencyCC[15] == 20.0f);
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" });
REQUIRE(region.equalizers[1].gainCC.contains(123));
REQUIRE(region.equalizers[1].gainCC[123] == 2.0f);
@ -1576,103 +1583,103 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("amplitude_cc")
{
REQUIRE(region.amplitudeCC.empty());
REQUIRE(region.modifiers[Mod::amplitude].empty());
region.parseOpcode({ "amplitude_cc1", "40" });
REQUIRE(region.amplitudeCC.contains(1));
REQUIRE(region.amplitudeCC[1].value == 40.0_a);
REQUIRE(region.modifiers[Mod::amplitude].contains(1));
REQUIRE(region.modifiers[Mod::amplitude][1].value == 40.0_a);
region.parseOpcode({ "amplitude_oncc2", "30" });
REQUIRE(region.amplitudeCC.contains(2));
REQUIRE(region.amplitudeCC[2].value == 30.0_a);
REQUIRE(region.modifiers[Mod::amplitude].contains(2));
REQUIRE(region.modifiers[Mod::amplitude][2].value == 30.0_a);
region.parseOpcode({ "amplitude_curvecc17", "18" });
REQUIRE(region.amplitudeCC[17].curve == 18);
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 18);
region.parseOpcode({ "amplitude_curvecc17", "15482" });
REQUIRE(region.amplitudeCC[17].curve == 255);
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 255);
region.parseOpcode({ "amplitude_curvecc17", "-2" });
REQUIRE(region.amplitudeCC[17].curve == 0);
REQUIRE(region.modifiers[Mod::amplitude][17].curve == 0);
region.parseOpcode({ "amplitude_smoothcc14", "85" });
REQUIRE(region.amplitudeCC[14].smooth == 85);
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 85);
region.parseOpcode({ "amplitude_smoothcc14", "15482" });
REQUIRE(region.amplitudeCC[14].smooth == 127);
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 100);
region.parseOpcode({ "amplitude_smoothcc14", "-2" });
REQUIRE(region.amplitudeCC[14].smooth == 0);
REQUIRE(region.modifiers[Mod::amplitude][14].smooth == 0);
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" });
REQUIRE(region.amplitudeCC[120].step == 100.0_a);
REQUIRE(region.[Mod::amplitude][120].step == 100.0_a);
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")
{
REQUIRE(region.volumeCC.empty());
REQUIRE(region.modifiers[Mod::volume].empty());
region.parseOpcode({ "gain_cc1", "40" });
REQUIRE(region.volumeCC.contains(1));
REQUIRE(region.volumeCC[1].value == 40_a);
REQUIRE(region.modifiers[Mod::volume].contains(1));
REQUIRE(region.modifiers[Mod::volume][1].value == 40_a);
region.parseOpcode({ "volume_oncc2", "-76" });
REQUIRE(region.volumeCC.contains(2));
REQUIRE(region.volumeCC[2].value == -76.0_a);
REQUIRE(region.modifiers[Mod::volume].contains(2));
REQUIRE(region.modifiers[Mod::volume][2].value == -76.0_a);
region.parseOpcode({ "gain_oncc4", "-1" });
REQUIRE(region.volumeCC.contains(4));
REQUIRE(region.volumeCC[4].value == -1.0_a);
REQUIRE(region.modifiers[Mod::volume].contains(4));
REQUIRE(region.modifiers[Mod::volume][4].value == -1.0_a);
region.parseOpcode({ "volume_curvecc17", "18" });
REQUIRE(region.volumeCC[17].curve == 18);
REQUIRE(region.modifiers[Mod::volume][17].curve == 18);
region.parseOpcode({ "volume_curvecc17", "15482" });
REQUIRE(region.volumeCC[17].curve == 255);
REQUIRE(region.modifiers[Mod::volume][17].curve == 255);
region.parseOpcode({ "volume_curvecc17", "-2" });
REQUIRE(region.volumeCC[17].curve == 0);
REQUIRE(region.modifiers[Mod::volume][17].curve == 0);
region.parseOpcode({ "volume_smoothcc14", "85" });
REQUIRE(region.volumeCC[14].smooth == 85);
REQUIRE(region.modifiers[Mod::volume][14].smooth == 85);
region.parseOpcode({ "volume_smoothcc14", "15482" });
REQUIRE(region.volumeCC[14].smooth == 127);
REQUIRE(region.modifiers[Mod::volume][14].smooth == 100);
region.parseOpcode({ "volume_smoothcc14", "-2" });
REQUIRE(region.volumeCC[14].smooth == 0);
REQUIRE(region.modifiers[Mod::volume][14].smooth == 0);
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" });
REQUIRE(region.volumeCC[120].step == 144.0f);
REQUIRE(region.modifiers[Mod::volume][120].step == 144.0f);
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")
{
REQUIRE(region.tuneCC.empty());
REQUIRE(region.modifiers[Mod::pitch].empty());
region.parseOpcode({ "pitch_cc1", "40" });
REQUIRE(region.tuneCC.contains(1));
REQUIRE(region.tuneCC[1].value == 40.0);
REQUIRE(region.modifiers[Mod::pitch].contains(1));
REQUIRE(region.modifiers[Mod::pitch][1].value == 40.0);
region.parseOpcode({ "tune_oncc2", "-76" });
REQUIRE(region.tuneCC.contains(2));
REQUIRE(region.tuneCC[2].value == -76.0);
REQUIRE(region.modifiers[Mod::pitch].contains(2));
REQUIRE(region.modifiers[Mod::pitch][2].value == -76.0);
region.parseOpcode({ "pitch_oncc4", "-1" });
REQUIRE(region.tuneCC.contains(4));
REQUIRE(region.tuneCC[4].value == -1.0);
REQUIRE(region.modifiers[Mod::pitch].contains(4));
REQUIRE(region.modifiers[Mod::pitch][4].value == -1.0);
region.parseOpcode({ "tune_curvecc17", "18" });
REQUIRE(region.tuneCC[17].curve == 18);
REQUIRE(region.modifiers[Mod::pitch][17].curve == 18);
region.parseOpcode({ "pitch_curvecc17", "15482" });
REQUIRE(region.tuneCC[17].curve == 255);
REQUIRE(region.modifiers[Mod::pitch][17].curve == 255);
region.parseOpcode({ "tune_curvecc17", "-2" });
REQUIRE(region.tuneCC[17].curve == 0);
REQUIRE(region.modifiers[Mod::pitch][17].curve == 0);
region.parseOpcode({ "pitch_smoothcc14", "85" });
REQUIRE(region.tuneCC[14].smooth == 85);
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 85);
region.parseOpcode({ "tune_smoothcc14", "15482" });
REQUIRE(region.tuneCC[14].smooth == 127);
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 100);
region.parseOpcode({ "pitch_smoothcc14", "-2" });
REQUIRE(region.tuneCC[14].smooth == 0);
REQUIRE(region.modifiers[Mod::pitch][14].smooth == 0);
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" });
REQUIRE(region.tuneCC[120].step == 9600.0f);
REQUIRE(region.modifiers[Mod::pitch][120].step == 9600.0f);
region.parseOpcode({ "tune_stepcc120", "-2" });
REQUIRE(region.tuneCC[120].step == 0.0f);
REQUIRE(region.modifiers[Mod::pitch][120].step == 0.0f);
}
}
// Specific region bugs
TEST_CASE("[Region] Non-conforming floating point values in integer opcodes")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "offset", "2014.5" });
REQUIRE(region.offset == 2014);
region.parseOpcode({ "pitch_keytrack", "-2.1" });

View file

@ -5,6 +5,7 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "sfizz/OnePoleFilter.h"
#include "sfizz/Smoothers.h"
#include "catch2/catch.hpp"
// #include "cnpy.h"
// #include "ghc/fs_std.hpp"
@ -16,14 +17,13 @@
using namespace Catch::literals;
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())
return false;
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';
return false;
}
@ -74,7 +74,8 @@ constexpr std::array<float, 64> floatInput01 = {
1.1666758495956464f, -0.04382336233428379f, 0.43819763320865857f, 0.1740610608056625f,
0.30473671729007396f, -0.4065881153810866f, -0.9784770671900811f, -0.17674381857142665f,
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 = {
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.21137141199133533f, 0.16368102816645502f, 0.008005824629720673f, -0.0984698603721838f,
-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 = {
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,
0.09336530529873863f, -0.5702691435475415f, -0.9864828918198018f, -0.07827395819924285f,
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 = {
-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.46464793721558995f, -1.7121509287301122f, 0.7887828546774234f, -0.902172963851904f,
-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 = {
-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.18242012613484115f, -0.7864063306935144f, -0.5699248015820677f, -0.22777163691884944f,
-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 = {
-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.2822278110807488f, -0.9257445980365978f, 1.358707656259491f, -0.6744013269330547f,
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 = {
-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,
2.1609909686692763f, -0.07213993925443297f, -0.5083174992310037f, -0.7489925703250175f,
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 = {
-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,
0.496816040067124f, 1.015603963410564f, -0.22150068331359823f, -0.6072258583851468f,
-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 = {
-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,
1.6641749286021523f, -1.087743902664997f, -0.28681681591740543f, -0.14176671193987078f,
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 = {
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,
0.30473671729007396, -0.4065881153810866, -0.9784770671900811, -0.17674381857142665,
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 = {
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.21137141199133533, 0.16368102816645502, 0.008005824629720673, -0.0984698603721838,
-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 = {
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,
0.09336530529873863, -0.5702691435475415, -0.9864828918198018, -0.07827395819924285,
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 = {
-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.46464793721558995, -1.7121509287301122, 0.7887828546774234, -0.902172963851904,
-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 = {
-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.18242012613484115, -0.7864063306935144, -0.5699248015820677, -0.22777163691884944,
-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 = {
-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.2822278110807488, -0.9257445980365978, 1.358707656259491, -0.6744013269330547,
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 = {
-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,
2.1609909686692763, -0.07213993925443297, -0.5083174992310037, -0.7489925703250175,
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 = {
-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,
0.496816040067124, 1.015603963410564, -0.22150068331359823, -0.6072258583851468,
-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 = {
-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,
1.6641749286021523, -1.087743902664997, -0.28681681591740543, -0.14176671193987078,
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")
{