Merge pull request #559 from paulfd/region-parsing

Opcode parsing and defaults
This commit is contained in:
Paul Ferrand 2021-02-12 10:02:46 +01:00 committed by GitHub
commit f9eb818895
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 2094 additions and 1695 deletions

View file

@ -0,0 +1,72 @@
// 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 "BM_opcodeSpec.h"
#include <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
class OpcodeSpecFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.0f, 1.0f };
value = dist(gen);
}
void TearDown(const ::benchmark::State& /* state */) {
}
float value;
float returned;
};
BENCHMARK_DEFINE_F(OpcodeSpecFixture, ConstexprClamp)(benchmark::State& state) {
for (auto _ : state)
{
if (constexprSpec.flags | (1 << 2))
returned = constexprSpec.bounds.clamp(value);
benchmark::DoNotOptimize(returned);
}
}
BENCHMARK_DEFINE_F(OpcodeSpecFixture, ConstexprDontClamp)(benchmark::State& state) {
for (auto _ : state)
{
if (constexprSpec.flags | (1 << 1))
returned = constexprSpec.bounds.clamp(value);
benchmark::DoNotOptimize(returned);
}
}
BENCHMARK_DEFINE_F(OpcodeSpecFixture, ConstClamp)(benchmark::State& state) {
for (auto _ : state)
{
if (constSpec.flags | (1 << 2))
returned = constSpec.bounds.clamp(value);
benchmark::DoNotOptimize(returned);
}
}
BENCHMARK_DEFINE_F(OpcodeSpecFixture, ConstDontClamp)(benchmark::State& state) {
for (auto _ : state)
{
if (constSpec.flags | (1 << 1))
returned = constSpec.bounds.clamp(value);
benchmark::DoNotOptimize(returned);
}
}
BENCHMARK_REGISTER_F(OpcodeSpecFixture, ConstexprClamp);
BENCHMARK_REGISTER_F(OpcodeSpecFixture, ConstexprDontClamp);
BENCHMARK_REGISTER_F(OpcodeSpecFixture, ConstClamp);
BENCHMARK_REGISTER_F(OpcodeSpecFixture, ConstDontClamp);
BENCHMARK_MAIN();

View file

@ -0,0 +1,14 @@
#pragma once
#include "Range.h"
template<class T>
struct OpcodeSpec
{
T defaultValue;
sfz::Range<T> bounds;
int flags { 0 };
};
constexpr OpcodeSpec<float> constexprSpec { 0.0f, sfz::Range<float>(0.0f, 0.5f), 1 << 2 };
extern const OpcodeSpec<float> constSpec;

View file

@ -0,0 +1,3 @@
#include "BM_opcodeSpec.h"
const OpcodeSpec<float> constSpec { 0.0f, sfz::Range<float>(0.0f, 0.5f), 1 << 2 };

View file

@ -38,6 +38,7 @@ sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp)
sfizz_add_benchmark(bm_random BM_random.cpp)
sfizz_add_benchmark(bm_clamp BM_clamp.cpp)
sfizz_add_benchmark(bm_allWithin BM_allWithin.cpp)
sfizz_add_benchmark(bm_opcodeSpec BM_opcodeSpec.cpp BM_opcodeSpec_def.cpp)
sfizz_add_benchmark(bm_logger BM_logger.cpp)
sfizz_add_benchmark(bm_smoothers BM_smoothers.cpp)

View file

@ -50,6 +50,7 @@ SFIZZ_SOURCES = \
src/sfizz/AudioReader.cpp \
src/sfizz/BeatClock.cpp \
src/sfizz/Curve.cpp \
src/sfizz/Defaults.cpp \
src/sfizz/effects/Apan.cpp \
src/sfizz/Effects.cpp \
src/sfizz/modulations/ModId.cpp \

View file

@ -200,6 +200,7 @@ set(SFIZZ_PARSER_HEADERS
set(SFIZZ_PARSER_SOURCES
sfizz/Opcode.cpp
sfizz/Defaults.cpp
sfizz/OpcodeCleanup.cpp
sfizz/parser/Parser.cpp
sfizz/parser/ParserPrivate.cpp)

View file

@ -53,7 +53,7 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, const
shouldRelease = false;
freeRunning = (
(this->sustain == Float(0.0))
|| (region.loopMode == SfzLoopMode::one_shot && region.isOscillator())
|| (region.loopMode == LoopMode::one_shot && region.isOscillator())
);
currentValue = this->start;
currentState = State::Delay;

View file

@ -150,6 +150,10 @@ namespace config {
(int(polyphony * config::overflowVoiceMultiplier) < int(config::maxVoices)) ?
int(polyphony * config::overflowVoiceMultiplier) : int(config::maxVoices);
}
/**
* @brief The smoothing time constant per "smooth" steps
*/
constexpr float smoothTauPerStep { 3e-3 };
} // namespace config
} // namespace sfz

View file

