diff --git a/benchmarks/BM_opcodeSpec.cpp b/benchmarks/BM_opcodeSpec.cpp new file mode 100644 index 00000000..da31a30a --- /dev/null +++ b/benchmarks/BM_opcodeSpec.cpp @@ -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 +#include +#include +#include +#include +#include + +class OpcodeSpecFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) { + std::random_device rd { }; + std::mt19937 gen { rd() }; + std::uniform_real_distribution 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(); diff --git a/benchmarks/BM_opcodeSpec.h b/benchmarks/BM_opcodeSpec.h new file mode 100644 index 00000000..8efcbe43 --- /dev/null +++ b/benchmarks/BM_opcodeSpec.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Range.h" + +template +struct OpcodeSpec +{ + T defaultValue; + sfz::Range bounds; + int flags { 0 }; +}; + +constexpr OpcodeSpec constexprSpec { 0.0f, sfz::Range(0.0f, 0.5f), 1 << 2 }; +extern const OpcodeSpec constSpec; diff --git a/benchmarks/BM_opcodeSpec_def.cpp b/benchmarks/BM_opcodeSpec_def.cpp new file mode 100644 index 00000000..076dd27d --- /dev/null +++ b/benchmarks/BM_opcodeSpec_def.cpp @@ -0,0 +1,3 @@ +#include "BM_opcodeSpec.h" + +const OpcodeSpec constSpec { 0.0f, sfz::Range(0.0f, 0.5f), 1 << 2 }; diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 89d7c8e3..77652c3e 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -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) diff --git a/common.mk b/common.mk index 81d513df..ba379cfe 100644 --- a/common.mk +++ b/common.mk @@ -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 \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d548788c..c2e40836 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index b6fab78e..be5b3199 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -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; diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 451a5bcc..faa06de3 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -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 diff --git a/src/sfizz/Curve.cpp b/src/sfizz/Curve.cpp index 8d204a39..30418d12 100644 --- a/src/sfizz/Curve.cpp +++ b/src/sfizz/Curve.cpp @@ -21,8 +21,7 @@ Curve Curve::buildCurveFromHeader( { Curve curve; bool fillStatus[NumValues] = {}; - const Range fullRange { -HUGE_VALF, +HUGE_VALF }; - + const OpcodeSpec fullRange {0.0f, Range{ -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(opc.value, fullRange); - if (!valueOpt) - continue; - - setPoint(static_cast(index), *valueOpt); + setPoint(static_cast(index), opc.read(fullRange)); } curve.fill(itp, fillStatus); @@ -267,12 +262,8 @@ void CurveSet::addCurveFromHeader(absl::Span members) int curveIndex = -1; Curve::Interpolator itp = Curve::Interpolator::Linear; - if (const Opcode* opc = findOpcode(hash("curve_index"))) { - if (auto opt = readOpcode(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"))) { diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp new file mode 100644 index 00000000..8ac6d3af --- /dev/null +++ b/src/sfizz/Defaults.cpp @@ -0,0 +1,169 @@ +#include "Defaults.h" + +namespace sfz { + +namespace Default { +constexpr auto uint32_t_max = std::numeric_limits::max(); + +extern const OpcodeSpec delay { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec delayRandom { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec offset { 0, Range(0, uint32_t_max), 0 }; +extern const OpcodeSpec offsetMod { 0, Range(0, uint32_t_max), 0 }; +extern const OpcodeSpec offsetRandom { 0, Range(0, uint32_t_max), 0 }; +extern const OpcodeSpec sampleEnd { uint32_t_max, Range(0, uint32_t_max), kEnforceLowerBound }; +extern const OpcodeSpec sampleCount { 1, Range(1, uint32_t_max), 0 }; +extern const OpcodeSpec loopStart { 0, Range(0, uint32_t_max), 0 }; +extern const OpcodeSpec loopEnd { uint32_t_max, Range(0, uint32_t_max), 0 }; +extern const OpcodeSpec loopCrossfade { 1e-3, Range(1e-3, 1.0f), 0 }; +extern const OpcodeSpec oscillator { OscillatorEnabled::Auto, Range(OscillatorEnabled::Auto, OscillatorEnabled::On), 0 }; +extern const OpcodeSpec oscillatorPhase { 0.0f, Range(-1000.0f, 1000.0f), 0 }; +extern const OpcodeSpec oscillatorMode { 0, Range(0, 2), 0 }; +extern const OpcodeSpec oscillatorMulti { 1, Range(1, config::oscillatorsPerVoice), 0 }; +extern const OpcodeSpec oscillatorDetune { 0.0f, Range(-12000.0f, 12000.0f), 0 }; +extern const OpcodeSpec oscillatorDetuneMod { 0.0f, Range(-12000.0f, 12000.0f), 0 }; +extern const OpcodeSpec oscillatorModDepth { 0.0f, Range(0.0f, 10000.0f), kNormalizePercent }; +extern const OpcodeSpec oscillatorModDepthMod { 0.0f, Range(0.0f, 10000.0f), kNormalizePercent }; +extern const OpcodeSpec oscillatorQuality { 1, Range(0, 3), 0 }; +extern const OpcodeSpec group { 0, Range(0, uint32_t_max), 0 }; +extern const OpcodeSpec offTime { 6e-3f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec polyphony { config::maxVoices, Range(0, config::maxVoices), 0 }; +extern const OpcodeSpec notePolyphony { config::maxVoices, Range(0, config::maxVoices), 0 }; +extern const OpcodeSpec key { 60, Range(0, 127), kCanBeNote }; +extern const OpcodeSpec loKey { 0, Range(0, 127), kCanBeNote }; +extern const OpcodeSpec hiKey { 127, Range(0, 127), kCanBeNote }; +extern const OpcodeSpec loCC { 0, Range(0.0f, 127.0f), kNormalizeMidi }; +extern const OpcodeSpec hiCC { 127, Range(0.0f, 127.0f), kNormalizeMidi }; +extern const OpcodeSpec loVel { 0, Range(0.0f, 127.0f), kNormalizeMidi }; +extern const OpcodeSpec hiVel { 127, Range(0.0f, 127.0f), kNormalizeMidi }; +extern const OpcodeSpec loChannelAftertouch { 0, Range(0, 127), 0 }; +extern const OpcodeSpec hiChannelAftertouch { 127, Range(0, 127), 0 }; +extern const OpcodeSpec loBend { -8192, Range(-8192.0f, 8192.0f), kNormalizeBend }; +extern const OpcodeSpec hiBend { 8192, Range(-8192.0f, 8192.0f), kNormalizeBend }; +extern const OpcodeSpec loNormalized { 0.0f, Range(0.0f, 1.0f), 0 }; +extern const OpcodeSpec hiNormalized { 1.0f, Range(0.0f, 1.0f), 0 }; +extern const OpcodeSpec loBipolar { -1.0f, Range(-1.0f, 1.0f), 0 }; +extern const OpcodeSpec hiBipolar { 1.0f, Range(-1.0f, 1.0f), 0 }; +extern const OpcodeSpec ccNumber { 0, Range(0, config::numCCs), 0 }; +extern const OpcodeSpec smoothCC { 0, Range(0, 100), 0 }; +extern const OpcodeSpec curveCC { 0, Range(0, 255), 0 }; +extern const OpcodeSpec sustainCC { 64, Range(0, 127), 0 }; +extern const OpcodeSpec sustainThreshold { 1.0f, Range(0.0f, 127.0f), kNormalizeMidi }; +extern const OpcodeSpec checkSustain { true, Range(0, 1), 0 }; +extern const OpcodeSpec checkSostenuto { true, Range(0, 1), 0 }; +extern const OpcodeSpec loBPM { 0.0f, Range(0.0f, 500.0f), 0 }; +extern const OpcodeSpec hiBPM { 500.0f, Range(0.0f, 500.0f), 0 }; +extern const OpcodeSpec sequence { 1, Range(1, 100), 0 }; +extern const OpcodeSpec volume { 0.0f, Range(-144.0f, 48.0f), 0 }; +extern const OpcodeSpec volumeMod { 0.0f, Range(-144.0f, 48.0f), 0 }; +extern const OpcodeSpec amplitude { 100.0f, Range(0.0f, 10000.0f), kNormalizePercent }; +extern const OpcodeSpec amplitudeMod { 0.0f, Range(0.0f, 10000.0f), 0 }; +extern const OpcodeSpec pan { 0.0f, Range(-100.0f, 100.0f), kNormalizePercent }; +extern const OpcodeSpec panMod { 0.0f, Range(-200.0f, 200.0f), 0 }; +extern const OpcodeSpec position { 0.0f, Range(-100.0f, 100.0f), kNormalizePercent }; +extern const OpcodeSpec positionMod { 0.0f, Range(-200.0f, 200.0f), 0 }; +extern const OpcodeSpec width { 100.0f, Range(-100.0f, 100.0f), kNormalizePercent }; +extern const OpcodeSpec widthMod { 0.0f, Range(-200.0f, 200.0f), 0 }; +extern const OpcodeSpec crossfadeIn { 0.0f, Range(0.0f, 127.0f), kNormalizeMidi }; +extern const OpcodeSpec crossfadeInNorm { 0.0f, Range(0.0f, 1.0f), 0 }; +extern const OpcodeSpec crossfadeOut { 127.0f, Range(0.0f, 127.0f), kNormalizeMidi }; +extern const OpcodeSpec crossfadeOutNorm { 1.0f, Range(0.0f, 1.0f), 0 }; +extern const OpcodeSpec ampKeytrack { 0.0f, Range(-96.0f, 12.0f), 0 }; +extern const OpcodeSpec ampVeltrack { 100.0f, Range(-100.0f, 100.0f), kNormalizePercent }; +extern const OpcodeSpec ampVelcurve { 0.0f, Range(0.0f, 1.0f), 0 }; +extern const OpcodeSpec ampRandom { 0.0f, Range(0.0f, 24.0f), 0 }; +extern const OpcodeSpec rtDead { false, Range(0, 1), 0 }; +extern const OpcodeSpec rtDecay { 0.0f, Range(0.0f, 200.0f), 0 }; +extern const OpcodeSpec filterCutoff { 0.0f, Range(0.0f, 20000.0f), kEnforceUpperBound }; +extern const OpcodeSpec filterCutoffMod { 0.0f, Range(-12000.0f, 12000.0f), 0 }; +extern const OpcodeSpec filterResonance { 0.0f, Range(0.0f, 96.0f), 0 }; +extern const OpcodeSpec filterResonanceMod { 0.0f, Range(0.0f, 96.0f), 0 }; +extern const OpcodeSpec filterGain { 0.0f, Range(-96.0f, 96.0f), 0 }; +extern const OpcodeSpec filterGainMod { 0.0f, Range(-96.0f, 96.0f), 0 }; +extern const OpcodeSpec filterRandom { 0.0f, Range(0.0f, 12000.0f), 0 }; +extern const OpcodeSpec filterKeytrack { 0, Range(0, 1200), 0 }; +extern const OpcodeSpec filterVeltrack { 0, Range(-12000, 12000), 0 }; +extern const OpcodeSpec eqBandwidth { 1.0f, Range(0.001f, 4.0f), 0 }; +extern const OpcodeSpec eqBandwidthMod { 0.0f, Range(-4.0f, 4.0f), 0 }; +extern const OpcodeSpec eqFrequency { 0.0f, Range(0.0f, 20000.0f), kEnforceUpperBound }; +extern const OpcodeSpec eqFrequencyMod { 0.0f, Range(-20000.0f, 20000.0f), 0 }; +extern const OpcodeSpec eqGain { 0.0f, Range(-96.0f, 96.0f), 0 }; +extern const OpcodeSpec eqGainMod { 0.0f, Range(-96.0f, 96.0f), 0 }; +extern const OpcodeSpec eqVel2Frequency { 0.0f, Range(-30000.0f, 30000.0f), 0 }; +extern const OpcodeSpec eqVel2Gain { 0.0f, Range(-96.0f, 96.0f), 0 }; +extern const OpcodeSpec pitchKeytrack { 100, Range(-1200, 1200), 0 }; +extern const OpcodeSpec pitchRandom { 0.0f, Range(0.0f, 12000.0f), 0 }; +extern const OpcodeSpec pitchVeltrack { 0, Range(-12000, 12000), 0 }; +extern const OpcodeSpec transpose { 0, Range(-127, 127), 0 }; +extern const OpcodeSpec pitch { 0.0f, Range(-2400.0f, 2400.0f), 0 }; +extern const OpcodeSpec pitchMod { 0.0f, Range(-2400.0f, 2400.0f), 0 }; +extern const OpcodeSpec bendUp { 200.0f, Range(-12000.0f, 12000.0f), 0 }; +extern const OpcodeSpec bendDown { -200.0f, Range(-12000.0f, 12000.0f), 0 }; +extern const OpcodeSpec bendStep { 1.0f, Range(1.0f, 1200.0f), 0 }; +extern const OpcodeSpec lfoFreq { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec lfoFreqMod { 0.0f, Range(-100.0f, 100.0f), 0 }; +extern const OpcodeSpec lfoBeats { 0.0f, Range(0.0f, 1000.0f), 0 }; +extern const OpcodeSpec lfoBeatsMod { 0.0f, Range(-1000.0f, 1000.0f), 0 }; +extern const OpcodeSpec lfoPhase { 0.0f, Range(0.0f, 1.0f), kWrapPhase }; +extern const OpcodeSpec lfoDelay { 0.0f, Range(0.0f, 30.0f), 0 }; +extern const OpcodeSpec lfoFade { 0.0f, Range(0.0f, 30.0f), 0 }; +extern const OpcodeSpec lfoCount { 0, Range(0, 1000), 0 }; +extern const OpcodeSpec lfoSteps { 0, Range(0, static_cast(config::maxLFOSteps)), 0 }; +extern const OpcodeSpec lfoStepX { 0.0f, Range(-100.0f, 100.0f), kNormalizePercent }; +extern const OpcodeSpec lfoWave { LFOWave::Triangle, Range(LFOWave::Triangle, LFOWave::RandomSH), 0 }; +extern const OpcodeSpec lfoOffset { 0.0f, Range(-1.0f, 1.0f), 0 }; +extern const OpcodeSpec lfoRatio { 1.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec lfoScale { 1.0f, Range(0.0f, 1.0f), 0 }; +extern const OpcodeSpec egTime { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec egRelease { 0.001f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec egTimeMod { 0.0f, Range(-100.0f, 100.0f), 0 }; +extern const OpcodeSpec egPercent { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec egPercentMod { 0.0f, Range(-100.0f, 100.0f), 0 }; +extern const OpcodeSpec egDepth { 0.0f, Range(-12000.0f, 12000.0f), 0 }; +extern const OpcodeSpec egVel2Depth { 0.0f, Range(-12000.0f, 12000.0f), 0 }; +extern const OpcodeSpec flexEGAmpeg { false, Range(0, 1), 0 }; +extern const OpcodeSpec flexEGDynamic { 0, Range(0, 1), 0 }; +extern const OpcodeSpec flexEGSustain { 0, Range(0, 100), 0 }; +extern const OpcodeSpec flexEGPointTime { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec flexEGPointLevel { 0.0f, Range(-1.0f, 1.0f), 0 }; +extern const OpcodeSpec flexEGPointShape { 0.0f, Range(-100.0f, 100.0f), 0 }; +extern const OpcodeSpec sampleQuality { 1, Range(1, 10), 0 }; +extern const OpcodeSpec octaveOffset { 0, Range(-10, 10), 0 }; +extern const OpcodeSpec noteOffset { 0, Range(-127, 127), 0 }; +extern const OpcodeSpec effect { 0.0f, Range(0.0f, 100.0f), kNormalizePercent }; +extern const OpcodeSpec apanWaveform { 0, Range(0, std::numeric_limits::max()), 0 }; +extern const OpcodeSpec apanFrequency { 0.0f, Range(0.0f, std::numeric_limits::max()), 0 }; +extern const OpcodeSpec apanPhase { 0.5f, Range(0.0f, 1.0f), kWrapPhase }; +extern const OpcodeSpec apanLevel { 0.0f, Range(0.0f, 100.0f), kNormalizePercent }; +extern const OpcodeSpec distoTone { 100.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec distoDepth { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec distoStages { 1, Range(1, maxDistoStages), 0 }; +extern const OpcodeSpec compAttack { 0.005f, Range(0.0f, 10.0f), 0 }; +extern const OpcodeSpec compRelease { 0.05f, Range(0.0f, 10.0f), 0 }; +extern const OpcodeSpec compSTLink { false, Range(0, 1), 0 }; +extern const OpcodeSpec compThreshold { 0.0f, Range(-100.0f, 0.0f), 0 }; +extern const OpcodeSpec compRatio { 1.0f, Range(1.0f, 50.0f), 0 }; +extern const OpcodeSpec compGain { 0.0f, Range(-100.0f, 100.0f), kDb2Mag }; +extern const OpcodeSpec fverbSize { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec fverbPredelay { 0.0f, Range(0.0f, 10.0f), 0 }; +extern const OpcodeSpec fverbTone { 100.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec fverbDamp { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec gateSTLink { false, Range(0, 1), 0 }; +extern const OpcodeSpec gateAttack { 0.005f, Range(0.0f, 10.0f), 0 }; +extern const OpcodeSpec gateRelease { 0.05f, Range(0.0f, 10.0f), 0 }; +extern const OpcodeSpec gateHold { 0.0f, Range(0.0f, 10.0f), 0 }; +extern const OpcodeSpec gateThreshold { 0.0f, Range(-100.0f, 0.0f), 0 }; +extern const OpcodeSpec lofiBitred { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec lofiDecim { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec rectify { 0.0f, Range(0.0f, 100.0f), 0 }; +extern const OpcodeSpec stringsNumber { maxStrings, Range(0, maxStrings), 0 }; +extern const OpcodeSpec trigger { Trigger::attack, Range(Trigger::attack, Trigger::release_key), 0}; +extern const OpcodeSpec crossfadeCurve { CrossfadeCurve::power, Range(CrossfadeCurve::gain, CrossfadeCurve::power), 0}; +extern const OpcodeSpec offMode { OffMode::fast, Range(OffMode::fast, OffMode::time), 0}; +extern const OpcodeSpec loopMode { LoopMode::no_loop, Range(LoopMode::no_loop, LoopMode::loop_sustain), 0}; +extern const OpcodeSpec velocityOverride { VelocityOverride::current, Range(VelocityOverride::current, VelocityOverride::previous), 0}; +extern const OpcodeSpec selfMask { SelfMask::mask, Range(SelfMask::mask, SelfMask::dontMask), 0}; +extern const OpcodeSpec filter { FilterType::kFilterNone, Range(FilterType::kFilterNone, FilterType::kFilterPeq), 0}; +extern const OpcodeSpec eq { EqType::kEqNone, Range(EqType::kEqNone, EqType::kEqHighShelf), 0}; +} // namespace Default + +} // namespace sfz diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 2ebca118..557ae19f 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -24,267 +24,290 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once -#include "Range.h" -#include "Config.h" #include #include +#include +#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 +struct OpcodeSpec +{ + T defaultInputValue; + Range bounds; + int flags; + + /** + * @brief Normalizes an input as needed for the spec + * + * @tparam U + * @param input + * @return U + */ + template + typename std::enable_if::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(normalizePercents(input)); + else if (flags & kNormalizeMidi) + return static_cast(normalize7Bits(input)); + else if (flags & kNormalizeBend) + return static_cast(normalizeBend(input)); + else if (flags & kDb2Mag) + return static_cast(db2mag(input)); + else // just in case + return input; + } + + /** + * @brief Normalizes an input as needed for the spec + * + * @tparam U + * @param input + * @return U + */ + template + typename std::enable_if::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 delayRange { 0.0, 100.0 }; - constexpr int64_t offset { 0 }; - constexpr int64_t offsetRandom { 0 }; - constexpr Range offsetRange { 0, std::numeric_limits::max() }; - constexpr Range offsetCCRange = offsetRange; - constexpr Range sampleEndRange { 0, std::numeric_limits::max() }; - constexpr Range sampleCountRange { 0, std::numeric_limits::max() }; - constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop }; - constexpr Range loopRange { 0, std::numeric_limits::max() }; - constexpr float loopCrossfade { 1e-3 }; - constexpr Range loopCrossfadeRange { loopCrossfade, 1.0 }; + extern const OpcodeSpec delay; + extern const OpcodeSpec delayRandom; + extern const OpcodeSpec offset; + extern const OpcodeSpec offsetMod; + extern const OpcodeSpec offsetRandom; + extern const OpcodeSpec sampleEnd; + extern const OpcodeSpec sampleCount; + extern const OpcodeSpec loopStart; + extern const OpcodeSpec loopEnd; + extern const OpcodeSpec loopCrossfade; + extern const OpcodeSpec oscillatorPhase; + extern const OpcodeSpec oscillator; + extern const OpcodeSpec oscillatorMode; + extern const OpcodeSpec oscillatorMulti; + extern const OpcodeSpec oscillatorDetune; + extern const OpcodeSpec oscillatorDetuneMod; + extern const OpcodeSpec oscillatorModDepth; + extern const OpcodeSpec oscillatorModDepthMod; + extern const OpcodeSpec oscillatorQuality; + extern const OpcodeSpec group; + extern const OpcodeSpec offTime; + extern const OpcodeSpec polyphony; + extern const OpcodeSpec notePolyphony; + extern const OpcodeSpec key; + extern const OpcodeSpec loKey; + extern const OpcodeSpec hiKey; + extern const OpcodeSpec loVel; + extern const OpcodeSpec hiVel; + extern const OpcodeSpec loCC; + extern const OpcodeSpec hiCC; + extern const OpcodeSpec loBend; + extern const OpcodeSpec hiBend; + extern const OpcodeSpec loNormalized; + extern const OpcodeSpec hiNormalized; + extern const OpcodeSpec loBipolar; + extern const OpcodeSpec hiBipolar; + extern const OpcodeSpec loChannelAftertouch; + extern const OpcodeSpec hiChannelAftertouch; + extern const OpcodeSpec ccNumber; + extern const OpcodeSpec curveCC; + extern const OpcodeSpec smoothCC; + extern const OpcodeSpec sustainCC; + extern const OpcodeSpec checkSustain; + extern const OpcodeSpec checkSostenuto; + extern const OpcodeSpec sustainThreshold; + extern const OpcodeSpec loBPM; + extern const OpcodeSpec hiBPM; + extern const OpcodeSpec sequence; + extern const OpcodeSpec volume; + extern const OpcodeSpec volumeMod; + extern const OpcodeSpec amplitude; + extern const OpcodeSpec amplitudeMod; + extern const OpcodeSpec pan; + extern const OpcodeSpec panMod; + extern const OpcodeSpec position; + extern const OpcodeSpec positionMod; + extern const OpcodeSpec width; + extern const OpcodeSpec widthMod; + extern const OpcodeSpec crossfadeIn; + extern const OpcodeSpec crossfadeInNorm; + extern const OpcodeSpec crossfadeOut; + extern const OpcodeSpec crossfadeOutNorm; + extern const OpcodeSpec ampKeytrack; + extern const OpcodeSpec ampVeltrack; + extern const OpcodeSpec ampVelcurve; + extern const OpcodeSpec ampRandom; + extern const OpcodeSpec rtDead; + extern const OpcodeSpec rtDecay; + extern const OpcodeSpec filterCutoff; + extern const OpcodeSpec filterCutoffMod; + extern const OpcodeSpec filterResonance; + extern const OpcodeSpec filterResonanceMod; + extern const OpcodeSpec filterGain; + extern const OpcodeSpec filterGainMod; + extern const OpcodeSpec filterRandom; + extern const OpcodeSpec filterKeytrack; + extern const OpcodeSpec filterVeltrack; + extern const OpcodeSpec eqBandwidth; + extern const OpcodeSpec eqBandwidthMod; + extern const OpcodeSpec eqFrequency; + extern const OpcodeSpec eqFrequencyMod; + extern const OpcodeSpec eqGain; + extern const OpcodeSpec eqGainMod; + extern const OpcodeSpec eqVel2Frequency; + extern const OpcodeSpec eqVel2Gain; + extern const OpcodeSpec pitchKeytrack; + extern const OpcodeSpec pitchRandom; + extern const OpcodeSpec pitchVeltrack; + extern const OpcodeSpec transpose; + extern const OpcodeSpec pitch; + extern const OpcodeSpec pitchMod; + extern const OpcodeSpec bendUp; + extern const OpcodeSpec bendDown; + extern const OpcodeSpec bendStep; + extern const OpcodeSpec lfoFreq; + extern const OpcodeSpec lfoFreqMod; + extern const OpcodeSpec lfoBeats; + extern const OpcodeSpec lfoBeatsMod; + extern const OpcodeSpec lfoPhase; + extern const OpcodeSpec lfoDelay; + extern const OpcodeSpec lfoFade; + extern const OpcodeSpec lfoCount; + extern const OpcodeSpec lfoSteps; + extern const OpcodeSpec lfoStepX; + extern const OpcodeSpec lfoWave; + extern const OpcodeSpec lfoOffset; + extern const OpcodeSpec lfoRatio; + extern const OpcodeSpec lfoScale; + extern const OpcodeSpec egTime; + extern const OpcodeSpec egRelease; + extern const OpcodeSpec egTimeMod; + extern const OpcodeSpec egPercent; + extern const OpcodeSpec egPercentMod; + extern const OpcodeSpec egDepth; + extern const OpcodeSpec egVel2Depth; + extern const OpcodeSpec flexEGAmpeg; + extern const OpcodeSpec flexEGDynamic; + extern const OpcodeSpec flexEGSustain; + extern const OpcodeSpec flexEGPointTime; + extern const OpcodeSpec flexEGPointLevel; + extern const OpcodeSpec flexEGPointShape; + extern const OpcodeSpec sampleQuality; + extern const OpcodeSpec octaveOffset; + extern const OpcodeSpec noteOffset; + extern const OpcodeSpec effect; + extern const OpcodeSpec apanWaveform; + extern const OpcodeSpec apanFrequency; + extern const OpcodeSpec apanPhase; + extern const OpcodeSpec apanLevel; + extern const OpcodeSpec distoTone; + extern const OpcodeSpec distoDepth; + extern const OpcodeSpec distoStages; + extern const OpcodeSpec compAttack; + extern const OpcodeSpec compRelease; + extern const OpcodeSpec compThreshold; + extern const OpcodeSpec compSTLink; + extern const OpcodeSpec compRatio; + extern const OpcodeSpec compGain; + extern const OpcodeSpec fverbSize; + extern const OpcodeSpec fverbPredelay; + extern const OpcodeSpec fverbTone; + extern const OpcodeSpec fverbDamp; + extern const OpcodeSpec gateAttack; + extern const OpcodeSpec gateRelease; + extern const OpcodeSpec gateSTLink; + extern const OpcodeSpec gateHold; + extern const OpcodeSpec gateThreshold; + extern const OpcodeSpec lofiBitred; + extern const OpcodeSpec lofiDecim; + extern const OpcodeSpec rectify; + extern const OpcodeSpec stringsNumber; + extern const OpcodeSpec trigger; + extern const OpcodeSpec offMode; + extern const OpcodeSpec loopMode; + extern const OpcodeSpec crossfadeCurve; + extern const OpcodeSpec velocityOverride; + extern const OpcodeSpec selfMask; + extern const OpcodeSpec filter; + extern const OpcodeSpec eq; - // common defaults - constexpr Range midi7Range { 0, 127 }; - constexpr Range float7Range { 0.0f, 127.0f }; - constexpr Range normalizedRange { 0.0f, 1.0f }; - constexpr Range 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 oscillatorPhaseRange { -1.0, 1.0 }; - constexpr int oscillatorMode { 0 }; - constexpr int oscillatorMulti { 1 }; - constexpr Range oscillatorModeRange { 0, 2 }; - constexpr Range oscillatorMultiRange { 1, config::oscillatorsPerVoice }; - constexpr float oscillatorDetune { 0 }; - constexpr Range oscillatorDetuneRange { -12000, 12000 }; - constexpr Range oscillatorDetuneCCRange { -12000, 12000 }; - constexpr float oscillatorModDepth { 0 }; - constexpr Range oscillatorModDepthRange { 0, 10000 }; // depth%, allowed to be >100 for FM - constexpr Range oscillatorModDepthCCRange { 0, 10000 }; - constexpr int oscillatorQuality { 1 }; - constexpr Range oscillatorQualityRange { 0, 3 }; - - // Instrument setting: voice lifecycle - constexpr uint32_t group { 0 }; - constexpr Range groupRange { 0, std::numeric_limits::max() }; - constexpr SfzOffMode offMode { SfzOffMode::fast }; - constexpr float offTime { 6e-3f }; - constexpr Range polyphonyRange { 0, config::maxVoices }; - constexpr SfzSelfMask selfMask { SfzSelfMask::mask }; - - // Region logic: key mapping - constexpr Range keyRange { 0, 127 }; - constexpr auto velocityRange = normalizedRange; - - // Region logic: MIDI conditions - constexpr Range channelRange { 1, 16 }; - constexpr Range midiChannelRange { 0, 15 }; - constexpr Range smoothCCRange { 0, 100 }; - constexpr float smoothTauPerStep { 3e-3 }; - constexpr Range curveCCRange { 0, 255 }; - constexpr Range ccNumberRange { 0, config::numCCs }; - constexpr auto ccValueRange = normalizedRange; - constexpr Range bendRange = { -8192, 8192 }; - constexpr Range bendValueRange = symmetricNormalizedRange; - constexpr int bend { 0 }; - constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current }; - - // Region logic: internal conditions - constexpr Range randRange { 0.0, 1.0 }; - constexpr Range aftertouchRange { 0, 127 }; - constexpr uint8_t aftertouch { 0 }; - constexpr Range bpmRange { 0.0, 500.0 }; - constexpr float bpm { 120.0 }; - constexpr uint8_t sequenceLength{ 1 }; - constexpr uint8_t sequencePosition{ 1 }; - constexpr Range sequenceRange { 1, 100 }; - - // Region logic: Triggers - constexpr SfzTrigger trigger { SfzTrigger::attack }; - constexpr Range ccTriggerValueRange = normalizedRange; - - // Performance parameters: amplifier - constexpr float globalVolume { -7.35f }; - constexpr float volume { 0.0f }; - constexpr Range volumeRange { -144.0, 48.0 }; - constexpr Range volumeCCRange { -144.0, 48.0 }; - constexpr float amplitude { 100.0 }; - constexpr Range amplitudeRange { 0.0, 1e8 }; - constexpr float pan { 0.0 }; - constexpr Range panRange { -100.0, 100.0 }; - constexpr Range panCCRange { -200.0, 200.0 }; - constexpr float position { 0.0 }; - constexpr Range positionRange { -100.0, 100.0 }; - constexpr Range positionCCRange { -200.0, 200.0 }; - constexpr float width { 100.0 }; - constexpr Range widthRange { -100.0, 100.0 }; - constexpr Range widthCCRange { -200.0, 200.0 }; - constexpr uint8_t ampKeycenter { 60 }; - constexpr float ampKeytrack { 0.0 }; - constexpr Range ampKeytrackRange { -96, 12 }; - constexpr float ampVeltrack { 100.0 }; - constexpr Range ampVeltrackRange { -100.0, 100.0 }; - constexpr Range ampVelcurveRange { 0.0, 1.0 }; - constexpr float ampRandom { 0.0 }; - constexpr Range ampRandomRange { 0.0, 24.0 }; - constexpr Range crossfadeKeyInRange { 0, 0 }; - constexpr Range crossfadeKeyOutRange { 127, 127 }; + // Default values for ranges + constexpr Range crossfadeKeyInRange { 0, 0 }; + constexpr Range crossfadeKeyOutRange { 127, 127 }; constexpr Range crossfadeVelInRange { 0.0f, 0.0f }; constexpr Range crossfadeVelOutRange { 1.0f, 1.0f }; constexpr Range crossfadeCCInRange { 0.0f, 0.0f }; constexpr Range 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 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 filterCutoffRange { 0.0f, 20000.0f }; - constexpr Range filterCutoffModRange { -12000, 12000 }; - constexpr Range filterGainRange { -96.0f, 96.0f }; - constexpr Range filterGainModRange { -96.0f, 96.0f }; - constexpr Range filterKeytrackRange { 0, 1200 }; - constexpr Range filterRandomRange { 0, 12000 }; - constexpr Range filterVeltrackRange { -12000, 12000 }; - constexpr Range filterResonanceRange { 0.0f, 96.0f }; - constexpr Range 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 eqBandwidthRange { 0.001f, 4.0f }; - constexpr Range eqBandwidthModRange { -4.0f, 4.0f }; - constexpr Range eqFrequencyRange { 0.0f, 30000.0f }; - constexpr Range eqFrequencyModRange { -30000.0f, 30000.0f }; - constexpr Range eqGainRange { -96.0f, 96.0f }; - constexpr Range eqGainModRange { -96.0f, 96.0f }; - - // Performance parameters: pitch - constexpr uint8_t pitchKeycenter { 60 }; - constexpr int pitchKeytrack { 100 }; - constexpr Range pitchKeytrackRange { -1200, 1200 }; - constexpr float pitchRandom { 0 }; - constexpr Range pitchRandomRange { 0, 12000 }; - constexpr int pitchVeltrack { 0 }; - constexpr Range pitchVeltrackRange { -12000, 12000 }; - constexpr int transpose { 0 }; - constexpr Range transposeRange { -127, 127 }; - constexpr float tune { 0 }; - constexpr Range tuneRange { -12000, 12000 }; // ±100 in SFZv1, more in ARIA - constexpr Range tuneCCRange { -12000, 12000 }; - constexpr Range bendBoundRange { -12000, 12000 }; - constexpr Range 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 lfoFreqRange { 0.0, 100.0 }; - constexpr Range lfoFreqModRange { -100.0, 100.0 }; - constexpr Range lfoBeatsRange { 0.0, 1000.0 }; - constexpr Range lfoBeatsModRange { -1000.0, 1000.0 }; - constexpr Range lfoPhaseRange { 0.0, 1.0 }; - constexpr Range lfoDelayRange { 0.0, 30.0 }; - constexpr Range lfoFadeRange { 0.0, 30.0 }; - constexpr Range lfoCountRange { 0, 1000 }; - constexpr Range lfoStepsRange { 0, static_cast(config::maxLFOSteps) }; - constexpr Range lfoStepXRange { -100.0, 100.0 }; - constexpr Range lfoWaveRange { 0, 15 }; - constexpr Range lfoOffsetRange { -1.0, 1.0 }; - constexpr Range lfoRatioRange { 0.0, 100.0 }; - constexpr Range 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 egTimeRange { 0.0, 100.0 }; - constexpr Range egPercentRange { 0.0, 100.0 }; - constexpr Range egDepthRange { -12000, 12000 }; - constexpr Range egOnCCTimeRange { -100.0, 100.0 }; - constexpr Range egOnCCPercentRange { -100.0, 100.0 }; - constexpr Range pitchEgDepthRange { -12000.0, 12000.0 }; - constexpr Range 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 flexEGDynamicRange { 0, 1 }; - constexpr Range flexEGSustainRange { 0, 100 }; - constexpr Range flexEGPointTimeRange { 0.0f, 100.0f }; - constexpr Range flexEGPointLevelRange { -1.0f, 1.0f }; - constexpr Range flexEGPointShapeRange { -100.0f, 100.0f }; - - // ***** SFZ v2 ******** - constexpr int sampleQuality { 1 }; - constexpr int sampleQualityInFreewheelingMode { 10 }; // for future use, possibly excessive - constexpr Range sampleQualityRange { 1, 10 }; // sample_quality - - constexpr bool checkSustain { true }; // sustain_sw - constexpr bool checkSostenuto { true }; // sostenuto_sw - constexpr Range octaveOffsetRange { -10, 10 }; // octave_offset - constexpr Range noteOffsetRange { -127, 127 }; // note_offset - - constexpr Range apanWaveformRange { 0, std::numeric_limits::max() }; - constexpr Range apanFrequencyRange { 0, std::numeric_limits::max() }; - constexpr Range apanPhaseRange { 0.0, 1.0 }; - constexpr Range apanLevelRange { 0.0, 100.0 }; -} -} +} // namespace sfz diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index 5cfe0d94..5bf81ca4 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -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 ccAttack; CCMap 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); }; diff --git a/src/sfizz/EQDescription.h b/src/sfizz/EQDescription.h index c109a4a0..7860d756 100644 --- a/src/sfizz/EQDescription.h +++ b/src/sfizz/EQDescription.h @@ -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 }; }; } diff --git a/src/sfizz/EQPool.h b/src/sfizz/EQPool.h index 4670daac..54567b2f 100644 --- a/src/sfizz/EQPool.h +++ b/src/sfizz/EQPool.h @@ -44,7 +44,7 @@ private: const EQDescription* description; std::unique_ptr eq; float baseBandwidth { Default::eqBandwidth }; - float baseFrequency { Default::eqFrequency1 }; + float baseFrequency { Default::eqFrequency }; float baseGain { Default::eqGain }; bool prepared { false }; ModMatrix::TargetId gainTarget; diff --git a/src/sfizz/Effects.h b/src/sfizz/Effects.h index 2056cac2..8cc5d63f 100644 --- a/src/sfizz/Effects.h +++ b/src/sfizz/Effects.h @@ -6,6 +6,7 @@ #pragma once #include "AudioBuffer.h" +#include "Defaults.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include @@ -178,8 +179,8 @@ private: std::vector> _effects; AudioBuffer _inputs { EffectChannels, config::defaultSamplesPerBlock }; AudioBuffer _outputs { EffectChannels, config::defaultSamplesPerBlock }; - float _gainToMain = 0.0; - float _gainToMix = 0.0; + float _gainToMain { Default::effect }; + float _gainToMix { Default::effect }; }; } // namespace sfz diff --git a/src/sfizz/FileMetadata.h b/src/sfizz/FileMetadata.h index 96facb03..a6a152be 100644 --- a/src/sfizz/FileMetadata.h +++ b/src/sfizz/FileMetadata.h @@ -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, diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index b86bf76f..50617169 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -255,7 +255,7 @@ absl::optional 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(oversamplingFactor); fileInformation->sampleRate = factor * static_cast(reader->sampleRate()); fileInformation->end = static_cast(factor * fileInformation->end); - fileInformation->loopBegin = static_cast(factor * fileInformation->loopBegin); + fileInformation->loopStart = static_cast(factor * fileInformation->loopStart); fileInformation->loopEnd = static_cast(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(oversamplingFactor); fileInformation->sampleRate = factor * static_cast(reader->sampleRate()); fileInformation->end = static_cast(factor * fileInformation->end); - fileInformation->loopBegin = static_cast(factor * fileInformation->loopBegin); + fileInformation->loopStart = static_cast(factor * fileInformation->loopStart); fileInformation->loopEnd = static_cast(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(samplerateChange * information.end); - information.loopBegin = static_cast(samplerateChange * information.loopBegin); + information.loopStart = static_cast(samplerateChange * information.loopStart); information.loopEnd = static_cast(samplerateChange * information.loopEnd); if (preloadedFile.second.status == FileData::Status::Done) { diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index fa67db72..6936fb63 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -51,10 +51,10 @@ using FileAudioBuffer = AudioBuffer; 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 }; diff --git a/src/sfizz/FilterDescription.h b/src/sfizz/FilterDescription.h index 2f3193a0..612d1e58 100644 --- a/src/sfizz/FilterDescription.h +++ b/src/sfizz/FilterDescription.h @@ -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 }; diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index d8c16e40..46b38d41 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -37,7 +37,7 @@ void sfz::FilterHolder::setup(const Region& region, unsigned filterId, int noteN baseCutoff *= centsFactor(keytrack); const auto veltrack = static_cast(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(*resonanceSpan, baseResonance); if (float* mod = mm.getModulation(resonanceTarget)) diff --git a/src/sfizz/FilterPool.h b/src/sfizz/FilterPool.h index d995e0db..ab761124 100644 --- a/src/sfizz/FilterPool.h +++ b/src/sfizz/FilterPool.h @@ -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(Default::filterKeycenter), float velocity = 0); + void setup(const Region& region, unsigned filterId, int noteNumber = static_cast(Default::key), float velocity = 0); /** * @brief Process a block of stereo inputs * diff --git a/src/sfizz/FlexEGDescription.h b/src/sfizz/FlexEGDescription.h index b8a0f59b..8ba22069 100644 --- a/src/sfizz/FlexEGDescription.h +++ b/src/sfizz/FlexEGDescription.h @@ -35,7 +35,7 @@ struct FlexEGDescription { int sustain { Default::flexEGSustain }; // index of the sustain point (default to 0 in ARIA) std::vector 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 diff --git a/src/sfizz/LFODescription.h b/src/sfizz/LFODescription.h index 2628430f..82859779 100644 --- a/src/sfizz/LFODescription.h +++ b/src/sfizz/LFODescription.h @@ -5,39 +5,27 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #pragma once +#include "Defaults.h" #include #include 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 steps {}; // lfoN_stepX - normalized to unity diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index e9b76361..f3061111 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -16,7 +16,7 @@ namespace sfz { * @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...) */ template -float crossfadeIn(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) +float crossfadeIn(const sfz::Range& crossfadeRange, U value, CrossfadeCurve curve) { if (value < crossfadeRange.getStart()) return 0.0f; @@ -27,9 +27,9 @@ float crossfadeIn(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurv else if (value < crossfadeRange.getEnd()) { const auto crossfadePosition = static_cast(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& crossfadeRange, U value, SfzCrossfadeCurv * @brief Compute a crossfade out value with respect to a crossfade range (note, velocity, cc, ...) */ template -float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) +float crossfadeOut(const sfz::Range& crossfadeRange, U value, CrossfadeCurve curve) { if (value > crossfadeRange.getEnd()) return 0.0f; @@ -51,9 +51,9 @@ float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCur else if (value > crossfadeRange.getStart()) { const auto crossfadePosition = static_cast(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; } diff --git a/src/sfizz/Opcode.cpp b/src/sfizz/Opcode.cpp index 1a60ff78..1628f292 100644 --- a/src/sfizz/Opcode.cpp +++ b/src/sfizz/Opcode.cpp @@ -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 +absl::optional readInt_(OpcodeSpec 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(spec.bounds.getEnd())) { + if (spec.flags & kEnforceUpperBound) + return spec.bounds.getEnd(); + + return absl::nullopt; + } else if (returnedValue < static_cast(spec.bounds.getStart())) { + if (spec.flags & kEnforceLowerBound) + return spec.bounds.getStart(); + + return absl::nullopt; + } + + return returnedValue; +} + +#define INSTANTIATE_FOR_INTEGRAL(T) \ + template <> \ + absl::optional Opcode::readOptional(OpcodeSpec spec) const \ + { \ + return readInt_(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 +absl::optional readFloat_(OpcodeSpec 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(spec.bounds.getEnd())) { + if (spec.flags & kEnforceUpperBound) + return spec.bounds.getEnd(); + + return absl::nullopt; + } else if (returnedValue < static_cast(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 Opcode::readOptional(OpcodeSpec spec) const \ + { \ + return readFloat_(spec, value); \ + } + +INSTANTIATE_FOR_FLOATING_POINT(float) +INSTANTIATE_FOR_FLOATING_POINT(double) + absl::optional 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 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 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(noteNumber); -} - -/// -template ::value, int>> -absl::optional readOpcode(absl::string_view value, const Range& 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::max()) - returnedValue = std::numeric_limits::max(); - if (returnedValue < std::numeric_limits::min()) - returnedValue = std::numeric_limits::min(); - - return validRange.clamp(static_cast(returnedValue)); -} - -template ::value, int>> -absl::optional readOpcode(absl::string_view value, const Range& 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(noteNumber); } absl::optional readBooleanFromOpcode(const Opcode& opcode) @@ -222,88 +273,176 @@ absl::optional 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::wholeRange())) - return *value != 0; + const OpcodeSpec fullInt64 { 0, Range::wholeRange(), 0 }; + const auto v = opcode.readOptional(fullInt64); + + if (v) + return v != 0; return absl::nullopt; } -template -void setValueFromOpcode(const Opcode& opcode, ValueType& target, const Range& validRange) +template <> +absl::optional Opcode::readOptional(OpcodeSpec) 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 -inline void setValueFromOpcode(const Opcode& opcode, absl::optional& target, const Range& validRange) +template <> +absl::optional Opcode::readOptional(OpcodeSpec) 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 -void setRangeEndFromOpcode(const Opcode& opcode, Range& target, const Range& validRange) +template <> +absl::optional Opcode::readOptional(OpcodeSpec) 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 -void setRangeStartFromOpcode(const Opcode& opcode, Range& target, const Range& validRange) +template <> +absl::optional Opcode::readOptional(OpcodeSpec) 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 -void setCCPairFromOpcode(const Opcode& opcode, absl::optional>& target, const Range& validRange) +template <> +absl::optional Opcode::readOptional(OpcodeSpec) 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 readOpcode(absl::string_view value, const Range& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \ - template void setValueFromOpcode(const Opcode& opcode, T& target, const Range& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \ - template void setValueFromOpcode(const Opcode& opcode, absl::optional& target, const Range& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \ - template void setRangeEndFromOpcode(const Opcode& opcode, Range& target, const Range& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \ - template void setRangeStartFromOpcode(const Opcode& opcode, Range& target, const Range& validRange); /*NOLINT(bugprone-macro-parentheses)*/ \ - template void setCCPairFromOpcode(const Opcode& opcode, absl::optional>& target, const Range& validRange); /*NOLINT(bugprone-macro-parentheses)*/ +template <> +absl::optional Opcode::readOptional(OpcodeSpec) 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 Opcode::readOptional(OpcodeSpec) 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 Opcode::readOptional(OpcodeSpec) 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 Opcode::readOptional(OpcodeSpec) 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 Opcode::readOptional(OpcodeSpec) const +{ + return readBooleanFromOpcode(*this); +} + +template <> +absl::optional Opcode::readOptional(OpcodeSpec spec) const +{ + const OpcodeSpec intSpec { + static_cast(spec.defaultInputValue), + Range(static_cast(spec.bounds.getStart()), static_cast(spec.bounds.getEnd())), + 0 + }; + + if (auto value = readOptional(intSpec)) + return static_cast(*value); + + return absl::nullopt; +} } // namespace sfz diff --git a/src/sfizz/Opcode.h b/src/sfizz/Opcode.h index a6c3a1d6..314e64f0 100644 --- a/src/sfizz/Opcode.h +++ b/src/sfizz/Opcode.h @@ -101,6 +101,12 @@ struct Opcode { category == kOpcodeStepCcN || category == kOpcodeSmoothCcN; } + template + absl::optional readOptional(OpcodeSpec spec) const; + + template + T read(OpcodeSpec 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 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 the cast value, or null - */ -template ::value, int> = 0> -absl::optional readOpcode(absl::string_view value, const Range& 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 the cast value, or null - */ -template ::value, int> = 0> -absl::optional readOpcode(absl::string_view value, const Range& validRange); - /** * @brief Read a boolean value from the sfz file and cast it to the destination parameter. */ absl::optional 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 -void setValueFromOpcode(const Opcode& opcode, ValueType& target, const Range& 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 -void setValueFromOpcode(const Opcode& opcode, absl::optional& target, const Range& 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 -void setRangeEndFromOpcode(const Opcode& opcode, Range& target, const Range& 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 -void setRangeStartFromOpcode(const Opcode& opcode, Range& target, const Range& 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 -void setCCPairFromOpcode(const Opcode& opcode, absl::optional>& target, const Range& validRange); - } std::ostream &operator<<(std::ostream &os, const sfz::Opcode &opcode); diff --git a/src/sfizz/Range.h b/src/sfizz/Range.h index 7bbe369f..04240553 100644 --- a/src/sfizz/Range.h +++ b/src/sfizz/Range.h @@ -19,7 +19,9 @@ namespace sfz */ template class Range { - static_assert(std::is_arithmetic::value, "The Type should be arithmetic"); + // static_assert(std::is_arithmetic::value + // || (std::is_enum::value && std::is_same::type, int>::value), + // "The Type should be arithmetic"); public: constexpr Range() = default; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index fbbee939..3d3008bf 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -33,6 +33,18 @@ bool extendIfNecessary(std::vector& vec, unsigned size, unsigned defaultCapac return true; } +sfz::Region::Region(int regionNumber, const MidiState& midiState, absl::string_view defaultPath) +: id{regionNumber}, midiState(midiState), defaultPath(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::egRelease; +} + bool sfz::Region::parseOpcode(const Opcode& rawOpcode) { const Opcode opcode = rawOpcode.cleanUp(kOpcodeScopeRegion); @@ -45,21 +57,19 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) case hash(x "_stepcc&"): \ case hash(x "_smoothcc&") - #define LFO_EG_filter_EQ_target(sourceKey, targetKey, range) \ - { \ - const auto number = opcode.parameters.front(); \ - if (number == 0) \ - return false; \ - \ - const auto index = opcode.parameters.size() == 2 ? opcode.parameters.back() - 1 : 0; \ - if (!extendIfNecessary(filters, index + 1, Default::numFilters)) \ - return false; \ - \ - if (auto value = readOpcode(opcode.value, range)) { \ - const ModKey source = ModKey::createNXYZ(sourceKey, id, number - 1); \ - const ModKey target = ModKey::createNXYZ(targetKey, id, index); \ - getOrCreateConnection(source, target).sourceDepth = *value; \ - } \ + #define LFO_EG_filter_EQ_target(sourceKey, targetKey, spec) \ + { \ + const auto number = opcode.parameters.front(); \ + if (number == 0) \ + return false; \ + \ + const auto index = opcode.parameters.size() == 2 ? opcode.parameters.back() - 1 : 0; \ + if (!extendIfNecessary(filters, index + 1, Default::numFilters)) \ + return false; \ + \ + const ModKey source = ModKey::createNXYZ(sourceKey, id, number - 1); \ + const ModKey target = ModKey::createNXYZ(targetKey, id, index); \ + getOrCreateConnection(source, target).sourceDepth = opcode.read(spec); \ } // Sound source: sample playback @@ -79,245 +89,196 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) } break; case hash("sample_quality"): - { - if (opcode.value == "-1") - sampleQuality.reset(); - else - setValueFromOpcode(opcode, sampleQuality, Default::sampleQualityRange); - break; - } + sampleQuality = opcode.read(Default::sampleQuality); break; case hash("direction"): *sampleId = sampleId->reversed(opcode.value == "reverse"); break; case hash("delay"): - setValueFromOpcode(opcode, delay, Default::delayRange); + delay = opcode.read(Default::delay); break; case hash("delay_random"): - setValueFromOpcode(opcode, delayRandom, Default::delayRange); + delayRandom = opcode.read(Default::delayRandom); break; case hash("offset"): - setValueFromOpcode(opcode, offset, Default::offsetRange); + offset = opcode.read(Default::offset); break; case hash("offset_random"): - setValueFromOpcode(opcode, offsetRandom, Default::offsetRange); + offsetRandom = opcode.read(Default::offsetRandom); break; case hash("offset_oncc&"): // also offset_cc& if (opcode.parameters.back() > config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::offsetCCRange)) - offsetCC[opcode.parameters.back()] = *value; + + offsetCC[opcode.parameters.back()] = opcode.read(Default::offsetMod); break; case hash("end"): - setValueFromOpcode(opcode, sampleEnd, Default::sampleEndRange); + sampleEnd = opcode.read(Default::sampleEnd); break; case hash("count"): - setValueFromOpcode(opcode, sampleCount, Default::sampleCountRange); + sampleCount = opcode.read(Default::sampleCount); break; case hash("loop_mode"): // also loopmode - switch (hash(opcode.value)) { - case hash("no_loop"): - loopMode = SfzLoopMode::no_loop; - break; - case hash("one_shot"): - loopMode = SfzLoopMode::one_shot; - break; - case hash("loop_continuous"): - loopMode = SfzLoopMode::loop_continuous; - break; - case hash("loop_sustain"): - loopMode = SfzLoopMode::loop_sustain; - break; - default: - DBG("Unkown loop mode:" << opcode.value); - } + loopMode = opcode.readOptional(Default::loopMode); break; case hash("loop_end"): // also loopend - setRangeEndFromOpcode(opcode, loopRange, Default::loopRange); + loopRange.setEnd(opcode.read(Default::loopEnd)); break; case hash("loop_start"): // also loopstart - setRangeStartFromOpcode(opcode, loopRange, Default::loopRange); + loopRange.setStart(opcode.read(Default::loopStart)); break; case hash("loop_crossfade"): - setValueFromOpcode(opcode, loopCrossfade, Default::loopCrossfadeRange); + loopCrossfade = opcode.read(Default::loopCrossfade); break; // Wavetable oscillator case hash("oscillator_phase"): - if (auto value = readOpcode(opcode.value, Default::oscillatorPhaseRange)) - oscillatorPhase = (*value >= 0) ? wrapPhase(*value) : -1.0f; + { + auto phase = opcode.read(Default::oscillatorPhase); + oscillatorPhase = (phase >= 0) ? wrapPhase(phase) : -1.0f; + } break; case hash("oscillator"): - if (auto value = readBooleanFromOpcode(opcode)) - oscillatorEnabled = *value ? OscillatorEnabled::On : OscillatorEnabled::Off; + oscillatorEnabled = opcode.read(Default::oscillator); break; case hash("oscillator_mode"): - setValueFromOpcode(opcode, oscillatorMode, Default::oscillatorModeRange); + oscillatorMode = opcode.read(Default::oscillatorMode); break; case hash("oscillator_multi"): - setValueFromOpcode(opcode, oscillatorMulti, Default::oscillatorMultiRange); + oscillatorMulti = opcode.read(Default::oscillatorMulti); break; case hash("oscillator_detune"): - setValueFromOpcode(opcode, oscillatorDetune, Default::oscillatorDetuneRange); + oscillatorDetune = opcode.read(Default::oscillatorDetune); break; case_any_ccN("oscillator_detune"): - processGenericCc(opcode, Default::oscillatorDetuneCCRange, ModKey::createNXYZ(ModId::OscillatorDetune, id)); + processGenericCc(opcode, Default::oscillatorDetuneMod, + ModKey::createNXYZ(ModId::OscillatorDetune, id)); break; case hash("oscillator_mod_depth"): - if (auto value = readOpcode(opcode.value, Default::oscillatorModDepthRange)) - oscillatorModDepth = normalizePercents(*value); + oscillatorModDepth = opcode.read(Default::oscillatorModDepth); break; case_any_ccN("oscillator_mod_depth"): - processGenericCc(opcode, Default::oscillatorModDepthCCRange, ModKey::createNXYZ(ModId::OscillatorModDepth, id)); + processGenericCc(opcode, Default::oscillatorModDepthMod, + ModKey::createNXYZ(ModId::OscillatorModDepth, id)); break; case hash("oscillator_quality"): - if (opcode.value == "-1") - oscillatorQuality.reset(); - else - setValueFromOpcode(opcode, oscillatorQuality, Default::oscillatorQualityRange); + oscillatorQuality = opcode.readOptional(Default::oscillatorQuality); break; // Instrument settings: voice lifecycle case hash("group"): // also polyphony_group - setValueFromOpcode(opcode, group, Default::groupRange); + group = opcode.read(Default::group); break; case hash("off_by"): // also offby - if (opcode.value == "-1") - offBy.reset(); - else - setValueFromOpcode(opcode, offBy, Default::groupRange); + offBy = opcode.readOptional(Default::group); break; case hash("off_mode"): // also offmode - switch (hash(opcode.value)) { - case hash("fast"): - offMode = SfzOffMode::fast; - break; - case hash("normal"): - offMode = SfzOffMode::normal; - break; - case hash("time"): - offMode = SfzOffMode::time; - break; - default: - DBG("Unkown off mode:" << opcode.value); - } + offMode = opcode.read(Default::offMode); break; case hash("off_time"): - offMode = SfzOffMode::time; - setValueFromOpcode(opcode, offTime, Default::egTimeRange); + offMode = OffMode::time; + offTime = opcode.read(Default::offTime); break; case hash("polyphony"): - if (auto value = readOpcode(opcode.value, Default::polyphonyRange)) - polyphony = *value; + polyphony = opcode.read(Default::polyphony); break; case hash("note_polyphony"): - if (auto value = readOpcode(opcode.value, Default::polyphonyRange)) - notePolyphony = *value; + notePolyphony = opcode.read(Default::notePolyphony); break; case hash("note_selfmask"): - switch (hash(opcode.value)) { - case hash("on"): - selfMask = SfzSelfMask::mask; - break; - case hash("off"): - selfMask = SfzSelfMask::dontMask; - break; - default: - DBG("Unkown self mask value:" << opcode.value); - } + selfMask = opcode.read(Default::selfMask); break; case hash("rt_dead"): - if (opcode.value == "on") { - rtDead = true; - } else if (opcode.value == "off") { - rtDead = false; - } else { - DBG("Unkown rt_dead value:" << opcode.value); - } + rtDead = opcode.read(Default::rtDead); break; // Region logic: key mapping case hash("lokey"): triggerOnNote = true; - setRangeStartFromOpcode(opcode, keyRange, Default::keyRange); + keyRange.setStart(opcode.read(Default::loKey)); break; case hash("hikey"): triggerOnNote = (opcode.value != "-1"); - setRangeEndFromOpcode(opcode, keyRange, Default::keyRange); + keyRange.setEnd(opcode.read(Default::hiKey)); break; case hash("key"): triggerOnNote = (opcode.value != "-1"); - setRangeStartFromOpcode(opcode, keyRange, Default::keyRange); - setRangeEndFromOpcode(opcode, keyRange, Default::keyRange); - setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange); + { + auto value = opcode.read(Default::key); + keyRange.setStart(value); + keyRange.setEnd(value); + pitchKeycenter = value; + } break; case hash("lovel"): - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - velocityRange.setStart(normalizeVelocity(*value)); + velocityRange.setStart(opcode.read(Default::loVel)); break; case hash("hivel"): - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - velocityRange.setEnd(normalizeVelocity(*value)); + velocityRange.setEnd(opcode.read(Default::hiVel)); break; // Region logic: MIDI conditions case hash("lobend"): - if (auto value = readOpcode(opcode.value, Default::bendRange)) - bendRange.setStart(normalizeBend(*value)); + bendRange.setStart(opcode.read(Default::loBend)); break; case hash("hibend"): - if (auto value = readOpcode(opcode.value, Default::bendRange)) - bendRange.setEnd(normalizeBend(*value)); + bendRange.setEnd(opcode.read(Default::hiBend)); break; case hash("locc&"): if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - ccConditions[opcode.parameters.back()].setStart(normalizeCC(*value)); + ccConditions[opcode.parameters.back()].setStart( + opcode.read(Default::loCC) + ); break; case hash("hicc&"): if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value)); + ccConditions[opcode.parameters.back()].setEnd( + opcode.read(Default::hiCC) + ); break; case hash("lohdcc&"): // also lorealcc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::normalizedRange)) - ccConditions[opcode.parameters.back()].setStart(*value); + ccConditions[opcode.parameters.back()].setStart( + opcode.read(Default::loNormalized) + ); break; case hash("hihdcc&"): // also hirealcc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::normalizedRange)) - ccConditions[opcode.parameters.back()].setEnd(*value); + ccConditions[opcode.parameters.back()].setEnd( + opcode.read(Default::hiNormalized) + ); break; case hash("sw_lokey"): // fallthrough case hash("sw_hikey"): break; case hash("sw_last"): if (!lastKeyswitchRange) { - setValueFromOpcode(opcode, lastKeyswitch, Default::keyRange); - keySwitched = false; + lastKeyswitch = opcode.readOptional(Default::key); + keySwitched = !lastKeyswitch.has_value(); } break; case hash("sw_lolast"): - if (auto value = readOpcode(opcode.value, Default::keyRange)) { + { + auto value = opcode.read(Default::key); if (!lastKeyswitchRange) - lastKeyswitchRange.emplace(*value, *value); + lastKeyswitchRange.emplace(value, value); else - lastKeyswitchRange->setStart(*value); + lastKeyswitchRange->setStart(value); keySwitched = false; lastKeyswitch = absl::nullopt; } break; case hash("sw_hilast"): - if (auto value = readOpcode(opcode.value, Default::keyRange)) { + { + auto value = opcode.read(Default::key); if (!lastKeyswitchRange) - lastKeyswitchRange.emplace(*value, *value); + lastKeyswitchRange.emplace(value, value); else - lastKeyswitchRange->setEnd(*value); + lastKeyswitchRange->setEnd(value); keySwitched = false; lastKeyswitch = absl::nullopt; @@ -327,294 +288,228 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) keyswitchLabel = opcode.value; break; case hash("sw_down"): - setValueFromOpcode(opcode, downKeyswitch, Default::keyRange); - keySwitched = false; + downKeyswitch = opcode.readOptional(Default::key); + keySwitched = !downKeyswitch.has_value(); break; case hash("sw_up"): - setValueFromOpcode(opcode, upKeyswitch, Default::keyRange); + upKeyswitch = opcode.readOptional(Default::key); break; case hash("sw_previous"): - setValueFromOpcode(opcode, previousKeyswitch, Default::keyRange); - previousKeySwitched = false; + previousKeyswitch = opcode.readOptional(Default::key); + previousKeySwitched = !previousKeyswitch.has_value(); break; case hash("sw_vel"): - switch (hash(opcode.value)) { - case hash("current"): - velocityOverride = SfzVelocityOverride::current; - break; - case hash("previous"): - velocityOverride = SfzVelocityOverride::previous; - break; - default: - DBG("Unknown velocity mode: " << opcode.value); - } + velocityOverride = + opcode.read(Default::velocityOverride); break; case hash("sustain_cc"): - setValueFromOpcode(opcode, sustainCC, Default::ccNumberRange); + sustainCC = opcode.read(Default::sustainCC); break; case hash("sustain_lo"): - if (auto value = readOpcode(opcode.value, Default::float7Range)) { - sustainThreshold = normalizeCC(*value); - } + sustainThreshold = opcode.read(Default::sustainThreshold); break; case hash("sustain_sw"): - checkSustain = readBooleanFromOpcode(opcode).value_or(Default::checkSustain); + checkSustain = opcode.read(Default::checkSustain); break; case hash("sostenuto_sw"): - checkSostenuto = readBooleanFromOpcode(opcode).value_or(Default::checkSostenuto); + checkSostenuto = opcode.read(Default::checkSostenuto); break; // Region logic: internal conditions case hash("lochanaft"): - setRangeStartFromOpcode(opcode, aftertouchRange, Default::aftertouchRange); + aftertouchRange.setStart(opcode.read(Default::loChannelAftertouch)); break; case hash("hichanaft"): - setRangeEndFromOpcode(opcode, aftertouchRange, Default::aftertouchRange); + aftertouchRange.setEnd(opcode.read(Default::hiChannelAftertouch)); break; case hash("lobpm"): - setRangeStartFromOpcode(opcode, bpmRange, Default::bpmRange); + bpmRange.setStart(opcode.read(Default::loBPM)); break; case hash("hibpm"): - setRangeEndFromOpcode(opcode, bpmRange, Default::bpmRange); + bpmRange.setEnd(opcode.read(Default::hiBPM)); break; case hash("lorand"): - setRangeStartFromOpcode(opcode, randRange, Default::randRange); + randRange.setStart(opcode.read(Default::loNormalized)); break; case hash("hirand"): - setRangeEndFromOpcode(opcode, randRange, Default::randRange); + randRange.setEnd(opcode.read(Default::hiNormalized)); break; case hash("seq_length"): - setValueFromOpcode(opcode, sequenceLength, Default::sequenceRange); + sequenceLength = opcode.read(Default::sequence); break; case hash("seq_position"): - setValueFromOpcode(opcode, sequencePosition, Default::sequenceRange); + sequencePosition = opcode.read(Default::sequence); sequenceSwitched = false; break; // Region logic: triggers case hash("trigger"): - switch (hash(opcode.value)) { - case hash("attack"): - trigger = SfzTrigger::attack; - break; - case hash("first"): - trigger = SfzTrigger::first; - break; - case hash("legato"): - trigger = SfzTrigger::legato; - break; - case hash("release"): - trigger = SfzTrigger::release; - break; - case hash("release_key"): - trigger = SfzTrigger::release_key; - break; - default: - DBG("Unknown trigger mode: " << opcode.value); - } + trigger = opcode.read(Default::trigger); break; case hash("start_locc&"): // also on_locc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::midi7Range)) { - triggerOnCC = true; - ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value)); - } + triggerOnCC = true; + ccTriggers[opcode.parameters.back()].setStart( + opcode.read(Default::loCC) + ); break; case hash("start_hicc&"): // also on_hicc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::midi7Range)) { - triggerOnCC = true; - ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value)); - } + triggerOnCC = true; + ccTriggers[opcode.parameters.back()].setEnd( + opcode.read(Default::hiCC) + ); break; case hash("start_lohdcc&"): // also on_lohdcc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::normalizedRange)) { - triggerOnCC = true; - ccTriggers[opcode.parameters.back()].setStart(*value); - } + triggerOnCC = true; + ccTriggers[opcode.parameters.back()].setStart( + opcode.read(Default::loNormalized) + ); break; case hash("start_hihdcc&"): // also on_hihdcc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::normalizedRange)) { - triggerOnCC = true; - ccTriggers[opcode.parameters.back()].setEnd(*value); - } + ccTriggers[opcode.parameters.back()].setEnd( + opcode.read(Default::hiNormalized) + ); break; // Performance parameters: amplifier case hash("volume"): // also gain - setValueFromOpcode(opcode, volume, Default::volumeRange); + volume = opcode.read(Default::volume); break; case_any_ccN("volume"): // also gain - processGenericCc(opcode, Default::volumeCCRange, ModKey::createNXYZ(ModId::Volume, id)); + processGenericCc(opcode, Default::volumeMod, ModKey::createNXYZ(ModId::Volume, id)); break; case hash("amplitude"): - if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) - amplitude = normalizePercents(*value); + amplitude = opcode.read(Default::amplitude); break; case_any_ccN("amplitude"): - processGenericCc(opcode, Default::amplitudeRange, ModKey::createNXYZ(ModId::Amplitude, id)); + processGenericCc(opcode, Default::amplitudeMod, ModKey::createNXYZ(ModId::Amplitude, id)); break; case hash("pan"): - if (auto value = readOpcode(opcode.value, Default::panRange)) - pan = normalizePercents(*value); + pan = opcode.read(Default::pan); break; case_any_ccN("pan"): - processGenericCc(opcode, Default::panCCRange, ModKey::createNXYZ(ModId::Pan, id)); + processGenericCc(opcode, Default::panMod, ModKey::createNXYZ(ModId::Pan, id)); break; case hash("position"): - if (auto value = readOpcode(opcode.value, Default::positionRange)) - position = normalizePercents(*value); + position = opcode.read(Default::position); break; case_any_ccN("position"): - processGenericCc(opcode, Default::positionCCRange, ModKey::createNXYZ(ModId::Position, id)); + processGenericCc(opcode, Default::positionMod, ModKey::createNXYZ(ModId::Position, id)); break; case hash("width"): - if (auto value = readOpcode(opcode.value, Default::widthRange)) - width = normalizePercents(*value); + width = opcode.read(Default::width); break; case_any_ccN("width"): - processGenericCc(opcode, Default::widthCCRange, ModKey::createNXYZ(ModId::Width, id)); + processGenericCc(opcode, Default::widthMod, ModKey::createNXYZ(ModId::Width, id)); break; case hash("amp_keycenter"): - setValueFromOpcode(opcode, ampKeycenter, Default::keyRange); + ampKeycenter = opcode.read(Default::key); break; case hash("amp_keytrack"): - setValueFromOpcode(opcode, ampKeytrack, Default::ampKeytrackRange); + ampKeytrack = opcode.read(Default::ampKeytrack); break; case hash("amp_veltrack"): - if (auto value = readOpcode(opcode.value, Default::ampVeltrackRange)) - ampVeltrack = normalizePercents(*value); + ampVeltrack = opcode.read(Default::ampVeltrack); break; case hash("amp_random"): - setValueFromOpcode(opcode, ampRandom, Default::ampRandomRange); + ampRandom = opcode.read(Default::ampRandom); break; case hash("amp_velcurve_&"): { - auto value = readOpcode(opcode.value, Default::ampVelcurveRange); if (opcode.parameters.back() > 127) return false; const auto inputVelocity = static_cast(opcode.parameters.back()); - if (value) - velocityPoints.emplace_back(inputVelocity, *value); + velocityPoints.emplace_back(inputVelocity, opcode.read(Default::ampVelcurve)); } break; case hash("xfin_lokey"): - setRangeStartFromOpcode(opcode, crossfadeKeyInRange, Default::keyRange); + crossfadeKeyInRange.setStart(opcode.read(Default::loKey)); break; case hash("xfin_hikey"): - setRangeEndFromOpcode(opcode, crossfadeKeyInRange, Default::keyRange); + crossfadeKeyInRange.setEnd(opcode.read(Default::loKey)); // loKey for the proper default break; case hash("xfout_lokey"): - setRangeStartFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange); + crossfadeKeyOutRange.setStart(opcode.read(Default::hiKey)); // hiKey for the proper default break; case hash("xfout_hikey"): - setRangeEndFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange); + crossfadeKeyOutRange.setEnd(opcode.read(Default::hiKey)); break; case hash("xfin_lovel"): - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - crossfadeVelInRange.setStart(normalizeVelocity(*value)); + crossfadeVelInRange.setStart(opcode.read(Default::loVel)); break; case hash("xfin_hivel"): - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - crossfadeVelInRange.setEnd(normalizeVelocity(*value)); + crossfadeVelInRange.setEnd(opcode.read(Default::loVel)); // loVel for the proper default break; case hash("xfout_lovel"): - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - crossfadeVelOutRange.setStart(normalizeVelocity(*value)); + crossfadeVelOutRange.setStart(opcode.read(Default::hiVel)); // hiVel for the proper default break; case hash("xfout_hivel"): - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - crossfadeVelOutRange.setEnd(normalizeVelocity(*value)); + crossfadeVelOutRange.setEnd(opcode.read(Default::hiVel)); break; case hash("xf_keycurve"): - switch (hash(opcode.value)) { - case hash("power"): - crossfadeKeyCurve = SfzCrossfadeCurve::power; - break; - case hash("gain"): - crossfadeKeyCurve = SfzCrossfadeCurve::gain; - break; - default: - DBG("Unknown crossfade power curve: " << opcode.value); - } + crossfadeKeyCurve = opcode.read(Default::crossfadeCurve); break; case hash("xf_velcurve"): - switch (hash(opcode.value)) { - case hash("power"): - crossfadeVelCurve = SfzCrossfadeCurve::power; - break; - case hash("gain"): - crossfadeVelCurve = SfzCrossfadeCurve::gain; - break; - default: - DBG("Unknown crossfade power curve: " << opcode.value); - } + crossfadeVelCurve = opcode.read(Default::crossfadeCurve); break; case hash("xfin_locc&"): if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - crossfadeCCInRange[opcode.parameters.back()].setStart(normalizeCC(*value)); + crossfadeCCInRange[opcode.parameters.back()].setStart( + opcode.read(Default::loCC) + ); break; case hash("xfin_hicc&"): if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - crossfadeCCInRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); + crossfadeCCInRange[opcode.parameters.back()].setEnd( + opcode.read(Default::loCC) // loCC for the proper default + ); break; case hash("xfout_locc&"): if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - crossfadeCCOutRange[opcode.parameters.back()].setStart(normalizeCC(*value)); + crossfadeCCOutRange[opcode.parameters.back()].setStart( + opcode.read(Default::hiCC) // hiCC for the proper default + ); break; case hash("xfout_hicc&"): if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::midi7Range)) - crossfadeCCOutRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); + crossfadeCCOutRange[opcode.parameters.back()].setEnd( + opcode.read(Default::hiCC) + ); break; case hash("xf_cccurve"): - switch (hash(opcode.value)) { - case hash("power"): - crossfadeCCCurve = SfzCrossfadeCurve::power; - break; - case hash("gain"): - crossfadeCCCurve = SfzCrossfadeCurve::gain; - break; - default: - DBG("Unknown crossfade power curve: " << opcode.value); - } + crossfadeCCCurve = opcode.read(Default::crossfadeCurve); break; case hash("rt_decay"): - setValueFromOpcode(opcode, rtDecay, Default::rtDecayRange); + rtDecay = opcode.read(Default::rtDecay); break; case hash("global_amplitude"): - if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) - globalAmplitude = normalizePercents(*value); + globalAmplitude = opcode.read(Default::amplitude); break; case hash("master_amplitude"): - if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) - masterAmplitude = normalizePercents(*value); + masterAmplitude = opcode.read(Default::amplitude); break; case hash("group_amplitude"): - if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) - groupAmplitude = normalizePercents(*value); + groupAmplitude = opcode.read(Default::amplitude); break; case hash("global_volume"): - setValueFromOpcode(opcode, globalVolume, Default::volumeRange); + globalVolume = opcode.read(Default::volume); break; case hash("master_volume"): - setValueFromOpcode(opcode, masterVolume, Default::volumeRange); + masterVolume = opcode.read(Default::volume); break; case hash("group_volume"): - setValueFromOpcode(opcode, groupVolume, Default::volumeRange); + groupVolume = opcode.read(Default::volume); break; // Performance parameters: filters @@ -623,7 +518,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.back() - 1); if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - setValueFromOpcode(opcode, filters[filterIndex].cutoff, Default::filterCutoffRange); + filters[filterIndex].cutoff = opcode.read(Default::filterCutoff); } break; case hash("resonance&"): // also resonance @@ -631,7 +526,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.back() - 1); if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - setValueFromOpcode(opcode, filters[filterIndex].resonance, Default::filterResonanceRange); + filters[filterIndex].resonance = opcode.read(Default::filterResonance); } break; case_any_ccN("cutoff&"): // also cutoff_oncc&, cutoff_cc&, cutoff&_cc& @@ -640,7 +535,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - processGenericCc(opcode, Default::filterCutoffModRange, ModKey::createNXYZ(ModId::FilCutoff, id, filterIndex)); + processGenericCc(opcode, Default::filterCutoffMod, ModKey::createNXYZ(ModId::FilCutoff, id, filterIndex)); } break; case_any_ccN("resonance&"): // also resonance_oncc&, resonance_cc&, resonance&_cc& @@ -649,7 +544,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - processGenericCc(opcode, Default::filterResonanceModRange, ModKey::createNXYZ(ModId::FilResonance, id, filterIndex)); + processGenericCc(opcode, Default::filterResonanceMod, ModKey::createNXYZ(ModId::FilResonance, id, filterIndex)); } break; case hash("fil&_keytrack"): // also fil_keytrack @@ -657,8 +552,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto filterIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - - setValueFromOpcode(opcode, filters[filterIndex].keytrack, Default::filterKeytrackRange); + filters[filterIndex].keytrack = opcode.read(Default::filterKeytrack); } break; case hash("fil&_keycenter"): // also fil_keycenter @@ -666,8 +560,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto filterIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - - setValueFromOpcode(opcode, filters[filterIndex].keycenter, Default::keyRange); + filters[filterIndex].keycenter = opcode.read(Default::key); } break; case hash("fil&_veltrack"): // also fil_veltrack @@ -675,8 +568,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto filterIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - - setValueFromOpcode(opcode, filters[filterIndex].veltrack, Default::filterVeltrackRange); + filters[filterIndex].veltrack = opcode.read(Default::filterVeltrack); } break; case hash("fil&_random"): // also fil_random, cutoff_random, cutoff&_random @@ -684,8 +576,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto filterIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - - setValueFromOpcode(opcode, filters[filterIndex].random, Default::filterRandomRange); + filters[filterIndex].random = opcode.read(Default::filterRandom); } break; case hash("fil&_gain"): // also fil_gain @@ -693,8 +584,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto filterIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - - setValueFromOpcode(opcode, filters[filterIndex].gain, Default::filterGainRange); + filters[filterIndex].gain = opcode.read(Default::filterGain); } break; case_any_ccN("fil&_gain"): // also fil_gain_oncc& @@ -703,7 +593,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - processGenericCc(opcode, Default::filterGainModRange, ModKey::createNXYZ(ModId::FilGain, id, filterIndex)); + processGenericCc(opcode, Default::filterGainMod, ModKey::createNXYZ(ModId::FilGain, id, filterIndex)); } break; case hash("fil&_type"): // also fil_type, filtype @@ -712,14 +602,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) return false; - absl::optional ftype = Filter::typeFromName(opcode.value); - - if (ftype) - filters[filterIndex].type = *ftype; - else { - filters[filterIndex].type = FilterType::kFilterNone; - DBG("Unknown filter type: " << opcode.value); - } + filters[filterIndex].type = opcode.read(Default::filter); } break; @@ -729,8 +612,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto eqIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - - setValueFromOpcode(opcode, equalizers[eqIndex].bandwidth, Default::eqBandwidthRange); + equalizers[eqIndex].bandwidth = opcode.read(Default::eqBandwidth); } break; case_any_ccN("eq&_bw"): // also eq&_bwcc& @@ -739,7 +621,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - processGenericCc(opcode, Default::eqBandwidthModRange, ModKey::createNXYZ(ModId::EqBandwidth, id, eqIndex)); + processGenericCc(opcode, Default::eqBandwidthMod, ModKey::createNXYZ(ModId::EqBandwidth, id, eqIndex)); } break; case hash("eq&_freq"): @@ -747,7 +629,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto eqIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - setValueFromOpcode(opcode, equalizers[eqIndex].frequency, Default::eqFrequencyRange); + equalizers[eqIndex].frequency = opcode.read(Default::eqFrequency); } break; case_any_ccN("eq&_freq"): // also eq&_freqcc& @@ -756,7 +638,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - processGenericCc(opcode, Default::eqFrequencyModRange, ModKey::createNXYZ(ModId::EqFrequency, id, eqIndex)); + processGenericCc(opcode, Default::eqFrequencyMod, ModKey::createNXYZ(ModId::EqFrequency, id, eqIndex)); } break; case hash("eq&_veltofreq"): // also eq&_vel2freq @@ -764,8 +646,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto eqIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - - setValueFromOpcode(opcode, equalizers[eqIndex].vel2frequency, Default::eqFrequencyModRange); + equalizers[eqIndex].vel2frequency = opcode.read(Default::eqVel2Frequency); } break; case hash("eq&_gain"): @@ -773,7 +654,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto eqIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - setValueFromOpcode(opcode, equalizers[eqIndex].gain, Default::eqGainRange); + equalizers[eqIndex].gain = opcode.read(Default::eqGain); } break; case_any_ccN("eq&_gain"): // also eq&_gaincc& @@ -782,7 +663,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - processGenericCc(opcode, Default::eqGainModRange, ModKey::createNXYZ(ModId::EqGain, id, eqIndex)); + processGenericCc(opcode, Default::eqGainMod, ModKey::createNXYZ(ModId::EqGain, id, eqIndex)); } break; case hash("eq&_veltogain"): // also eq&_vel2gain @@ -790,8 +671,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto eqIndex = opcode.parameters.front() - 1; if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - - setValueFromOpcode(opcode, equalizers[eqIndex].vel2gain, Default::eqGainModRange); + equalizers[eqIndex].vel2gain = opcode.read(Default::eqVel2Gain); } break; case hash("eq&_type"): @@ -800,15 +680,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(equalizers, eqIndex + 1, Default::numEQs)) return false; - absl::optional ftype = FilterEq::typeFromName(opcode.value); + equalizers[eqIndex].type = + opcode.read(Default::eq); - if (ftype) - equalizers[eqIndex].type = *ftype; - else { - equalizers[eqIndex].type = EqType::kEqNone; - DBG("Unknown EQ type: " << opcode.value); - } - } + } break; // Performance parameters: pitch @@ -817,38 +692,38 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) pitchKeycenterFromSample = true; else { pitchKeycenterFromSample = false; - setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange); + pitchKeycenter = opcode.read(Default::key); } break; case hash("pitch_keytrack"): - setValueFromOpcode(opcode, pitchKeytrack, Default::pitchKeytrackRange); + pitchKeytrack = opcode.read(Default::pitchKeytrack); break; case hash("pitch_veltrack"): - setValueFromOpcode(opcode, pitchVeltrack, Default::pitchVeltrackRange); + pitchVeltrack = opcode.read(Default::pitchVeltrack); break; case hash("pitch_random"): - setValueFromOpcode(opcode, pitchRandom, Default::pitchRandomRange); + pitchRandom = opcode.read(Default::pitchRandom); break; case hash("transpose"): - setValueFromOpcode(opcode, transpose, Default::transposeRange); + transpose = opcode.read(Default::transpose); break; case hash("pitch"): // also tune - setValueFromOpcode(opcode, tune, Default::tuneRange); + pitch = opcode.read(Default::pitch); break; case_any_ccN("pitch"): // also tune - processGenericCc(opcode, Default::tuneCCRange, ModKey::createNXYZ(ModId::Pitch, id)); + processGenericCc(opcode, Default::pitchMod, ModKey::createNXYZ(ModId::Pitch, id)); break; case hash("bend_up"): // also bendup - setValueFromOpcode(opcode, bendUp, Default::bendBoundRange); + bendUp = opcode.read(Default::bendUp); break; case hash("bend_down"): // also benddown - setValueFromOpcode(opcode, bendDown, Default::bendBoundRange); + bendDown = opcode.read(Default::bendDown); break; case hash("bend_step"): - setValueFromOpcode(opcode, bendStep, Default::bendStepRange); + bendStep = opcode.read(Default::bendStep); break; case hash("bend_smooth"): - setValueFromOpcode(opcode, bendSmooth, Default::smoothCCRange); + bendSmooth = opcode.read(Default::smoothCC); break; // Modulation: LFO @@ -859,7 +734,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - setValueFromOpcode(opcode, lfos[lfoNumber - 1].freq, Default::lfoFreqRange); + lfos[lfoNumber - 1].freq = opcode.read(Default::lfoFreq); } break; case_any_ccN("lfo&_freq"): @@ -869,7 +744,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - processGenericCc(opcode, Default::lfoFreqModRange, ModKey::createNXYZ(ModId::LFOFrequency, id, lfoNumber - 1)); + processGenericCc(opcode, Default::lfoFreqMod, ModKey::createNXYZ(ModId::LFOFrequency, id, lfoNumber - 1)); } break; case hash("lfo&_beats"): @@ -879,7 +754,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - setValueFromOpcode(opcode, lfos[lfoNumber - 1].beats, Default::lfoBeatsRange); + lfos[lfoNumber - 1].beats = opcode.read(Default::lfoBeats); } break; case_any_ccN("lfo&_beats"): @@ -889,7 +764,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - processGenericCc(opcode, Default::lfoBeatsModRange, ModKey::createNXYZ(ModId::LFOBeats, id, lfoNumber - 1)); + processGenericCc(opcode, Default::lfoBeatsMod, ModKey::createNXYZ(ModId::LFOBeats, id, lfoNumber - 1)); } break; case hash("lfo&_phase"): @@ -899,8 +774,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - if (auto value = readOpcode(opcode.value, Default::lfoPhaseRange)) - lfos[lfoNumber - 1].phase0 = wrapPhase(*value); + lfos[lfoNumber - 1].phase0 = opcode.read(Default::lfoPhase); } break; case hash("lfo&_delay"): @@ -910,7 +784,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - setValueFromOpcode(opcode, lfos[lfoNumber - 1].delay, Default::lfoDelayRange); + lfos[lfoNumber - 1].delay = opcode.read(Default::lfoDelay); } break; case hash("lfo&_fade"): @@ -920,7 +794,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - setValueFromOpcode(opcode, lfos[lfoNumber - 1].fade, Default::lfoFadeRange); + lfos[lfoNumber - 1].fade = opcode.read(Default::lfoFade); } break; case hash("lfo&_count"): @@ -930,7 +804,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - setValueFromOpcode(opcode, lfos[lfoNumber - 1].count, Default::lfoCountRange); + lfos[lfoNumber - 1].count = opcode.read(Default::lfoCount); } break; case hash("lfo&_steps"): @@ -940,11 +814,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - if (auto value = readOpcode(opcode.value, Default::lfoStepsRange)) { - if (!lfos[lfoNumber - 1].seq) - lfos[lfoNumber - 1].seq = LFODescription::StepSequence(); - lfos[lfoNumber - 1].seq->steps.resize(*value); - } + if (!lfos[lfoNumber - 1].seq) + lfos[lfoNumber - 1].seq = LFODescription::StepSequence(); + lfos[lfoNumber - 1].seq->steps.resize(opcode.read(Default::lfoSteps)); } break; case hash("lfo&_step&"): @@ -955,13 +827,11 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - if (auto value = readOpcode(opcode.value, Default::lfoStepXRange)) { - if (!lfos[lfoNumber - 1].seq) - lfos[lfoNumber - 1].seq = LFODescription::StepSequence(); - if (!extendIfNecessary(lfos[lfoNumber - 1].seq->steps, stepNumber, Default::numLFOSteps)) - return false; - lfos[lfoNumber - 1].seq->steps[stepNumber - 1] = *value * 0.01f; - } + if (!lfos[lfoNumber - 1].seq) + lfos[lfoNumber - 1].seq = LFODescription::StepSequence(); + if (!extendIfNecessary(lfos[lfoNumber - 1].seq->steps, stepNumber, Default::numLFOSteps)) + return false; + lfos[lfoNumber - 1].seq->steps[stepNumber - 1] = opcode.read(Default::lfoStepX); } break; case hash("lfo&_wave&"): // also lfo&_wave @@ -972,11 +842,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - if (auto value = readOpcode(opcode.value, Default::lfoWaveRange)) { - if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) + if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) return false; - lfos[lfoNumber - 1].sub[subNumber - 1].wave = static_cast(*value); - } + lfos[lfoNumber - 1].sub[subNumber - 1].wave = opcode.read(Default::lfoWave); } break; case hash("lfo&_offset&"): // also lfo&_offset @@ -987,11 +855,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - if (auto value = readOpcode(opcode.value, Default::lfoOffsetRange)) { - if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) - return false; - lfos[lfoNumber - 1].sub[subNumber - 1].offset = *value; - } + if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) + return false; + lfos[lfoNumber - 1].sub[subNumber - 1].offset = opcode.read(Default::lfoOffset); } break; case hash("lfo&_ratio&"): // also lfo&_ratio @@ -1002,11 +868,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - if (auto value = readOpcode(opcode.value, Default::lfoRatioRange)) { - if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) - return false; - lfos[lfoNumber - 1].sub[subNumber - 1].ratio = *value; - } + if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) + return false; + lfos[lfoNumber - 1].sub[subNumber - 1].ratio = opcode.read(Default::lfoRatio); } break; case hash("lfo&_scale&"): // also lfo&_scale @@ -1017,11 +881,9 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) return false; - if (auto value = readOpcode(opcode.value, Default::lfoScaleRange)) { - if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) - return false; - lfos[lfoNumber - 1].sub[subNumber - 1].scale = *value; - } + if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) + return false; + lfos[lfoNumber - 1].sub[subNumber - 1].scale = opcode.read(Default::lfoScale); } break; @@ -1031,11 +893,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto lfoNumber = opcode.parameters.front(); if (lfoNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) { - const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::amplitudeMod); } break; case hash("lfo&_pan"): @@ -1043,11 +904,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto lfoNumber = opcode.parameters.front(); if (lfoNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::panCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Pan, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Pan, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::panMod); } break; case hash("lfo&_width"): @@ -1055,11 +915,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto lfoNumber = opcode.parameters.front(); if (lfoNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::widthCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Width, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Width, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::widthMod); } break; case hash("lfo&_position"): // sfizz extension @@ -1067,11 +926,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto lfoNumber = opcode.parameters.front(); if (lfoNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::positionCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Position, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Position, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::positionMod); } break; case hash("lfo&_pitch"): @@ -1079,11 +937,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto lfoNumber = opcode.parameters.front(); if (lfoNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::tuneCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Pitch, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Pitch, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::pitchMod); } break; case hash("lfo&_volume"): @@ -1091,30 +948,29 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto lfoNumber = opcode.parameters.front(); if (lfoNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::volumeCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Volume, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Volume, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::volumeMod); } break; case hash("lfo&_cutoff&"): - LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilCutoff, Default::filterCutoffModRange); + LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilCutoff, Default::filterCutoffMod); break; case hash("lfo&_resonance&"): - LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilResonance, Default::filterResonanceModRange); + LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilResonance, Default::filterResonanceMod); break; case hash("lfo&_fil&gain"): - LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilGain, Default::filterGainModRange); + LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilGain, Default::filterGainMod); break; case hash("lfo&_eq&gain"): - LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqGain, Default::eqGainModRange); + LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqGain, Default::eqGainMod); break; case hash("lfo&_eq&freq"): - LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqFrequency, Default::eqFrequencyModRange); + LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqFrequency, Default::eqFrequencyMod); break; case hash("lfo&_eq&bw"): - LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqBandwidth, Default::eqBandwidthModRange); + LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqBandwidth, Default::eqBandwidthMod); break; // Modulation: Flex EG (targets) @@ -1123,11 +979,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto egNumber = opcode.parameters.front(); if (egNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) { - const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::amplitudeMod); } break; case hash("eg&_pan"): @@ -1135,11 +990,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto egNumber = opcode.parameters.front(); if (egNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::panCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Pan, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Pan, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::panMod); } break; case hash("eg&_width"): @@ -1147,11 +1001,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto egNumber = opcode.parameters.front(); if (egNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::widthCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Width, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Width, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::widthMod); } break; case hash("eg&_position"): // sfizz extension @@ -1159,11 +1012,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto egNumber = opcode.parameters.front(); if (egNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::positionCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Position, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Position, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::positionMod); } break; case hash("eg&_pitch"): @@ -1171,11 +1023,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto egNumber = opcode.parameters.front(); if (egNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::tuneCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Pitch, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Pitch, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::pitchMod); } break; case hash("eg&_volume"): @@ -1183,30 +1034,29 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto egNumber = opcode.parameters.front(); if (egNumber == 0) return false; - if (auto value = readOpcode(opcode.value, Default::volumeCCRange)) { - const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); - const ModKey target = ModKey::createNXYZ(ModId::Volume, id); - getOrCreateConnection(source, target).sourceDepth = *value; - } + const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Volume, id); + getOrCreateConnection(source, target).sourceDepth = + opcode.read(Default::volumeMod); } break; case hash("eg&_cutoff&"): - LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilCutoff, Default::filterCutoffModRange); + LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilCutoff, Default::filterCutoffMod); break; case hash("eg&_resonance&"): - LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilResonance, Default::filterResonanceModRange); + LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilResonance, Default::filterResonanceMod); break; case hash("eg&_fil&gain"): - LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilGain, Default::filterGainModRange); + LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilGain, Default::filterGainMod); break; case hash("eg&_eq&gain"): - LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqGain, Default::eqGainModRange); + LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqGain, Default::eqGainMod); break; case hash("eg&_eq&freq"): - LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqFrequency, Default::eqFrequencyModRange); + LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqFrequency, Default::eqFrequencyMod); break; case hash("eg&_eq&bw"): - LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqBandwidth, Default::eqBandwidthModRange); + LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqBandwidth, Default::eqBandwidthMod); break; case hash("eg&_ampeg"): @@ -1216,15 +1066,14 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) return false; if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs)) return false; - if (auto ampeg = readBooleanFromOpcode(opcode)) { - FlexEGDescription& desc = flexEGs[egNumber - 1]; - if (desc.ampeg != *ampeg) { - desc.ampeg = *ampeg; - flexAmpEG = absl::nullopt; - for (size_t i = 0, n = flexEGs.size(); i < n && !flexAmpEG; ++i) { - if (flexEGs[i].ampeg) - flexAmpEG = static_cast(i); - } + auto ampeg = opcode.read(Default::flexEGAmpeg); + FlexEGDescription& desc = flexEGs[egNumber - 1]; + if (desc.ampeg != ampeg) { + desc.ampeg = ampeg; + flexAmpEG = absl::nullopt; + for (size_t i = 0, n = flexEGs.size(); i < n && !flexAmpEG; ++i) { + if (flexEGs[i].ampeg) + flexAmpEG = static_cast(i); } } break; @@ -1307,29 +1156,25 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) break; case hash("pitcheg_depth"): - if (auto value = readOpcode(opcode.value, Default::pitchEgDepthRange)) - getOrCreateConnection( - ModKey::createNXYZ(ModId::PitchEG, id), - ModKey::createNXYZ(ModId::Pitch, id)).sourceDepth = *value; + getOrCreateConnection( + ModKey::createNXYZ(ModId::PitchEG, id), + ModKey::createNXYZ(ModId::Pitch, id)).sourceDepth = opcode.read(Default::egDepth); break; case hash("fileg_depth"): - if (auto value = readOpcode(opcode.value, Default::filterEgDepthRange)) - getOrCreateConnection( - ModKey::createNXYZ(ModId::FilEG, id), - ModKey::createNXYZ(ModId::FilCutoff, id)).sourceDepth = *value; + getOrCreateConnection( + ModKey::createNXYZ(ModId::FilEG, id), + ModKey::createNXYZ(ModId::FilCutoff, id)).sourceDepth = opcode.read(Default::egDepth); break; case hash("pitcheg_veltodepth"): // also pitcheg_vel2depth - if (auto value = readOpcode(opcode.value, Default::pitchEgDepthRange)) - getOrCreateConnection( - ModKey::createNXYZ(ModId::PitchEG, id), - ModKey::createNXYZ(ModId::Pitch, id)).velToDepth = *value; + getOrCreateConnection( + ModKey::createNXYZ(ModId::PitchEG, id), + ModKey::createNXYZ(ModId::Pitch, id)).velToDepth = opcode.read(Default::egVel2Depth); break; case hash("fileg_veltodepth"): // also fileg_vel2depth - if (auto value = readOpcode(opcode.value, Default::filterEgDepthRange)) - getOrCreateConnection( - ModKey::createNXYZ(ModId::FilEG, id), - ModKey::createNXYZ(ModId::FilCutoff, id)).velToDepth = *value; + getOrCreateConnection( + ModKey::createNXYZ(ModId::FilEG, id), + ModKey::createNXYZ(ModId::FilCutoff, id)).velToDepth = opcode.read(Default::egVel2Depth); break; // Flex envelopes @@ -1341,7 +1186,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs)) return false; auto& eg = flexEGs[egNumber - 1]; - setValueFromOpcode(opcode, eg.dynamic, Default::flexEGDynamicRange); + eg.dynamic = opcode.read(Default::flexEGDynamic); } break; case hash("eg&_sustain"): @@ -1352,7 +1197,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs)) return false; auto& eg = flexEGs[egNumber - 1]; - setValueFromOpcode(opcode, eg.sustain, Default::flexEGSustainRange); + eg.sustain = opcode.read(Default::flexEGSustain); } break; case hash("eg&_time&"): @@ -1366,7 +1211,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto pointNumber = opcode.parameters[1]; if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints)) return false; - setValueFromOpcode(opcode, eg.points[pointNumber].time, Default::flexEGPointTimeRange); + eg.points[pointNumber].time = opcode.read(Default::flexEGPointTime); } break; case hash("eg&_level&"): @@ -1380,7 +1225,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto pointNumber = opcode.parameters[1]; if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints)) return false; - setValueFromOpcode(opcode, eg.points[pointNumber].level, Default::flexEGPointLevelRange); + eg.points[pointNumber].level = opcode.read(Default::flexEGPointLevel); } break; case hash("eg&_shape&"): @@ -1394,8 +1239,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto pointNumber = opcode.parameters[1]; if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints)) return false; - if (auto value = readOpcode(opcode.value, Default::flexEGPointShapeRange)) - eg.points[pointNumber].setShape(*value); + eg.points[pointNumber].setShape(opcode.read(Default::flexEGPointShape)); } break; @@ -1404,16 +1248,13 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) const auto effectNumber = opcode.parameters.back(); if (!effectNumber || effectNumber < 1 || effectNumber > config::maxEffectBuses) break; - auto value = readOpcode(opcode.value, { 0, 100 }); - if (!value) - break; if (static_cast(effectNumber + 1) > gainToEffect.size()) gainToEffect.resize(effectNumber + 1); - gainToEffect[effectNumber] = *value / 100; + gainToEffect[effectNumber] = opcode.read(Default::effect); break; } case hash("sw_default"): - setValueFromOpcode(opcode, defaultSwitch, Default::keyRange); + defaultSwitch = opcode.read(Default::key); break; // Ignored opcodes @@ -1441,98 +1282,91 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg) switch (opcode.lettersOnlyHash) { case_any_eg("attack"): - setValueFromOpcode(opcode, eg.attack, Default::egTimeRange); + eg.attack = opcode.read(Default::egTime); break; case_any_eg("decay"): - setValueFromOpcode(opcode, eg.decay, Default::egTimeRange); + eg.decay = opcode.read(Default::egTime); break; case_any_eg("delay"): - setValueFromOpcode(opcode, eg.delay, Default::egTimeRange); + eg.delay = opcode.read(Default::egTime); break; case_any_eg("hold"): - setValueFromOpcode(opcode, eg.hold, Default::egTimeRange); + eg.hold = opcode.read(Default::egTime); break; case_any_eg("release"): - setValueFromOpcode(opcode, eg.release, Default::egTimeRange); + eg.release = opcode.read(Default::egRelease); break; case_any_eg("start"): - setValueFromOpcode(opcode, eg.start, Default::egPercentRange); + eg.start = opcode.read(Default::egPercent); break; case_any_eg("sustain"): - setValueFromOpcode(opcode, eg.sustain, Default::egPercentRange); + eg.sustain = opcode.read(Default::egPercent); break; case_any_eg("veltoattack"): // also vel2attack - setValueFromOpcode(opcode, eg.vel2attack, Default::egOnCCTimeRange); + eg.vel2attack = opcode.read(Default::egTimeMod); break; case_any_eg("veltodecay"): // also vel2decay - setValueFromOpcode(opcode, eg.vel2decay, Default::egOnCCTimeRange); + eg.vel2decay = opcode.read(Default::egTimeMod); break; case_any_eg("veltodelay"): // also vel2delay - setValueFromOpcode(opcode, eg.vel2delay, Default::egOnCCTimeRange); + eg.vel2delay = opcode.read(Default::egTimeMod); break; case_any_eg("veltohold"): // also vel2hold - setValueFromOpcode(opcode, eg.vel2hold, Default::egOnCCTimeRange); + eg.vel2hold = opcode.read(Default::egTimeMod); break; case_any_eg("veltorelease"): // also vel2release - setValueFromOpcode(opcode, eg.vel2release, Default::egOnCCTimeRange); + eg.vel2release = opcode.read(Default::egTimeMod); break; case_any_eg("veltosustain"): // also vel2sustain - setValueFromOpcode(opcode, eg.vel2sustain, Default::egOnCCPercentRange); + eg.vel2sustain = opcode.read(Default::egPercentMod); break; case_any_eg("attack_oncc&"): // also attackcc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange)) - eg.ccAttack[opcode.parameters.back()] = *value; + eg.ccAttack[opcode.parameters.back()] = opcode.read(Default::egTimeMod); break; case_any_eg("decay_oncc&"): // also decaycc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange)) - eg.ccDecay[opcode.parameters.back()] = *value; + eg.ccDecay[opcode.parameters.back()] = opcode.read(Default::egTimeMod); break; case_any_eg("delay_oncc&"): // also delaycc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange)) - eg.ccDelay[opcode.parameters.back()] = *value; + eg.ccDelay[opcode.parameters.back()] = opcode.read(Default::egTimeMod); break; case_any_eg("hold_oncc&"): // also holdcc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange)) - eg.ccHold[opcode.parameters.back()] = *value; + eg.ccHold[opcode.parameters.back()] = opcode.read(Default::egTimeMod); break; case_any_eg("release_oncc&"): // also releasecc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange)) - eg.ccRelease[opcode.parameters.back()] = *value; + eg.ccRelease[opcode.parameters.back()] = opcode.read(Default::egTimeMod); break; case_any_eg("start_oncc&"): // also startcc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::egOnCCPercentRange)) - eg.ccStart[opcode.parameters.back()] = *value; + eg.ccStart[opcode.parameters.back()] = opcode.read(Default::egPercentMod); break; case_any_eg("sustain_oncc&"): // also sustaincc& if (opcode.parameters.back() >= config::numCCs) return false; - if (auto value = readOpcode(opcode.value, Default::egOnCCPercentRange)) - eg.ccSustain[opcode.parameters.back()] = *value; + eg.ccSustain[opcode.parameters.back()] = opcode.read(Default::egPercentMod); break; default: @@ -1557,7 +1391,7 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, absl::optional range, const ModKey& target) +bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec spec, const ModKey& target) { if (!opcode.isAnyCcN()) return false; @@ -1591,19 +1425,21 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, Range range, con ModKey::Parameters p = conn->source.parameters(); switch (opcode.category) { case kOpcodeOnCcN: - setValueFromOpcode(opcode, conn->sourceDepth, range); + conn->sourceDepth = opcode.read(spec); break; case kOpcodeCurveCcN: - setValueFromOpcode(opcode, p.curve, Default::curveCCRange); + p.curve = opcode.read(Default::curveCC); break; case kOpcodeStepCcN: { - const Range stepCCRange { 0.0f, std::max(std::abs(range.getStart()), std::abs(range.getEnd())) }; - setValueFromOpcode(opcode, p.step, stepCCRange); + const float maxStep = + max(std::abs(spec.bounds.getStart()), std::abs(spec.bounds.getEnd())); + const OpcodeSpec stepCC { 0.0f, Range(0.0f, maxStep), 0 }; + p.step = opcode.read(stepCC); } break; case kOpcodeSmoothCcN: - setValueFromOpcode(opcode, p.smooth, Default::smoothCCRange); + p.smooth = opcode.read(Default::smoothCC); break; default: assert(false); @@ -1639,9 +1475,9 @@ bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue const bool velOk = velocityRange.containsWithEnd(velocity); const bool randOk = randRange.contains(randValue) || (randValue == 1.0f && randRange.getEnd() == 1.0f); - const bool firstLegatoNote = (trigger == SfzTrigger::first && midiState.getActiveNotes() == 1); - const bool attackTrigger = (trigger == SfzTrigger::attack); - const bool notFirstLegatoNote = (trigger == SfzTrigger::legato && midiState.getActiveNotes() > 1); + const bool firstLegatoNote = (trigger == Trigger::first && midiState.getActiveNotes() == 1); + const bool attackTrigger = (trigger == Trigger::attack); + const bool notFirstLegatoNote = (trigger == Trigger::legato && midiState.getActiveNotes() > 1); return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote); } @@ -1667,10 +1503,10 @@ bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValu // Release logic - if (trigger == SfzTrigger::release_key) + if (trigger == Trigger::release_key) return true; - if (trigger == SfzTrigger::release) { + if (trigger == Trigger::release) { if (midiState.getCCValue(sustainCC) < sustainThreshold) return true; @@ -1683,9 +1519,11 @@ bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValu return false; } + #include bool sfz::Region::registerCC(int ccNumber, float ccValue) noexcept { ASSERT(ccValue >= 0.0f && ccValue <= 1.0f); + if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue)) ccSwitched.set(ccNumber, true); else @@ -1734,7 +1572,7 @@ float sfz::Region::getBasePitchVariation(float noteNumber, float velocity) const fast_real_distribution pitchDistribution { -pitchRandom, pitchRandom }; auto pitchVariationInCents = pitchKeytrack * (noteNumber - pitchKeycenter); // note difference with pitch center - pitchVariationInCents += tune; // sample tuning + pitchVariationInCents += pitch; // sample tuning pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose pitchVariationInCents += velocity * pitchVeltrack; // track velocity pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes @@ -1748,7 +1586,7 @@ float sfz::Region::getBaseVolumedB(int noteNumber) const noexcept baseVolumedB += globalVolume; baseVolumedB += masterVolume; baseVolumedB += groupVolume; - if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key) + if (trigger == Trigger::release || trigger == Trigger::release_key) baseVolumedB -= rtDecay * midiState.getNoteDuration(noteNumber); return baseVolumedB; } @@ -1782,7 +1620,7 @@ uint64_t sfz::Region::getOffset(Oversampling factor) const noexcept uint64_t finalOffset = offset + offsetDistribution(Random::randomGenerator); for (const auto& mod: offsetCC) finalOffset += static_cast(mod.data * midiState.getCCValue(mod.cc)); - return Default::offsetRange.clamp(finalOffset) * static_cast(factor); + return Default::offset.bounds.clamp(finalOffset) * static_cast(factor); } float sfz::Region::getDelay() const noexcept @@ -1871,37 +1709,37 @@ float sfz::Region::velocityCurve(float velocity) const noexcept void sfz::Region::offsetAllKeys(int offset) noexcept { // Offset key range - if (keyRange != Default::keyRange) { + if (keyRange != Default::key.bounds) { const auto start = keyRange.getStart(); const auto end = keyRange.getEnd(); - keyRange.setStart(offsetAndClampKey(start, offset, Default::keyRange)); - keyRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange)); + keyRange.setStart(offsetAndClampKey(start, offset)); + keyRange.setEnd(offsetAndClampKey(end, offset)); } - pitchKeycenter = offsetAndClampKey(pitchKeycenter, offset, Default::keyRange); + pitchKeycenter = offsetAndClampKey(pitchKeycenter, offset); // Offset key switches if (upKeyswitch) - upKeyswitch = offsetAndClampKey(*upKeyswitch, offset, Default::keyRange); + upKeyswitch = offsetAndClampKey(*upKeyswitch, offset); if (lastKeyswitch) - lastKeyswitch = offsetAndClampKey(*lastKeyswitch, offset, Default::keyRange); + lastKeyswitch = offsetAndClampKey(*lastKeyswitch, offset); if (downKeyswitch) - downKeyswitch = offsetAndClampKey(*downKeyswitch, offset, Default::keyRange); + downKeyswitch = offsetAndClampKey(*downKeyswitch, offset); if (previousKeyswitch) - previousKeyswitch = offsetAndClampKey(*previousKeyswitch, offset, Default::keyRange); + previousKeyswitch = offsetAndClampKey(*previousKeyswitch, offset); // Offset crossfade ranges if (crossfadeKeyInRange != Default::crossfadeKeyInRange) { const auto start = crossfadeKeyInRange.getStart(); const auto end = crossfadeKeyInRange.getEnd(); - crossfadeKeyInRange.setStart(offsetAndClampKey(start, offset, Default::keyRange)); - crossfadeKeyInRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange)); + crossfadeKeyInRange.setStart(offsetAndClampKey(start, offset)); + crossfadeKeyInRange.setEnd(offsetAndClampKey(end, offset)); } if (crossfadeKeyOutRange != Default::crossfadeKeyOutRange) { const auto start = crossfadeKeyOutRange.getStart(); const auto end = crossfadeKeyOutRange.getEnd(); - crossfadeKeyOutRange.setStart(offsetAndClampKey(start, offset, Default::keyRange)); - crossfadeKeyOutRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange)); + crossfadeKeyOutRange.setStart(offsetAndClampKey(start, offset)); + crossfadeKeyOutRange.setEnd(offsetAndClampKey(end, offset)); } } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 92ecf255..d8d6368e 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -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 range, const ModKey& target); + bool processGenericCc(const Opcode& opcode, OpcodeSpec 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 offsetCC { Default::offset }; - uint32_t sampleEnd { Default::sampleEndRange.getEnd() }; // end - absl::optional sampleCount {}; // count - absl::optional loopMode {}; // loopmode - Range loopRange { Default::loopRange }; //loopstart and loopend + CCMap offsetCC { Default::offsetMod }; + uint32_t sampleEnd { Default::sampleEnd }; // end + uint32_t sampleCount { Default::sampleCount }; // count + absl::optional loopMode {}; // loopmode + Range 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 oscillatorQuality; // Instrument settings: voice lifecycle uint32_t group { Default::group }; // group absl::optional offBy {}; // off_by - SfzOffMode offMode { Default::offMode }; // off_mode + OffMode offMode { Default::offMode }; // off_mode float offTime { Default::offTime }; // off_mode absl::optional 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 keyRange { Default::keyRange }; //lokey, hikey and key - Range velocityRange { Default::velocityRange }; // hivel and lovel + Range keyRange { Default::loKey, Default::hiKey }; //lokey, hikey and key + Range velocityRange { Default::loVel, Default::hiVel }; // hivel and lovel // Region logic: MIDI conditions - Range bendRange { Default::bendValueRange }; // hibend and lobend - CCMap> ccConditions { Default::ccValueRange }; + Range bendRange { Default::loBend, Default::hiBend }; // hibend and lobend + CCMap> ccConditions {{ Default::loCC, Default::hiCC }}; absl::optional lastKeyswitch {}; // sw_last absl::optional> lastKeyswitchRange {}; // sw_last absl::optional keyswitchLabel {}; @@ -375,32 +364,32 @@ struct Region { absl::optional downKeyswitch {}; // sw_down absl::optional previousKeyswitch {}; // sw_previous absl::optional 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 aftertouchRange { Default::aftertouchRange }; // hichanaft and lochanaft - Range bpmRange { Default::bpmRange }; // hibpm and lobpm - Range randRange { Default::randRange }; // hirand and lorand - uint8_t sequenceLength { Default::sequenceLength }; // seq_length - uint8_t sequencePosition { Default::sequencePosition }; // seq_position + Range aftertouchRange { Default::loChannelAftertouch, Default::hiChannelAftertouch }; // hichanaft and lochanaft + Range bpmRange { Default::loBPM, Default::hiBPM }; // hibpm and lobpm + Range 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> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN + Trigger trigger { Default::trigger }; // trigger + CCMap> 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> velocityPoints; // amp_velcurve_N absl::optional velCurve {}; float ampRandom { Default::ampRandom }; // amp_random @@ -408,9 +397,9 @@ struct Region { Range crossfadeKeyOutRange { Default::crossfadeKeyOutRange }; Range crossfadeVelInRange { Default::crossfadeVelInRange }; Range 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> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN float rtDecay { Default::rtDecay }; // rt_decay @@ -427,17 +416,17 @@ struct Region { std::vector 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 ccSwitched; - absl::string_view defaultPath { "" }; + std::string defaultPath { "" }; int sequenceCounter { 0 }; LEAK_DETECTOR(Region); diff --git a/src/sfizz/SfzFilter.cpp b/src/sfizz/SfzFilter.cpp index 039dddb2..a1d22239 100644 --- a/src/sfizz/SfzFilter.cpp +++ b/src/sfizz/SfzFilter.cpp @@ -204,39 +204,6 @@ void Filter::setType(FilterType type) } } -absl::optional Filter::typeFromName(absl::string_view name) -{ - absl::optional 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 FilterEq::typeFromName(absl::string_view name) -{ - absl::optional 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)) { diff --git a/src/sfizz/SfzFilter.h b/src/sfizz/SfzFilter.h index 62cba612..b64ffe8e 100644 --- a/src/sfizz/SfzFilter.h +++ b/src/sfizz/SfzFilter.h @@ -84,11 +84,6 @@ public: */ void setType(FilterType type); - /** - Get the filter type associated with the given name. - */ - static absl::optional typeFromName(absl::string_view name); - private: struct Impl; std::unique_ptr P; @@ -194,11 +189,6 @@ public: */ void setType(EqType type); - /** - Get the filter type associated with the given name. - */ - static absl::optional typeFromName(absl::string_view name); - private: struct Impl; std::unique_ptr P; diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index b26f6560..8fd15b33 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -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(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 range) +inline CXX14_CONSTEXPR uint8_t offsetAndClampKey(uint8_t key, int offset) { const int offsetKey { key + offset }; if (offsetKey > std::numeric_limits::max()) - return range.getEnd(); + return 127; if (offsetKey < std::numeric_limits::min()) - return range.getStart(); + return 0; - return range.clamp(static_cast(offsetKey)); + return clamp(static_cast(offsetKey), 0, 127); } namespace literals { diff --git a/src/sfizz/Smoothers.cpp b/src/sfizz/Smoothers.cpp index fda61be0..c2f6e2fb 100644 --- a/src/sfizz/Smoothers.cpp +++ b/src/sfizz/Smoothers.cpp @@ -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))); } } diff --git a/src/sfizz/Smoothers.h b/src/sfizz/Smoothers.h index 598dd203..50f99821 100644 --- a/src/sfizz/Smoothers.h +++ b/src/sfizz/Smoothers.h @@ -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 namespace sfz { diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index b82cb372..58c04f4d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -276,11 +276,10 @@ void Synth::Impl::handleMasterOpcodes(const std::vector& 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& 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& 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& 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(member.parameters.back()); insertPairUniquely(keyLabels_, noteNumber, std::string(member.value)); } @@ -380,10 +374,10 @@ void Synth::Impl::handleControlOpcodes(const std::vector& 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& rawMembers) // note(jpc): gain opcodes are linear volumes in % units case hash("directtomain"): - if (auto valueOpt = readOpcode(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(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(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 diff --git a/src/sfizz/SynthConfig.h b/src/sfizz/SynthConfig.h index 3f710f89..8fdbd510 100644 --- a/src/sfizz/SynthConfig.h +++ b/src/sfizz/SynthConfig.h @@ -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 { diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 9902b58b..123fac10 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -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]; diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 0e251024..b71e9810 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -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 genController_; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 64ddebe9..8a13834f 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -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 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); diff --git a/src/sfizz/VoiceManager.cpp b/src/sfizz/VoiceManager.cpp index 490927b2..ff978f18 100644 --- a/src/sfizz/VoiceManager.cpp +++ b/src/sfizz/VoiceManager.cpp @@ -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; diff --git a/src/sfizz/effects/Apan.cpp b/src/sfizz/effects/Apan.cpp index a51f4a4e..d100361f 100644 --- a/src/sfizz/effects/Apan.cpp +++ b/src/sfizz/effects/Apan.cpp @@ -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; } } diff --git a/src/sfizz/effects/Apan.h b/src/sfizz/effects/Apan.h index 52b85dc6..9e30c144 100644 --- a/src/sfizz/effects/Apan.h +++ b/src/sfizz/effects/Apan.h @@ -47,20 +47,20 @@ namespace fx { template void computeLfos(float* left, float* right, unsigned nframes); private: - float _samplePeriod = 0.0; + float _samplePeriod { 0.0f }; sfz::Buffer _lfoOutLeft { config::defaultSamplesPerBlock }; sfz::Buffer _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 diff --git a/src/sfizz/effects/Compressor.cpp b/src/sfizz/effects/Compressor.cpp index 3aaf04a3..8fffd680 100644 --- a/src/sfizz/effects/Compressor.cpp +++ b/src/sfizz/effects/Compressor.cpp @@ -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 _tempBuffer2x { 2, _oversampling * config::defaultSamplesPerBlock }; AudioBuffer _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(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(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(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(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(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; } } diff --git a/src/sfizz/effects/Disto.cpp b/src/sfizz/effects/Disto.cpp index 9e83856a..946fdae6 100644 --- a/src/sfizz/effects/Disto.cpp +++ b/src/sfizz/effects/Disto.cpp @@ -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 Disto::makeInstance(absl::Span 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(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(opc.value, {0.0f, 100.0f})) - impl._wet = *value * 0.01f; + impl._wet = opc.read(Default::effect); break; } } diff --git a/src/sfizz/effects/Eq.cpp b/src/sfizz/effects/Eq.cpp index 1eda27e4..25036195 100644 --- a/src/sfizz/effects/Eq.cpp +++ b/src/sfizz/effects/Eq.cpp @@ -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 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; } } diff --git a/src/sfizz/effects/Filter.cpp b/src/sfizz/effects/Filter.cpp index d39d74f1..8b2dff72 100644 --- a/src/sfizz/effects/Filter.cpp +++ b/src/sfizz/effects/Filter.cpp @@ -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 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; } } diff --git a/src/sfizz/effects/Fverb.cpp b/src/sfizz/effects/Fverb.cpp index 71e73efa..c8ab7938 100644 --- a/src/sfizz/effects/Fverb.cpp +++ b/src/sfizz/effects/Fverb.cpp @@ -180,13 +180,13 @@ namespace fx { std::unique_ptr 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; } } diff --git a/src/sfizz/effects/Gain.cpp b/src/sfizz/effects/Gain.cpp index 04cca832..aa8d6d03 100644 --- a/src/sfizz/effects/Gain.cpp +++ b/src/sfizz/effects/Gain.cpp @@ -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; } } diff --git a/src/sfizz/effects/Gate.cpp b/src/sfizz/effects/Gate.cpp index b7a819d5..7bbc158a 100644 --- a/src/sfizz/effects/Gate.cpp +++ b/src/sfizz/effects/Gate.cpp @@ -34,7 +34,7 @@ namespace fx { struct Gate::Impl { faustGate _gate[2]; - bool _stlink = false; + bool _stlink { Default::gateSTLink }; float _inputGain = 1.0; AudioBuffer _tempBuffer2x { 2, _oversampling * config::defaultSamplesPerBlock }; AudioBuffer _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(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(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(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(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); } } diff --git a/src/sfizz/effects/Lofi.cpp b/src/sfizz/effects/Lofi.cpp index 12d7759f..24aaf772 100644 --- a/src/sfizz/effects/Lofi.cpp +++ b/src/sfizz/effects/Lofi.cpp @@ -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; } } diff --git a/src/sfizz/effects/Rectify.cpp b/src/sfizz/effects/Rectify.cpp index 56be4682..5d25be46 100644 --- a/src/sfizz/effects/Rectify.cpp +++ b/src/sfizz/effects/Rectify.cpp @@ -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; } } diff --git a/src/sfizz/effects/Strings.cpp b/src/sfizz/effects/Strings.cpp index 9e7295d4..2ef50f7c 100644 --- a/src/sfizz/effects/Strings.cpp +++ b/src/sfizz/effects/Strings.cpp @@ -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; } } diff --git a/src/sfizz/effects/Strings.h b/src/sfizz/effects/Strings.h index 2f46254b..29121174 100644 --- a/src/sfizz/effects/Strings.h +++ b/src/sfizz/effects/Strings.h @@ -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 _stringsArray; diff --git a/src/sfizz/effects/Width.cpp b/src/sfizz/effects/Width.cpp index a9c3c202..98906e52 100644 --- a/src/sfizz/effects/Width.cpp +++ b/src/sfizz/effects/Width.cpp @@ -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; } } diff --git a/src/sfizz/modulations/sources/FlexEnvelope.cpp b/src/sfizz/modulations/sources/FlexEnvelope.cpp index 5cea2ad8..c9112376 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.cpp +++ b/src/sfizz/modulations/sources/FlexEnvelope.cpp @@ -38,7 +38,7 @@ void FlexEnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, FlexEnvelope* eg = voice->getFlexEG(egIndex); eg->configure(®ion->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); diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 28c41dd9..07d12c03 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -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(36, 36)); } - REQUIRE(synth.getRegionView(0)->velocityRange == Range(1_norm, 26_norm)); - REQUIRE(synth.getRegionView(1)->velocityRange == Range(27_norm, 52_norm)); - REQUIRE(synth.getRegionView(2)->velocityRange == Range(53_norm, 77_norm)); - REQUIRE(synth.getRegionView(3)->velocityRange == Range(78_norm, 102_norm)); - REQUIRE(synth.getRegionView(4)->velocityRange == Range(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(12, 22)); - REQUIRE(synth.getRegionView(i)->velocityRange == Range(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(0_norm, 13_norm)); + almostEqualRanges(synth.getRegionView(i)->ccConditions.getWithDefault(107), { 0_norm, 13_norm }); } - REQUIRE(synth.getRegionView(0)->randRange == Range(0, 0.25)); - REQUIRE(synth.getRegionView(1)->randRange == Range(0.25, 0.5)); - REQUIRE(synth.getRegionView(2)->randRange == Range(0.5, 0.75)); - REQUIRE(synth.getRegionView(3)->randRange == Range(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"( + note_offset=1 + key=63 sample=*sine + lokey=50 hikey=55 pitch_keycenter=50 sample=*sine + lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=44 xfout_hikey=48 sample=*sine + note_offset=-1 + key=63 sw_lokey=24 sw_hikey=28 sw_last=25 sw_up=25 sw_down=25 sw_previous=62 sample=*sine + note_offset=1 octave_offset=1 + key=63 sample=*sine + note_offset=-1 octave_offset=-1 + key=63 sample=*sine + // Check that this does not reset either note or octave offset + key=63 sample=*sine + )"); REQUIRE( synth.getNumRegions() == 7 ); REQUIRE(synth.getRegionView(0)->keyRange == Range(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 { 77554, 186581 }); REQUIRE(synth.getRegionView(1)->loopRange == Range { 77554, 186581 }); @@ -533,7 +547,7 @@ TEST_CASE("[Files] Looped regions can start at 0") 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 { 0, synth.getRegionView(0)->sampleEnd } ); } @@ -550,13 +564,13 @@ TEST_CASE("[Synth] Release triggers automatically sets the loop mode") 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") diff --git a/tests/OpcodeT.cpp b/tests/OpcodeT.cpp index 24d6a98c..20ce6a24 100644 --- a/tests/OpcodeT.cpp +++ b/tests/OpcodeT.cpp @@ -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"); // @@ -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); } // @@ -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(0, 100)).value() == 16 ); - REQUIRE( sfz::readOpcode("+16", sfz::Range(0, 100)).value() == 16 ); - REQUIRE( sfz::readOpcode("110", sfz::Range(0, 100)).value() == 100 ); - REQUIRE( sfz::readOpcode("-1", sfz::Range(0, 100)).value() == 0 ); - REQUIRE( sfz::readOpcode("12.5", sfz::Range(-100, 100)).value() == 12 ); - REQUIRE( sfz::readOpcode("+12.5", sfz::Range(-100, 100)).value() == 12 ); - REQUIRE( sfz::readOpcode("-40", sfz::Range(-100, 100)).value() == -40 ); - REQUIRE( sfz::readOpcode("-140", sfz::Range(-100, 100)).value() == -100 ); - REQUIRE( sfz::readOpcode("12.5", sfz::Range(0.0f, 100.0f)).value() == 12.5_a ); - REQUIRE( sfz::readOpcode("+12.5", sfz::Range(0.0f, 100.0f)).value() == 12.5_a ); - REQUIRE( sfz::readOpcode("-22.5", sfz::Range(-20.0f, 100.0f)).value() == -20.0_a ); - REQUIRE( sfz::readOpcode("150.5", sfz::Range(-20.0f, 100.0f)).value() == 100.0_a ); - REQUIRE( sfz::readOpcode("50.25garbage", sfz::Range(-20.0f, 100.0f)).value() == 50.25_a ); - REQUIRE( sfz::readOpcode("50.25garbage", sfz::Range(-20, 100)).value() == 50 ); - REQUIRE( !sfz::readOpcode("garbage50.25", sfz::Range(-20, 100)) ); - REQUIRE( !sfz::readOpcode("garbage", sfz::Range(-20, 100)) ); + SECTION("Basic") + { + Opcode opcode { "", "16" }; + OpcodeSpec spec { 0, Range(0, 100), 0 }; + REQUIRE( opcode.read(spec) == 16); + } + + SECTION("Sign") + { + Opcode opcode { "", "+16" }; + OpcodeSpec spec { 0, Range(0, 100), 0 }; + REQUIRE( opcode.read(spec) == 16); + } + + SECTION("Ignore") + { + Opcode opcode { "", "110" }; + OpcodeSpec spec { 0, Range(0, 100), 0 }; + REQUIRE( opcode.read(spec) == spec.defaultInputValue ); + } + + SECTION("Clamp upper") + { + Opcode opcode { "", "110" }; + OpcodeSpec spec { 0, Range(0, 100), kEnforceUpperBound }; + REQUIRE( opcode.read(spec) == 100 ); + } + + SECTION("Clamp lower") + { + Opcode opcode { "", "10" }; + OpcodeSpec spec { 0, Range(20, 100), kEnforceLowerBound }; + REQUIRE( opcode.read(spec) == 20 ); + } + + SECTION("Floating point") + { + Opcode opcode { "", "10.5" }; + OpcodeSpec spec { 0, Range(0, 100), 0 }; + REQUIRE( opcode.read(spec) == 10 ); + } + + SECTION("Text after") + { + Opcode opcode { "", "10garbage" }; + OpcodeSpec spec { 0, Range(0, 100), 0 }; + REQUIRE( opcode.read(spec) == 10 ); + } + + SECTION("Text before") + { + Opcode opcode { "", "garbage10" }; + OpcodeSpec spec { 0, Range(0, 100), 0 }; + REQUIRE( !opcode.readOptional(spec) ); + REQUIRE( opcode.read(spec) == 0 ); + } + + SECTION("Can be note") + { + Opcode opcode { "", "c4" }; + OpcodeSpec spec { 0, Range(0, 100), kCanBeNote }; + REQUIRE( opcode.read(spec) == 60 ); + } +} + +TEST_CASE("[Opcode] opcode read (int)") +{ + SECTION("Basic") + { + Opcode opcode { "", "16" }; + OpcodeSpec spec { 0, Range(-100, 100), 0 }; + REQUIRE( opcode.read(spec) == 16); + } + + SECTION("Sign") + { + Opcode opcode { "", "+16" }; + OpcodeSpec spec { 0, Range(-100, 100), 0 }; + REQUIRE( opcode.read(spec) == 16); + } + + SECTION("Sign") + { + Opcode opcode { "", "-16" }; + OpcodeSpec spec { 0, Range(-100, 100), 0 }; + REQUIRE( opcode.read(spec) == -16); + } + + SECTION("Clamp upper") + { + Opcode opcode { "", "110" }; + OpcodeSpec spec { 0, Range(-100, 100), kEnforceUpperBound }; + REQUIRE( opcode.read(spec) == 100 ); + } + + SECTION("Clamp lower") + { + Opcode opcode { "", "-110" }; + OpcodeSpec spec { 0, Range(-100, 100), kEnforceLowerBound }; + REQUIRE( opcode.read(spec) == -100 ); + } + + SECTION("Floating point") + { + Opcode opcode { "", "10.5" }; + OpcodeSpec spec { 0, Range(-100, 100), 0 }; + REQUIRE( opcode.read(spec) == 10 ); + } + + SECTION("Text after") + { + Opcode opcode { "", "10garbage" }; + OpcodeSpec spec { 0, Range(0, 100), 0 }; + REQUIRE( opcode.read(spec) == 10 ); + } + + SECTION("Text before") + { + Opcode opcode { "", "garbage10" }; + OpcodeSpec spec { 0, Range(20, 100), 0 }; + REQUIRE( !opcode.readOptional(spec) ); + REQUIRE( opcode.read(spec) == 0 ); + } + + SECTION("Can be note") + { + Opcode opcode { "", "c4" }; + OpcodeSpec spec { 0, Range(20, 100), kCanBeNote }; + REQUIRE( opcode.read(spec) == 60 ); + } +} + + +TEST_CASE("[Opcode] opcode read (float)") +{ + SECTION("Basic") + { + Opcode opcode { "", "16.4" }; + OpcodeSpec spec { 0.0f, Range(-100.0f, 100.0f), 0 }; + REQUIRE( opcode.read(spec) == 16.4_a); + } + + SECTION("Plus sign") + { + Opcode opcode { "", "+16.4" }; + OpcodeSpec spec { 0.0f, Range(-100.0f, 100.0f), 0 }; + REQUIRE( opcode.read(spec) == 16.4_a); + } + + SECTION("Minus sign") + { + Opcode opcode { "", "-16.4" }; + OpcodeSpec spec { 0.0f, Range(-100.0f, 100.0f), 0 }; + REQUIRE( opcode.read(spec) == -16.4_a); + } + + SECTION("Ignore") + { + Opcode opcode { "", "110" }; + OpcodeSpec spec { 0.0f, Range(-100.0f, 100.0f), 0 }; + REQUIRE( opcode.read(spec) == spec.defaultInputValue ); + } + + SECTION("Clamp upper") + { + Opcode opcode { "", "110" }; + OpcodeSpec spec { 0.0f, Range(-100.0f, 100.0f), kEnforceUpperBound }; + REQUIRE( opcode.read(spec) == 100.0f ); + } + + SECTION("Clamp lower") + { + Opcode opcode { "", "-110" }; + OpcodeSpec spec { 0.0f, Range(-100.0f, 100.0f), kEnforceLowerBound }; + REQUIRE( opcode.read(spec) == -100.0f ); + } + + SECTION("Text after") + { + Opcode opcode { "", "10.5garbage" }; + OpcodeSpec spec { 0.0f, Range(0.0f, 100.0f), kEnforceLowerBound }; + REQUIRE( opcode.read(spec) == 10.5f ); + } + + SECTION("Text before") + { + Opcode opcode { "", "garbage10" }; + OpcodeSpec spec { 0.0f, Range(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); } diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index 22723ff9..89a4254a 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -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); diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index f0274fd1..197fe80a 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -248,9 +248,9 @@ TEST_CASE("[Values] Count") synth.dispatchMessage(client, 0, "/region1/count", "", nullptr); synth.dispatchMessage(client, 0, "/region2/count", "", nullptr); std::vector 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"( + sample=kick.wav sample=kick.wav loop_start=10 loop_end=100 sample=kick.wav loopstart=10 loopend=100 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 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 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 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 messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav hikey=-1 + sample=kick.wav key=-1 + 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 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 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 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 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 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 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 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 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 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 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 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") sample=kick.wav pitch=4.2 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 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"( sample=kick.wav - sample=kick.wav tune_oncc42=4.2 + sample=kick.wav pitch_oncc42=4.2 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 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"( sample=kick.wav - sample=kick.wav tune_stepcc42=4.2 - sample=kick.wav tune_smoothcc42=4 - sample=kick.wav tune_curvecc42=2 - sample=kick.wav tune_stepcc42=-1 - sample=kick.wav tune_smoothcc42=-4 - sample=kick.wav tune_curvecc42=300 + sample=kick.wav pitch_stepcc42=4.2 + sample=kick.wav pitch_smoothcc42=4 + sample=kick.wav pitch_curvecc42=2 + sample=kick.wav pitch_stepcc42=-1 + sample=kick.wav pitch_smoothcc42=-4 + 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 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") sample=kick.wav pitch_smoothcc42=-4 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 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 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 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 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 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 messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav oscillator_quality=2 + 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 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 messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav oscillator_mode=2 + sample=kick.wav oscillator_mode=1 oscillator_mode=-2 + sample=kick.wav oscillator_multi=9 + 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 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 messageList; + Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + sample=kick.wav + sample=kick.wav oscillator_detune=9.2 + sample=kick.wav oscillator_detune=-1200.2 + sample=kick.wav oscillator_mod_depth=1564.75 + 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 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 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 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"( - sample=kick.wav cutoff=20000000 // Bound this to 20k + sample=kick.wav cutoff=20000000 // Clamp the value 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 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"( 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 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"( - sample=kick.wav keycenter=40 keycenter=-5 - sample=kick.wav keycenter=40 keycenter=1000 - sample=kick.wav keycenter=c3 + sample=kick.wav fil_keycenter=40 + sample=kick.wav fil_keycenter=40 fil_keycenter=1000 + 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 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"( - sample=kick.wav eq1_freq=20000000 // Bound this to 30k + sample=kick.wav eq1_freq=20000000 // Clamp the value 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 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"( 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 expected { - // "/region0/eq0/bandwidth,f : { 5 }", + "/region0/eq0/bandwidth,f : { 1 }", }; REQUIRE(messageList == expected); } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index b9bf7ab8..78701e5b 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -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") locc4=64 hicc67=32 pan_cc5=200 sample=*sine width_cc98=200 sample=*sine position_cc42=200 pitch_oncc56=200 sample=*sine - start_locc44=200 hikey=-1 sample=*sine + start_locc44=120 hikey=-1 sample=*sine )"); auto usedCCs = synth.getUsedCCs(); REQUIRE( usedCCs.test(1) ); diff --git a/tests/TestFiles/note_offset.sfz b/tests/TestFiles/note_offset.sfz deleted file mode 100644 index 40760e49..00000000 --- a/tests/TestFiles/note_offset.sfz +++ /dev/null @@ -1,12 +0,0 @@ - note_offset=1 - key=63 sample=*sine - lokey=50 hikey=55 pitch_keycenter=50 sample=*sine - lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=44 xfout_hikey=48 sample=*sine - note_offset=-1 - key=63 sw_lokey=24 sw_hikey=28 sw_last=25 sw_up=25 sw_down=25 sw_previous=62 sample=*sine - note_offset=1 octave_offset=1 - key=63 sample=*sine - note_offset=-1 octave_offset=-1 - key=63 sample=*sine - // Check that this does not reset either note or octave offset - key=63 sample=*sine diff --git a/tests/TestHelpers.h b/tests/TestHelpers.h index 31c5feb5..0c60fb91 100644 --- a/tests/TestHelpers.h +++ b/tests/TestHelpers.h @@ -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 +void almostEqualRanges(const sfz::Range& lhs, const sfz::Range& rhs) +{ + REQUIRE(lhs.getStart() == Approx(rhs.getStart())); + REQUIRE(lhs.getEnd() == Approx(rhs.getEnd())); +} + template void sortAll(C& container) {