diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 24fc06c3..290c1248 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -65,7 +65,7 @@ namespace Default // Wavetable oscillator constexpr float oscillatorPhase { 0.0 }; - constexpr Range oscillatorPhaseRange { -1.0, 360.0 }; + constexpr Range oscillatorPhaseRange { -1.0, 1.0 }; constexpr int oscillatorMode { 0 }; constexpr int oscillatorMulti { 1 }; constexpr Range oscillatorModeRange { 0, 2 }; @@ -222,7 +222,7 @@ namespace Default constexpr int numLFOSubs { 2 }; constexpr int numLFOSteps { 8 }; constexpr Range lfoFreqRange { 0.0, 100.0 }; - constexpr Range lfoPhaseRange { 0.0, 360.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 }; @@ -281,7 +281,7 @@ namespace Default constexpr Range apanWaveformRange { 0, std::numeric_limits::max() }; constexpr Range apanFrequencyRange { 0, std::numeric_limits::max() }; - constexpr Range apanPhaseRange { 0.0, 360.0 }; + constexpr Range apanPhaseRange { 0.0, 1.0 }; constexpr Range apanLevelRange { 0.0, 100.0 }; } } diff --git a/src/sfizz/MathHelpers.h b/src/sfizz/MathHelpers.h index 9fb035f1..50b012bd 100644 --- a/src/sfizz/MathHelpers.h +++ b/src/sfizz/MathHelpers.h @@ -288,6 +288,17 @@ constexpr long int lroundPositive(T value) return static_cast(0.5f + value); // NOLINT } +/** + @brief Wrap a normalized phase into the domain [0;1[ + */ +template +static T wrapPhase(T phase) +{ + T wrapped = phase - static_cast(phase); + wrapped += wrapped < 0; + return wrapped; +} + /** @brief A fraction which is parameterized by integer type */ diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index be7959ef..810b0d79 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -144,7 +144,8 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) // Wavetable oscillator case hash("oscillator_phase"): - setValueFromOpcode(opcode, oscillatorPhase, Default::oscillatorPhaseRange); + if (auto value = readOpcode(opcode.value, Default::oscillatorPhaseRange)) + oscillatorPhase = (*value >= 0) ? wrapPhase(*value) : -1.0f; break; case hash("oscillator"): if (auto value = readBooleanFromOpcode(opcode)) @@ -851,12 +852,8 @@ 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)) { - float normalPhase = *value * (1.0 / 360.0); - normalPhase -= int(normalPhase); - normalPhase += (normalPhase < 0) ? 1 : 0; - lfos[lfoNumber - 1].phase0 = normalPhase; - } + if (auto value = readOpcode(opcode.value, Default::lfoPhaseRange)) + lfos[lfoNumber - 1].phase0 = wrapPhase(*value); } break; case hash("lfo&_delay"): @@ -1762,10 +1759,9 @@ float sfz::Region::getBaseGain() const noexcept float sfz::Region::getPhase() const noexcept { float phase; - if (oscillatorPhase >= 0) { - phase = oscillatorPhase * (1.0f / 360.0f); - phase -= static_cast(static_cast(phase)); - } else { + if (oscillatorPhase >= 0) + phase = oscillatorPhase; + else { fast_real_distribution phaseDist { 0.0001f, 0.9999f }; phase = phaseDist(Random::randomGenerator); } diff --git a/src/sfizz/effects/Apan.cpp b/src/sfizz/effects/Apan.cpp index c21af6c3..a51f4a4e 100644 --- a/src/sfizz/effects/Apan.cpp +++ b/src/sfizz/effects/Apan.cpp @@ -89,11 +89,8 @@ namespace fx { apan->_lfoFrequency = *value; break; case hash("apan_phase"): - if (auto value = readOpcode(opc.value, Default::apanPhaseRange)) { - float phase = *value / 360.0f; - phase -= static_cast(phase); - apan->_lfoPhaseOffset = phase; - } + if (auto value = readOpcode(opc.value, Default::apanPhaseRange)) + apan->_lfoPhaseOffset = wrapPhase(*value); break; case hash("apan_dry"): if (auto value = readOpcode(opc.value, Default::apanLevelRange)) diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 8bd77f62..945ae0bf 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -1595,14 +1595,14 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("Wavetable phase") { REQUIRE(region.oscillatorPhase == 0.0f); - region.parseOpcode({ "oscillator_phase", "45" }); - REQUIRE(region.oscillatorPhase == 45.0f); - region.parseOpcode({ "oscillator_phase", "45.32" }); - REQUIRE(region.oscillatorPhase == 45.32_a); + region.parseOpcode({ "oscillator_phase", "0.25" }); + REQUIRE(region.oscillatorPhase == 0.25f); + region.parseOpcode({ "oscillator_phase", "0.3" }); + REQUIRE(region.oscillatorPhase == 0.3_a); region.parseOpcode({ "oscillator_phase", "-1" }); REQUIRE(region.oscillatorPhase == -1.0f); - region.parseOpcode({ "oscillator_phase", "361" }); - REQUIRE(region.oscillatorPhase == 360.0f); + region.parseOpcode({ "oscillator_phase", "1.1" }); + REQUIRE(region.oscillatorPhase == 0.0f); } SECTION("Note polyphony") diff --git a/tests/lfo/lfo_subwave.sfz b/tests/lfo/lfo_subwave.sfz index eb493764..6aa09f79 100644 --- a/tests/lfo/lfo_subwave.sfz +++ b/tests/lfo/lfo_subwave.sfz @@ -7,23 +7,23 @@ fil_type=brf_2p lfo1_cutoff=1200.0 // lfo1_freq=1 -lfo1_phase=180 +lfo1_phase=0.5 lfo1_wave=3 // lfo2_freq=1 -lfo2_phase=180 +lfo2_phase=0.5 lfo2_wave=3 lfo2_wave2=1 // lfo3_freq=1 -lfo3_phase=180 +lfo3_phase=0.5 lfo3_wave=3 lfo3_wave2=1 lfo3_ratio2=2 // lfo4_freq=1 -lfo4_phase=180 +lfo4_phase=0.5 lfo4_wave=3 lfo4_wave2=1 lfo4_ratio2=2 diff --git a/tests/lfo/lfo_waves.sfz b/tests/lfo/lfo_waves.sfz index 091245dd..07db6b3c 100644 --- a/tests/lfo/lfo_waves.sfz +++ b/tests/lfo/lfo_waves.sfz @@ -18,6 +18,6 @@ lfo6_wave=5 lfo6_freq=2.0 lfo7_wave=6 lfo7_freq=1.0 -lfo7_phase=180 +lfo7_phase=0.5 lfo8_wave=7 lfo8_freq=2.0