@ -21,8 +21,7 @@ Curve Curve::buildCurveFromHeader(
{
Curve curve;
bool fillStatus[NumValues] = {};
const Range<float> fullRange { -HUGE_VALF, +HUGE_VALF };
const OpcodeSpec<float> fullRange {0.0f, Range<float>{ -1e16, 1e16 }, 0 };
auto setPoint = [&curve, &fillStatus](int i, float x) {
curve._points[i] = x;
fillStatus[i] = true;
@ -40,11 +39,7 @@ Curve Curve::buildCurveFromHeader(
if (index >= NumValues)
continue;
auto valueOpt = readOpcode<float>(opc.value, fullRange);
if (!valueOpt)
continue;
setPoint(static_cast<int>(index), *valueOpt);
setPoint(static_cast<int>(index), opc.read(fullRange));
}
curve.fill(itp, fillStatus);
@ -267,12 +262,8 @@ void CurveSet::addCurveFromHeader(absl::Span<const Opcode> members)
int curveIndex = -1;
Curve::Interpolator itp = Curve::Interpolator::Linear;
if (const Opcode* opc = findOpcode(hash("curve_index"))) {
if (auto opt = readOpcode<int>(opc->value, {0, 255}))
curveIndex = *opt;
else
DBG("Invalid value for curve index: " << opc->value);
}
if (const Opcode* opc = findOpcode(hash("curve_index")))
curveIndex = opc->read(Default::curveCC);
#if 0 // potential sfizz extension
if (const Opcode* opc = findOpcode(hash("sfizz:curve_interpolator"))) {

169
src/sfizz/Defaults.cpp Normal file
View file

@ -0,0 +1,169 @@
#include "Defaults.h"
namespace sfz {
namespace Default {
constexpr auto uint32_t_max = std::numeric_limits<uint32_t>::max();
extern const OpcodeSpec<float> delay { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> delayRandom { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<int64_t> offset { 0, Range<int64_t>(0, uint32_t_max), 0 };
extern const OpcodeSpec<int64_t> offsetMod { 0, Range<int64_t>(0, uint32_t_max), 0 };
extern const OpcodeSpec<int64_t> offsetRandom { 0, Range<int64_t>(0, uint32_t_max), 0 };
extern const OpcodeSpec<uint32_t> sampleEnd { uint32_t_max, Range<uint32_t>(0, uint32_t_max), kEnforceLowerBound };
extern const OpcodeSpec<uint32_t> sampleCount { 1, Range<uint32_t>(1, uint32_t_max), 0 };
extern const OpcodeSpec<uint32_t> loopStart { 0, Range<uint32_t>(0, uint32_t_max), 0 };
extern const OpcodeSpec<uint32_t> loopEnd { uint32_t_max, Range<uint32_t>(0, uint32_t_max), 0 };
extern const OpcodeSpec<float> loopCrossfade { 1e-3, Range<float>(1e-3, 1.0f), 0 };
extern const OpcodeSpec<OscillatorEnabled> oscillator { OscillatorEnabled::Auto, Range<OscillatorEnabled>(OscillatorEnabled::Auto, OscillatorEnabled::On), 0 };
extern const OpcodeSpec<float> oscillatorPhase { 0.0f, Range<float>(-1000.0f, 1000.0f), 0 };
extern const OpcodeSpec<int> oscillatorMode { 0, Range<int>(0, 2), 0 };
extern const OpcodeSpec<int> oscillatorMulti { 1, Range<int>(1, config::oscillatorsPerVoice), 0 };
extern const OpcodeSpec<float> oscillatorDetune { 0.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<float> oscillatorDetuneMod { 0.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<float> oscillatorModDepth { 0.0f, Range<float>(0.0f, 10000.0f), kNormalizePercent };
extern const OpcodeSpec<float> oscillatorModDepthMod { 0.0f, Range<float>(0.0f, 10000.0f), kNormalizePercent };
extern const OpcodeSpec<int> oscillatorQuality { 1, Range<int>(0, 3), 0 };
extern const OpcodeSpec<uint32_t> group { 0, Range<uint32_t>(0, uint32_t_max), 0 };
extern const OpcodeSpec<float> offTime { 6e-3f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<uint32_t> polyphony { config::maxVoices, Range<uint32_t>(0, config::maxVoices), 0 };
extern const OpcodeSpec<uint32_t> notePolyphony { config::maxVoices, Range<uint32_t>(0, config::maxVoices), 0 };
extern const OpcodeSpec<uint8_t> key { 60, Range<uint8_t>(0, 127), kCanBeNote };
extern const OpcodeSpec<uint8_t> loKey { 0, Range<uint8_t>(0, 127), kCanBeNote };
extern const OpcodeSpec<uint8_t> hiKey { 127, Range<uint8_t>(0, 127), kCanBeNote };
extern const OpcodeSpec<float> loCC { 0, Range<float>(0.0f, 127.0f), kNormalizeMidi };
extern const OpcodeSpec<float> hiCC { 127, Range<float>(0.0f, 127.0f), kNormalizeMidi };
extern const OpcodeSpec<float> loVel { 0, Range<float>(0.0f, 127.0f), kNormalizeMidi };
extern const OpcodeSpec<float> hiVel { 127, Range<float>(0.0f, 127.0f), kNormalizeMidi };
extern const OpcodeSpec<uint8_t> loChannelAftertouch { 0, Range<uint8_t>(0, 127), 0 };
extern const OpcodeSpec<uint8_t> hiChannelAftertouch { 127, Range<uint8_t>(0, 127), 0 };
extern const OpcodeSpec<float> loBend { -8192, Range<float>(-8192.0f, 8192.0f), kNormalizeBend };
extern const OpcodeSpec<float> hiBend { 8192, Range<float>(-8192.0f, 8192.0f), kNormalizeBend };
extern const OpcodeSpec<float> loNormalized { 0.0f, Range<float>(0.0f, 1.0f), 0 };
extern const OpcodeSpec<float> hiNormalized { 1.0f, Range<float>(0.0f, 1.0f), 0 };
extern const OpcodeSpec<float> loBipolar { -1.0f, Range<float>(-1.0f, 1.0f), 0 };
extern const OpcodeSpec<float> hiBipolar { 1.0f, Range<float>(-1.0f, 1.0f), 0 };
extern const OpcodeSpec<uint16_t> ccNumber { 0, Range<uint16_t>(0, config::numCCs), 0 };
extern const OpcodeSpec<uint8_t> smoothCC { 0, Range<uint8_t>(0, 100), 0 };
extern const OpcodeSpec<uint8_t> curveCC { 0, Range<uint8_t>(0, 255), 0 };
extern const OpcodeSpec<uint8_t> sustainCC { 64, Range<uint8_t>(0, 127), 0 };
extern const OpcodeSpec<float> sustainThreshold { 1.0f, Range<float>(0.0f, 127.0f), kNormalizeMidi };
extern const OpcodeSpec<bool> checkSustain { true, Range<bool>(0, 1), 0 };
extern const OpcodeSpec<bool> checkSostenuto { true, Range<bool>(0, 1), 0 };
extern const OpcodeSpec<float> loBPM { 0.0f, Range<float>(0.0f, 500.0f), 0 };
extern const OpcodeSpec<float> hiBPM { 500.0f, Range<float>(0.0f, 500.0f), 0 };
extern const OpcodeSpec<uint8_t> sequence { 1, Range<uint8_t>(1, 100), 0 };
extern const OpcodeSpec<float> volume { 0.0f, Range<float>(-144.0f, 48.0f), 0 };
extern const OpcodeSpec<float> volumeMod { 0.0f, Range<float>(-144.0f, 48.0f), 0 };
extern const OpcodeSpec<float> amplitude { 100.0f, Range<float>(0.0f, 10000.0f), kNormalizePercent };
extern const OpcodeSpec<float> amplitudeMod { 0.0f, Range<float>(0.0f, 10000.0f), 0 };
extern const OpcodeSpec<float> pan { 0.0f, Range<float>(-100.0f, 100.0f), kNormalizePercent };
extern const OpcodeSpec<float> panMod { 0.0f, Range<float>(-200.0f, 200.0f), 0 };
extern const OpcodeSpec<float> position { 0.0f, Range<float>(-100.0f, 100.0f), kNormalizePercent };
extern const OpcodeSpec<float> positionMod { 0.0f, Range<float>(-200.0f, 200.0f), 0 };
extern const OpcodeSpec<float> width { 100.0f, Range<float>(-100.0f, 100.0f), kNormalizePercent };
extern const OpcodeSpec<float> widthMod { 0.0f, Range<float>(-200.0f, 200.0f), 0 };
extern const OpcodeSpec<float> crossfadeIn { 0.0f, Range<float>(0.0f, 127.0f), kNormalizeMidi };
extern const OpcodeSpec<float> crossfadeInNorm { 0.0f, Range<float>(0.0f, 1.0f), 0 };
extern const OpcodeSpec<float> crossfadeOut { 127.0f, Range<float>(0.0f, 127.0f), kNormalizeMidi };
extern const OpcodeSpec<float> crossfadeOutNorm { 1.0f, Range<float>(0.0f, 1.0f), 0 };
extern const OpcodeSpec<float> ampKeytrack { 0.0f, Range<float>(-96.0f, 12.0f), 0 };
extern const OpcodeSpec<float> ampVeltrack { 100.0f, Range<float>(-100.0f, 100.0f), kNormalizePercent };
extern const OpcodeSpec<float> ampVelcurve { 0.0f, Range<float>(0.0f, 1.0f), 0 };
extern const OpcodeSpec<float> ampRandom { 0.0f, Range<float>(0.0f, 24.0f), 0 };
extern const OpcodeSpec<bool> rtDead { false, Range<bool>(0, 1), 0 };
extern const OpcodeSpec<float> rtDecay { 0.0f, Range<float>(0.0f, 200.0f), 0 };
extern const OpcodeSpec<float> filterCutoff { 0.0f, Range<float>(0.0f, 20000.0f), kEnforceUpperBound };
extern const OpcodeSpec<float> filterCutoffMod { 0.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<float> filterResonance { 0.0f, Range<float>(0.0f, 96.0f), 0 };
extern const OpcodeSpec<float> filterResonanceMod { 0.0f, Range<float>(0.0f, 96.0f), 0 };
extern const OpcodeSpec<float> filterGain { 0.0f, Range<float>(-96.0f, 96.0f), 0 };
extern const OpcodeSpec<float> filterGainMod { 0.0f, Range<float>(-96.0f, 96.0f), 0 };
extern const OpcodeSpec<float> filterRandom { 0.0f, Range<float>(0.0f, 12000.0f), 0 };
extern const OpcodeSpec<int> filterKeytrack { 0, Range<int>(0, 1200), 0 };
extern const OpcodeSpec<int> filterVeltrack { 0, Range<int>(-12000, 12000), 0 };
extern const OpcodeSpec<float> eqBandwidth { 1.0f, Range<float>(0.001f, 4.0f), 0 };
extern const OpcodeSpec<float> eqBandwidthMod { 0.0f, Range<float>(-4.0f, 4.0f), 0 };
extern const OpcodeSpec<float> eqFrequency { 0.0f, Range<float>(0.0f, 20000.0f), kEnforceUpperBound };
extern const OpcodeSpec<float> eqFrequencyMod { 0.0f, Range<float>(-20000.0f, 20000.0f), 0 };
extern const OpcodeSpec<float> eqGain { 0.0f, Range<float>(-96.0f, 96.0f), 0 };
extern const OpcodeSpec<float> eqGainMod { 0.0f, Range<float>(-96.0f, 96.0f), 0 };
extern const OpcodeSpec<float> eqVel2Frequency { 0.0f, Range<float>(-30000.0f, 30000.0f), 0 };
extern const OpcodeSpec<float> eqVel2Gain { 0.0f, Range<float>(-96.0f, 96.0f), 0 };
extern const OpcodeSpec<int> pitchKeytrack { 100, Range<int>(-1200, 1200), 0 };
extern const OpcodeSpec<float> pitchRandom { 0.0f, Range<float>(0.0f, 12000.0f), 0 };
extern const OpcodeSpec<int> pitchVeltrack { 0, Range<int>(-12000, 12000), 0 };
extern const OpcodeSpec<int> transpose { 0, Range<int>(-127, 127), 0 };
extern const OpcodeSpec<float> pitch { 0.0f, Range<float>(-2400.0f, 2400.0f), 0 };
extern const OpcodeSpec<float> pitchMod { 0.0f, Range<float>(-2400.0f, 2400.0f), 0 };
extern const OpcodeSpec<float> bendUp { 200.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<float> bendDown { -200.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<float> bendStep { 1.0f, Range<float>(1.0f, 1200.0f), 0 };
extern const OpcodeSpec<float> lfoFreq { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> lfoFreqMod { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
extern const OpcodeSpec<float> lfoBeats { 0.0f, Range<float>(0.0f, 1000.0f), 0 };
extern const OpcodeSpec<float> lfoBeatsMod { 0.0f, Range<float>(-1000.0f, 1000.0f), 0 };
extern const OpcodeSpec<float> lfoPhase { 0.0f, Range<float>(0.0f, 1.0f), kWrapPhase };
extern const OpcodeSpec<float> lfoDelay { 0.0f, Range<float>(0.0f, 30.0f), 0 };
extern const OpcodeSpec<float> lfoFade { 0.0f, Range<float>(0.0f, 30.0f), 0 };
extern const OpcodeSpec<unsigned> lfoCount { 0, Range<unsigned>(0, 1000), 0 };
extern const OpcodeSpec<unsigned> lfoSteps { 0, Range<unsigned>(0, static_cast<unsigned>(config::maxLFOSteps)), 0 };
extern const OpcodeSpec<float> lfoStepX { 0.0f, Range<float>(-100.0f, 100.0f), kNormalizePercent };
extern const OpcodeSpec<LFOWave> lfoWave { LFOWave::Triangle, Range<LFOWave>(LFOWave::Triangle, LFOWave::RandomSH), 0 };
extern const OpcodeSpec<float> lfoOffset { 0.0f, Range<float>(-1.0f, 1.0f), 0 };
extern const OpcodeSpec<float> lfoRatio { 1.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> lfoScale { 1.0f, Range<float>(0.0f, 1.0f), 0 };
extern const OpcodeSpec<float> egTime { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> egRelease { 0.001f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> egTimeMod { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
extern const OpcodeSpec<float> egPercent { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> egPercentMod { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
extern const OpcodeSpec<float> egDepth { 0.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<float> egVel2Depth { 0.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<bool> flexEGAmpeg { false, Range<bool>(0, 1), 0 };
extern const OpcodeSpec<int> flexEGDynamic { 0, Range<int>(0, 1), 0 };
extern const OpcodeSpec<int> flexEGSustain { 0, Range<int>(0, 100), 0 };
extern const OpcodeSpec<float> flexEGPointTime { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> flexEGPointLevel { 0.0f, Range<float>(-1.0f, 1.0f), 0 };
extern const OpcodeSpec<float> flexEGPointShape { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
extern const OpcodeSpec<int> sampleQuality { 1, Range<int>(1, 10), 0 };
extern const OpcodeSpec<int> octaveOffset { 0, Range<int>(-10, 10), 0 };
extern const OpcodeSpec<int> noteOffset { 0, Range<int>(-127, 127), 0 };
extern const OpcodeSpec<float> effect { 0.0f, Range<float>(0.0f, 100.0f), kNormalizePercent };
extern const OpcodeSpec<int> apanWaveform { 0, Range<int>(0, std::numeric_limits<int>::max()), 0 };
extern const OpcodeSpec<float> apanFrequency { 0.0f, Range<float>(0.0f, std::numeric_limits<float>::max()), 0 };
extern const OpcodeSpec<float> apanPhase { 0.5f, Range<float>(0.0f, 1.0f), kWrapPhase };
extern const OpcodeSpec<float> apanLevel { 0.0f, Range<float>(0.0f, 100.0f), kNormalizePercent };
extern const OpcodeSpec<float> distoTone { 100.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> distoDepth { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<unsigned> distoStages { 1, Range<unsigned>(1, maxDistoStages), 0 };
extern const OpcodeSpec<float> compAttack { 0.005f, Range<float>(0.0f, 10.0f), 0 };
extern const OpcodeSpec<float> compRelease { 0.05f, Range<float>(0.0f, 10.0f), 0 };
extern const OpcodeSpec<bool> compSTLink { false, Range<bool>(0, 1), 0 };
extern const OpcodeSpec<float> compThreshold { 0.0f, Range<float>(-100.0f, 0.0f), 0 };
extern const OpcodeSpec<float> compRatio { 1.0f, Range<float>(1.0f, 50.0f), 0 };
extern const OpcodeSpec<float> compGain { 0.0f, Range<float>(-100.0f, 100.0f), kDb2Mag };
extern const OpcodeSpec<float> fverbSize { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> fverbPredelay { 0.0f, Range<float>(0.0f, 10.0f), 0 };
extern const OpcodeSpec<float> fverbTone { 100.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> fverbDamp { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<bool> gateSTLink { false, Range<bool>(0, 1), 0 };
extern const OpcodeSpec<float> gateAttack { 0.005f, Range<float>(0.0f, 10.0f), 0 };
extern const OpcodeSpec<float> gateRelease { 0.05f, Range<float>(0.0f, 10.0f), 0 };
extern const OpcodeSpec<float> gateHold { 0.0f, Range<float>(0.0f, 10.0f), 0 };
extern const OpcodeSpec<float> gateThreshold { 0.0f, Range<float>(-100.0f, 0.0f), 0 };
extern const OpcodeSpec<float> lofiBitred { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> lofiDecim { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> rectify { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<unsigned> stringsNumber { maxStrings, Range<unsigned>(0, maxStrings), 0 };
extern const OpcodeSpec<Trigger> trigger { Trigger::attack, Range<Trigger>(Trigger::attack, Trigger::release_key), 0};
extern const OpcodeSpec<CrossfadeCurve> crossfadeCurve { CrossfadeCurve::power, Range<CrossfadeCurve>(CrossfadeCurve::gain, CrossfadeCurve::power), 0};
extern const OpcodeSpec<OffMode> offMode { OffMode::fast, Range<OffMode>(OffMode::fast, OffMode::time), 0};
extern const OpcodeSpec<LoopMode> loopMode { LoopMode::no_loop, Range<LoopMode>(LoopMode::no_loop, LoopMode::loop_sustain), 0};
extern const OpcodeSpec<VelocityOverride> velocityOverride { VelocityOverride::current, Range<VelocityOverride>(VelocityOverride::current, VelocityOverride::previous), 0};
extern const OpcodeSpec<SelfMask> selfMask { SelfMask::mask, Range<SelfMask>(SelfMask::mask, SelfMask::dontMask), 0};
extern const OpcodeSpec<FilterType> filter { FilterType::kFilterNone, Range<FilterType>(FilterType::kFilterNone, FilterType::kFilterPeq), 0};
extern const OpcodeSpec<EqType> eq { EqType::kEqNone, Range<EqType>(EqType::kEqNone, EqType::kEqHighShelf), 0};
} // namespace Default
} // namespace sfz

View file

@ -24,267 +24,290 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "Range.h"
#include "Config.h"
#include <limits>
#include <cstdint>
#include <type_traits>
#include "Range.h"
#include "Config.h"
#include "SfzFilter.h"
#include "SfzHelpers.h"
#include "MathHelpers.h"
enum class SfzTrigger { attack, release, release_key, first, legato };
enum class SfzLoopMode { no_loop, one_shot, loop_continuous, loop_sustain };
enum class SfzOffMode { fast, normal, time };
enum class SfzVelocityOverride { current, previous };
enum class SfzCrossfadeCurve { gain, power };
enum class SfzSelfMask { mask, dontMask };
namespace sfz
{
enum class Trigger { attack = 0, release, release_key, first, legato };
enum class LoopMode { no_loop = 0, one_shot, loop_continuous, loop_sustain };
enum class OffMode { fast = 0, normal, time };
enum class VelocityOverride { current = 0, previous };
enum class CrossfadeCurve { gain = 0, power };
enum class SelfMask { mask = 0, dontMask };
enum class OscillatorEnabled { Auto = -1, Off = 0, On = 1 };
enum class LFOWave : int {
Triangle,
Sine,
Pulse75,
Square,
Pulse25,
Pulse12_5,
Ramp,
Saw,
// ARIA extra
RandomSH = 12,
};
enum OpcodeFlags : int {
kCanBeNote = 1,
kEnforceLowerBound = 1 << 1,
kEnforceUpperBound = 1 << 2,
kNormalizePercent = 1 << 3,
kNormalizeMidi = 1 << 4,
kNormalizeBend = 1 << 5,
kWrapPhase = 1 << 6,
kDb2Mag = 1 << 7,
};
template<class T>
struct OpcodeSpec
{
T defaultInputValue;
Range<T> bounds;
int flags;
/**
* @brief Normalizes an input as needed for the spec
*
* @tparam U
* @param input
* @return U
*/
template<class U=T>
typename std::enable_if<std::is_arithmetic<U>::value, U>::type normalizeInput(U input) const
{
constexpr int needsOperation {
kNormalizePercent |
kNormalizeMidi |
kNormalizeBend |
kDb2Mag
};
if (!(flags & needsOperation))
return input;
else if (flags & kNormalizePercent)
return static_cast<U>(normalizePercents(input));
else if (flags & kNormalizeMidi)
return static_cast<U>(normalize7Bits(input));
else if (flags & kNormalizeBend)
return static_cast<U>(normalizeBend(input));
else if (flags & kDb2Mag)
return static_cast<U>(db2mag(input));
else // just in case
return input;
}
/**
* @brief Normalizes an input as needed for the spec
*
* @tparam U
* @param input
* @return U
*/
template<class U=T>
typename std::enable_if<!std::is_arithmetic<U>::value, U>::type normalizeInput(U input) const
{
return input;
}
operator T() const { return normalizeInput(defaultInputValue); }
};
namespace Default
{
// The categories match http://sfzformat.com/
// ******* SFZ 1 *******
// Sound source: sample playback
constexpr float delay { 0.0 };
constexpr float delayRandom { 0.0 };
constexpr Range<float> delayRange { 0.0, 100.0 };
constexpr int64_t offset { 0 };
constexpr int64_t offsetRandom { 0 };
constexpr Range<int64_t> offsetRange { 0, std::numeric_limits<uint32_t>::max() };
constexpr Range<int64_t> offsetCCRange = offsetRange;
constexpr Range<uint32_t> sampleEndRange { 0, std::numeric_limits<uint32_t>::max() };
constexpr Range<uint32_t> sampleCountRange { 0, std::numeric_limits<uint32_t>::max() };
constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop };
constexpr Range<uint32_t> loopRange { 0, std::numeric_limits<uint32_t>::max() };
constexpr float loopCrossfade { 1e-3 };
constexpr Range<float> loopCrossfadeRange { loopCrossfade, 1.0 };
extern const OpcodeSpec<float> delay;
extern const OpcodeSpec<float> delayRandom;
extern const OpcodeSpec<int64_t> offset;
extern const OpcodeSpec<int64_t> offsetMod;
extern const OpcodeSpec<int64_t> offsetRandom;
extern const OpcodeSpec<uint32_t> sampleEnd;
extern const OpcodeSpec<uint32_t> sampleCount;
extern const OpcodeSpec<uint32_t> loopStart;
extern const OpcodeSpec<uint32_t> loopEnd;
extern const OpcodeSpec<float> loopCrossfade;
extern const OpcodeSpec<float> oscillatorPhase;
extern const OpcodeSpec<OscillatorEnabled> oscillator;
extern const OpcodeSpec<int> oscillatorMode;
extern const OpcodeSpec<int> oscillatorMulti;
extern const OpcodeSpec<float> oscillatorDetune;
extern const OpcodeSpec<float> oscillatorDetuneMod;
extern const OpcodeSpec<float> oscillatorModDepth;
extern const OpcodeSpec<float> oscillatorModDepthMod;
extern const OpcodeSpec<int> oscillatorQuality;
extern const OpcodeSpec<uint32_t> group;
extern const OpcodeSpec<float> offTime;
extern const OpcodeSpec<uint32_t> polyphony;
extern const OpcodeSpec<uint32_t> notePolyphony;
extern const OpcodeSpec<uint8_t> key;
extern const OpcodeSpec<uint8_t> loKey;
extern const OpcodeSpec<uint8_t> hiKey;
extern const OpcodeSpec<float> loVel;
extern const OpcodeSpec<float> hiVel;
extern const OpcodeSpec<float> loCC;
extern const OpcodeSpec<float> hiCC;
extern const OpcodeSpec<float> loBend;
extern const OpcodeSpec<float> hiBend;
extern const OpcodeSpec<float> loNormalized;
extern const OpcodeSpec<float> hiNormalized;
extern const OpcodeSpec<float> loBipolar;
extern const OpcodeSpec<float> hiBipolar;
extern const OpcodeSpec<uint8_t> loChannelAftertouch;
extern const OpcodeSpec<uint8_t> hiChannelAftertouch;
extern const OpcodeSpec<uint16_t> ccNumber;
extern const OpcodeSpec<uint8_t> curveCC;
extern const OpcodeSpec<uint8_t> smoothCC;
extern const OpcodeSpec<uint8_t> sustainCC;
extern const OpcodeSpec<bool> checkSustain;
extern const OpcodeSpec<bool> checkSostenuto;
extern const OpcodeSpec<float> sustainThreshold;
extern const OpcodeSpec<float> loBPM;
extern const OpcodeSpec<float> hiBPM;
extern const OpcodeSpec<uint8_t> sequence;
extern const OpcodeSpec<float> volume;
extern const OpcodeSpec<float> volumeMod;
extern const OpcodeSpec<float> amplitude;
extern const OpcodeSpec<float> amplitudeMod;
extern const OpcodeSpec<float> pan;
extern const OpcodeSpec<float> panMod;
extern const OpcodeSpec<float> position;
extern const OpcodeSpec<float> positionMod;
extern const OpcodeSpec<float> width;
extern const OpcodeSpec<float> widthMod;
extern const OpcodeSpec<float> crossfadeIn;
extern const OpcodeSpec<float> crossfadeInNorm;
extern const OpcodeSpec<float> crossfadeOut;
extern const OpcodeSpec<float> crossfadeOutNorm;
extern const OpcodeSpec<float> ampKeytrack;
extern const OpcodeSpec<float> ampVeltrack;
extern const OpcodeSpec<float> ampVelcurve;
extern const OpcodeSpec<float> ampRandom;
extern const OpcodeSpec<bool> rtDead;
extern const OpcodeSpec<float> rtDecay;
extern const OpcodeSpec<float> filterCutoff;
extern const OpcodeSpec<float> filterCutoffMod;
extern const OpcodeSpec<float> filterResonance;
extern const OpcodeSpec<float> filterResonanceMod;
extern const OpcodeSpec<float> filterGain;
extern const OpcodeSpec<float> filterGainMod;
extern const OpcodeSpec<float> filterRandom;
extern const OpcodeSpec<int> filterKeytrack;
extern const OpcodeSpec<int> filterVeltrack;
extern const OpcodeSpec<float> eqBandwidth;
extern const OpcodeSpec<float> eqBandwidthMod;
extern const OpcodeSpec<float> eqFrequency;
extern const OpcodeSpec<float> eqFrequencyMod;
extern const OpcodeSpec<float> eqGain;
extern const OpcodeSpec<float> eqGainMod;
extern const OpcodeSpec<float> eqVel2Frequency;
extern const OpcodeSpec<float> eqVel2Gain;
extern const OpcodeSpec<int> pitchKeytrack;
extern const OpcodeSpec<float> pitchRandom;
extern const OpcodeSpec<int> pitchVeltrack;
extern const OpcodeSpec<int> transpose;
extern const OpcodeSpec<float> pitch;
extern const OpcodeSpec<float> pitchMod;
extern const OpcodeSpec<float> bendUp;
extern const OpcodeSpec<float> bendDown;
extern const OpcodeSpec<float> bendStep;
extern const OpcodeSpec<float> lfoFreq;
extern const OpcodeSpec<float> lfoFreqMod;
extern const OpcodeSpec<float> lfoBeats;
extern const OpcodeSpec<float> lfoBeatsMod;
extern const OpcodeSpec<float> lfoPhase;
extern const OpcodeSpec<float> lfoDelay;
extern const OpcodeSpec<float> lfoFade;
extern const OpcodeSpec<unsigned> lfoCount;
extern const OpcodeSpec<unsigned> lfoSteps;
extern const OpcodeSpec<float> lfoStepX;
extern const OpcodeSpec<LFOWave> lfoWave;
extern const OpcodeSpec<float> lfoOffset;
extern const OpcodeSpec<float> lfoRatio;
extern const OpcodeSpec<float> lfoScale;
extern const OpcodeSpec<float> egTime;
extern const OpcodeSpec<float> egRelease;
extern const OpcodeSpec<float> egTimeMod;
extern const OpcodeSpec<float> egPercent;
extern const OpcodeSpec<float> egPercentMod;
extern const OpcodeSpec<float> egDepth;
extern const OpcodeSpec<float> egVel2Depth;
extern const OpcodeSpec<bool> flexEGAmpeg;
extern const OpcodeSpec<int> flexEGDynamic;
extern const OpcodeSpec<int> flexEGSustain;
extern const OpcodeSpec<float> flexEGPointTime;
extern const OpcodeSpec<float> flexEGPointLevel;
extern const OpcodeSpec<float> flexEGPointShape;
extern const OpcodeSpec<int> sampleQuality;
extern const OpcodeSpec<int> octaveOffset;
extern const OpcodeSpec<int> noteOffset;
extern const OpcodeSpec<float> effect;
extern const OpcodeSpec<int> apanWaveform;
extern const OpcodeSpec<float> apanFrequency;
extern const OpcodeSpec<float> apanPhase;
extern const OpcodeSpec<float> apanLevel;
extern const OpcodeSpec<float> distoTone;
extern const OpcodeSpec<float> distoDepth;
extern const OpcodeSpec<unsigned> distoStages;
extern const OpcodeSpec<float> compAttack;
extern const OpcodeSpec<float> compRelease;
extern const OpcodeSpec<float> compThreshold;
extern const OpcodeSpec<bool> compSTLink;
extern const OpcodeSpec<float> compRatio;
extern const OpcodeSpec<float> compGain;
extern const OpcodeSpec<float> fverbSize;
extern const OpcodeSpec<float> fverbPredelay;
extern const OpcodeSpec<float> fverbTone;
extern const OpcodeSpec<float> fverbDamp;
extern const OpcodeSpec<float> gateAttack;
extern const OpcodeSpec<float> gateRelease;
extern const OpcodeSpec<bool> gateSTLink;
extern const OpcodeSpec<float> gateHold;
extern const OpcodeSpec<float> gateThreshold;
extern const OpcodeSpec<float> lofiBitred;
extern const OpcodeSpec<float> lofiDecim;
extern const OpcodeSpec<float> rectify;
extern const OpcodeSpec<unsigned> stringsNumber;
extern const OpcodeSpec<Trigger> trigger;
extern const OpcodeSpec<OffMode> offMode;
extern const OpcodeSpec<LoopMode> loopMode;
extern const OpcodeSpec<CrossfadeCurve> crossfadeCurve;
extern const OpcodeSpec<VelocityOverride> velocityOverride;
extern const OpcodeSpec<SelfMask> selfMask;
extern const OpcodeSpec<FilterType> filter;
extern const OpcodeSpec<EqType> eq;
// common defaults
constexpr Range<uint8_t> midi7Range { 0, 127 };
constexpr Range<float> float7Range { 0.0f, 127.0f };
constexpr Range<float> normalizedRange { 0.0f, 1.0f };
constexpr Range<float> symmetricNormalizedRange { -1.0, 1.0 };
// Default/max count for objects
constexpr int numEQs { 3 };
constexpr int numFilters { 2 };
constexpr int numFlexEGs { 4 };
constexpr int numFlexEGPoints { 8 };
constexpr int numLFOs { 4 };
constexpr int numLFOSubs { 2 };
constexpr int numLFOSteps { 8 };
constexpr int maxDistoStages { 4 };
constexpr unsigned maxStrings { 88 };
// Wavetable oscillator
constexpr float oscillatorPhase { 0.0 };
constexpr Range<float> oscillatorPhaseRange { -1.0, 1.0 };
constexpr int oscillatorMode { 0 };
constexpr int oscillatorMulti { 1 };
constexpr Range<int> oscillatorModeRange { 0, 2 };
constexpr Range<int> oscillatorMultiRange { 1, config::oscillatorsPerVoice };
constexpr float oscillatorDetune { 0 };
constexpr Range<float> oscillatorDetuneRange { -12000, 12000 };
constexpr Range<float> oscillatorDetuneCCRange { -12000, 12000 };
constexpr float oscillatorModDepth { 0 };
constexpr Range<float> oscillatorModDepthRange { 0, 10000 }; // depth%, allowed to be >100 for FM
constexpr Range<float> oscillatorModDepthCCRange { 0, 10000 };
constexpr int oscillatorQuality { 1 };
constexpr Range<int> oscillatorQualityRange { 0, 3 };
// Instrument setting: voice lifecycle
constexpr uint32_t group { 0 };
constexpr Range<uint32_t> groupRange { 0, std::numeric_limits<uint32_t>::max() };
constexpr SfzOffMode offMode { SfzOffMode::fast };
constexpr float offTime { 6e-3f };
constexpr Range<uint32_t> polyphonyRange { 0, config::maxVoices };
constexpr SfzSelfMask selfMask { SfzSelfMask::mask };
// Region logic: key mapping
constexpr Range<uint8_t> keyRange { 0, 127 };
constexpr auto velocityRange = normalizedRange;
// Region logic: MIDI conditions
constexpr Range<uint8_t> channelRange { 1, 16 };
constexpr Range<uint8_t> midiChannelRange { 0, 15 };
constexpr Range<uint8_t> smoothCCRange { 0, 100 };
constexpr float smoothTauPerStep { 3e-3 };
constexpr Range<uint8_t> curveCCRange { 0, 255 };
constexpr Range<uint16_t> ccNumberRange { 0, config::numCCs };
constexpr auto ccValueRange = normalizedRange;
constexpr Range<int> bendRange = { -8192, 8192 };
constexpr Range<float> bendValueRange = symmetricNormalizedRange;
constexpr int bend { 0 };
constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current };
// Region logic: internal conditions
constexpr Range<float> randRange { 0.0, 1.0 };
constexpr Range<uint8_t> aftertouchRange { 0, 127 };
constexpr uint8_t aftertouch { 0 };
constexpr Range<float> bpmRange { 0.0, 500.0 };
constexpr float bpm { 120.0 };
constexpr uint8_t sequenceLength{ 1 };
constexpr uint8_t sequencePosition{ 1 };
constexpr Range<uint8_t> sequenceRange { 1, 100 };
// Region logic: Triggers
constexpr SfzTrigger trigger { SfzTrigger::attack };
constexpr Range<float> ccTriggerValueRange = normalizedRange;
// Performance parameters: amplifier
constexpr float globalVolume { -7.35f };
constexpr float volume { 0.0f };
constexpr Range<float> volumeRange { -144.0, 48.0 };
constexpr Range<float> volumeCCRange { -144.0, 48.0 };
constexpr float amplitude { 100.0 };
constexpr Range<float> amplitudeRange { 0.0, 1e8 };
constexpr float pan { 0.0 };
constexpr Range<float> panRange { -100.0, 100.0 };
constexpr Range<float> panCCRange { -200.0, 200.0 };
constexpr float position { 0.0 };
constexpr Range<float> positionRange { -100.0, 100.0 };
constexpr Range<float> positionCCRange { -200.0, 200.0 };
constexpr float width { 100.0 };
constexpr Range<float> widthRange { -100.0, 100.0 };
constexpr Range<float> widthCCRange { -200.0, 200.0 };
constexpr uint8_t ampKeycenter { 60 };
constexpr float ampKeytrack { 0.0 };
constexpr Range<float> ampKeytrackRange { -96, 12 };
constexpr float ampVeltrack { 100.0 };
constexpr Range<float> ampVeltrackRange { -100.0, 100.0 };
constexpr Range<float> ampVelcurveRange { 0.0, 1.0 };
constexpr float ampRandom { 0.0 };
constexpr Range<float> ampRandomRange { 0.0, 24.0 };
constexpr Range<uint8_t> crossfadeKeyInRange { 0, 0 };
constexpr Range<uint8_t> crossfadeKeyOutRange { 127, 127 };
// Default values for ranges
constexpr Range<uint8_t> crossfadeKeyInRange { 0, 0 };
constexpr Range<uint8_t> crossfadeKeyOutRange { 127, 127 };
constexpr Range<float> crossfadeVelInRange { 0.0f, 0.0f };
constexpr Range<float> crossfadeVelOutRange { 1.0f, 1.0f };
constexpr Range<float> crossfadeCCInRange { 0.0f, 0.0f };
constexpr Range<float> crossfadeCCOutRange { 1.0f, 1.0f };
constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power };
constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power };
constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power };
constexpr float rtDecay { 0.0f };
constexpr bool rtDead { false };
constexpr Range<float> rtDecayRange { 0.0f, 200.0f };
// Performance parameters: Filters
constexpr int numFilters { 2 };
constexpr float filterCutoff { 0 };
constexpr float filterResonance { 0 };
constexpr float filterGain { 0 };
constexpr int filterKeytrack { 0 };
constexpr uint8_t filterKeycenter { 60 };
constexpr float filterRandom { 0 };
constexpr int filterVeltrack { 0 };
constexpr float filterCutoffCC { 0 };
constexpr float filterResonanceCC { 0 };
constexpr float filterGainCC { 0 };
constexpr Range<float> filterCutoffRange { 0.0f, 20000.0f };
constexpr Range<float> filterCutoffModRange { -12000, 12000 };
constexpr Range<float> filterGainRange { -96.0f, 96.0f };
constexpr Range<float> filterGainModRange { -96.0f, 96.0f };
constexpr Range<int> filterKeytrackRange { 0, 1200 };
constexpr Range<float> filterRandomRange { 0, 12000 };
constexpr Range<int> filterVeltrackRange { -12000, 12000 };
constexpr Range<float> filterResonanceRange { 0.0f, 96.0f };
constexpr Range<float> filterResonanceModRange { 0.0f, 96.0f };
// Various defaut values
// e.g. "additional" or multiple defautl values
constexpr int freewheelingQuality { 10 };
constexpr float globalVolume { -7.35f };
constexpr float defaultEQFreq [numEQs] { 50.0f, 500.0f, 5000.0f };
} // namespace Default
// Performance parameters: EQ
constexpr int numEQs { 3 };
constexpr float eqBandwidth { 1.0f };
constexpr float eqBandwidthCC { 0.0f };
constexpr float eqFrequencyUnset { 0.0f };
constexpr float eqFrequency1 { 50.0f };
constexpr float eqFrequency2 { 500.0f };
constexpr float eqFrequency3 { 5000.0f };
constexpr float eqFrequencyCC { 0.0f };
constexpr float eqGain { 0.0f };
constexpr float eqGainCC { 0.0f };
constexpr float eqVel2frequency { 0.0f };
constexpr float eqVel2gain { 0.0f };
constexpr Range<float> eqBandwidthRange { 0.001f, 4.0f };
constexpr Range<float> eqBandwidthModRange { -4.0f, 4.0f };
constexpr Range<float> eqFrequencyRange { 0.0f, 30000.0f };
constexpr Range<float> eqFrequencyModRange { -30000.0f, 30000.0f };
constexpr Range<float> eqGainRange { -96.0f, 96.0f };
constexpr Range<float> eqGainModRange { -96.0f, 96.0f };
// Performance parameters: pitch
constexpr uint8_t pitchKeycenter { 60 };
constexpr int pitchKeytrack { 100 };
constexpr Range<int> pitchKeytrackRange { -1200, 1200 };
constexpr float pitchRandom { 0 };
constexpr Range<float> pitchRandomRange { 0, 12000 };
constexpr int pitchVeltrack { 0 };
constexpr Range<int> pitchVeltrackRange { -12000, 12000 };
constexpr int transpose { 0 };
constexpr Range<int> transposeRange { -127, 127 };
constexpr float tune { 0 };
constexpr Range<float> tuneRange { -12000, 12000 }; // ±100 in SFZv1, more in ARIA
constexpr Range<float> tuneCCRange { -12000, 12000 };
constexpr Range<int> bendBoundRange { -12000, 12000 };
constexpr Range<int> bendStepRange { 1, 1200 };
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 };
// Modulation: LFO
constexpr int numLFOs { 4 };
constexpr int numLFOSubs { 2 };
constexpr int numLFOSteps { 8 };
constexpr Range<float> lfoFreqRange { 0.0, 100.0 };
constexpr Range<float> lfoFreqModRange { -100.0, 100.0 };
constexpr Range<float> lfoBeatsRange { 0.0, 1000.0 };
constexpr Range<float> lfoBeatsModRange { -1000.0, 1000.0 };
constexpr Range<float> lfoPhaseRange { 0.0, 1.0 };
constexpr Range<float> lfoDelayRange { 0.0, 30.0 };
constexpr Range<float> lfoFadeRange { 0.0, 30.0 };
constexpr Range<unsigned> lfoCountRange { 0, 1000 };
constexpr Range<unsigned> lfoStepsRange { 0, static_cast<unsigned>(config::maxLFOSteps) };
constexpr Range<float> lfoStepXRange { -100.0, 100.0 };
constexpr Range<int> lfoWaveRange { 0, 15 };
constexpr Range<float> lfoOffsetRange { -1.0, 1.0 };
constexpr Range<float> lfoRatioRange { 0.0, 100.0 };
constexpr Range<float> lfoScaleRange { 0.0, 1.0 };
// Envelope generators
constexpr float attack { 0 };
constexpr float decay { 0 };
constexpr float delayEG { 0 };
constexpr float hold { 0 };
constexpr float release { 0 };
constexpr float ampegRelease { 0.001 }; // Default release to avoid clicks
constexpr float vel2release { 0.0f };
constexpr float start { 0.0 };
constexpr float sustain { 100.0 };
constexpr uint16_t sustainCC { 64 };
constexpr float sustainThreshold { 0.0039f }; // sforzando default (0.5f/127.0f)
constexpr float vel2sustain { 0.0 };
constexpr int depth { 0 };
constexpr Range<float> egTimeRange { 0.0, 100.0 };
constexpr Range<float> egPercentRange { 0.0, 100.0 };
constexpr Range<int> egDepthRange { -12000, 12000 };
constexpr Range<float> egOnCCTimeRange { -100.0, 100.0 };
constexpr Range<float> egOnCCPercentRange { -100.0, 100.0 };
constexpr Range<float> pitchEgDepthRange { -12000.0, 12000.0 };
constexpr Range<float> filterEgDepthRange { -12000.0, 12000.0 };
// Flex envelope generators
constexpr int numFlexEGs { 4 };
constexpr int numFlexEGPoints { 8 };
constexpr int flexEGDynamic { 0 };
constexpr int flexEGSustain { 0 };
constexpr float flexEGPointTime { 0 };
constexpr float flexEGPointLevel { 0 };
constexpr float flexEGPointShape { 0 };
constexpr Range<int> flexEGDynamicRange { 0, 1 };
constexpr Range<int> flexEGSustainRange { 0, 100 };
constexpr Range<float> flexEGPointTimeRange { 0.0f, 100.0f };
constexpr Range<float> flexEGPointLevelRange { -1.0f, 1.0f };
constexpr Range<float> flexEGPointShapeRange { -100.0f, 100.0f };
// ***** SFZ v2 ********
constexpr int sampleQuality { 1 };
constexpr int sampleQualityInFreewheelingMode { 10 }; // for future use, possibly excessive
constexpr Range<int> sampleQualityRange { 1, 10 }; // sample_quality
constexpr bool checkSustain { true }; // sustain_sw
constexpr bool checkSostenuto { true }; // sostenuto_sw
constexpr Range<int> octaveOffsetRange { -10, 10 }; // octave_offset
constexpr Range<int> noteOffsetRange { -127, 127 }; // note_offset
constexpr Range<int> apanWaveformRange { 0, std::numeric_limits<int>::max() };
constexpr Range<float> apanFrequencyRange { 0, std::numeric_limits<float>::max() };
constexpr Range<float> apanPhaseRange { 0.0, 1.0 };
constexpr Range<float> apanLevelRange { 0.0, 100.0 };
}
}
} // namespace sfz

View file

@ -66,21 +66,21 @@ struct EGDescription {
EGDescription& operator=(const EGDescription&) = default;
EGDescription& operator=(EGDescription&&) = default;
float attack { Default::attack };
float decay { Default::decay };
float delay { Default::delayEG };
float hold { Default::hold };
float release { Default::release };
float start { Default::start };
float sustain { Default::sustain };
int depth { Default::depth };
float vel2attack { Default::attack };
float vel2decay { Default::decay };
float vel2delay { Default::delayEG };
float vel2hold { Default::hold };
float vel2release { Default::vel2release };
float vel2sustain { Default::vel2sustain };
int vel2depth { Default::depth };
float attack { Default::egTime };
float decay { Default::egTime };
float delay { Default::egTime };
float hold { Default::egTime };
float release { Default::egTime };
float start { Default::egPercent.bounds.getStart() };
float sustain { Default::egPercent.bounds.getEnd() };
float depth { Default::egDepth };
float vel2attack { Default::egTimeMod };
float vel2decay { Default::egTimeMod };
float vel2delay { Default::egTimeMod };
float vel2hold { Default::egTimeMod };
float vel2release { Default::egPercentMod };
float vel2sustain { Default::egPercentMod };
float vel2depth { Default::egVel2Depth };
CCMap<float> ccAttack;
CCMap<float> ccDecay;
@ -104,7 +104,7 @@ struct EGDescription {
for (auto& mod: ccAttack) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
return Default::egTime.bounds.clamp(returnedValue);
}
/**
* @brief Get the decay with possibly a CC modifier and a velocity modifier
@ -120,7 +120,7 @@ struct EGDescription {
for (auto& mod: ccDecay) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
return Default::egTime.bounds.clamp(returnedValue);
}
/**
* @brief Get the delay with possibly a CC modifier and a velocity modifier
@ -136,7 +136,7 @@ struct EGDescription {
for (auto& mod: ccDelay) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
return Default::egTime.bounds.clamp(returnedValue);
}
/**
* @brief Get the holding duration with possibly a CC modifier and a velocity modifier
@ -152,7 +152,7 @@ struct EGDescription {
for (auto& mod: ccHold) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
return Default::egTime.bounds.clamp(returnedValue);
}
/**
* @brief Get the release duration with possibly a CC modifier and a velocity modifier
@ -168,7 +168,7 @@ struct EGDescription {
for (auto& mod: ccRelease) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
return Default::egTime.bounds.clamp(returnedValue);
}
/**
* @brief Get the starting level with possibly a CC modifier and a velocity modifier
@ -184,7 +184,7 @@ struct EGDescription {
for (auto& mod: ccStart) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egPercentRange.clamp(returnedValue);
return Default::egPercent.bounds.clamp(returnedValue);
}
/**
* @brief Get the sustain level with possibly a CC modifier and a velocity modifier
@ -200,7 +200,7 @@ struct EGDescription {
for (auto& mod: ccSustain) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egPercentRange.clamp(returnedValue);
return Default::egPercent.bounds.clamp(returnedValue);
}
LEAK_DETECTOR(EGDescription);
};

View file

@ -15,10 +15,10 @@ namespace sfz
struct EQDescription
{
float bandwidth { Default::eqBandwidth };
float frequency { Default::eqFrequencyUnset };
float frequency { Default::eqFrequency };
float gain { Default::eqGain };
float vel2frequency { Default::eqVel2frequency };
float vel2gain { Default::eqVel2gain };
float vel2frequency { Default::eqVel2Frequency };
float vel2gain { Default::eqVel2Gain };
EqType type { EqType::kEqPeak };
};
}

View file

@ -44,7 +44,7 @@ private:
const EQDescription* description;
std::unique_ptr<FilterEq> eq;
float baseBandwidth { Default::eqBandwidth };
float baseFrequency { Default::eqFrequency1 };
float baseFrequency { Default::eqFrequency };
float baseGain { Default::eqGain };
bool prepared { false };
ModMatrix::TargetId gainTarget;

View file

@ -6,6 +6,7 @@
#pragma once
#include "AudioBuffer.h"
#include "Defaults.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include <array>
@ -178,8 +179,8 @@ private:
std::vector<std::unique_ptr<Effect>> _effects;
AudioBuffer<float> _inputs { EffectChannels, config::defaultSamplesPerBlock };
AudioBuffer<float> _outputs { EffectChannels, config::defaultSamplesPerBlock };
float _gainToMain = 0.0;
float _gainToMix = 0.0;
float _gainToMain { Default::effect };
float _gainToMix { Default::effect };
};
} // namespace sfz

View file

@ -28,7 +28,7 @@ struct RiffChunkInfo {
/**
@brief Loop mode, like SF_LOOP_*
*/
enum LoopMode {
enum FileLoopMode {
LoopNone,
LoopForward,
LoopBackward,
@ -52,7 +52,7 @@ struct InstrumentInfo {
} loops[16];
};
#else
enum LoopMode {
enum FileLoopMode {
LoopNone = SF_LOOP_NONE,
LoopForward = SF_LOOP_FORWARD,
LoopBackward = SF_LOOP_BACKWARD,

View file

@ -255,7 +255,7 @@ absl::optional<sfz::FileInformation> sfz::FilePool::getFileInformation(const Fil
if (!fileId.isReverse()) {
if (haveInstrumentInfo && instrumentInfo.loop_count > 0) {
returnedValue.hasLoop = true;
returnedValue.loopBegin = instrumentInfo.loops[0].start;
returnedValue.loopStart = instrumentInfo.loops[0].start;
returnedValue.loopEnd = min(returnedValue.end, instrumentInfo.loops[0].end - 1);
}
} else {
@ -297,7 +297,7 @@ bool sfz::FilePool::preloadFile(const FileId& fileId, uint32_t maxOffset) noexce
const auto factor = static_cast<double>(oversamplingFactor);
fileInformation->sampleRate = factor * static_cast<double>(reader->sampleRate());
fileInformation->end = static_cast<uint32_t>(factor * fileInformation->end);
fileInformation->loopBegin = static_cast<uint32_t>(factor * fileInformation->loopBegin);
fileInformation->loopStart = static_cast<uint32_t>(factor * fileInformation->loopStart);
fileInformation->loopEnd = static_cast<uint32_t>(factor * fileInformation->loopEnd);
auto insertedPair = preloadedFiles.insert_or_assign(fileId, {
readFromFile(*reader, framesToLoad, oversamplingFactor),
@ -329,7 +329,7 @@ sfz::FileDataHolder sfz::FilePool::loadFile(const FileId& fileId) noexcept
const auto factor = static_cast<double>(oversamplingFactor);
fileInformation->sampleRate = factor * static_cast<double>(reader->sampleRate());
fileInformation->end = static_cast<uint32_t>(factor * fileInformation->end);
fileInformation->loopBegin = static_cast<uint32_t>(factor * fileInformation->loopBegin);
fileInformation->loopStart = static_cast<uint32_t>(factor * fileInformation->loopStart);
fileInformation->loopEnd = static_cast<uint32_t>(factor * fileInformation->loopEnd);
auto insertedPair = preloadedFiles.insert_or_assign(fileId, {
readFromFile(*reader, frames, oversamplingFactor),
@ -461,7 +461,7 @@ void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept
FileInformation& information = preloadedFile.second.information;
information.sampleRate *= samplerateChange;
information.end = static_cast<uint32_t>(samplerateChange * information.end);
information.loopBegin = static_cast<uint32_t>(samplerateChange * information.loopBegin);
information.loopStart = static_cast<uint32_t>(samplerateChange * information.loopStart);
information.loopEnd = static_cast<uint32_t>(samplerateChange * information.loopEnd);
if (preloadedFile.second.status == FileData::Status::Done) {

View file

@ -51,10 +51,10 @@ using FileAudioBuffer = AudioBuffer<float, 2, config::defaultAlignment,
using FileAudioBufferPtr = std::shared_ptr<FileAudioBuffer>;
struct FileInformation {
uint32_t end { Default::sampleEndRange.getEnd() };
uint32_t end { Default::sampleEnd };
uint32_t maxOffset { 0 };
uint32_t loopBegin { Default::loopRange.getStart() };
uint32_t loopEnd { Default::loopRange.getEnd() };
uint32_t loopStart { Default::loopStart };
uint32_t loopEnd { Default::loopEnd };
bool hasLoop { false };
double sampleRate { config::defaultSampleRate };
int numChannels { 0 };

View file

@ -18,7 +18,7 @@ struct FilterDescription
float resonance { Default::filterCutoff };
float gain { Default::filterGain };
int keytrack { Default::filterKeytrack };
uint8_t keycenter { Default::filterKeycenter };
uint8_t keycenter { Default::key };
int veltrack { Default::filterVeltrack };
float random { Default::filterRandom };
FilterType type { FilterType::kFilterLpf2p };

View file

@ -37,7 +37,7 @@ void sfz::FilterHolder::setup(const Region& region, unsigned filterId, int noteN
baseCutoff *= centsFactor(keytrack);
const auto veltrack = static_cast<float>(description->veltrack) * velocity;
baseCutoff *= centsFactor(veltrack);
baseCutoff = Default::filterCutoffRange.clamp(baseCutoff);
baseCutoff = Default::filterCutoff.bounds.clamp(baseCutoff);
baseGain = description->gain;
baseResonance = description->resonance;
@ -75,7 +75,7 @@ void sfz::FilterHolder::process(const float** inputs, float** outputs, unsigned
for (size_t i = 0; i < numFrames; ++i)
(*cutoffSpan)[i] *= centsFactor(mod[i]);
}
sfz::clampAll(*cutoffSpan, Default::filterCutoffRange);
sfz::clampAll(*cutoffSpan, Default::filterCutoff.bounds);
fill<float>(*resonanceSpan, baseResonance);
if (float* mod = mm.getModulation(resonanceTarget))

View file

@ -22,7 +22,7 @@ public:
* @param noteNumber the triggering note number
* @param velocity the triggering note velocity/value
*/
void setup(const Region& region, unsigned filterId, int noteNumber = static_cast<int>(Default::filterKeycenter), float velocity = 0);
void setup(const Region& region, unsigned filterId, int noteNumber = static_cast<int>(Default::key), float velocity = 0);
/**
* @brief Process a block of stereo inputs
*

View file

@ -35,7 +35,7 @@ struct FlexEGDescription {
int sustain { Default::flexEGSustain }; // index of the sustain point (default to 0 in ARIA)
std::vector<FlexEGPoint> points;
// ARIA
bool ampeg = false; // replaces the SFZv1 AmpEG (lowest with this bit wins)
bool ampeg { Default::flexEGAmpeg }; // replaces the SFZv1 AmpEG (lowest with this bit wins)
};
} // namespace sfz

View file

@ -5,39 +5,27 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include "Defaults.h"
#include <absl/types/optional.h>
#include <vector>
namespace sfz {
enum class LFOWave : int {
Triangle,
Sine,
Pulse75,
Square,
Pulse25,
Pulse12_5,
Ramp,
Saw,
// ARIA extra
RandomSH = 12,
};
struct LFODescription {
LFODescription();
~LFODescription();
static const LFODescription& getDefault();
float freq = 0; // lfoN_freq
float beats = 0; // lfoN_beats
float phase0 = 0; // lfoN_phase
float delay = 0; // lfoN_delay
float fade = 0; // lfoN_fade
unsigned count = 0; // lfoN_count
float freq { Default::lfoFreq }; // lfoN_freq
float beats { Default::lfoBeats }; // lfoN_beats
float phase0 { Default::lfoPhase }; // lfoN_phase
float delay { Default::lfoDelay }; // lfoN_delay
float fade { Default::lfoFade }; // lfoN_fade
unsigned count { Default::lfoCount }; // lfoN_count
struct Sub {
LFOWave wave = LFOWave::Triangle; // lfoN_wave[X]
float offset = 0; // lfoN_offset[X]
float ratio = 1; // lfoN_ratio[X]
float scale = 1; // lfoN_scale[X]
LFOWave wave { Default::lfoWave }; // lfoN_wave[X]
float offset { Default::lfoOffset }; // lfoN_offset[X]
float ratio { Default::lfoRatio }; // lfoN_ratio[X]
float scale { Default::lfoScale }; // lfoN_scale[X]
};
struct StepSequence {
std::vector<float> steps {}; // lfoN_stepX - normalized to unity

View file

@ -16,7 +16,7 @@ namespace sfz {
* @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...)
*/
template <class T, class U>
float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve)
float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, CrossfadeCurve curve)
{
if (value < crossfadeRange.getStart())
return 0.0f;
@ -27,9 +27,9 @@ float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurv
else if (value < crossfadeRange.getEnd()) {
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
if (curve == SfzCrossfadeCurve::power)
if (curve == CrossfadeCurve::power)
return sqrt(crossfadePosition);
if (curve == SfzCrossfadeCurve::gain)
if (curve == CrossfadeCurve::gain)
return crossfadePosition;
}
@ -40,7 +40,7 @@ float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurv
* @brief Compute a crossfade out value with respect to a crossfade range (note, velocity, cc, ...)
*/
template <class T, class U>
float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve)
float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, CrossfadeCurve curve)
{
if (value > crossfadeRange.getEnd())
return 0.0f;
@ -51,9 +51,9 @@ float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCur
else if (value > crossfadeRange.getStart()) {
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
if (curve == SfzCrossfadeCurve::power)
if (curve == CrossfadeCurve::power)
return std::sqrt(1 - crossfadePosition);
if (curve == SfzCrossfadeCurve::gain)
if (curve == CrossfadeCurve::gain)
return 1 - crossfadePosition;
}

View file

@ -5,6 +5,7 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "Opcode.h"
#include "LFODescription.h"
#include "StringViewHelpers.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
@ -117,12 +118,112 @@ OpcodeCategory Opcode::identifyCategory(absl::string_view name)
return category;
}
template <typename T>
absl::optional<T> readInt_(OpcodeSpec<T> spec, absl::string_view v)
{
size_t numberEnd = 0;
if (numberEnd < v.size() && (v[numberEnd] == '+' || v[numberEnd] == '-'))
++numberEnd;
while (numberEnd < v.size() && absl::ascii_isdigit(v[numberEnd]))
++numberEnd;
if (numberEnd == 0 && (spec.flags & kCanBeNote))
return readNoteValue(v);
v = v.substr(0, numberEnd);
int64_t returnedValue;
if (!absl::SimpleAtoi(v, &returnedValue))
return absl::nullopt;
if (returnedValue > static_cast<int64_t>(spec.bounds.getEnd())) {
if (spec.flags & kEnforceUpperBound)
return spec.bounds.getEnd();
return absl::nullopt;
} else if (returnedValue < static_cast<int64_t>(spec.bounds.getStart())) {
if (spec.flags & kEnforceLowerBound)
return spec.bounds.getStart();
return absl::nullopt;
}
return returnedValue;
}
#define INSTANTIATE_FOR_INTEGRAL(T) \
template <> \
absl::optional<T> Opcode::readOptional(OpcodeSpec<T> spec) const \
{ \
return readInt_<T>(spec, value); \
}
INSTANTIATE_FOR_INTEGRAL(uint8_t)
INSTANTIATE_FOR_INTEGRAL(uint16_t)
INSTANTIATE_FOR_INTEGRAL(uint32_t)
INSTANTIATE_FOR_INTEGRAL(int8_t)
INSTANTIATE_FOR_INTEGRAL(int16_t)
INSTANTIATE_FOR_INTEGRAL(int32_t)
INSTANTIATE_FOR_INTEGRAL(int64_t)
template <typename T>
absl::optional<T> readFloat_(OpcodeSpec<T> spec, absl::string_view v)
{
size_t numberEnd = 0;
if (numberEnd < v.size() && (v[numberEnd] == '+' || v[numberEnd] == '-'))
++numberEnd;
while (numberEnd < v.size() && absl::ascii_isdigit(v[numberEnd]))
++numberEnd;
if (numberEnd < v.size() && v[numberEnd] == '.') {
++numberEnd;
while (numberEnd < v.size() && absl::ascii_isdigit(v[numberEnd]))
++numberEnd;
}
v = v.substr(0, numberEnd);
float returnedValue;
if (!absl::SimpleAtof(v, &returnedValue))
return absl::nullopt;
if (spec.flags & kWrapPhase)
returnedValue = wrapPhase(returnedValue);
else if (returnedValue > static_cast<int64_t>(spec.bounds.getEnd())) {
if (spec.flags & kEnforceUpperBound)
return spec.bounds.getEnd();
return absl::nullopt;
} else if (returnedValue < static_cast<int64_t>(spec.bounds.getStart())) {
if (spec.flags & kEnforceLowerBound)
return spec.bounds.getStart();
return absl::nullopt;
}
return spec.normalizeInput(returnedValue);
}
#define INSTANTIATE_FOR_FLOATING_POINT(T) \
template <> \
absl::optional<T> Opcode::readOptional(OpcodeSpec<T> spec) const \
{ \
return readFloat_<T>(spec, value); \
}
INSTANTIATE_FOR_FLOATING_POINT(float)
INSTANTIATE_FOR_FLOATING_POINT(double)
absl::optional<uint8_t> readNoteValue(absl::string_view value)
{
char noteLetter = absl::ascii_tolower(value.empty() ? '\0' : value.front());
value.remove_prefix(1);
if (noteLetter < 'a' || noteLetter > 'g')
return {};
return absl::nullopt;
constexpr int offsetsABCDEFG[] = { 9, 11, 0, 2, 4, 5, 7 };
int noteNumber = offsetsABCDEFG[noteLetter - 'a'];
@ -143,11 +244,11 @@ absl::optional<uint8_t> readNoteValue(absl::string_view value)
if (absl::StartsWith(value, prefix.first)) {
if (prefix.second == +1) {
if (validSharpLetters.find(noteLetter) == absl::string_view::npos)
return {};
return absl::nullopt;
}
else if (prefix.second == -1) {
if (validFlatLetters.find(noteLetter) == absl::string_view::npos)
return {};
return absl::nullopt;
}
noteNumber += prefix.second;
value.remove_prefix(prefix.first.size());
@ -157,64 +258,14 @@ absl::optional<uint8_t> readNoteValue(absl::string_view value)
int octaveNumber;
if (!absl::SimpleAtoi(value, &octaveNumber))
return {};
return absl::nullopt;
noteNumber += (octaveNumber + 1) * 12;
if (noteNumber < 0 || noteNumber >= 128)
return {};
return static_cast<uint8_t>(noteNumber);
}
///
template <typename ValueType, absl::enable_if_t<std::is_integral<ValueType>::value, int>>
absl::optional<ValueType> readOpcode(absl::string_view value, const Range<ValueType>& validRange)
{
size_t numberEnd = 0;
if (numberEnd < value.size() && (value[numberEnd] == '+' || value[numberEnd] == '-'))
++numberEnd;
while (numberEnd < value.size() && absl::ascii_isdigit(value[numberEnd]))
++numberEnd;
value = value.substr(0, numberEnd);
int64_t returnedValue;
if (!absl::SimpleAtoi(value, &returnedValue))
return absl::nullopt;
if (returnedValue > std::numeric_limits<ValueType>::max())
returnedValue = std::numeric_limits<ValueType>::max();
if (returnedValue < std::numeric_limits<ValueType>::min())
returnedValue = std::numeric_limits<ValueType>::min();
return validRange.clamp(static_cast<ValueType>(returnedValue));
}
template <typename ValueType, absl::enable_if_t<std::is_floating_point<ValueType>::value, int>>
absl::optional<ValueType> readOpcode(absl::string_view value, const Range<ValueType>& validRange)
{
size_t numberEnd = 0;
if (numberEnd < value.size() && (value[numberEnd] == '+' || value[numberEnd] == '-'))
++numberEnd;
while (numberEnd < value.size() && absl::ascii_isdigit(value[numberEnd]))
++numberEnd;
if (numberEnd < value.size() && value[numberEnd] == '.') {
++numberEnd;
while (numberEnd < value.size() && absl::ascii_isdigit(value[numberEnd]))
++numberEnd;
}
value = value.substr(0, numberEnd);
float returnedValue;
if (!absl::SimpleAtof(value, &returnedValue))
return absl::nullopt;
return validRange.clamp(returnedValue);
return static_cast<uint8_t>(noteNumber);
}
absl::optional<bool> readBooleanFromOpcode(const Opcode& opcode)
@ -222,88 +273,176 @@ absl::optional<bool> readBooleanFromOpcode(const Opcode& opcode)
// Cakewalk-style booleans, case-insensitive
if (absl::EqualsIgnoreCase(opcode.value, "off"))
return false;
if (absl::EqualsIgnoreCase(opcode.value, "on"))
return true;
// ARIA-style booleans? (seen in egN_dynamic=1 for example)
// TODO check this
if (auto value = readOpcode(opcode.value, Range<int64_t>::wholeRange()))
return *value != 0;
const OpcodeSpec<int64_t> fullInt64 { 0, Range<int64_t>::wholeRange(), 0 };
const auto v = opcode.readOptional(fullInt64);
if (v)
return v != 0;
return absl::nullopt;
}
template <class ValueType>
void setValueFromOpcode(const Opcode& opcode, ValueType& target, const Range<ValueType>& validRange)
template <>
absl::optional<OscillatorEnabled> Opcode::readOptional(OpcodeSpec<OscillatorEnabled>) const
{
auto value = readOpcode(opcode.value, validRange);
if (!value) // Try and read a note rather than a number
value = readNoteValue(opcode.value);
if (value)
target = *value;
auto v = readBooleanFromOpcode(*this);
if (!v)
return absl::nullopt;
return *v ? OscillatorEnabled::On : OscillatorEnabled::Off;
}
template <class ValueType>
inline void setValueFromOpcode(const Opcode& opcode, absl::optional<ValueType>& target, const Range<ValueType>& validRange)
template <>
absl::optional<Trigger> Opcode::readOptional(OpcodeSpec<Trigger>) const
{
auto value = readOpcode(opcode.value, validRange);
if (!value) // Try and read a note rather than a number
value = readNoteValue(opcode.value);
if (value)
target = *value;
switch (hash(value)) {
case hash("attack"): return Trigger::attack;
case hash("first"): return Trigger::first;
case hash("legato"): return Trigger::legato;
case hash("release"): return Trigger::release;
case hash("release_key"): return Trigger::release_key;
}
DBG("Unknown trigger value: " << value);
return absl::nullopt;
}
template <class ValueType>
void setRangeEndFromOpcode(const Opcode& opcode, Range<ValueType>& target, const Range<ValueType>& validRange)
template <>
absl::optional<CrossfadeCurve> Opcode::readOptional(OpcodeSpec<CrossfadeCurve>) const
{
auto value = readOpcode(opcode.value, validRange);
if (!value) // Try and read a note rather than a number
value = readNoteValue(opcode.value);
if (value)
target.setEnd(*value);
switch (hash(value)) {
case hash("power"): return CrossfadeCurve::power;
case hash("gain"): return CrossfadeCurve::gain;
}
DBG("Unknown crossfade power curve: " << value);
return absl::nullopt;
}
template <class ValueType>
void setRangeStartFromOpcode(const Opcode& opcode, Range<ValueType>& target, const Range<ValueType>& validRange)
template <>
absl::optional<OffMode> Opcode::readOptional(OpcodeSpec<OffMode>) const
{
auto value = readOpcode(opcode.value, validRange);
if (!value) // Try and read a note rather than a number
value = readNoteValue(opcode.value);
if (value)
target.setStart(*value);
switch (hash(value)) {
case hash("fast"): return OffMode::fast;
case hash("normal"): return OffMode::normal;
case hash("time"): return OffMode::time;
}
DBG("Unknown off mode: " << value);
return absl::nullopt;
}
template <class ValueType>
void setCCPairFromOpcode(const Opcode& opcode, absl::optional<CCData<ValueType>>& target, const Range<ValueType>& validRange)
template <>
absl::optional<FilterType> Opcode::readOptional(OpcodeSpec<FilterType>) const
{
auto value = readOpcode(opcode.value, validRange);
if (value && Default::ccNumberRange.containsWithEnd(opcode.parameters.back()))
target = { opcode.parameters.back(), *value };
else
target = {};
switch (hash(value)) {
case hash("lpf_1p"): return kFilterLpf1p;
case hash("hpf_1p"): return kFilterHpf1p;
case hash("lpf_2p"): return kFilterLpf2p;
case hash("hpf_2p"): return kFilterHpf2p;
case hash("bpf_2p"): return kFilterBpf2p;
case hash("brf_2p"): return kFilterBrf2p;
case hash("bpf_1p"): return kFilterBpf1p;
case hash("brf_1p"): return kFilterBrf1p;
case hash("apf_1p"): return kFilterApf1p;
case hash("lpf_2p_sv"): return kFilterLpf2pSv;
case hash("hpf_2p_sv"): return kFilterHpf2pSv;
case hash("bpf_2p_sv"): return kFilterBpf2pSv;
case hash("brf_2p_sv"): return kFilterBrf2pSv;
case hash("lpf_4p"): return kFilterLpf4p;
case hash("hpf_4p"): return kFilterHpf4p;
case hash("lpf_6p"): return kFilterLpf6p;
case hash("hpf_6p"): return kFilterHpf6p;
case hash("pink"): return kFilterPink;
case hash("lsh"): return kFilterLsh;
case hash("hsh"): return kFilterHsh;
case hash("bpk_2p"): //fallthrough
case hash("pkf_2p"): //fallthrough
case hash("peq"): return kFilterPeq;
}
DBG("Unknown filter type: " << value);
return absl::nullopt;
}
///
#define INSTANCIATE_FOR(T) \
template absl::optional<T> readOpcode<T>(absl::string_view value, const Range<T>& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \
template void setValueFromOpcode<T>(const Opcode& opcode, T& target, const Range<T>& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \
template void setValueFromOpcode<T>(const Opcode& opcode, absl::optional<T>& target, const Range<T>& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \
template void setRangeEndFromOpcode(const Opcode& opcode, Range<T>& target, const Range<T>& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \
template void setRangeStartFromOpcode(const Opcode& opcode, Range<T>& target, const Range<T>& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \
template void setCCPairFromOpcode(const Opcode& opcode, absl::optional<CCData<T>>& target, const Range<T>& validRange); /*NOLINT(bugprone-macro-parentheses)*/
template <>
absl::optional<EqType> Opcode::readOptional(OpcodeSpec<EqType>) const
{
switch (hash(value)) {
case hash("peak"): return kEqPeak;
case hash("lshelf"): return kEqLowShelf;
case hash("hshelf"): return kEqHighShelf;
}
INSTANCIATE_FOR(float)
INSTANCIATE_FOR(double)
INSTANCIATE_FOR(int8_t)
INSTANCIATE_FOR(int16_t)
INSTANCIATE_FOR(int32_t)
INSTANCIATE_FOR(int64_t)
INSTANCIATE_FOR(uint8_t)
INSTANCIATE_FOR(uint16_t)
INSTANCIATE_FOR(uint32_t)
//INSTANCIATE_FOR(uint64_t)
DBG("Unknown EQ type: " << value);
return absl::nullopt;
}
#undef INSTANCIATE_FOR
template <>
absl::optional<VelocityOverride> Opcode::readOptional(OpcodeSpec<VelocityOverride>) const
{
switch (hash(value)) {
case hash("current"): return VelocityOverride::current;
case hash("previous"): return VelocityOverride::previous;
}
DBG("Unknown velocity override: " << value);
return absl::nullopt;
}
template <>
absl::optional<SelfMask> Opcode::readOptional(OpcodeSpec<SelfMask>) const
{
switch (hash(value)) {
case hash("on"):
case hash("mask"): return SelfMask::mask;
case hash("off"): return SelfMask::dontMask;
}
DBG("Unknown velocity override: " << value);
return absl::nullopt;
}
template <>
absl::optional<LoopMode> Opcode::readOptional(OpcodeSpec<LoopMode>) const
{
switch (hash(value)) {
case hash("no_loop"): return LoopMode::no_loop;
case hash("one_shot"): return LoopMode::one_shot;
case hash("loop_continuous"): return LoopMode::loop_continuous;
case hash("loop_sustain"): return LoopMode::loop_sustain;
}
DBG("Unknown loop mode: " << value);
return absl::nullopt;
}
template <>
absl::optional<bool> Opcode::readOptional(OpcodeSpec<bool>) const
{
return readBooleanFromOpcode(*this);
}
template <>
absl::optional<LFOWave> Opcode::readOptional(OpcodeSpec<LFOWave> spec) const
{
const OpcodeSpec<int> intSpec {
static_cast<int>(spec.defaultInputValue),
Range<int>(static_cast<int>(spec.bounds.getStart()), static_cast<int>(spec.bounds.getEnd())),
0
};
if (auto value = readOptional(intSpec))
return static_cast<LFOWave>(*value);
return absl::nullopt;
}
} // namespace sfz

View file

@ -101,6 +101,12 @@ struct Opcode {
category == kOpcodeStepCcN || category == kOpcodeSmoothCcN;
}
template <class T>
absl::optional<T> readOptional(OpcodeSpec<T> spec) const;
template <class T>
T read(OpcodeSpec<T> spec) const { return readOptional(spec).value_or(spec); }
private:
static OpcodeCategory identifyCategory(absl::string_view name);
LEAK_DETECTOR(Opcode);
@ -114,96 +120,11 @@ private:
*/
absl::optional<uint8_t> readNoteValue(absl::string_view value);
/**
* @brief Read a value from the sfz file and cast it to the destination parameter along
* with a proper clamping into range if needed. This particular template version acts on
* integral target types, but can accept floats as an input.
*
* @tparam ValueType the target casting type
* @param value the string value to be read and stored
* @param validRange the range of admitted values
* @return absl::optional<ValueType> the cast value, or null
*/
template <typename ValueType, absl::enable_if_t<std::is_integral<ValueType>::value, int> = 0>
absl::optional<ValueType> readOpcode(absl::string_view value, const Range<ValueType>& validRange);
/**
* @brief Read a value from the sfz file and cast it to the destination parameter along
* with a proper clamping into range if needed. This particular template version acts on
* floating types.
*
* @tparam ValueType the target casting type
* @param value the string value to be read and stored
* @param validRange the range of admitted values
* @return absl::optional<ValueType> the cast value, or null
*/
template <typename ValueType, absl::enable_if_t<std::is_floating_point<ValueType>::value, int> = 0>
absl::optional<ValueType> readOpcode(absl::string_view value, const Range<ValueType>& validRange);
/**
* @brief Read a boolean value from the sfz file and cast it to the destination parameter.
*/
absl::optional<bool> readBooleanFromOpcode(const Opcode& opcode);
/**
* @brief Set a target parameter from an opcode value, with possibly a textual note rather
* than a number
*
* @tparam ValueType
* @param opcode the source opcode
* @param target the value to update
* @param validRange the range of admitted values used to clamp the opcode
*/
template <class ValueType>
void setValueFromOpcode(const Opcode& opcode, ValueType& target, const Range<ValueType>& validRange);
/**
* @brief Set a target parameter from an opcode value, with possibly a textual note rather
* than a number
*
* @tparam ValueType
* @param opcode the source opcode
* @param target the value to update
* @param validRange the range of admitted values used to clamp the opcode
*/
template <class ValueType>
void setValueFromOpcode(const Opcode& opcode, absl::optional<ValueType>& target, const Range<ValueType>& validRange);
/**
* @brief Set a target end of a range from an opcode value, with possibly a textual note rather
* than a number
*
* @tparam ValueType
* @param opcode the source opcode
* @param target the value to update
* @param validRange the range of admitted values used to clamp the opcode
*/
template <class ValueType>
void setRangeEndFromOpcode(const Opcode& opcode, Range<ValueType>& target, const Range<ValueType>& validRange);
/**
* @brief Set a target beginning of a range from an opcode value, with possibly a textual note rather
* than a number
*
* @tparam ValueType
* @param opcode the source opcode
* @param target the value to update
* @param validRange the range of admitted values used to clamp the opcode
*/
template <class ValueType>
void setRangeStartFromOpcode(const Opcode& opcode, Range<ValueType>& target, const Range<ValueType>& validRange);
/**
* @brief Set a CC modulation parameter from an opcode value.
*
* @tparam ValueType
* @param opcode the source opcode
* @param target the new CC modulation parameter
* @param validRange the range of admitted values used to clamp the opcode
*/
template <class ValueType>
void setCCPairFromOpcode(const Opcode& opcode, absl::optional<CCData<ValueType>>& target, const Range<ValueType>& validRange);
}
std::ostream &operator<<(std::ostream &os, const sfz::Opcode &opcode);

View file

@ -19,7 +19,9 @@ namespace sfz
*/
template <class Type>
class Range {
static_assert(std::is_arithmetic<Type>::value, "The Type should be arithmetic");
// static_assert(std::is_arithmetic<Type>::value
// || (std::is_enum<Type>::value && std::is_same<typename std::underlying_type<Type>::type, int>::value),
// "The Type should be arithmetic");
public:
constexpr Range() = default;

File diff suppressed because it is too large Load diff

View file

@ -43,17 +43,7 @@ class RegionSet;
*
*/
struct Region {
Region(int regionNumber, const MidiState& midiState, absl::string_view defaultPath = "")
: id{regionNumber}, midiState(midiState), defaultPath(std::move(defaultPath))
{
ccSwitched.set();
gainToEffect.reserve(5); // sufficient room for main and fx1-4
gainToEffect.push_back(1.0); // contribute 100% into the main bus
// Default amplitude release
amplitudeEG.release = Default::ampegRelease;
}
Region(int regionNumber, const MidiState& midiState, absl::string_view defaultPath = "");
Region(const Region&) = default;
~Region() = default;
@ -71,7 +61,7 @@ struct Region {
* @return true
* @return false
*/
bool isRelease() const noexcept { return trigger == SfzTrigger::release || trigger == SfzTrigger::release_key; }
bool isRelease() const noexcept { return trigger == Trigger::release || trigger == Trigger::release_key; }
/**
* @brief Is a generator (*sine or *silence mostly)?
*
@ -107,7 +97,7 @@ struct Region {
* @return true
* @return false
*/
bool shouldLoop() const noexcept { return (loopMode == SfzLoopMode::loop_continuous || loopMode == SfzLoopMode::loop_sustain); }
bool shouldLoop() const noexcept { return (loopMode == LoopMode::loop_continuous || loopMode == LoopMode::loop_sustain); }
/**
* @brief Given the current midi state, is the region switched on?
*
@ -280,12 +270,12 @@ struct Region {
* @brief Process a generic CC opcode, and fill the modulation parameters.
*
* @param opcode
* @param range
* @param spec
* @param target
* @return true if the opcode was properly read and stored.
* @return false
*/
bool processGenericCc(const Opcode& opcode, Range<float> range, const ModKey& target);
bool processGenericCc(const Opcode& opcode, OpcodeSpec<float> spec, const ModKey& target);
void offsetAllKeys(int offset) noexcept;
@ -333,41 +323,40 @@ struct Region {
float delayRandom { Default::delayRandom }; // delay_random
int64_t offset { Default::offset }; // offset
int64_t offsetRandom { Default::offsetRandom }; // offset_random
CCMap<int64_t> offsetCC { Default::offset };
uint32_t sampleEnd { Default::sampleEndRange.getEnd() }; // end
absl::optional<uint32_t> sampleCount {}; // count
absl::optional<SfzLoopMode> loopMode {}; // loopmode
Range<uint32_t> loopRange { Default::loopRange }; //loopstart and loopend
CCMap<int64_t> offsetCC { Default::offsetMod };
uint32_t sampleEnd { Default::sampleEnd }; // end
uint32_t sampleCount { Default::sampleCount }; // count
absl::optional<LoopMode> loopMode {}; // loopmode
Range<uint32_t> loopRange { Default::loopStart, Default::loopEnd }; //loopstart and loopend
float loopCrossfade { Default::loopCrossfade }; // loop_crossfade
// Wavetable oscillator
float oscillatorPhase { Default::oscillatorPhase };
enum class OscillatorEnabled { Auto = -1, Off = 0, On = 1 };
OscillatorEnabled oscillatorEnabled = OscillatorEnabled::Auto; // oscillator
bool hasWavetableSample = false; // (set according to sample file)
int oscillatorMode = Default::oscillatorMode;
int oscillatorMulti = Default::oscillatorMulti;
float oscillatorDetune = Default::oscillatorDetune;
float oscillatorModDepth = Default::oscillatorModDepth;
OscillatorEnabled oscillatorEnabled { Default::oscillator }; // oscillator
bool hasWavetableSample { false }; // (set according to sample file)
int oscillatorMode { Default::oscillatorMode };
int oscillatorMulti { Default::oscillatorMulti };
float oscillatorDetune { Default::oscillatorDetune };
float oscillatorModDepth { Default::oscillatorModDepth };
absl::optional<int> oscillatorQuality;
// Instrument settings: voice lifecycle
uint32_t group { Default::group }; // group
absl::optional<uint32_t> offBy {}; // off_by
SfzOffMode offMode { Default::offMode }; // off_mode
OffMode offMode { Default::offMode }; // off_mode
float offTime { Default::offTime }; // off_mode
absl::optional<uint32_t> notePolyphony {}; // note_polyphony
unsigned polyphony { config::maxVoices }; // polyphony
SfzSelfMask selfMask { Default::selfMask };
uint32_t polyphony { config::maxVoices }; // polyphony
SelfMask selfMask { Default::selfMask };
bool rtDead { Default::rtDead };
// Region logic: key mapping
Range<uint8_t> keyRange { Default::keyRange }; //lokey, hikey and key
Range<float> velocityRange { Default::velocityRange }; // hivel and lovel
Range<uint8_t> keyRange { Default::loKey, Default::hiKey }; //lokey, hikey and key
Range<float> velocityRange { Default::loVel, Default::hiVel }; // hivel and lovel
// Region logic: MIDI conditions
Range<float> bendRange { Default::bendValueRange }; // hibend and lobend
CCMap<Range<float>> ccConditions { Default::ccValueRange };
Range<float> bendRange { Default::loBend, Default::hiBend }; // hibend and lobend
CCMap<Range<float>> ccConditions {{ Default::loCC, Default::hiCC }};
absl::optional<uint8_t> lastKeyswitch {}; // sw_last
absl::optional<Range<uint8_t>> lastKeyswitchRange {}; // sw_last
absl::optional<std::string> keyswitchLabel {};
@ -375,32 +364,32 @@ struct Region {
absl::optional<uint8_t> downKeyswitch {}; // sw_down
absl::optional<uint8_t> previousKeyswitch {}; // sw_previous
absl::optional<uint8_t> defaultSwitch {};
SfzVelocityOverride velocityOverride { Default::velocityOverride }; // sw_vel
VelocityOverride velocityOverride { Default::velocityOverride }; // sw_vel
bool checkSustain { Default::checkSustain }; // sustain_sw
bool checkSostenuto { Default::checkSostenuto }; // sostenuto_sw
uint16_t sustainCC { Default::sustainCC }; // sustain_cc
float sustainThreshold { Default::sustainThreshold }; // sustain_cc
// Region logic: internal conditions
Range<uint8_t> aftertouchRange { Default::aftertouchRange }; // hichanaft and lochanaft
Range<float> bpmRange { Default::bpmRange }; // hibpm and lobpm
Range<float> randRange { Default::randRange }; // hirand and lorand
uint8_t sequenceLength { Default::sequenceLength }; // seq_length
uint8_t sequencePosition { Default::sequencePosition }; // seq_position
Range<uint8_t> aftertouchRange { Default::loChannelAftertouch, Default::hiChannelAftertouch }; // hichanaft and lochanaft
Range<float> bpmRange { Default::loBPM, Default::hiBPM }; // hibpm and lobpm
Range<float> randRange { Default::loNormalized, Default::hiNormalized }; // hirand and lorand
uint8_t sequenceLength { Default::sequence }; // seq_length
uint8_t sequencePosition { Default::sequence }; // seq_position
// Region logic: triggers
SfzTrigger trigger { Default::trigger }; // trigger
CCMap<Range<float>> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN
Trigger trigger { Default::trigger }; // trigger
CCMap<Range<float>> ccTriggers {{ Default::loCC, Default::hiCC }}; // on_loccN on_hiccN
// Performance parameters: amplifier
float volume { Default::volume }; // volume
float amplitude { normalizePercents(Default::amplitude) }; // amplitude
float pan { normalizePercents(Default::pan) }; // pan
float width { normalizePercents(Default::width) }; // width
float position { normalizePercents(Default::position) }; // position
uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter
float amplitude { Default::amplitude }; // amplitude
float pan { Default::pan }; // pan
float width { Default::width }; // width
float position { Default::position }; // position
uint8_t ampKeycenter { Default::key }; // amp_keycenter
float ampKeytrack { Default::ampKeytrack }; // amp_keytrack
float ampVeltrack { normalizePercents(Default::ampVeltrack) }; // amp_keytrack
float ampVeltrack { Default::ampVeltrack }; // amp_veltrack
std::vector<std::pair<uint8_t, float>> velocityPoints; // amp_velcurve_N
absl::optional<Curve> velCurve {};
float ampRandom { Default::ampRandom }; // amp_random
@ -408,9 +397,9 @@ struct Region {
Range<uint8_t> crossfadeKeyOutRange { Default::crossfadeKeyOutRange };
Range<float> crossfadeVelInRange { Default::crossfadeVelInRange };
Range<float> crossfadeVelOutRange { Default::crossfadeVelOutRange };
SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve };
SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve };
SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve };
CrossfadeCurve crossfadeKeyCurve { Default::crossfadeCurve };
CrossfadeCurve crossfadeVelCurve { Default::crossfadeCurve };
CrossfadeCurve crossfadeCCCurve { Default::crossfadeCurve };
CCMap<Range<float>> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN
CCMap<Range<float>> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN
float rtDecay { Default::rtDecay }; // rt_decay
@ -427,17 +416,17 @@ struct Region {
std::vector<FilterDescription> filters;
// Performance parameters: pitch
uint8_t pitchKeycenter { Default::pitchKeycenter }; // pitch_keycenter
uint8_t pitchKeycenter { Default::key }; // pitch_keycenter
bool pitchKeycenterFromSample { false };
int pitchKeytrack { Default::pitchKeytrack }; // pitch_keytrack
float pitchRandom { Default::pitchRandom }; // pitch_random
int pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack
int transpose { Default::transpose }; // transpose
float tune { Default::tune }; // tune
int bendUp { Default::bendUp };
int bendDown { Default::bendDown };
int bendStep { Default::bendStep };
uint8_t bendSmooth { Default::bendSmooth };
float pitch { Default::pitch }; // tune
float bendUp { Default::bendUp };
float bendDown { Default::bendDown };
float bendStep { Default::bendStep };
uint8_t bendSmooth { Default::smoothCC };
// Envelopes
EGDescription amplitudeEG;
@ -485,7 +474,7 @@ struct Region {
bool bpmSwitched { true };
bool aftertouchSwitched { true };
std::bitset<config::numCCs> ccSwitched;
absl::string_view defaultPath { "" };
std::string defaultPath { "" };
int sequenceCounter { 0 };
LEAK_DETECTOR(Region);

View file

@ -204,39 +204,6 @@ void Filter::setType(FilterType type)
}
}
absl::optional<FilterType> Filter::typeFromName(absl::string_view name)
{
absl::optional<FilterType> ftype;
switch (hash(name)) {
case hash("lpf_1p"): ftype = kFilterLpf1p; break;
case hash("hpf_1p"): ftype = kFilterHpf1p; break;
case hash("lpf_2p"): ftype = kFilterLpf2p; break;
case hash("hpf_2p"): ftype = kFilterHpf2p; break;
case hash("bpf_2p"): ftype = kFilterBpf2p; break;
case hash("brf_2p"): ftype = kFilterBrf2p; break;
case hash("bpf_1p"): ftype = kFilterBpf1p; break;
case hash("brf_1p"): ftype = kFilterBrf1p; break;
case hash("apf_1p"): ftype = kFilterApf1p; break;
case hash("lpf_2p_sv"): ftype = kFilterLpf2pSv; break;
case hash("hpf_2p_sv"): ftype = kFilterHpf2pSv; break;
case hash("bpf_2p_sv"): ftype = kFilterBpf2pSv; break;
case hash("brf_2p_sv"): ftype = kFilterBrf2pSv; break;
case hash("lpf_4p"): ftype = kFilterLpf4p; break;
case hash("hpf_4p"): ftype = kFilterHpf4p; break;
case hash("lpf_6p"): ftype = kFilterLpf6p; break;
case hash("hpf_6p"): ftype = kFilterHpf6p; break;
case hash("pink"): ftype = kFilterPink; break;
case hash("lsh"): ftype = kFilterLsh; break;
case hash("hsh"): ftype = kFilterHsh; break;
case hash("bpk_2p"): //fallthrough
case hash("pkf_2p"): //fallthrough
case hash("peq"): ftype = kFilterPeq; break;
}
return ftype;
}
sfzFilterDsp *Filter::Impl::getDsp(unsigned channels, FilterType type)
{
switch (idDsp(channels, type)) {
@ -444,19 +411,6 @@ void FilterEq::setType(EqType type)
}
}
absl::optional<EqType> FilterEq::typeFromName(absl::string_view name)
{
absl::optional<EqType> ftype;
switch (hash(name)) {
case hash("peak"): ftype = kEqPeak; break;
case hash("lshelf"): ftype = kEqLowShelf; break;
case hash("hshelf"): ftype = kEqHighShelf; break;
}
return ftype;
}
sfzFilterDsp *FilterEq::Impl::getDsp(unsigned channels, EqType type)
{
switch (idDsp(channels, type)) {

View file

@ -84,11 +84,6 @@ public:
*/
void setType(FilterType type);
/**
Get the filter type associated with the given name.
*/
static absl::optional<FilterType> typeFromName(absl::string_view name);
private:
struct Impl;
std::unique_ptr<Impl> P;
@ -194,11 +189,6 @@ public:
*/
void setType(EqType type);
/**
Get the filter type associated with the given name.
*/
static absl::optional<EqType> typeFromName(absl::string_view name);
private:
struct Impl;
std::unique_ptr<Impl> P;

View file

@ -16,7 +16,6 @@
#include "MathHelpers.h"
#include "SIMDHelpers.h"
#include "absl/meta/type_traits.h"
#include "Defaults.h"
namespace sfz {
@ -126,6 +125,12 @@ constexpr float normalize7Bits(T value)
return static_cast<float>(min(max(value, T { 0 }), T { 127 })) / 127.0f;
}
template <>
constexpr float normalize7Bits(bool value)
{
return value ? 1.0f : 0.0f;
}
/**
* @brief Normalize a CC value between 0.0 and 1.0
*
@ -182,18 +187,17 @@ constexpr float normalizeBend(float bendValue)
*
* @param key
* @param offset
* @param range
* @return uint8_t
*/
inline CXX14_CONSTEXPR uint8_t offsetAndClampKey(uint8_t key, int offset, sfz::Range<uint8_t> range)
inline CXX14_CONSTEXPR uint8_t offsetAndClampKey(uint8_t key, int offset)
{
const int offsetKey { key + offset };
if (offsetKey > std::numeric_limits<uint8_t>::max())
return range.getEnd();
return 127;
if (offsetKey < std::numeric_limits<uint8_t>::min())
return range.getStart();
return 0;
return range.clamp(static_cast<uint8_t>(offsetKey));
return clamp<uint8_t>(static_cast<uint8_t>(offsetKey), 0, 127);
}
namespace literals {

View file

@ -5,6 +5,10 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "Smoothers.h"
#include "Config.h"
#include "MathHelpers.h"
#include "SfzHelpers.h"
#include "SIMDHelpers.h"
namespace sfz {
@ -16,7 +20,7 @@ void Smoother::setSmoothing(uint8_t smoothValue, float sampleRate)
{
smoothing = (smoothValue > 0);
if (smoothing) {
filter.setGain(std::tan(1.0f / (2 * Default::smoothTauPerStep * smoothValue * sampleRate)));
filter.setGain(std::tan(1.0f / (2 * config::smoothTauPerStep * smoothValue * sampleRate)));
}
}

View file

@ -6,12 +6,7 @@
#pragma once
#include "Config.h"
#include "Defaults.h"
#include "MathHelpers.h"
#include "SfzHelpers.h"
#include "OnePoleFilter.h"
#include "SIMDHelpers.h"
#include <array>
namespace sfz {

View file

@ -276,11 +276,10 @@ void Synth::Impl::handleMasterOpcodes(const std::vector<Opcode>& members)
switch (member.lettersOnlyHash) {
case hash("polyphony"):
ASSERT(currentSet_ != nullptr);
if (auto value = readOpcode(member.value, Default::polyphonyRange))
currentSet_->setPolyphonyLimit(*value);
currentSet_->setPolyphonyLimit(member.read(Default::polyphony));
break;
case hash("sw_default"):
setValueFromOpcode(member, currentSwitch_, Default::keyRange);
currentSwitch_ = member.read(Default::key);
break;
}
}
@ -294,15 +293,14 @@ void Synth::Impl::handleGlobalOpcodes(const std::vector<Opcode>& members)
switch (member.lettersOnlyHash) {
case hash("polyphony"):
ASSERT(currentSet_ != nullptr);
if (auto value = readOpcode(member.value, Default::polyphonyRange))
currentSet_->setPolyphonyLimit(*value);
currentSet_->setPolyphonyLimit(member.read(Default::polyphony));
break;
case hash("sw_default"):
setValueFromOpcode(member, currentSwitch_, Default::keyRange);
currentSwitch_ = member.read(Default::key);
break;
case hash("volume"):
// FIXME : Probably best not to mess with this and let the host control the volume
// setValueFromOpcode(member, volume, Default::volumeRange);
// setValueFromOpcode(member, volume, OldDefault::volumeRange);
break;
}
}
@ -318,13 +316,13 @@ void Synth::Impl::handleGroupOpcodes(const std::vector<Opcode>& members, const s
switch (member.lettersOnlyHash) {
case hash("group"):
setValueFromOpcode(member, groupIdx, Default::groupRange);
groupIdx = member.read(Default::group);
break;
case hash("polyphony"):
setValueFromOpcode(member, maxPolyphony, Default::polyphonyRange);
maxPolyphony = member.read(Default::polyphony);
break;
case hash("sw_default"):
setValueFromOpcode(member, currentSwitch_, Default::keyRange);
currentSwitch_ = member.read(Default::key);
break;
}
};
@ -352,25 +350,21 @@ void Synth::Impl::handleControlOpcodes(const std::vector<Opcode>& members)
switch (member.lettersOnlyHash) {
case hash("set_cc&"):
if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) {
const auto ccValue = readOpcode(member.value, Default::midi7Range);
if (ccValue)
setDefaultHdcc(member.parameters.back(), normalizeCC(*ccValue));
if (Default::ccNumber.bounds.containsWithEnd(member.parameters.back())) {
setDefaultHdcc(member.parameters.back(), member.read(Default::loCC));
}
break;
case hash("set_hdcc&"):
if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) {
const auto ccValue = readOpcode(member.value, Default::normalizedRange);
if (ccValue)
setDefaultHdcc(member.parameters.back(), *ccValue);
if (Default::ccNumber.bounds.containsWithEnd(member.parameters.back())) {
setDefaultHdcc(member.parameters.back(), member.read(Default::loNormalized));
}
break;
case hash("label_cc&"):
if (Default::ccNumberRange.containsWithEnd(member.parameters.back()))
if (Default::ccNumber.bounds.containsWithEnd(member.parameters.back()))
setCCLabel(member.parameters.back(), std::string(member.value));
break;
case hash("label_key&"):
if (member.parameters.back() <= Default::keyRange.getEnd()) {
if (member.parameters.back() <= Default::key.bounds.getEnd()) {
const auto noteNumber = static_cast<uint8_t>(member.parameters.back());
insertPairUniquely(keyLabels_, noteNumber, std::string(member.value));
}
@ -380,10 +374,10 @@ void Synth::Impl::handleControlOpcodes(const std::vector<Opcode>& members)
DBG("Changing default sample path to " << defaultPath_);
break;
case hash("note_offset"):
setValueFromOpcode(member, noteOffset_, Default::noteOffsetRange);
noteOffset_ = member.read(Default::noteOffset);
break;
case hash("octave_offset"):
setValueFromOpcode(member, octaveOffset_, Default::octaveOffsetRange);
octaveOffset_ = member.read(Default::octaveOffset);
break;
case hash("hint_ram_based"):
if (member.value == "1")
@ -446,22 +440,19 @@ void Synth::Impl::handleEffectOpcodes(const std::vector<Opcode>& rawMembers)
// note(jpc): gain opcodes are linear volumes in % units
case hash("directtomain"):
if (auto valueOpt = readOpcode<float>(opcode.value, { 0, 100 }))
getOrCreateBus(0).setGainToMain(*valueOpt / 100);
getOrCreateBus(0).setGainToMain(opcode.read(Default::effect));
break;
case hash("fx&tomain"): // fx&tomain
if (opcode.parameters.front() < 1 || opcode.parameters.front() > config::maxEffectBuses)
break;
if (auto valueOpt = readOpcode<float>(opcode.value, { 0, 100 }))
getOrCreateBus(opcode.parameters.front()).setGainToMain(*valueOpt / 100);
getOrCreateBus(opcode.parameters.front()).setGainToMain(opcode.read(Default::effect));
break;
case hash("fx&tomix"): // fx&tomix
if (opcode.parameters.front() < 1 || opcode.parameters.front() > config::maxEffectBuses)
break;
if (auto valueOpt = readOpcode<float>(opcode.value, { 0, 100 }))
getOrCreateBus(opcode.parameters.front()).setGainToMix(*valueOpt / 100);
getOrCreateBus(opcode.parameters.front()).setGainToMix(opcode.read(Default::effect));
break;
}
}
@ -584,20 +575,20 @@ void Synth::Impl::finalizeSfzLoad()
region->sampleEnd = std::min(region->sampleEnd, fileInformation->end);
if (fileInformation->hasLoop) {
if (region->loopRange.getStart() == Default::loopRange.getStart())
region->loopRange.setStart(fileInformation->loopBegin);
if (region->loopRange.getStart() == Default::loopStart)
region->loopRange.setStart(fileInformation->loopStart);
if (region->loopRange.getEnd() == Default::loopRange.getEnd())
if (region->loopRange.getEnd() == Default::loopEnd)
region->loopRange.setEnd(fileInformation->loopEnd);
if (!region->loopMode)
region->loopMode = SfzLoopMode::loop_continuous;
region->loopMode = LoopMode::loop_continuous;
}
if (region->isRelease() && !region->loopMode)
region->loopMode = SfzLoopMode::one_shot;
region->loopMode = LoopMode::one_shot;
if (region->loopRange.getEnd() == Default::loopRange.getEnd())
if (region->loopRange.getEnd() == Default::loopEnd)
region->loopRange.setEnd(region->sampleEnd);
if (fileInformation->numChannels == 2)
@ -611,7 +602,7 @@ void Synth::Impl::finalizeSfzLoad()
uint64_t sumOffsetCC = region->offset + region->offsetRandom;
for (const auto& offsets : region->offsetCC)
sumOffsetCC += offsets.data;
return Default::offsetCCRange.clamp(sumOffsetCC);
return Default::offsetMod.bounds.clamp(sumOffsetCC);
}();
if (!resources_.filePool.preloadFile(*region->sampleId, maxOffset))
@ -651,7 +642,7 @@ void Synth::Impl::finalizeSfzLoad()
for (int cc = 0; cc < config::numCCs; cc++) {
if (region->ccTriggers.contains(cc)
|| region->ccConditions.contains(cc)
|| (cc == region->sustainCC && region->trigger == SfzTrigger::release))
|| (cc == region->sustainCC && region->trigger == Trigger::release))
ccActivationLists_[cc].push_back(region);
}
@ -663,14 +654,14 @@ void Synth::Impl::finalizeSfzLoad()
// Set the default frequencies on equalizers if needed
if (region->equalizers.size() > 0
&& region->equalizers[0].frequency == Default::eqFrequencyUnset) {
region->equalizers[0].frequency = Default::eqFrequency1;
&& region->equalizers[0].frequency == Default::eqFrequency) {
region->equalizers[0].frequency = Default::defaultEQFreq[0];
if (region->equalizers.size() > 1
&& region->equalizers[1].frequency == Default::eqFrequencyUnset) {
region->equalizers[1].frequency = Default::eqFrequency2;
&& region->equalizers[1].frequency == Default::eqFrequency) {
region->equalizers[1].frequency = Default::defaultEQFreq[1];
if (region->equalizers.size() > 2
&& region->equalizers[2].frequency == Default::eqFrequencyUnset) {
region->equalizers[2].frequency = Default::eqFrequency3;
&& region->equalizers[2].frequency == Default::eqFrequency) {
region->equalizers[2].frequency = Default::defaultEQFreq[2];
}
}
}
@ -1047,7 +1038,7 @@ void Synth::Impl::noteOffDispatch(int delay, int noteNumber, float velocity) noe
for (auto& region : noteActivationLists_[noteNumber]) {
if (region->registerNoteOff(noteNumber, velocity, randValue)) {
if (region->trigger == SfzTrigger::release && !region->rtDead && !voiceManager_.playingAttackVoice(region))
if (region->trigger == Trigger::release && !region->rtDead && !voiceManager_.playingAttackVoice(region))
continue;
startVoice(region, delay, triggerEvent, ring);
@ -1479,7 +1470,7 @@ float Synth::getVolume() const noexcept
void Synth::setVolume(float volume) noexcept
{
Impl& impl = *impl_;
impl.volume_ = Default::volumeRange.clamp(volume);
impl.volume_ = Default::volume.bounds.clamp(volume);
}
int Synth::getNumVoices() const noexcept

View file

@ -13,8 +13,8 @@ struct SynthConfig
{
bool freeWheeling { false };
int liveSampleQuality { sfz::Default::sampleQuality };
int freeWheelingSampleQuality { sfz::Default::sampleQualityInFreewheelingMode };
int liveSampleQuality { Default::sampleQuality };
int freeWheelingSampleQuality { Default::freewheelingQuality };
int currentSampleQuality() const noexcept
{

View file

@ -124,13 +124,27 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
}
} break;
MATCH("/region&/trigger_on_note", "") {
GET_REGION_OR_BREAK(indices[0])
if (region.triggerOnNote) {
client.receive<'T'>(delay, path, {});
} else {
client.receive<'F'>(delay, path, {});
}
} break;
MATCH("/region&/trigger_on_cc", "") {
GET_REGION_OR_BREAK(indices[0])
if (region.triggerOnCC) {
client.receive<'T'>(delay, path, {});
} else {
client.receive<'F'>(delay, path, {});
}
} break;
MATCH("/region&/count", "") {
GET_REGION_OR_BREAK(indices[0])
if (!region.sampleCount) {
client.receive<'N'>(delay, path, {});
} else {
client.receive<'h'>(delay, path, *region.sampleCount);
}
client.receive<'h'>(delay, path, region.sampleCount);
} break;
MATCH("/region&/loop_range", "") {
@ -149,16 +163,16 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
}
switch (*region.loopMode) {
case SfzLoopMode::no_loop:
case LoopMode::no_loop:
client.receive<'s'>(delay, path, "no_loop");
break;
case SfzLoopMode::loop_continuous:
case LoopMode::loop_continuous:
client.receive<'s'>(delay, path, "loop_continuous");
break;
case SfzLoopMode::loop_sustain:
case LoopMode::loop_sustain:
client.receive<'s'>(delay, path, "loop_sustain");
break;
case SfzLoopMode::one_shot:
case LoopMode::one_shot:
client.receive<'s'>(delay, path, "one_shot");
break;
}
@ -186,13 +200,13 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/off_mode", "") {
GET_REGION_OR_BREAK(indices[0])
switch (region.offMode) {
case SfzOffMode::time:
case OffMode::time:
client.receive<'s'>(delay, path, "time");
break;
case SfzOffMode::normal:
case OffMode::normal:
client.receive<'s'>(delay, path, "normal");
break;
case SfzOffMode::fast:
case OffMode::fast:
client.receive<'s'>(delay, path, "fast");
break;
}
@ -295,10 +309,10 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/sw_vel", "") {
GET_REGION_OR_BREAK(indices[0])
switch (region.velocityOverride) {
case SfzVelocityOverride::current:
case VelocityOverride::current:
client.receive<'s'>(delay, path, "current");
break;
case SfzVelocityOverride::previous:
case VelocityOverride::previous:
client.receive<'s'>(delay, path, "previous");
break;
}
@ -341,19 +355,19 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/trigger", "") {
GET_REGION_OR_BREAK(indices[0])
switch (region.trigger) {
case SfzTrigger::attack:
case Trigger::attack:
client.receive<'s'>(delay, path, "attack");
break;
case SfzTrigger::first:
case Trigger::first:
client.receive<'s'>(delay, path, "first");
break;
case SfzTrigger::release:
case Trigger::release:
client.receive<'s'>(delay, path, "release");
break;
case SfzTrigger::release_key:
case Trigger::release_key:
client.receive<'s'>(delay, path, "release_key");
break;
case SfzTrigger::legato:
case Trigger::legato:
client.receive<'s'>(delay, path, "legato");
break;
}
@ -678,10 +692,10 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/xf_keycurve", "") {
GET_REGION_OR_BREAK(indices[0])
switch (region.crossfadeKeyCurve) {
case SfzCrossfadeCurve::gain:
case CrossfadeCurve::gain:
client.receive<'s'>(delay, path, "gain");
break;
case SfzCrossfadeCurve::power:
case CrossfadeCurve::power:
client.receive<'s'>(delay, path, "power");
break;
}
@ -690,10 +704,10 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/xf_velcurve", "") {
GET_REGION_OR_BREAK(indices[0])
switch (region.crossfadeVelCurve) {
case SfzCrossfadeCurve::gain:
case CrossfadeCurve::gain:
client.receive<'s'>(delay, path, "gain");
break;
case SfzCrossfadeCurve::power:
case CrossfadeCurve::power:
client.receive<'s'>(delay, path, "power");
break;
}
@ -702,10 +716,10 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/xf_cccurve", "") {
GET_REGION_OR_BREAK(indices[0])
switch (region.crossfadeCCCurve) {
case SfzCrossfadeCurve::gain:
case CrossfadeCurve::gain:
client.receive<'s'>(delay, path, "gain");
break;
case SfzCrossfadeCurve::power:
case CrossfadeCurve::power:
client.receive<'s'>(delay, path, "power");
break;
}
@ -761,12 +775,12 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
client.receive<'i'>(delay, path, region.transpose);
} break;
MATCH("/region&/tune", "") {
MATCH("/region&/pitch", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'f'>(delay, path, region.tune);
client.receive<'f'>(delay, path, region.pitch);
} break;
MATCH("/region&/tune_cc&", "") {
MATCH("/region&/pitch_cc&", "") {
GET_REGION_OR_BREAK(indices[0])
auto value = region.ccModDepth(indices[1], ModId::Pitch);
if (value) {
@ -776,7 +790,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
}
} break;
MATCH("/region&/tune_stepcc&", "") {
MATCH("/region&/pitch_stepcc&", "") {
GET_REGION_OR_BREAK(indices[0])
auto params = region.ccModParameters(indices[1], ModId::Pitch);
if (params) {
@ -786,7 +800,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
}
} break;
MATCH("/region&/tune_smoothcc&", "") {
MATCH("/region&/pitch_smoothcc&", "") {
GET_REGION_OR_BREAK(indices[0])
auto params = region.ccModParameters(indices[1], ModId::Pitch);
if (params) {
@ -796,7 +810,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
}
} break;
MATCH("/region&/tune_curvecc&", "") {
MATCH("/region&/pitch_curvecc&", "") {
GET_REGION_OR_BREAK(indices[0])
auto params = region.ccModParameters(indices[1], ModId::Pitch);
if (params) {
@ -808,17 +822,17 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/bend_up", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'i'>(delay, path, region.bendUp);
client.receive<'f'>(delay, path, region.bendUp);
} break;
MATCH("/region&/bend_down", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'i'>(delay, path, region.bendDown);
client.receive<'f'>(delay, path, region.bendDown);
} break;
MATCH("/region&/bend_step", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'i'>(delay, path, region.bendStep);
client.receive<'f'>(delay, path, region.bendStep);
} break;
MATCH("/region&/bend_smooth", "") {
@ -863,7 +877,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/ampeg_depth", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'i'>(delay, path, region.amplitudeEG.depth);
client.receive<'f'>(delay, path, region.amplitudeEG.depth);
} break;
MATCH("/region&/ampeg_vel&attack", "") {
@ -912,7 +926,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
GET_REGION_OR_BREAK(indices[0])
if (indices[1] != 2)
break;
client.receive<'i'>(delay, path, region.amplitudeEG.vel2depth);
client.receive<'f'>(delay, path, region.amplitudeEG.vel2depth);
} break;
MATCH("/region&/note_polyphony", "") {
@ -927,10 +941,10 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
MATCH("/region&/note_selfmask", "") {
GET_REGION_OR_BREAK(indices[0])
switch(region.selfMask) {
case SfzSelfMask::mask:
case SelfMask::mask:
client.receive(delay, path, "T", nullptr);
break;
case SfzSelfMask::dontMask:
case SelfMask::dontMask:
client.receive(delay, path, "F", nullptr);
break;
}
@ -978,6 +992,37 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
client.receive<'f'>(delay, path, region.oscillatorPhase);
} break;
MATCH("/region&/oscillator_quality", "") {
GET_REGION_OR_BREAK(indices[0])
if (region.oscillatorQuality) {
client.receive<'i'>(delay, path, *region.oscillatorQuality);
} else {
client.receive<'N'>(delay, path, {});
}
} break;
MATCH("/region&/oscillator_mode", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'i'>(delay, path, region.oscillatorMode);
} break;
MATCH("/region&/oscillator_multi", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'i'>(delay, path, region.oscillatorMulti);
} break;
MATCH("/region&/oscillator_detune", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'f'>(delay, path, region.oscillatorDetune);
} break;
MATCH("/region&/oscillator_mod_depth", "") {
GET_REGION_OR_BREAK(indices[0])
client.receive<'f'>(delay, path, region.oscillatorModDepth * 100.0f);
} break;
// TODO: detune cc, mod depth cc
MATCH("/region&/effect&", "") {
GET_REGION_OR_BREAK(indices[0])
auto effectIdx = indices[1];

View file

@ -257,8 +257,8 @@ struct Synth::Impl final: public Parser::Listener {
// Control opcodes
std::string defaultPath_ { "" };
int noteOffset_ { 0 };
int octaveOffset_ { 0 };
int noteOffset_ { Default::noteOffset };
int octaveOffset_ { Default::octaveOffset };
// Modulation source generators
std::unique_ptr<ControllerSource> genController_;

View file

@ -370,7 +370,8 @@ void Voice::startVoice(Region* region, int delay, const TriggerEvent& event) noe
}
}
const float phase = region->getPhase();
const int quality = region->oscillatorQuality.value_or(Default::oscillatorQuality);
const int quality =
region->oscillatorQuality.value_or(Default::oscillatorQuality);
for (WavetableOscillator& osc : impl.waveOscillators_) {
osc.setWavetable(wave);
osc.setPhase(phase);
@ -463,9 +464,9 @@ void Voice::off(int delay, bool fast) noexcept
{
Impl& impl = *impl_;
if (!impl.region_->flexAmpEG) {
if (impl.region_->offMode == SfzOffMode::fast || fast) {
if (impl.region_->offMode == OffMode::fast || fast) {
impl.egAmplitude_.setReleaseTime(Default::offTime);
} else if (impl.region_->offMode == SfzOffMode::time) {
} else if (impl.region_->offMode == OffMode::time) {
impl.egAmplitude_.setReleaseTime(impl.region_->offTime);
}
}
@ -491,7 +492,7 @@ void Voice::registerNoteOff(int delay, int noteNumber, float velocity) noexcept
if (impl.triggerEvent_.number == noteNumber && impl.triggerEvent_.type == TriggerEventType::NoteOn) {
impl.noteIsOff_ = true;
if (impl.region_->loopMode == SfzLoopMode::one_shot)
if (impl.region_->loopMode == LoopMode::one_shot)
return;
if (!impl.region_->checkSustain
@ -1652,7 +1653,7 @@ void Voice::Impl::pitchEnvelope(absl::Span<float> pitchSpan) noexcept
return centsFactor(region_->getBendInCents(bend));
};
if (region_->bendStep > 1)
if (region_->bendStep > 1.0f)
pitchBendEnvelope(events, *bends, bendLambda, bendStepFactor_);
else
pitchBendEnvelope(events, *bends, bendLambda);

View file

@ -198,7 +198,7 @@ void VoiceManager::checkNotePolyphony(const Region* region, int delay, const Tri
&& voiceTriggerEvent.type == triggerEvent.type) {
notePolyphonyCounter += 1;
switch (region->selfMask) {
case SfzSelfMask::mask:
case SelfMask::mask:
if (voiceTriggerEvent.value <= triggerEvent.value) {
if (!selfMaskCandidate
|| selfMaskCandidate->getTriggerEvent().value > voiceTriggerEvent.value) {
@ -206,7 +206,7 @@ void VoiceManager::checkNotePolyphony(const Region* region, int delay, const Tri
}
}
break;
case SfzSelfMask::dontMask:
case SelfMask::dontMask:
if (!selfMaskCandidate || selfMaskCandidate->getAge() < voice->getAge())
selfMaskCandidate = voice;
break;

View file

@ -81,28 +81,22 @@ namespace fx {
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("apan_waveform"):
if (auto value = readOpcode(opc.value, Default::apanWaveformRange))
apan->_lfoWave = *value;
apan->_lfoWave = opc.read(Default::apanWaveform);
break;
case hash("apan_freq"):
if (auto value = readOpcode(opc.value, Default::apanFrequencyRange))
apan->_lfoFrequency = *value;
apan->_lfoFrequency = opc.read(Default::apanFrequency);
break;
case hash("apan_phase"):
if (auto value = readOpcode(opc.value, Default::apanPhaseRange))
apan->_lfoPhaseOffset = wrapPhase(*value);
apan->_lfoPhaseOffset = opc.read(Default::apanPhase);
break;
case hash("apan_dry"):
if (auto value = readOpcode(opc.value, Default::apanLevelRange))
apan->_dry = *value / 100.0f;
apan->_dry = opc.read(Default::apanLevel);
break;
case hash("apan_wet"):
if (auto value = readOpcode(opc.value, Default::apanLevelRange))
apan->_wet = *value / 100.0f;
apan->_wet = opc.read(Default::apanLevel);
break;
case hash("apan_depth"):
if (auto value = readOpcode(opc.value, Default::apanLevelRange))
apan->_depth = *value / 100.0f;
apan->_depth = opc.read(Default::apanLevel);
break;
}
}

View file

@ -47,20 +47,20 @@ namespace fx {
template <int Wave> void computeLfos(float* left, float* right, unsigned nframes);
private:
float _samplePeriod = 0.0;
float _samplePeriod { 0.0f };
sfz::Buffer<float> _lfoOutLeft { config::defaultSamplesPerBlock };
sfz::Buffer<float> _lfoOutRight { config::defaultSamplesPerBlock };
// Controls
float _dry = 0.0;
float _wet = 0.0;
float _depth = 0.0;
int _lfoWave = 0;
float _lfoFrequency = 0.0;
float _lfoPhaseOffset = 0.5;
float _dry { Default::apanLevel };
float _wet { Default::apanLevel };
float _depth { Default::apanLevel };
int _lfoWave { Default::apanWaveform };
float _lfoFrequency { Default::apanFrequency };
float _lfoPhaseOffset { Default::apanPhase };
// State
float _lfoPhase = 0.0;
float _lfoPhase { 0.0f };
};
} // namespace fx

View file

@ -31,8 +31,8 @@ namespace fx {
struct Compressor::Impl {
faustCompressor _compressor[2];
bool _stlink = false;
float _inputGain = 1.0;
bool _stlink { Default::compSTLink };
float _inputGain { Default::compGain };
AudioBuffer<float, 2> _tempBuffer2x { 2, _oversampling * config::defaultSamplesPerBlock };
AudioBuffer<float, 2> _gain2x { 2, _oversampling * config::defaultSamplesPerBlock };
hiir::Downsampler2xFpu<12> _downsampler2x[EffectChannels];
@ -163,36 +163,38 @@ namespace fx {
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("comp_attack"):
if (auto value = readOpcode<float>(opc.value, {0.0, 10.0})) {
{
auto value = opc.read(Default::compAttack);
for (size_t c = 0; c < 2; ++c)
impl.set_Attack(c, *value);
impl.set_Attack(c, value);
}
break;
case hash("comp_release"):
if (auto value = readOpcode<float>(opc.value, {0.0, 10.0})) {
{
auto value = opc.read(Default::compRelease);
for (size_t c = 0; c < 2; ++c)
impl.set_Release(c, *value);
impl.set_Release(c, value);
}
break;
case hash("comp_threshold"):
if (auto value = readOpcode<float>(opc.value, {-100.0, 0.0})) {
{
auto value = opc.read(Default::compThreshold);
for (size_t c = 0; c < 2; ++c)
impl.set_Threshold(c, *value);
impl.set_Threshold(c, value);
}
break;
case hash("comp_ratio"):
if (auto value = readOpcode<float>(opc.value, {1.0, 50.0})) {
{
auto value = opc.read(Default::compRatio);
for (size_t c = 0; c < 2; ++c)
impl.set_Ratio(c, *value);
impl.set_Ratio(c, value);
}
break;
case hash("comp_gain"):
if (auto value = readOpcode<float>(opc.value, {-100.0, 100.0}))
impl._inputGain = db2mag(*value);
impl._inputGain = opc.read(Default::compGain);
break;
case hash("comp_stlink"):
if (auto value = readBooleanFromOpcode(opc))
impl._stlink = *value;
impl._stlink = opc.read(Default::compSTLink);
break;
}
}

View file

@ -37,15 +37,15 @@ namespace fx {
struct Disto::Impl {
enum { maxStages = 4 };
float _samplePeriod = 1.0 / config::defaultSampleRate;
float _tone = 100.0;
float _depth = 0.0;
float _dry = 0.0;
float _wet = 0.0;
unsigned _numStages = 1;
float _samplePeriod { 1.0f / config::defaultSampleRate };
float _tone { Default::distoTone };
float _depth { Default::distoDepth };
float _dry { Default::effect };
float _wet { Default::effect };
unsigned _numStages = { Default::distoStages };
float _toneLpfMem[EffectChannels] = {};
faustDisto _stages[EffectChannels][maxStages];
faustDisto _stages[EffectChannels][Default::maxDistoStages];
hiir::Upsampler2xFpu<12> _up2x[EffectChannels];
hiir::Upsampler2xFpu<4> _up4x[EffectChannels];
@ -205,21 +205,19 @@ std::unique_ptr<Effect> Disto::makeInstance(absl::Span<const Opcode> members)
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("disto_tone"):
setValueFromOpcode(opc, impl._tone, {0.0f, 100.0f});
impl._tone = opc.read(Default::distoTone);
break;
case hash("disto_depth"):
setValueFromOpcode(opc, impl._depth, {0.0f, 100.0f});
impl._depth = opc.read(Default::distoDepth);
break;
case hash("disto_stages"):
setValueFromOpcode(opc, impl._numStages, {1, Impl::maxStages});
impl._numStages = opc.read(Default::distoStages);
break;
case hash("disto_dry"):
if (auto value = readOpcode<float>(opc.value, {0.0f, 100.0f}))
impl._dry = *value * 0.01f;
impl._dry = opc.read(Default::effect);
break;
case hash("disto_wet"):
if (auto value = readOpcode<float>(opc.value, {0.0f, 100.0f}))
impl._wet = *value * 0.01f;
impl._wet = opc.read(Default::effect);
break;
}
}

View file

@ -70,25 +70,17 @@ namespace fx {
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("eq_freq"):
setValueFromOpcode(opc, desc.frequency, Default::eqFrequencyRange);
desc.frequency = opc.read(Default::eqFrequency);
break;
case hash("eq_bw"):
setValueFromOpcode(opc, desc.bandwidth, Default::eqBandwidthRange);
desc.bandwidth = opc.read(Default::eqBandwidth);
break;
case hash("eq_gain"):
setValueFromOpcode(opc, desc.gain, Default::eqGainRange);
desc.gain = opc.read(Default::eqGain);
break;
case hash("eq_type"):
{
absl::optional<EqType> ftype = sfz::FilterEq::typeFromName(opc.value);
if (ftype)
desc.type = *ftype;
else {
desc.type = EqType::kEqNone;
DBG("Unknown EQ type: " << std::string(opc.value));
}
break;
}
desc.type = opc.read(Default::eq);
break;
}
}

View file

@ -72,25 +72,17 @@ namespace fx {
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("filter_cutoff"):
setValueFromOpcode(opc, desc.cutoff, Default::filterCutoffRange);
desc.cutoff = opc.read(Default::filterCutoff);
break;
case hash("filter_resonance"):
setValueFromOpcode(opc, desc.resonance, Default::filterResonanceRange);
desc.resonance = opc.read(Default::filterResonance);
break;
case hash("filter_type"):
{
absl::optional<FilterType> ftype = sfz::Filter::typeFromName(opc.value);
if (ftype)
desc.type = *ftype;
else {
desc.type = FilterType::kFilterNone;
DBG("Unknown filter type: " << std::string(opc.value));
}
break;
}
desc.type = opc.read(Default::filter);
break;
// extension
case hash("sfizz:filter_gain"):
setValueFromOpcode(opc, desc.gain, Default::filterGainRange);
desc.gain = opc.read(Default::filterGain);
break;
}
}

View file

@ -180,13 +180,13 @@ namespace fx {
std::unique_ptr<Effect> fx { reverb };
const Impl::Profile* profile = &Impl::largeHall;
float dry = 0;
float wet = 0;
float input = 0;
float size = 0;
float predelay = 0;
float tone = 100;
float damp = 0;
float dry { Default::effect };
float wet { Default::effect };
float input { Default::effect };
float size { Default::fverbSize };
float predelay { Default::fverbPredelay };
float tone { Default::fverbTone };
float damp { Default::fverbDamp };
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
@ -211,25 +211,26 @@ namespace fx {
}
break;
case hash("reverb_dry"):
setValueFromOpcode(opc, dry, {0.0f, 100.0f});
dry = opc.read(Default::effect);
break;
case hash("reverb_wet"):
setValueFromOpcode(opc, wet, {0.0f, 100.0f});
wet = opc.read(Default::effect);
break;
case hash("reverb_input"):
setValueFromOpcode(opc, input, {0.0f, 100.0f});
input = opc.read(Default::effect);
break;
case hash("reverb_size"):
setValueFromOpcode(opc, size, {0.0f, 100.0f});
size = opc.read(Default::fverbSize);
break;
case hash("reverb_predelay"):
setValueFromOpcode(opc, predelay, {0.0f, 10.0f});
predelay = opc.read(Default::fverbPredelay);
break;
case hash("reverb_tone"):
setValueFromOpcode(opc, tone, {0.0f, 100.0f});
tone = opc.read(Default::fverbTone);
break;
case hash("reverb_damp"):
setValueFromOpcode(opc, damp, {0.0f, 100.0f});
damp = opc.read(Default::fverbDamp);
break;
}
}

View file

@ -62,7 +62,7 @@ namespace fx {
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("gain"):
setValueFromOpcode(opc, gain->_gain, {-96.0f, 96.0f});
gain->_gain = opc.read(Default::volume);
break;
}
}

View file

@ -34,7 +34,7 @@ namespace fx {
struct Gate::Impl {
faustGate _gate[2];
bool _stlink = false;
bool _stlink { Default::gateSTLink };
float _inputGain = 1.0;
AudioBuffer<float, 2> _tempBuffer2x { 2, _oversampling * config::defaultSamplesPerBlock };
AudioBuffer<float, 2> _gain2x { 2, _oversampling * config::defaultSamplesPerBlock };
@ -166,33 +166,35 @@ namespace fx {
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("gate_attack"):
if (auto value = readOpcode<float>(opc.value, {0.0, 10.0})) {
{
auto value = opc.read(Default::gateAttack);
for (size_t c = 0; c < 2; ++c)
impl.set_Attack(c, *value);
impl.set_Attack(c, value);
}
break;
case hash("gate_hold"):
if (auto value = readOpcode<float>(opc.value, {0.0, 10.0})) {
{
auto value = opc.read(Default::gateHold);
for (size_t c = 0; c < 2; ++c)
impl.set_Hold(c, *value);
impl.set_Hold(c, value);
}
break;
case hash("gate_release"):
if (auto value = readOpcode<float>(opc.value, {0.0, 10.0})) {
{
auto value = opc.read(Default::gateRelease);
for (size_t c = 0; c < 2; ++c)
impl.set_Release(c, *value);
impl.set_Release(c, value);
}
break;
case hash("gate_threshold"):
if (auto value = readOpcode<float>(opc.value, {-100.0, 0.0})) {
{
auto value = opc.read(Default::gateThreshold);
for (size_t c = 0; c < 2; ++c)
impl.set_Threshold(c, *value);
impl.set_Threshold(c, value);
}
break;
case hash("gate_stlink"):
if (auto value = readBooleanFromOpcode(opc))
impl._stlink = *value;
break;
impl._stlink = opc.read(Default::gateSTLink);
}
}

View file

@ -85,10 +85,10 @@ namespace fx {
for (const Opcode& opcode : members) {
switch (opcode.lettersOnlyHash) {
case hash("bitred"):
setValueFromOpcode(opcode, lofi->_bitred_depth, { 0.0, 100.0 });
lofi->_bitred_depth = opcode.read(Default::lofiBitred);
break;
case hash("decim"):
setValueFromOpcode(opcode, lofi->_decim_depth, { 0.0, 100.0 });
lofi->_decim_depth = opcode.read(Default::lofiDecim);
break;
}
}

View file

@ -95,7 +95,7 @@ namespace fx {
rectify->_full = false;
break;
case hash("rectify"):
setValueFromOpcode(opc, rectify->_amount, { 0.0, 100.0 });
rectify->_amount = opc.read(Default::rectify);
break;
}
}

View file

@ -132,10 +132,10 @@ namespace fx {
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("strings_number"):
setValueFromOpcode(opc, strings->_numStrings, {0, MaximumNumStrings});
strings->_numStrings = opc.read(Default::stringsNumber);
break;
case hash("strings_wet"):
setValueFromOpcode(opc, strings->_wet, {0.0f, 100.0f});
strings->_wet = opc.read(Default::effect);
break;
}
}

View file

@ -51,8 +51,8 @@ namespace fx {
private:
enum { MaximumNumStrings = 88 };
unsigned _numStrings = MaximumNumStrings;
float _wet = 0;
unsigned _numStrings { Default::maxStrings };
float _wet { Default::effect };
std::unique_ptr<ResonantArray> _stringsArray;

View file

@ -69,7 +69,7 @@ namespace fx {
for (const Opcode& opc : members) {
switch (opc.lettersOnlyHash) {
case hash("width"):
setValueFromOpcode(opc, width->_width, {-100.0f, 100.0f});
width->_width = opc.read(Default::width);
break;
}
}

View file

@ -38,7 +38,7 @@ void FlexEnvelopeSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId,
FlexEnvelope* eg = voice->getFlexEG(egIndex);
eg->configure(&region->flexEGs[egIndex]);
bool freeRunning = (
(region->loopMode == SfzLoopMode::one_shot && region->isOscillator())
(region->loopMode == LoopMode::one_shot && region->isOscillator())
);
if (freeRunning && region->flexAmpEG && egIndex == *region->flexAmpEG)
eg->setFreeRunning(true);

View file

@ -51,7 +51,7 @@ TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
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);
REQUIRE(synth.getRegionView(0)->loopMode == LoopMode::loop_sustain);
}
TEST_CASE("[Files] (regions_bad.sfz)")
@ -147,11 +147,12 @@ TEST_CASE("[Files] Group from AVL")
REQUIRE(synth.getRegionView(i)->volume == 6.0f);
REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(36, 36));
}
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));
almostEqualRanges(synth.getRegionView(0)->velocityRange, { 1_norm, 26_norm });
almostEqualRanges(synth.getRegionView(1)->velocityRange, { 27_norm, 52_norm });
almostEqualRanges(synth.getRegionView(2)->velocityRange, { 53_norm, 77_norm });
almostEqualRanges(synth.getRegionView(3)->velocityRange, { 78_norm, 102_norm });
almostEqualRanges(synth.getRegionView(4)->velocityRange, { 103_norm, 127_norm });
}
TEST_CASE("[Files] Full hierarchy")
@ -242,14 +243,14 @@ TEST_CASE("[Files] Pizz basic")
REQUIRE(synth.getNumRegions() == 4);
for (int i = 0; i < synth.getNumRegions(); ++i) {
REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(12, 22));
REQUIRE(synth.getRegionView(i)->velocityRange == Range<float>(97_norm, 127_norm));
almostEqualRanges(synth.getRegionView(i)->velocityRange, { 97_norm, 127_norm });
REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21);
REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == Range<float>(0_norm, 13_norm));
almostEqualRanges(synth.getRegionView(i)->ccConditions.getWithDefault(107), { 0_norm, 13_norm });
}
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));
almostEqualRanges(synth.getRegionView(0)->randRange, { 0, 0.25 });
almostEqualRanges(synth.getRegionView(1)->randRange, { 0.25, 0.5 });
almostEqualRanges(synth.getRegionView(2)->randRange, { 0.5, 0.75 });
almostEqualRanges(synth.getRegionView(3)->randRange, { 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)");
@ -282,7 +283,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(!region->isStereo());
REQUIRE(region->isGenerator());
REQUIRE(region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
// generator with multi
region = synth.getRegionView(regionNumber++);
@ -290,7 +291,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(region->isStereo());
REQUIRE(region->isGenerator());
REQUIRE(region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
// explicit wavetable
region = synth.getRegionView(regionNumber++);
@ -298,7 +299,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(!region->isStereo());
REQUIRE(!region->isGenerator());
REQUIRE(region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::On);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::On);
// explicit wavetable with multi
region = synth.getRegionView(regionNumber++);
@ -306,7 +307,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(region->isStereo());
REQUIRE(!region->isGenerator());
REQUIRE(region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::On);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::On);
// explicit disabled wavetable
region = synth.getRegionView(regionNumber++);
@ -314,7 +315,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(!region->isStereo());
REQUIRE(!region->isGenerator());
REQUIRE(!region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Off);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Off);
// explicit disabled wavetable with multi
region = synth.getRegionView(regionNumber++);
@ -322,7 +323,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(!region->isStereo());
REQUIRE(!region->isGenerator());
REQUIRE(!region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Off);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Off);
// implicit wavetable (sound file < 3000 frames)
region = synth.getRegionView(regionNumber++);
@ -330,7 +331,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(!region->isStereo());
REQUIRE(!region->isGenerator());
REQUIRE(region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
// implicit non-wavetable (sound file >= 3000 frames)
region = synth.getRegionView(regionNumber++);
@ -338,7 +339,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(!region->isStereo());
REQUIRE(!region->isGenerator());
REQUIRE(!region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
// generator with multi=1 (single)
region = synth.getRegionView(regionNumber++);
@ -346,7 +347,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(!region->isStereo());
REQUIRE(region->isGenerator());
REQUIRE(region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
// generator with multi=2 (ring modulation)
region = synth.getRegionView(regionNumber++);
@ -354,7 +355,7 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
REQUIRE(!region->isStereo());
REQUIRE(region->isGenerator());
REQUIRE(region->isOscillator());
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto);
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
}
TEST_CASE("[Files] wrong (overlapping) replacement for defines")
@ -447,7 +448,20 @@ TEST_CASE("[Files] Set RealCC applies properly")
TEST_CASE("[Files] Note and octave offsets")
{
Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/note_offset.sfz");
synth.loadSfzString(fs::current_path() / "tests/TestFiles/note_offset.sfz", R"(
<control> note_offset=1
<region> key=63 sample=*sine
<region> lokey=50 hikey=55 pitch_keycenter=50 sample=*sine
<region> lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=44 xfout_hikey=48 sample=*sine
<control> note_offset=-1
<region> key=63 sw_lokey=24 sw_hikey=28 sw_last=25 sw_up=25 sw_down=25 sw_previous=62 sample=*sine
<control> note_offset=1 octave_offset=1
<region> key=63 sample=*sine
<control> note_offset=-1 octave_offset=-1
<region> key=63 sample=*sine
<control> // Check that this does not reset either note or octave offset
<region> key=63 sample=*sine
)");
REQUIRE( synth.getNumRegions() == 7 );
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(64, 64));
@ -492,11 +506,11 @@ TEST_CASE("[Files] Off modes")
synth.noteOn(0, 64, 63);
REQUIRE( synth.getNumActiveVoices() == 2 );
const auto* fastVoice =
synth.getVoiceView(0)->getRegion()->offMode == SfzOffMode::fast ?
synth.getVoiceView(0)->getRegion()->offMode == OffMode::fast ?
synth.getVoiceView(0) :
synth.getVoiceView(1) ;
const auto* normalVoice =
synth.getVoiceView(0)->getRegion()->offMode == SfzOffMode::fast ?
synth.getVoiceView(0)->getRegion()->offMode == OffMode::fast ?
synth.getVoiceView(1) :
synth.getVoiceView(0) ;
synth.noteOn(100, 63, 63);
@ -517,9 +531,9 @@ TEST_CASE("[Files] Looped regions taken from files and possibly overriden")
synth.setSampleRate(44100);
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/looped_regions.sfz");
REQUIRE( synth.getNumRegions() == 3 );
REQUIRE( synth.getRegionView(0)->loopMode == SfzLoopMode::loop_continuous );
REQUIRE( synth.getRegionView(1)->loopMode == SfzLoopMode::no_loop );
REQUIRE( synth.getRegionView(2)->loopMode == SfzLoopMode::loop_continuous );
REQUIRE( synth.getRegionView(0)->loopMode == LoopMode::loop_continuous );
REQUIRE( synth.getRegionView(1)->loopMode == LoopMode::no_loop );
REQUIRE( synth.getRegionView(2)->loopMode == LoopMode::loop_continuous );
REQUIRE(synth.getRegionView(0)->loopRange == Range<uint32_t> { 77554, 186581 });
REQUIRE(synth.getRegionView(1)->loopRange == Range<uint32_t> { 77554, 186581 });
@ -533,7 +547,7 @@ TEST_CASE("[Files] Looped regions can start at 0")
<region> sample=wavetable_with_loop_at_endings.wav
)");
REQUIRE( synth.getNumRegions() == 1 );
REQUIRE( synth.getRegionView(0)->loopMode == SfzLoopMode::loop_continuous );
REQUIRE( synth.getRegionView(0)->loopMode == LoopMode::loop_continuous );
REQUIRE( synth.getRegionView(0)->loopRange == Range<uint32_t> { 0, synth.getRegionView(0)->sampleEnd } );
}
@ -550,13 +564,13 @@ TEST_CASE("[Synth] Release triggers automatically sets the loop mode")
<region> sample=kick.wav pitch_keycenter=69 trigger=release
)");
REQUIRE( synth.getNumRegions() == 7 );
REQUIRE( synth.getRegionView(0)->loopMode == SfzLoopMode::loop_sustain );
REQUIRE( synth.getRegionView(1)->loopMode == SfzLoopMode::loop_sustain );
REQUIRE( synth.getRegionView(2)->loopMode == SfzLoopMode::loop_sustain );
REQUIRE( synth.getRegionView(3)->loopMode == SfzLoopMode::loop_sustain );
REQUIRE( synth.getRegionView(4)->loopMode == SfzLoopMode::loop_continuous );
REQUIRE( synth.getRegionView(5)->loopMode == SfzLoopMode::one_shot );
REQUIRE( synth.getRegionView(6)->loopMode == SfzLoopMode::one_shot );
REQUIRE( synth.getRegionView(0)->loopMode == LoopMode::loop_sustain );
REQUIRE( synth.getRegionView(1)->loopMode == LoopMode::loop_sustain );
REQUIRE( synth.getRegionView(2)->loopMode == LoopMode::loop_sustain );
REQUIRE( synth.getRegionView(3)->loopMode == LoopMode::loop_sustain );
REQUIRE( synth.getRegionView(4)->loopMode == LoopMode::loop_continuous );
REQUIRE( synth.getRegionView(5)->loopMode == LoopMode::one_shot );
REQUIRE( synth.getRegionView(6)->loopMode == LoopMode::one_shot );
}
TEST_CASE("[Files] Case sentitiveness")

View file

@ -7,12 +7,13 @@
#include "sfizz/Region.h"
#include "catch2/catch.hpp"
using namespace Catch::literals;
using namespace sfz;
TEST_CASE("[Opcode] Construction")
{
SECTION("Normal construction")
{
sfz::Opcode opcode { "sample", "dummy" };
Opcode opcode { "sample", "dummy" };
REQUIRE(opcode.opcode == "sample");
REQUIRE(opcode.lettersOnlyHash == hash("sample"));
REQUIRE(opcode.parameters.empty());
@ -21,7 +22,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Normal construction with underscore")
{
sfz::Opcode opcode { "sample_underscore", "dummy" };
Opcode opcode { "sample_underscore", "dummy" };
REQUIRE(opcode.opcode == "sample_underscore");
REQUIRE(opcode.lettersOnlyHash == hash("sample_underscore"));
REQUIRE(opcode.parameters.empty());
@ -30,7 +31,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Normal construction with ampersand")
{
sfz::Opcode opcode { "sample&_ampersand", "dummy" };
Opcode opcode { "sample&_ampersand", "dummy" };
REQUIRE(opcode.opcode == "sample&_ampersand");
REQUIRE(opcode.lettersOnlyHash == hash("sample_ampersand"));
REQUIRE(opcode.parameters.empty());
@ -39,7 +40,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Normal construction with multiple ampersands")
{
sfz::Opcode opcode { "&sample&_ampersand&", "dummy" };
Opcode opcode { "&sample&_ampersand&", "dummy" };
REQUIRE(opcode.opcode == "&sample&_ampersand&");
REQUIRE(opcode.lettersOnlyHash == hash("sample_ampersand"));
REQUIRE(opcode.parameters.empty());
@ -48,7 +49,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Parameterized opcode")
{
sfz::Opcode opcode { "sample123", "dummy" };
Opcode opcode { "sample123", "dummy" };
REQUIRE(opcode.opcode == "sample123");
REQUIRE(opcode.lettersOnlyHash == hash("sample&"));
REQUIRE(opcode.value == "dummy");
@ -58,7 +59,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Parameterized opcode with ampersand")
{
sfz::Opcode opcode { "sample&123", "dummy" };
Opcode opcode { "sample&123", "dummy" };
REQUIRE(opcode.opcode == "sample&123");
REQUIRE(opcode.lettersOnlyHash == hash("sample&"));
REQUIRE(opcode.value == "dummy");
@ -68,7 +69,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Parameterized opcode with underscore")
{
sfz::Opcode opcode { "sample_underscore123", "dummy" };
Opcode opcode { "sample_underscore123", "dummy" };
REQUIRE(opcode.opcode == "sample_underscore123");
REQUIRE(opcode.lettersOnlyHash == hash("sample_underscore&"));
REQUIRE(opcode.value == "dummy");
@ -77,7 +78,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Parameterized opcode within the opcode")
{
sfz::Opcode opcode { "sample1_underscore", "dummy" };
Opcode opcode { "sample1_underscore", "dummy" };
REQUIRE(opcode.opcode == "sample1_underscore");
REQUIRE(opcode.lettersOnlyHash == hash("sample&_underscore"));
REQUIRE(opcode.value == "dummy");
@ -86,7 +87,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Parameterized opcode within the opcode")
{
sfz::Opcode opcode { "sample123_underscore", "dummy" };
Opcode opcode { "sample123_underscore", "dummy" };
REQUIRE(opcode.opcode == "sample123_underscore");
REQUIRE(opcode.lettersOnlyHash == hash("sample&_underscore"));
REQUIRE(opcode.value == "dummy");
@ -96,7 +97,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Parameterized opcode within the opcode twice")
{
sfz::Opcode opcode { "sample123_double44_underscore", "dummy" };
Opcode opcode { "sample123_double44_underscore", "dummy" };
REQUIRE(opcode.opcode == "sample123_double44_underscore");
REQUIRE(opcode.lettersOnlyHash == hash("sample&_double&_underscore"));
REQUIRE(opcode.value == "dummy");
@ -108,7 +109,7 @@ TEST_CASE("[Opcode] Construction")
SECTION("Parameterized opcode within the opcode twice, with a back parameter")
{
sfz::Opcode opcode { "sample123_double44_underscore23", "dummy" };
Opcode opcode { "sample123_double44_underscore23", "dummy" };
REQUIRE(opcode.opcode == "sample123_double44_underscore23");
REQUIRE(opcode.lettersOnlyHash == hash("sample&_double&_underscore&"));
REQUIRE(opcode.value == "dummy");
@ -119,86 +120,86 @@ TEST_CASE("[Opcode] Construction")
TEST_CASE("[Opcode] Note values")
{
auto noteValue = sfz::readNoteValue("c-1");
auto noteValue = readNoteValue("c-1");
REQUIRE(noteValue);
REQUIRE(*noteValue == 0);
noteValue = sfz::readNoteValue("C-1");
noteValue = readNoteValue("C-1");
REQUIRE(noteValue);
REQUIRE(*noteValue == 0);
noteValue = sfz::readNoteValue("g9");
noteValue = readNoteValue("g9");
REQUIRE(noteValue);
REQUIRE(*noteValue == 127);
noteValue = sfz::readNoteValue("G9");
noteValue = readNoteValue("G9");
REQUIRE(noteValue);
REQUIRE(*noteValue == 127);
noteValue = sfz::readNoteValue("c#4");
noteValue = readNoteValue("c#4");
REQUIRE(noteValue);
REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue(u8"c♯4");
noteValue = readNoteValue(u8"c♯4");
REQUIRE(noteValue);
REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue("C#4");
noteValue = readNoteValue("C#4");
REQUIRE(noteValue);
REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue(u8"C♯4");
noteValue = readNoteValue(u8"C♯4");
REQUIRE(noteValue);
REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue("e#4");
noteValue = readNoteValue("e#4");
REQUIRE(!noteValue);
noteValue = sfz::readNoteValue(u8"e♯4");
noteValue = readNoteValue(u8"e♯4");
REQUIRE(!noteValue);
noteValue = sfz::readNoteValue("E#4");
noteValue = readNoteValue("E#4");
REQUIRE(!noteValue);
noteValue = sfz::readNoteValue(u8"E♯4");
noteValue = readNoteValue(u8"E♯4");
REQUIRE(!noteValue);
noteValue = sfz::readNoteValue("db4");
noteValue = readNoteValue("db4");
REQUIRE(noteValue);
REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue(u8"d♭4");
noteValue = readNoteValue(u8"d♭4");
REQUIRE(noteValue);
REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue("Db4");
noteValue = readNoteValue("Db4");
REQUIRE(noteValue);
REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue(u8"D♭4");
noteValue = readNoteValue(u8"D♭4");
REQUIRE(noteValue);
REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue("fb4");
noteValue = readNoteValue("fb4");
REQUIRE(!noteValue);
noteValue = sfz::readNoteValue(u8"f♭4");
noteValue = readNoteValue(u8"f♭4");
REQUIRE(!noteValue);
noteValue = sfz::readNoteValue("Fb4");
noteValue = readNoteValue("Fb4");
REQUIRE(!noteValue);
noteValue = sfz::readNoteValue(u8"F♭4");
noteValue = readNoteValue(u8"F♭4");
REQUIRE(!noteValue);
}
TEST_CASE("[Opcode] Categories")
{
REQUIRE(sfz::Opcode("sample", "").category == sfz::kOpcodeNormal);
REQUIRE(sfz::Opcode("amplitude_oncc11", "").category == sfz::kOpcodeOnCcN);
REQUIRE(sfz::Opcode("cutoff_cc22", "").category == sfz::kOpcodeOnCcN);
REQUIRE(sfz::Opcode("lfo01_pitch_curvecc33", "").category == sfz::kOpcodeCurveCcN);
REQUIRE(sfz::Opcode("pan_stepcc44", "").category == sfz::kOpcodeStepCcN);
REQUIRE(sfz::Opcode("noise_level_smoothcc55", "").category == sfz::kOpcodeSmoothCcN);
REQUIRE(Opcode("sample", "").category == kOpcodeNormal);
REQUIRE(Opcode("amplitude_oncc11", "").category == kOpcodeOnCcN);
REQUIRE(Opcode("cutoff_cc22", "").category == kOpcodeOnCcN);
REQUIRE(Opcode("lfo01_pitch_curvecc33", "").category == kOpcodeCurveCcN);
REQUIRE(Opcode("pan_stepcc44", "").category == kOpcodeStepCcN);
REQUIRE(Opcode("noise_level_smoothcc55", "").category == kOpcodeSmoothCcN);
}
TEST_CASE("[Opcode] Derived names")
{
REQUIRE(sfz::Opcode("sample", "").getDerivedName(sfz::kOpcodeNormal) == "sample");
REQUIRE(sfz::Opcode("cutoff_cc22", "").getDerivedName(sfz::kOpcodeNormal) == "cutoff");
REQUIRE(sfz::Opcode("lfo01_pitch_curvecc33", "").getDerivedName(sfz::kOpcodeOnCcN) == "lfo01_pitch_oncc33");
REQUIRE(sfz::Opcode("pan_stepcc44", "").getDerivedName(sfz::kOpcodeCurveCcN) == "pan_curvecc44");
REQUIRE(sfz::Opcode("noise_level_smoothcc55", "").getDerivedName(sfz::kOpcodeStepCcN) == "noise_level_stepcc55");
REQUIRE(sfz::Opcode("sample", "").getDerivedName(sfz::kOpcodeSmoothCcN, 66) == "sample_smoothcc66");
REQUIRE(Opcode("sample", "").getDerivedName(kOpcodeNormal) == "sample");
REQUIRE(Opcode("cutoff_cc22", "").getDerivedName(kOpcodeNormal) == "cutoff");
REQUIRE(Opcode("lfo01_pitch_curvecc33", "").getDerivedName(kOpcodeOnCcN) == "lfo01_pitch_oncc33");
REQUIRE(Opcode("pan_stepcc44", "").getDerivedName(kOpcodeCurveCcN) == "pan_curvecc44");
REQUIRE(Opcode("noise_level_smoothcc55", "").getDerivedName(kOpcodeStepCcN) == "noise_level_stepcc55");
REQUIRE(Opcode("sample", "").getDerivedName(kOpcodeSmoothCcN, 66) == "sample_smoothcc66");
}
TEST_CASE("[Opcode] Normalization")
{
// *_ccN
REQUIRE(sfz::Opcode("foo_cc7", "").cleanUp(sfz::kOpcodeScopeRegion).opcode == "foo_oncc7");
REQUIRE(sfz::Opcode("foo_cc7", "").cleanUp(sfz::kOpcodeScopeControl).opcode == "foo_cc7");
REQUIRE(Opcode("foo_cc7", "").cleanUp(kOpcodeScopeRegion).opcode == "foo_oncc7");
REQUIRE(Opcode("foo_cc7", "").cleanUp(kOpcodeScopeControl).opcode == "foo_cc7");
// <region>
@ -274,8 +275,8 @@ TEST_CASE("[Opcode] Normalization")
for (auto pair : regionSpecific) {
absl::string_view input = pair.first;
absl::string_view expected = pair.second;
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeRegion).opcode == expected);
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeGeneric).opcode == input);
REQUIRE(Opcode(input, "").cleanUp(kOpcodeScopeRegion).opcode == expected);
REQUIRE(Opcode(input, "").cleanUp(kOpcodeScopeGeneric).opcode == input);
}
// <control>
@ -288,42 +289,217 @@ TEST_CASE("[Opcode] Normalization")
for (auto pair : controlSpecific) {
absl::string_view input = pair.first;
absl::string_view expected = pair.second;
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeControl).opcode == expected);
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeGeneric).opcode == input);
REQUIRE(Opcode(input, "").cleanUp(kOpcodeScopeControl).opcode == expected);
REQUIRE(Opcode(input, "").cleanUp(kOpcodeScopeGeneric).opcode == input);
}
// case
REQUIRE(sfz::Opcode("SaMpLe", "").cleanUp(sfz::kOpcodeScopeRegion).opcode == "sample");
REQUIRE(Opcode("SaMpLe", "").cleanUp(kOpcodeScopeRegion).opcode == "sample");
}
TEST_CASE("[Opcode] readOpcode")
TEST_CASE("[Opcode] opcode read (uint8_t)")
{
REQUIRE( sfz::readOpcode("16", sfz::Range<uint8_t>(0, 100)).value() == 16 );
REQUIRE( sfz::readOpcode("+16", sfz::Range<uint8_t>(0, 100)).value() == 16 );
REQUIRE( sfz::readOpcode("110", sfz::Range<uint8_t>(0, 100)).value() == 100 );
REQUIRE( sfz::readOpcode("-1", sfz::Range<uint8_t>(0, 100)).value() == 0 );
REQUIRE( sfz::readOpcode("12.5", sfz::Range<int>(-100, 100)).value() == 12 );
REQUIRE( sfz::readOpcode("+12.5", sfz::Range<int>(-100, 100)).value() == 12 );
REQUIRE( sfz::readOpcode("-40", sfz::Range<int>(-100, 100)).value() == -40 );
REQUIRE( sfz::readOpcode("-140", sfz::Range<int>(-100, 100)).value() == -100 );
REQUIRE( sfz::readOpcode("12.5", sfz::Range<float>(0.0f, 100.0f)).value() == 12.5_a );
REQUIRE( sfz::readOpcode("+12.5", sfz::Range<float>(0.0f, 100.0f)).value() == 12.5_a );
REQUIRE( sfz::readOpcode("-22.5", sfz::Range<float>(-20.0f, 100.0f)).value() == -20.0_a );
REQUIRE( sfz::readOpcode("150.5", sfz::Range<float>(-20.0f, 100.0f)).value() == 100.0_a );
REQUIRE( sfz::readOpcode("50.25garbage", sfz::Range<float>(-20.0f, 100.0f)).value() == 50.25_a );
REQUIRE( sfz::readOpcode("50.25garbage", sfz::Range<int>(-20, 100)).value() == 50 );
REQUIRE( !sfz::readOpcode("garbage50.25", sfz::Range<int>(-20, 100)) );
REQUIRE( !sfz::readOpcode("garbage", sfz::Range<int>(-20, 100)) );
SECTION("Basic")
{
Opcode opcode { "", "16" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(0, 100), 0 };
REQUIRE( opcode.read(spec) == 16);
}
SECTION("Sign")
{
Opcode opcode { "", "+16" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(0, 100), 0 };
REQUIRE( opcode.read(spec) == 16);
}
SECTION("Ignore")
{
Opcode opcode { "", "110" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(0, 100), 0 };
REQUIRE( opcode.read(spec) == spec.defaultInputValue );
}
SECTION("Clamp upper")
{
Opcode opcode { "", "110" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(0, 100), kEnforceUpperBound };
REQUIRE( opcode.read(spec) == 100 );
}
SECTION("Clamp lower")
{
Opcode opcode { "", "10" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(20, 100), kEnforceLowerBound };
REQUIRE( opcode.read(spec) == 20 );
}
SECTION("Floating point")
{
Opcode opcode { "", "10.5" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(0, 100), 0 };
REQUIRE( opcode.read(spec) == 10 );
}
SECTION("Text after")
{
Opcode opcode { "", "10garbage" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(0, 100), 0 };
REQUIRE( opcode.read(spec) == 10 );
}
SECTION("Text before")
{
Opcode opcode { "", "garbage10" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(0, 100), 0 };
REQUIRE( !opcode.readOptional(spec) );
REQUIRE( opcode.read(spec) == 0 );
}
SECTION("Can be note")
{
Opcode opcode { "", "c4" };
OpcodeSpec<uint8_t> spec { 0, Range<uint8_t>(0, 100), kCanBeNote };
REQUIRE( opcode.read(spec) == 60 );
}
}
TEST_CASE("[Opcode] opcode read (int)")
{
SECTION("Basic")
{
Opcode opcode { "", "16" };
OpcodeSpec<int> spec { 0, Range<int>(-100, 100), 0 };
REQUIRE( opcode.read(spec) == 16);
}
SECTION("Sign")
{
Opcode opcode { "", "+16" };
OpcodeSpec<int> spec { 0, Range<int>(-100, 100), 0 };
REQUIRE( opcode.read(spec) == 16);
}
SECTION("Sign")
{
Opcode opcode { "", "-16" };
OpcodeSpec<int> spec { 0, Range<int>(-100, 100), 0 };
REQUIRE( opcode.read(spec) == -16);
}
SECTION("Clamp upper")
{
Opcode opcode { "", "110" };
OpcodeSpec<int> spec { 0, Range<int>(-100, 100), kEnforceUpperBound };
REQUIRE( opcode.read(spec) == 100 );
}
SECTION("Clamp lower")
{
Opcode opcode { "", "-110" };
OpcodeSpec<int> spec { 0, Range<int>(-100, 100), kEnforceLowerBound };
REQUIRE( opcode.read(spec) == -100 );
}
SECTION("Floating point")
{
Opcode opcode { "", "10.5" };
OpcodeSpec<int> spec { 0, Range<int>(-100, 100), 0 };
REQUIRE( opcode.read(spec) == 10 );
}
SECTION("Text after")
{
Opcode opcode { "", "10garbage" };
OpcodeSpec<int> spec { 0, Range<int>(0, 100), 0 };
REQUIRE( opcode.read(spec) == 10 );
}
SECTION("Text before")
{
Opcode opcode { "", "garbage10" };
OpcodeSpec<int> spec { 0, Range<int>(20, 100), 0 };
REQUIRE( !opcode.readOptional(spec) );
REQUIRE( opcode.read(spec) == 0 );
}
SECTION("Can be note")
{
Opcode opcode { "", "c4" };
OpcodeSpec<int> spec { 0, Range<int>(20, 100), kCanBeNote };
REQUIRE( opcode.read(spec) == 60 );
}
}
TEST_CASE("[Opcode] opcode read (float)")
{
SECTION("Basic")
{
Opcode opcode { "", "16.4" };
OpcodeSpec<float> spec { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
REQUIRE( opcode.read(spec) == 16.4_a);
}
SECTION("Plus sign")
{
Opcode opcode { "", "+16.4" };
OpcodeSpec<float> spec { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
REQUIRE( opcode.read(spec) == 16.4_a);
}
SECTION("Minus sign")
{
Opcode opcode { "", "-16.4" };
OpcodeSpec<float> spec { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
REQUIRE( opcode.read(spec) == -16.4_a);
}
SECTION("Ignore")
{
Opcode opcode { "", "110" };
OpcodeSpec<float> spec { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
REQUIRE( opcode.read(spec) == spec.defaultInputValue );
}
SECTION("Clamp upper")
{
Opcode opcode { "", "110" };
OpcodeSpec<float> spec { 0.0f, Range<float>(-100.0f, 100.0f), kEnforceUpperBound };
REQUIRE( opcode.read(spec) == 100.0f );
}
SECTION("Clamp lower")
{
Opcode opcode { "", "-110" };
OpcodeSpec<float> spec { 0.0f, Range<float>(-100.0f, 100.0f), kEnforceLowerBound };
REQUIRE( opcode.read(spec) == -100.0f );
}
SECTION("Text after")
{
Opcode opcode { "", "10.5garbage" };
OpcodeSpec<float> spec { 0.0f, Range<float>(0.0f, 100.0f), kEnforceLowerBound };
REQUIRE( opcode.read(spec) == 10.5f );
}
SECTION("Text before")
{
Opcode opcode { "", "garbage10" };
OpcodeSpec<float> spec { 0.0f, Range<float>(0.0f, 100.0f), 0 };
REQUIRE( !opcode.readOptional(spec) );
REQUIRE( opcode.read(spec) == 0.0f );
}
}
TEST_CASE("[Opcode] readBooleanFromOpcode")
{
REQUIRE(sfz::readBooleanFromOpcode({"", "1"}) == true);
REQUIRE(sfz::readBooleanFromOpcode({"", "0"}) == false);
REQUIRE(sfz::readBooleanFromOpcode({"", "777"}) == true);
REQUIRE(sfz::readBooleanFromOpcode({"", "on"}) == true);
REQUIRE(sfz::readBooleanFromOpcode({"", "off"}) == false);
REQUIRE(sfz::readBooleanFromOpcode({"", "On"}) == true);
REQUIRE(sfz::readBooleanFromOpcode({"", "oFf"}) == false);
REQUIRE(readBooleanFromOpcode({"", "1"}) == true);
REQUIRE(readBooleanFromOpcode({"", "0"}) == false);
REQUIRE(readBooleanFromOpcode({"", "777"}) == true);
REQUIRE(readBooleanFromOpcode({"", "on"}) == true);
REQUIRE(readBooleanFromOpcode({"", "off"}) == false);
REQUIRE(readBooleanFromOpcode({"", "On"}) == true);
REQUIRE(readBooleanFromOpcode({"", "oFf"}) == false);
}

View file

@ -56,8 +56,8 @@ TEST_CASE("Region activation", "Region tests")
REQUIRE(!region.isSwitchedOn());
region.registerCC(54, 19_norm);
REQUIRE(region.isSwitchedOn());
region.registerCC(54, 18_norm);
REQUIRE(region.isSwitchedOn());
region.registerCC(54, 17_norm);
REQUIRE(!region.isSwitchedOn());
region.registerCC(54, 27_norm);
REQUIRE(region.isSwitchedOn());
region.registerCC(4, 56_norm);

View file

@ -248,9 +248,9 @@ TEST_CASE("[Values] Count")
synth.dispatchMessage(client, 0, "/region1/count", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/count", "", nullptr);
std::vector<std::string> expected {
"/region0/count,N : { }",
"/region0/count,h : { 1 }",
"/region1/count,h : { 2 }",
"/region2/count,h : { 0 }",
"/region2/count,h : { 1 }",
};
REQUIRE(messageList == expected);
}
@ -324,6 +324,7 @@ TEST_CASE("[Values] Loop range")
Client client(&messageList);
client.setReceiveCallback(&simpleMessageReceiver);
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav
<region> sample=kick.wav loop_start=10 loop_end=100
<region> sample=kick.wav loopstart=10 loopend=100
<region> sample=kick.wav loop_start=-1 loopend=-100
@ -331,10 +332,12 @@ TEST_CASE("[Values] Loop range")
synth.dispatchMessage(client, 0, "/region0/loop_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/loop_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/loop_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/loop_range", "", nullptr);
std::vector<std::string> expected {
"/region0/loop_range,hh : { 10, 100 }",
"/region0/loop_range,hh : { 0, 44011 }", // Default loop points in the file
"/region1/loop_range,hh : { 10, 100 }",
"/region2/loop_range,hh : { 0, 0 }",
"/region2/loop_range,hh : { 10, 100 }",
"/region3/loop_range,hh : { 0, 44011 }",
};
REQUIRE(messageList == expected);
}
@ -450,7 +453,7 @@ TEST_CASE("[Values] Off time")
std::vector<std::string> expected {
"/region0/off_time,f : { 0.006 }",
"/region1/off_time,f : { 0.1 }",
"/region2/off_time,f : { 0 }",
"/region2/off_time,f : { 0.006 }",
};
REQUIRE(messageList == expected);
}
@ -478,8 +481,7 @@ TEST_CASE("[Values] Key range")
synth.dispatchMessage(client, 0, "/region4/key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch_keycenter", "", nullptr);
synth.dispatchMessage(client, 0, "/region5/pitch_keycenter", "", nullptr);
// TODO: activate for the new region parser ; ignore the second value
// synth.dispatchMessage(client, 0, "/region6/pitch_keycenter", "", nullptr);
synth.dispatchMessage(client, 0, "/region6/pitch_keycenter", "", nullptr);
synth.dispatchMessage(client, 0, "/region7/key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region7/pitch_keycenter", "", nullptr);
std::vector<std::string> expected {
@ -487,16 +489,42 @@ TEST_CASE("[Values] Key range")
"/region1/key_range,ii : { 34, 60 }",
"/region2/key_range,ii : { 60, 83 }",
"/region3/key_range,ii : { 0, 60 }",
"/region4/key_range,ii : { 0, 0 }",
"/region4/key_range,ii : { 0, 127 }",
"/region0/pitch_keycenter,i : { 60 }",
"/region5/pitch_keycenter,i : { 32 }",
// "/region6/pitch_keycenter,i : { 60 }",
"/region6/pitch_keycenter,i : { 60 }",
"/region7/key_range,ii : { 26, 26 }",
"/region7/pitch_keycenter,i : { 26 }",
};
REQUIRE(messageList == expected);
}
TEST_CASE("[Values] Triggers on note")
{
Synth synth;
std::vector<std::string> messageList;
Client client(&messageList);
client.setReceiveCallback(&simpleMessageReceiver);
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav
<region> sample=kick.wav hikey=-1
<region> sample=kick.wav key=-1
<region> sample=kick.wav hikey=-1 lokey=12
)");
synth.dispatchMessage(client, 0, "/region0/trigger_on_note", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/trigger_on_note", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/trigger_on_note", "", nullptr);
// TODO: Double check with Sforzando/rgc
synth.dispatchMessage(client, 0, "/region3/trigger_on_note", "", nullptr);
std::vector<std::string> expected {
"/region0/trigger_on_note,T : { }",
"/region1/trigger_on_note,F : { }",
"/region2/trigger_on_note,F : { }",
"/region3/trigger_on_note,T : { }",
};
REQUIRE(messageList == expected);
}
TEST_CASE("[Values] Velocity range")
{
Synth synth;
@ -517,7 +545,7 @@ TEST_CASE("[Values] Velocity range")
"/region0/vel_range,ff : { 0, 1 }",
"/region1/vel_range,ff : { 0.267717, 0.472441 }",
"/region2/vel_range,ff : { 0, 0.472441 }",
"/region3/vel_range,ff : { 0, 0 }",
"/region3/vel_range,ff : { 0, 1 }",
};
REQUIRE(messageList == expected);
}
@ -542,7 +570,7 @@ TEST_CASE("[Values] Bend range")
"/region0/bend_range,ff : { -1, 1 }",
"/region1/bend_range,ff : { 0.108778, 0.24417 }",
"/region2/bend_range,ff : { -0.108778, 0.108778 }",
"/region3/bend_range,ff : { -1, -1 }",
"/region3/bend_range,ff : { -1, 1 }",
};
REQUIRE(messageList == expected);
}
@ -572,7 +600,7 @@ TEST_CASE("[Values] CC condition range")
"/region1/cc_range1,ff : { 0, 0.425197 }",
"/region2/cc_range1,ff : { 0, 0.425197 }",
"/region2/cc_range2,ff : { 0.015748, 0.0787402 }",
"/region3/cc_range1,ff : { 0, 0 }",
"/region3/cc_range1,ff : { 0.0787402, 1 }",
};
REQUIRE(messageList == expected);
}
@ -595,7 +623,7 @@ TEST_CASE("[Values] CC condition range")
"/region1/cc_range1,ff : { 0, 0.1 }",
"/region2/cc_range1,ff : { 0, 0.1 }",
"/region2/cc_range2,ff : { 0.1, 0.2 }",
"/region3/cc_range1,ff : { 0, 0 }",
"/region3/cc_range1,ff : { 0.1, 1 }",
};
REQUIRE(messageList == expected);
}
@ -618,7 +646,7 @@ TEST_CASE("[Values] CC condition range")
"/region1/cc_range1,ff : { 0, 0.1 }",
"/region2/cc_range1,ff : { 0, 0.1 }",
"/region2/cc_range2,ff : { 0.1, 0.2 }",
"/region3/cc_range1,ff : { 0, 0 }",
"/region3/cc_range1,ff : { 0.1, 1 }",
};
REQUIRE(messageList == expected);
}
@ -708,19 +736,17 @@ TEST_CASE("[Values] Upswitch")
)");
synth.dispatchMessage(client, 0, "/region0/sw_up", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/sw_up", "", nullptr);
// TODO: activate for the new region parser; ignore oob
// synth.dispatchMessage(client, 0, "/region2/sw_up", "", nullptr);
// synth.dispatchMessage(client, 0, "/region3/sw_up", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/sw_up", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/sw_up", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/sw_up", "", nullptr);
// TODO: activate for the new region parser; ignore the second value
// synth.dispatchMessage(client, 0, "/region5/sw_up", "", nullptr);
synth.dispatchMessage(client, 0, "/region5/sw_up", "", nullptr);
std::vector<std::string> expected {
"/region0/sw_up,N : { }",
"/region1/sw_up,i : { 16 }",
// "/region2/sw_up,N : { }",
// "/region3/sw_up,N : { }",
"/region2/sw_up,N : { }",
"/region3/sw_up,N : { }",
"/region4/sw_up,i : { 60 }",
// "/region5/sw_up,i : { 64 }",
"/region5/sw_up,N : { }",
};
REQUIRE(messageList == expected);
}
@ -741,19 +767,17 @@ TEST_CASE("[Values] Downswitch")
)");
synth.dispatchMessage(client, 0, "/region0/sw_down", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/sw_down", "", nullptr);
// TODO: activate for the new region parser; ignore oob
// synth.dispatchMessage(client, 0, "/region2/sw_down", "", nullptr);
// synth.dispatchMessage(client, 0, "/region3/sw_down", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/sw_down", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/sw_down", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/sw_down", "", nullptr);
// TODO: activate for the new region parser; ignore the second value
// synth.dispatchMessage(client, 0, "/region5/sw_down", "", nullptr);
synth.dispatchMessage(client, 0, "/region5/sw_down", "", nullptr);
std::vector<std::string> expected {
"/region0/sw_down,N : { }",
"/region1/sw_down,i : { 16 }",
// "/region2/sw_down,N : { }",
// "/region3/sw_down,N : { }",
"/region2/sw_down,N : { }",
"/region3/sw_down,N : { }",
"/region4/sw_down,i : { 60 }",
// "/region5/sw_down,i : { 64 }",
"/region5/sw_down,N : { }",
};
REQUIRE(messageList == expected);
}
@ -774,19 +798,17 @@ TEST_CASE("[Values] Previous keyswitch")
)");
synth.dispatchMessage(client, 0, "/region0/sw_previous", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/sw_previous", "", nullptr);
// TODO: activate for the new region parser; ignore oob
// synth.dispatchMessage(client, 0, "/region2/sw_previous", "", nullptr);
// synth.dispatchMessage(client, 0, "/region3/sw_previous", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/sw_previous", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/sw_previous", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/sw_previous", "", nullptr);
// TODO: activate for the new region parser; ignore the second value
// synth.dispatchMessage(client, 0, "/region5/sw_previous", "", nullptr);
synth.dispatchMessage(client, 0, "/region5/sw_previous", "", nullptr);
std::vector<std::string> expected {
"/region0/sw_previous,N : { }",
"/region1/sw_previous,i : { 16 }",
// "/region2/sw_previous,N : { }",
// "/region3/sw_previous,N : { }",
"/region2/sw_previous,N : { }",
"/region3/sw_previous,N : { }",
"/region4/sw_previous,i : { 60 }",
// "/region5/sw_previous,i : { 64 }",
"/region5/sw_previous,N : { }",
};
REQUIRE(messageList == expected);
}
@ -838,7 +860,7 @@ TEST_CASE("[Values] Aftertouch range")
"/region0/chanaft_range,ii : { 0, 127 }",
"/region1/chanaft_range,ii : { 34, 60 }",
"/region2/chanaft_range,ii : { 0, 60 }",
"/region3/chanaft_range,ii : { 0, 0 }",
"/region3/chanaft_range,ii : { 20, 127 }",
"/region4/chanaft_range,ii : { 10, 10 }",
};
REQUIRE(messageList == expected);
@ -866,7 +888,7 @@ TEST_CASE("[Values] BPM range")
"/region0/bpm_range,ff : { 0, 500 }",
"/region1/bpm_range,ff : { 34.1, 60.2 }",
"/region2/bpm_range,ff : { 0, 60 }",
"/region3/bpm_range,ff : { 0, 0 }",
"/region3/bpm_range,ff : { 20, 500 }",
"/region4/bpm_range,ff : { 10, 10 }",
};
REQUIRE(messageList == expected);
@ -894,7 +916,7 @@ TEST_CASE("[Values] Rand range")
"/region0/rand_range,ff : { 0, 1 }",
"/region1/rand_range,ff : { 0.2, 0.4 }",
"/region2/rand_range,ff : { 0, 0.4 }",
"/region3/rand_range,ff : { 0, 0 }",
"/region3/rand_range,ff : { 0.2, 1 }",
"/region4/rand_range,ff : { 0.1, 0.1 }",
};
REQUIRE(messageList == expected);
@ -1530,12 +1552,11 @@ TEST_CASE("[Values] Amp Veltrack")
)");
synth.dispatchMessage(client, 0, "/region0/amp_veltrack", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/amp_veltrack", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region2/amp_veltrack", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/amp_veltrack", "", nullptr);
std::vector<std::string> expected {
"/region0/amp_veltrack,f : { 100 }",
"/region1/amp_veltrack,f : { 10.1 }",
// "/region2/amp_veltrack,f : { 100 }",
"/region2/amp_veltrack,f : { 100 }",
};
REQUIRE(messageList == expected);
}
@ -1582,16 +1603,15 @@ TEST_CASE("[Values] Crossfade key range")
)");
synth.dispatchMessage(client, 0, "/region0/xfin_key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/xfin_key_range", "", nullptr);
// TODO: activate for the new region parser ; parse note value
// synth.dispatchMessage(client, 0, "/region2/xfin_key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/xfin_key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/xfin_key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/xfin_key_range", "", nullptr);
std::vector<std::string> expected {
"/region0/xfin_key_range,ii : { 0, 0 }",
"/region1/xfin_key_range,ii : { 10, 40 }",
// "/region2/xfin_key_range,ii : { 60, 83 }",
"/region2/xfin_key_range,ii : { 60, 83 }",
"/region3/xfin_key_range,ii : { 0, 40 }",
"/region4/xfin_key_range,ii : { 10, 127 }",
"/region4/xfin_key_range,ii : { 0, 0 }",
};
REQUIRE(messageList == expected);
}
@ -1607,15 +1627,14 @@ TEST_CASE("[Values] Crossfade key range")
)");
synth.dispatchMessage(client, 0, "/region0/xfout_key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/xfout_key_range", "", nullptr);
// TODO: activate for the new region parser ; parse note value
// synth.dispatchMessage(client, 0, "/region2/xfout_key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/xfout_key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/xfout_key_range", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/xfout_key_range", "", nullptr);
std::vector<std::string> expected {
"/region0/xfout_key_range,ii : { 127, 127 }",
"/region1/xfout_key_range,ii : { 10, 40 }",
// "/region2/xfout_key_range,ii : { 60, 83 }",
"/region3/xfout_key_range,ii : { 0, 40 }",
"/region2/xfout_key_range,ii : { 60, 83 }",
"/region3/xfout_key_range,ii : { 40, 40 }",
"/region4/xfout_key_range,ii : { 10, 127 }",
};
REQUIRE(messageList == expected);
@ -1646,7 +1665,7 @@ TEST_CASE("[Values] Crossfade velocity range")
"/region0/xfin_vel_range,ff : { 0, 0 }",
"/region1/xfin_vel_range,ff : { 0.0787402, 0.314961 }",
"/region2/xfin_vel_range,ff : { 0, 0.314961 }",
"/region3/xfin_vel_range,ff : { 0.0787402, 1 }",
"/region3/xfin_vel_range,ff : { 0, 0 }",
};
REQUIRE(messageList == expected);
}
@ -1666,7 +1685,7 @@ TEST_CASE("[Values] Crossfade velocity range")
std::vector<std::string> expected {
"/region0/xfout_vel_range,ff : { 1, 1 }",
"/region1/xfout_vel_range,ff : { 0.0787402, 0.314961 }",
"/region2/xfout_vel_range,ff : { 0, 0.314961 }",
"/region2/xfout_vel_range,ff : { 0.314961, 0.314961 }",
"/region3/xfout_vel_range,ff : { 0.0787402, 1 }",
};
REQUIRE(messageList == expected);
@ -1767,7 +1786,7 @@ TEST_CASE("[Values] Crossfade CC range")
"/region0/xfin_cc_range4,N : { }",
"/region1/xfin_cc_range4,ff : { 0.0787402, 0.314961 }",
"/region2/xfin_cc_range4,ff : { 0, 0.314961 }",
"/region3/xfin_cc_range4,ff : { 0.0787402, 1 }",
"/region3/xfin_cc_range4,ff : { 0, 0 }",
};
REQUIRE(messageList == expected);
}
@ -1787,7 +1806,7 @@ TEST_CASE("[Values] Crossfade CC range")
std::vector<std::string> expected {
"/region0/xfout_cc_range4,N : { }",
"/region1/xfout_cc_range4,ff : { 0.0787402, 0.314961 }",
"/region2/xfout_cc_range4,ff : { 0, 0.314961 }",
"/region2/xfout_cc_range4,ff : { 0.314961, 0.314961 }",
"/region3/xfout_cc_range4,ff : { 0.0787402, 1 }",
};
REQUIRE(messageList == expected);
@ -1914,12 +1933,11 @@ TEST_CASE("[Values] Pitch Random")
)");
synth.dispatchMessage(client, 0, "/region0/pitch_random", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/pitch_random", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region2/pitch_random", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/pitch_random", "", nullptr);
std::vector<std::string> expected {
"/region0/pitch_random,f : { 0 }",
"/region1/pitch_random,f : { 10 }",
// "/region2/pitch_random,f : { 0 }",
"/region2/pitch_random,f : { 0 }",
};
REQUIRE(messageList == expected);
}
@ -1941,15 +1959,14 @@ TEST_CASE("[Values] Transpose")
synth.dispatchMessage(client, 0, "/region0/transpose", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/transpose", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/transpose", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region3/transpose", "", nullptr);
// synth.dispatchMessage(client, 0, "/region4/transpose", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/transpose", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/transpose", "", nullptr);
std::vector<std::string> expected {
"/region0/transpose,i : { 0 }",
"/region1/transpose,i : { 10 }",
"/region2/transpose,i : { -4 }",
// "/region3/transpose,i : { 0 }",
// "/region4/transpose,i : { 0 }",
"/region3/transpose,i : { 0 }",
"/region4/transpose,i : { 0 }",
};
REQUIRE(messageList == expected);
}
@ -1968,13 +1985,13 @@ TEST_CASE("[Values] Pitch/Tune")
<region> sample=kick.wav pitch=4.2
<region> sample=kick.wav tune=-200
)");
synth.dispatchMessage(client, 0, "/region0/tune", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/tune", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/tune", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/pitch", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/pitch", "", nullptr);
std::vector<std::string> expected {
"/region0/tune,f : { 0 }",
"/region1/tune,f : { 4.2 }",
"/region2/tune,f : { -200 }",
"/region0/pitch,f : { 0 }",
"/region1/pitch,f : { 4.2 }",
"/region2/pitch,f : { -200 }",
};
REQUIRE(messageList == expected);
}
@ -1983,16 +2000,16 @@ TEST_CASE("[Values] Pitch/Tune")
{
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav
<region> sample=kick.wav tune_oncc42=4.2
<region> sample=kick.wav pitch_oncc42=4.2
<region> sample=kick.wav pitch_oncc2=-10
)");
synth.dispatchMessage(client, 0, "/region0/tune_cc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/tune_cc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/tune_cc2", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch_cc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/pitch_cc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/pitch_cc2", "", nullptr);
std::vector<std::string> expected {
"/region0/tune_cc42,N : { }",
"/region1/tune_cc42,f : { 4.2 }",
"/region2/tune_cc2,f : { -10 }",
"/region0/pitch_cc42,N : { }",
"/region1/pitch_cc42,f : { 4.2 }",
"/region2/pitch_cc2,f : { -10 }",
};
REQUIRE(messageList == expected);
}
@ -2001,33 +2018,33 @@ TEST_CASE("[Values] Pitch/Tune")
{
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav
<region> sample=kick.wav tune_stepcc42=4.2
<region> sample=kick.wav tune_smoothcc42=4
<region> sample=kick.wav tune_curvecc42=2
<region> sample=kick.wav tune_stepcc42=-1
<region> sample=kick.wav tune_smoothcc42=-4
<region> sample=kick.wav tune_curvecc42=300
<region> sample=kick.wav pitch_stepcc42=4.2
<region> sample=kick.wav pitch_smoothcc42=4
<region> sample=kick.wav pitch_curvecc42=2
<region> sample=kick.wav pitch_stepcc42=-1
<region> sample=kick.wav pitch_smoothcc42=-4
<region> sample=kick.wav pitch_curvecc42=300
)");
synth.dispatchMessage(client, 0, "/region0/tune_stepcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/tune_smoothcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/tune_curvecc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/tune_stepcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/tune_smoothcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/tune_curvecc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch_stepcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch_smoothcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch_curvecc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/pitch_stepcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/pitch_smoothcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/pitch_curvecc42", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region4/tune_stepcc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region5/tune_smoothcc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region6/tune_curvecc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region4/pitch_stepcc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region5/pitch_smoothcc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region6/pitch_curvecc42", "", nullptr);
std::vector<std::string> expected {
"/region0/tune_stepcc42,N : { }",
"/region0/tune_smoothcc42,N : { }",
"/region0/tune_curvecc42,N : { }",
"/region1/tune_stepcc42,f : { 4.2 }",
"/region2/tune_smoothcc42,i : { 4 }",
"/region3/tune_curvecc42,i : { 2 }",
// "/region4/tune_stepcc42,N : { }",
// "/region5/tune_smoothcc42,N : { }",
// "/region6/tune_curvecc42,N : { }",
"/region0/pitch_stepcc42,N : { }",
"/region0/pitch_smoothcc42,N : { }",
"/region0/pitch_curvecc42,N : { }",
"/region1/pitch_stepcc42,f : { 4.2 }",
"/region2/pitch_smoothcc42,i : { 4 }",
"/region3/pitch_curvecc42,i : { 2 }",
// "/region4/pitch_stepcc42,N : { }",
// "/region5/pitch_smoothcc42,N : { }",
// "/region6/pitch_curvecc42,N : { }",
};
REQUIRE(messageList == expected);
}
@ -2043,26 +2060,26 @@ TEST_CASE("[Values] Pitch/Tune")
<region> sample=kick.wav pitch_smoothcc42=-4
<region> sample=kick.wav pitch_curvecc42=300
)");
synth.dispatchMessage(client, 0, "/region0/tune_stepcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/tune_smoothcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/tune_curvecc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/tune_stepcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/tune_smoothcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/tune_curvecc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch_stepcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch_smoothcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/pitch_curvecc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/pitch_stepcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/pitch_smoothcc42", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/pitch_curvecc42", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region4/tune_stepcc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region5/tune_smoothcc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region6/tune_curvecc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region4/pitch_stepcc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region5/pitch_smoothcc42", "", nullptr);
// synth.dispatchMessage(client, 0, "/region6/pitch_curvecc42", "", nullptr);
std::vector<std::string> expected {
"/region0/tune_stepcc42,N : { }",
"/region0/tune_smoothcc42,N : { }",
"/region0/tune_curvecc42,N : { }",
"/region1/tune_stepcc42,f : { 4.2 }",
"/region2/tune_smoothcc42,i : { 4 }",
"/region3/tune_curvecc42,i : { 2 }",
// "/region4/tune_stepcc42,N : { }",
// "/region5/tune_smoothcc42,N : { }",
// "/region6/tune_curvecc42,N : { }",
"/region0/pitch_stepcc42,N : { }",
"/region0/pitch_smoothcc42,N : { }",
"/region0/pitch_curvecc42,N : { }",
"/region1/pitch_stepcc42,f : { 4.2 }",
"/region2/pitch_smoothcc42,i : { 4 }",
"/region3/pitch_curvecc42,i : { 2 }",
// "/region4/pitch_stepcc42,N : { }",
// "/region5/pitch_smoothcc42,N : { }",
// "/region6/pitch_curvecc42,N : { }",
};
REQUIRE(messageList == expected);
}
@ -2093,17 +2110,17 @@ TEST_CASE("[Values] Bend behavior")
synth.dispatchMessage(client, 0, "/region2/bend_step", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/bend_smooth", "", nullptr);
std::vector<std::string> expected {
"/region0/bend_up,i : { 200 }",
"/region0/bend_down,i : { -200 }",
"/region0/bend_step,i : { 1 }",
"/region0/bend_up,f : { 200 }",
"/region0/bend_down,f : { -200 }",
"/region0/bend_step,f : { 1 }",
"/region0/bend_smooth,i : { 0 }",
"/region1/bend_up,i : { 100 }",
"/region1/bend_down,i : { -400 }",
"/region1/bend_step,i : { 10 }",
"/region1/bend_up,f : { 100 }",
"/region1/bend_down,f : { -400 }",
"/region1/bend_step,f : { 10 }",
"/region1/bend_smooth,i : { 10 }",
"/region2/bend_up,i : { -100 }",
"/region2/bend_down,i : { 400 }",
"/region2/bend_step,i : { 1 }",
"/region2/bend_up,f : { -100 }",
"/region2/bend_down,f : { 400 }",
"/region2/bend_step,f : { 1 }",
"/region2/bend_smooth,i : { 0 }",
};
REQUIRE(messageList == expected);
@ -2162,7 +2179,7 @@ TEST_CASE("[Values] ampeg")
"/region0/ampeg_release,f : { 0.001 }",
"/region0/ampeg_start,f : { 0 }",
"/region0/ampeg_sustain,f : { 100 }",
"/region0/ampeg_depth,i : { 0 }",
"/region0/ampeg_depth,f : { 0 }",
"/region1/ampeg_attack,f : { 1 }",
"/region1/ampeg_delay,f : { 2 }",
"/region1/ampeg_decay,f : { 3 }",
@ -2170,7 +2187,7 @@ TEST_CASE("[Values] ampeg")
"/region1/ampeg_release,f : { 5 }",
"/region1/ampeg_start,f : { 6 }",
"/region1/ampeg_sustain,f : { 7 }",
"/region1/ampeg_depth,i : { 0 }",
"/region1/ampeg_depth,f : { 0 }",
// "/region2/ampeg_attack,f : { 0 }",
// "/region2/ampeg_delay,f : { 0 }",
// "/region2/ampeg_decay,f : { 0 }",
@ -2178,7 +2195,7 @@ TEST_CASE("[Values] ampeg")
// "/region2/ampeg_release,f : { 0.001 }",
// "/region2/ampeg_start,f : { 0 }",
// "/region2/ampeg_sustain,f : { 100 }",
// "/region2/ampeg_depth,i : { 0 }",
// "/region2/ampeg_depth,f : { 0 }",
};
REQUIRE(messageList == expected);
}
@ -2213,14 +2230,14 @@ TEST_CASE("[Values] ampeg")
"/region0/ampeg_vel2hold,f : { 0 }",
"/region0/ampeg_vel2release,f : { 0 }",
"/region0/ampeg_vel2sustain,f : { 0 }",
"/region0/ampeg_vel2depth,i : { 0 }",
"/region0/ampeg_vel2depth,f : { 0 }",
"/region1/ampeg_vel2attack,f : { 1 }",
"/region1/ampeg_vel2delay,f : { 2 }",
"/region1/ampeg_vel2decay,f : { 3 }",
"/region1/ampeg_vel2hold,f : { 4 }",
"/region1/ampeg_vel2release,f : { 5 }",
"/region1/ampeg_vel2sustain,f : { 7 }",
"/region1/ampeg_vel2depth,i : { 0 }",
"/region1/ampeg_vel2depth,f : { 0 }",
};
REQUIRE(messageList == expected);
}
@ -2274,7 +2291,7 @@ TEST_CASE("[Values] Self-mask")
"/region0/note_selfmask,T : { }",
"/region1/note_selfmask,F : { }",
"/region2/note_selfmask,T : { }",
"/region3/note_selfmask,F : { }",
"/region3/note_selfmask,T : { }",
};
REQUIRE(messageList == expected);
}
@ -2300,7 +2317,7 @@ TEST_CASE("[Values] RT dead")
"/region0/rt_dead,F : { }",
"/region1/rt_dead,T : { }",
"/region2/rt_dead,F : { }",
"/region3/rt_dead,T : { }",
"/region3/rt_dead,F : { }",
};
REQUIRE(messageList == expected);
}
@ -2371,12 +2388,11 @@ TEST_CASE("[Values] Sustain CC")
)");
synth.dispatchMessage(client, 0, "/region0/sustain_cc", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/sustain_cc", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region2/sustain_cc", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/sustain_cc", "", nullptr);
std::vector<std::string> expected {
"/region0/sustain_cc,i : { 64 }",
"/region1/sustain_cc,i : { 10 }",
// "/region2/sustain_cc,i : { 20 }",
"/region2/sustain_cc,i : { 64 }",
};
REQUIRE(messageList == expected);
}
@ -2395,12 +2411,11 @@ TEST_CASE("[Values] Sustain low")
)");
synth.dispatchMessage(client, 0, "/region0/sustain_lo", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/sustain_lo", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region2/sustain_lo", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/sustain_lo", "", nullptr);
std::vector<std::string> expected {
"/region0/sustain_lo,f : { 0.0039 }",
"/region0/sustain_lo,f : { 0.00787402 }",
"/region1/sustain_lo,f : { 0.0787402 }",
// "/region2/sustain_lo,f : { 0.0787402 }",
"/region2/sustain_lo,f : { 0.00787402 }",
};
REQUIRE(messageList == expected);
}
@ -2420,18 +2435,102 @@ TEST_CASE("[Values] Oscillator phase")
)");
synth.dispatchMessage(client, 0, "/region0/oscillator_phase", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/oscillator_phase", "", nullptr);
// TODO: activate for the new region parser ; properly wrap
// synth.dispatchMessage(client, 0, "/region2/oscillator_phase", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/oscillator_phase", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/oscillator_phase", "", nullptr);
std::vector<std::string> expected {
"/region0/oscillator_phase,f : { 0 }",
"/region1/oscillator_phase,f : { 0.1 }",
// "/region2/oscillator_phase,f : { 0.1 }",
"/region2/oscillator_phase,f : { 0.1 }",
"/region3/oscillator_phase,f : { -1 }",
};
REQUIRE(messageList == expected);
}
TEST_CASE("[Values] Oscillator quality")
{
Synth synth;
std::vector<std::string> messageList;
Client client(&messageList);
client.setReceiveCallback(&simpleMessageReceiver);
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav
<region> sample=kick.wav oscillator_quality=2
<region> sample=kick.wav oscillator_quality=0 oscillator_quality=-2
)");
synth.dispatchMessage(client, 0, "/region0/oscillator_quality", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/oscillator_quality", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/oscillator_quality", "", nullptr);
std::vector<std::string> expected {
"/region0/oscillator_quality,N : { }",
"/region1/oscillator_quality,i : { 2 }",
"/region2/oscillator_quality,N : { }",
};
REQUIRE(messageList == expected);
}
TEST_CASE("[Values] Oscillator mode/multi")
{
Synth synth;
std::vector<std::string> messageList;
Client client(&messageList);
client.setReceiveCallback(&simpleMessageReceiver);
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav
<region> sample=kick.wav oscillator_mode=2
<region> sample=kick.wav oscillator_mode=1 oscillator_mode=-2
<region> sample=kick.wav oscillator_multi=9
<region> sample=kick.wav oscillator_multi=-2
)");
synth.dispatchMessage(client, 0, "/region0/oscillator_mode", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/oscillator_mode", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/oscillator_mode", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/oscillator_multi", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/oscillator_multi", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/oscillator_multi", "", nullptr);
std::vector<std::string> expected {
"/region0/oscillator_mode,i : { 0 }",
"/region1/oscillator_mode,i : { 2 }",
"/region2/oscillator_mode,i : { 0 }",
"/region0/oscillator_multi,i : { 1 }",
"/region3/oscillator_multi,i : { 9 }",
"/region4/oscillator_multi,i : { 1 }",
};
REQUIRE(messageList == expected);
}
TEST_CASE("[Values] Oscillator detune/mod depth")
{
Synth synth;
std::vector<std::string> messageList;
Client client(&messageList);
client.setReceiveCallback(&simpleMessageReceiver);
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav
<region> sample=kick.wav oscillator_detune=9.2
<region> sample=kick.wav oscillator_detune=-1200.2
<region> sample=kick.wav oscillator_mod_depth=1564.75
<region> sample=kick.wav oscillator_mod_depth=-2.2
)");
synth.dispatchMessage(client, 0, "/region0/oscillator_detune", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/oscillator_detune", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/oscillator_detune", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/oscillator_mod_depth", "", nullptr);
synth.dispatchMessage(client, 0, "/region3/oscillator_mod_depth", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/oscillator_mod_depth", "", nullptr);
std::vector<std::string> expected {
"/region0/oscillator_detune,f : { 0 }",
"/region1/oscillator_detune,f : { 9.2 }",
"/region2/oscillator_detune,f : { -1200.2 }",
"/region0/oscillator_mod_depth,f : { 0 }",
"/region3/oscillator_mod_depth,f : { 1564.75 }",
"/region4/oscillator_mod_depth,f : { 0 }",
};
REQUIRE(messageList == expected);
}
TEST_CASE("[Values] Effect sends")
{
Synth synth;
@ -2449,14 +2548,13 @@ TEST_CASE("[Values] Effect sends")
synth.dispatchMessage(client, 0, "/region1/effect1", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/effect1", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/effect2", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region4/effect1", "", nullptr);
synth.dispatchMessage(client, 0, "/region4/effect1", "", nullptr);
std::vector<std::string> expected {
// No reply to the first question
"/region1/effect1,f : { 10 }",
"/region2/effect1,f : { 0 }",
"/region2/effect2,f : { 50.4 }",
// "/region4/effect1,f : { 100 }",
// No reply to the last question
};
REQUIRE(messageList == expected);
}
@ -2474,8 +2572,6 @@ TEST_CASE("[Values] Support floating point for int values")
)");
synth.dispatchMessage(client, 0, "/region0/offset", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/pitch_keytrack", "", nullptr);
// TODO: activate for the new region parser ; ignore oob
// synth.dispatchMessage(client, 0, "/region4/effect1", "", nullptr);
std::vector<std::string> expected {
"/region0/offset,h : { 1042 }",
"/region1/pitch_keytrack,i : { -2 }",
@ -2795,15 +2891,14 @@ TEST_CASE("[Values] Filter value bounds")
SECTION("Cutoff")
{
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav cutoff=20000000 // Bound this to 20k
<region> sample=kick.wav cutoff=20000000 // Clamp the value
<region> sample=kick.wav cutoff=50 cutoff=-100
)");
synth.dispatchMessage(client, 0, "/region0/filter0/cutoff", "", nullptr);
// TODO: activate after new parser; ignore OOB
// synth.dispatchMessage(client, 0, "/region0/filter0/cutoff", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/filter0/cutoff", "", nullptr);
std::vector<std::string> expected {
"/region0/filter0/cutoff,f : { 20000 }",
// "/region0/filter0/cutoff,f : { 50 }",
"/region1/filter0/cutoff,f : { 0 }",
};
REQUIRE(messageList == expected);
}
@ -2813,10 +2908,9 @@ TEST_CASE("[Values] Filter value bounds")
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav resonance=5 resonance=-5
)");
// TODO: activate after new parser; ignore OOB
// synth.dispatchMessage(client, 0, "/region0/filter0/resonance", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/filter0/resonance", "", nullptr);
std::vector<std::string> expected {
// "/region0/filter0/resonance,f : { 5 }",
"/region0/filter0/resonance,f : { 0 }",
};
REQUIRE(messageList == expected);
}
@ -2824,19 +2918,17 @@ TEST_CASE("[Values] Filter value bounds")
SECTION("Keycenter")
{
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav keycenter=40 keycenter=-5
<region> sample=kick.wav keycenter=40 keycenter=1000
<region> sample=kick.wav keycenter=c3
<region> sample=kick.wav fil_keycenter=40
<region> sample=kick.wav fil_keycenter=40 fil_keycenter=1000
<region> sample=kick.wav fil_keycenter=c3
)");
// TODO: activate after new parser; ignore OOB
// synth.dispatchMessage(client, 0, "/region0/filter0/keycenter", "", nullptr);
// synth.dispatchMessage(client, 0, "/region1/filter0/keycenter", "", nullptr);
// TODO: activate after new parser; parse note
// synth.dispatchMessage(client, 0, "/region2/filter0/keycenter", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/filter0/keycenter", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/filter0/keycenter", "", nullptr);
synth.dispatchMessage(client, 0, "/region2/filter0/keycenter", "", nullptr);
std::vector<std::string> expected {
// "/region0/filter0/keycenter,i : { 40 }",
// "/region1/filter0/keycenter,i : { 40 }",
// "/region2/filter0/keycenter,i : { 48 }",
"/region0/filter0/keycenter,i : { 40 }",
"/region1/filter0/keycenter,i : { 60 }",
"/region2/filter0/keycenter,i : { 48 }",
};
REQUIRE(messageList == expected);
}
@ -3005,15 +3097,14 @@ TEST_CASE("[Values] EQ value bounds")
SECTION("Frequency")
{
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav eq1_freq=20000000 // Bound this to 30k
<region> sample=kick.wav eq1_freq=20000000 // Clamp the value
<region> sample=kick.wav eq1_freq=50 eq1_freq=-100
)");
synth.dispatchMessage(client, 0, "/region0/eq0/frequency", "", nullptr);
// TODO: activate after new parser; ignore OOB
// synth.dispatchMessage(client, 0, "/region0/eq0/frequency", "", nullptr);
synth.dispatchMessage(client, 0, "/region1/eq0/frequency", "", nullptr);
std::vector<std::string> expected {
"/region0/eq0/frequency,f : { 30000 }",
// "/region0/eq0/frequency,f : { 50 }",
"/region0/eq0/frequency,f : { 20000 }",
"/region1/eq0/frequency,f : { 50 }",
};
REQUIRE(messageList == expected);
}
@ -3023,10 +3114,9 @@ TEST_CASE("[Values] EQ value bounds")
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav eq1_bw=5 eq1_bw=-5
)");
// TODO: activate after new parser; ignore OOB
// synth.dispatchMessage(client, 0, "/region0/eq0/bandwidth", "", nullptr);
synth.dispatchMessage(client, 0, "/region0/eq0/bandwidth", "", nullptr);
std::vector<std::string> expected {
// "/region0/eq0/bandwidth,f : { 5 }",
"/region0/eq0/bandwidth,f : { 1 }",
};
REQUIRE(messageList == expected);
}

View file

@ -579,7 +579,7 @@ TEST_CASE("[Synth] sample quality")
synth.enableFreeWheeling();
synth.noteOn(0, 60, 100);
REQUIRE(synth.getNumActiveVoices() == 1);
REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == sfz::Default::sampleQualityInFreewheelingMode);
REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == sfz::Default::freewheelingQuality);
synth.allSoundOff();
synth.disableFreeWheeling();
@ -1085,7 +1085,7 @@ TEST_CASE("[Synth] Used CCs")
<region> locc4=64 hicc67=32 pan_cc5=200 sample=*sine
<region> width_cc98=200 sample=*sine
<region> position_cc42=200 pitch_oncc56=200 sample=*sine
<region> start_locc44=200 hikey=-1 sample=*sine
<region> start_locc44=120 hikey=-1 sample=*sine
)");
auto usedCCs = synth.getUsedCCs();
REQUIRE( usedCCs.test(1) );

View file

@ -1,12 +0,0 @@
<control> note_offset=1
<region> key=63 sample=*sine
<region> lokey=50 hikey=55 pitch_keycenter=50 sample=*sine
<region> lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=44 xfout_hikey=48 sample=*sine
<control> note_offset=-1
<region> key=63 sw_lokey=24 sw_hikey=28 sw_last=25 sw_up=25 sw_down=25 sw_previous=62 sample=*sine
<control> note_offset=1 octave_offset=1
<region> key=63 sample=*sine
<control> note_offset=-1 octave_offset=-1
<region> key=63 sample=*sine
<control> // Check that this does not reset either note or octave offset
<region> key=63 sample=*sine

View file

@ -8,6 +8,8 @@
#include "sfizz/Synth.h"
#include "sfizz/Region.h"
#include "sfizz/Voice.h"
#include "sfizz/Range.h"
#include "catch2/catch.hpp"
#include "sfizz/modulations/ModKey.h"
class RegionCCView {
@ -30,6 +32,13 @@ private:
sfz::ModKey target_;
};
template<class T>
void almostEqualRanges(const sfz::Range<T>& lhs, const sfz::Range<T>& rhs)
{
REQUIRE(lhs.getStart() == Approx(rhs.getStart()));
REQUIRE(lhs.getEnd() == Approx(rhs.getEnd()));
}
template<class C>
void sortAll(C& container)
{