From f06ed50c574b860cf52c8bca6716ac0467f01290 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 15 Nov 2020 17:22:18 +0100 Subject: [PATCH] Implement lfoN_beats_onccX, lfoN_freq_onccX --- src/sfizz/Defaults.h | 2 + src/sfizz/LFO.cpp | 92 +++++++++++++++++++-------- src/sfizz/LFO.h | 9 ++- src/sfizz/Region.cpp | 20 ++++++ src/sfizz/Voice.cpp | 2 +- src/sfizz/modulations/ModId.cpp | 4 ++ src/sfizz/modulations/ModId.h | 2 + src/sfizz/modulations/ModKey.cpp | 4 ++ src/sfizz/modulations/sources/LFO.cpp | 2 +- 9 files changed, 106 insertions(+), 31 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index f20db102..eb7f7d25 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -222,7 +222,9 @@ namespace Default 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 }; diff --git a/src/sfizz/LFO.cpp b/src/sfizz/LFO.cpp index 353ab899..636ba74f 100644 --- a/src/sfizz/LFO.cpp +++ b/src/sfizz/LFO.cpp @@ -11,6 +11,9 @@ #include "MathHelpers.h" #include "SIMDHelpers.h" #include "Config.h" +#include "modulations/ModMatrix.h" +#include "modulations/ModKey.h" +#include "modulations/ModId.h" #include #include #include @@ -18,10 +21,11 @@ namespace sfz { struct LFO::Impl { - explicit Impl(NumericId id, BufferPool& bufferPool, BeatClock* beatClock) + explicit Impl(NumericId id, BufferPool& bufferPool, BeatClock* beatClock, ModMatrix* modMatrix) : id_(id), bufferPool_(bufferPool), beatClock_(beatClock), + modMatrix_(modMatrix), sampleRate_(config::defaultSampleRate), desc_(&LFODescription::getDefault()) { @@ -30,6 +34,7 @@ struct LFO::Impl { NumericId id_; BufferPool& bufferPool_; BeatClock* beatClock_ = nullptr; + ModMatrix* modMatrix_ = nullptr; float sampleRate_ = 0; // control @@ -43,8 +48,8 @@ struct LFO::Impl { std::array sampleHoldState_ {{}}; }; -LFO::LFO(NumericId id, BufferPool& bufferPool, BeatClock* beatClock) - : impl_(new Impl(id, bufferPool, beatClock)) +LFO::LFO(NumericId id, BufferPool& bufferPool, BeatClock* beatClock, ModMatrix* modMatrix) + : impl_(new Impl(id, bufferPool, beatClock, modMatrix)) { } @@ -212,7 +217,7 @@ void LFO::processSteps(absl::Span out, const float* phaseIn) } } -void LFO::process(absl::Span out) +void LFO::process(absl::Span out, NumericId regionId) { Impl& impl = *impl_; const LFODescription& desc = *impl.desc_; @@ -243,13 +248,13 @@ void LFO::process(absl::Span out) absl::Span phases = *phasesTemp; if (desc.seq) { - generatePhase(0, phases); + generatePhase(0, phases, regionId); processSteps(out, phases.data()); ++subno; } for (; subno < countSubs; ++subno) { - generatePhase(subno, phases); + generatePhase(subno, phases, regionId); switch (desc.sub[subno].wave) { case LFOWave::Triangle: processWave(subno, out, phases.data()); @@ -306,10 +311,13 @@ void LFO::processFadeIn(absl::Span out) impl.fadePosition_ = fadePosition; } -void LFO::generatePhase(unsigned nth, absl::Span phases) +void LFO::generatePhase(unsigned nth, absl::Span phases, NumericId regionId) { Impl& impl = *impl_; + BufferPool& bufferPool = impl.bufferPool_; BeatClock* beatClock = impl.beatClock_; + ModMatrix* modMatrix = impl.modMatrix_; + const NumericId id { impl.id_ }; const LFODescription& desc = *impl.desc_; const LFODescription::Sub& sub = desc.sub[nth]; const float samplePeriod = 1.0f / impl.sampleRate_; @@ -320,35 +328,67 @@ void LFO::generatePhase(unsigned nth, absl::Span phases) float phase = impl.subPhases_[nth]; const size_t numFrames = phases.size(); + // TODO(jpc) lfoN_count: number of repetitions + if (beatClock && beatClock->isPlaying() && beats > 0) { // generate using the beat clock float beatRatio = (ratio > 0) ? (1.0f / ratio) : 0.0f; - beatClock->calculatePhase(beats * beatRatio, phases.data()); - for (size_t i = 0; i < numFrames; ++i) { - float withOffset = phase + phaseOffset; - withOffset -= (int)withOffset; + const float* beatsMod = nullptr; + if (modMatrix && id && regionId) { + ModKey beatsKey = ModKey::createNXYZ(ModId::LFOBeats, regionId, id.number()); + beatsMod = modMatrix->getModulationByKey(beatsKey); + } - phases[i] = withOffset; - - // TODO(jpc) lfoN_count: number of repetitions + if (!beatsMod) + beatClock->calculatePhase(beats * beatRatio, phases.data()); + else { + auto temp = bufferPool.getBuffer(numFrames); + if (!temp) { + ASSERTFALSE; + beatClock->calculatePhase(beats * beatRatio, phases.data()); + } + else { + fill(*temp, beats); + add(absl::MakeConstSpan(beatsMod, numFrames), *temp); + applyGain1(beatRatio, *temp); + beatClock->calculatePhaseModulated(temp->data(), phases.data()); + } } } else { // generate using the frequency - for (size_t i = 0; i < numFrames; ++i) { - float withOffset = phase + phaseOffset; - withOffset -= (int)withOffset; - - phases[i] = withOffset; - - // TODO(jpc) lfoN_count: number of repetitions - - float incr = ratio * samplePeriod * baseFreq; - phase += incr; - int numWraps = (int)phase; - phase -= numWraps; + const float* freqMod = nullptr; + if (modMatrix && id && regionId) { + ModKey freqKey = ModKey::createNXYZ(ModId::LFOFrequency, regionId, id.number()); + freqMod = modMatrix->getModulationByKey(freqKey); } + + if (!freqMod) { + for (size_t i = 0; i < numFrames; ++i) { + phases[i] = phase; + float incr = ratio * samplePeriod * baseFreq; + phase += incr; + int numWraps = (int)phase; + phase -= numWraps; + } + } + else { + for (size_t i = 0; i < numFrames; ++i) { + phases[i] = phase; + float incr = ratio * samplePeriod * (baseFreq + freqMod[i]); + phase += incr; + int numWraps = (int)phase; + phase -= numWraps; + } + } + } + + // apply phase offsets + for (size_t i = 0; i < numFrames; ++i) { + float withOffset = phases[i] + phaseOffset; + withOffset -= (int)withOffset; + phases[i] = withOffset; } impl.subPhases_[nth] = phase; diff --git a/src/sfizz/LFO.h b/src/sfizz/LFO.h index fecadc8b..caac1c27 100644 --- a/src/sfizz/LFO.h +++ b/src/sfizz/LFO.h @@ -12,6 +12,8 @@ namespace sfz { class BufferPool; class BeatClock; +class ModMatrix; +struct Region; enum class LFOWave : int; struct LFODescription; @@ -55,7 +57,8 @@ public: explicit LFO( NumericId id, BufferPool& bufferPool, - BeatClock* beatClock = nullptr); + BeatClock* beatClock = nullptr, + ModMatrix* modMatrix = nullptr); ~LFO(); NumericId getId() const noexcept; @@ -82,7 +85,7 @@ public: TODO(jpc) frequency modulations */ - void process(absl::Span out); + void process(absl::Span out, NumericId regionId = {}); private: /** @@ -120,7 +123,7 @@ private: /** Generate the phase of the N-th generator */ - void generatePhase(unsigned nth, absl::Span phases); + void generatePhase(unsigned nth, absl::Span phases, NumericId regionId); private: struct Impl; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 88813c7e..4f0ce8f2 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -866,6 +866,16 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) setValueFromOpcode(opcode, lfos[lfoNumber - 1].freq, Default::lfoFreqRange); } break; + case_any_ccN("lfo&_freq"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + processGenericCc(opcode, Default::lfoFreqModRange, ModKey::createNXYZ(ModId::LFOFrequency, id, lfoNumber - 1)); + } + break; case hash("lfo&_beats"): { const auto lfoNumber = opcode.parameters.front(); @@ -876,6 +886,16 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) setValueFromOpcode(opcode, lfos[lfoNumber - 1].beats, Default::lfoBeatsRange); } break; + case_any_ccN("lfo&_beats"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + processGenericCc(opcode, Default::lfoBeatsModRange, ModKey::createNXYZ(ModId::LFOBeats, id, lfoNumber - 1)); + } + break; case hash("lfo&_phase"): { const auto lfoNumber = opcode.parameters.front(); diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 6c25eee8..fa6e4262 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -1481,7 +1481,7 @@ void Voice::setMaxLFOsPerVoice(size_t numLFOs) for (size_t i = 0; i < numLFOs; ++i) { const NumericId id { static_cast(i) }; - auto lfo = absl::make_unique(id, resources.bufferPool, &resources.beatClock); + auto lfo = absl::make_unique(id, resources.bufferPool, &resources.beatClock, &resources.modMatrix); lfo->setSampleRate(impl.sampleRate_); impl.lfos_[i] = std::move(lfo); } diff --git a/src/sfizz/modulations/ModId.cpp b/src/sfizz/modulations/ModId.cpp index 3d837ec5..388da189 100644 --- a/src/sfizz/modulations/ModId.cpp +++ b/src/sfizz/modulations/ModId.cpp @@ -68,6 +68,10 @@ int ModIds::flags(ModId id) noexcept return kModIsPerVoice|kModIsAdditive; case ModId::OscillatorModDepth: return kModIsPerVoice|kModIsPercentMultiplicative; + case ModId::LFOFrequency: + return kModIsPerVoice|kModIsAdditive; + case ModId::LFOBeats: + return kModIsPerVoice|kModIsAdditive; // unknown default: diff --git a/src/sfizz/modulations/ModId.h b/src/sfizz/modulations/ModId.h index 48d9b2d9..f4c45965 100644 --- a/src/sfizz/modulations/ModId.h +++ b/src/sfizz/modulations/ModId.h @@ -49,6 +49,8 @@ enum class ModId : int { EqBandwidth, OscillatorDetune, OscillatorModDepth, + LFOFrequency, + LFOBeats, _TargetsEnd, // [/targets] -------------------------------------------------------------- diff --git a/src/sfizz/modulations/ModKey.cpp b/src/sfizz/modulations/ModKey.cpp index 051a4f8e..7b75c8f5 100644 --- a/src/sfizz/modulations/ModKey.cpp +++ b/src/sfizz/modulations/ModKey.cpp @@ -118,6 +118,10 @@ std::string ModKey::toString() const return absl::StrCat("OscillatorDetune {", region_.number(), ", N=", 1 + params_.N, "}"); case ModId::OscillatorModDepth: return absl::StrCat("OscillatorModDepth {", region_.number(), ", N=", 1 + params_.N, "}"); + case ModId::LFOFrequency: + return absl::StrCat("LFOFrequency {", region_.number(), ", N=", 1 + params_.N, "}"); + case ModId::LFOBeats: + return absl::StrCat("LFOBeats {", region_.number(), ", N=", 1 + params_.N, "}"); default: return {}; diff --git a/src/sfizz/modulations/sources/LFO.cpp b/src/sfizz/modulations/sources/LFO.cpp index a485833d..206f0ca1 100644 --- a/src/sfizz/modulations/sources/LFO.cpp +++ b/src/sfizz/modulations/sources/LFO.cpp @@ -59,7 +59,7 @@ void LFOSource::generate(const ModKey& sourceKey, NumericId voiceId, absl } LFO* lfo = voice->getLFO(lfoIndex); - lfo->process(buffer); + lfo->process(buffer, region->getId()); } } // namespace sfz