Implement lfoN_beats_onccX, lfoN_freq_onccX

This commit is contained in:
Jean Pierre Cimalando 2020-11-15 17:22:18 +01:00
parent 04ff815cab
commit f06ed50c57
9 changed files with 106 additions and 31 deletions

View file

@ -222,7 +222,9 @@ namespace Default
constexpr int numLFOSubs { 2 };
constexpr int numLFOSteps { 8 };
constexpr Range<float> lfoFreqRange { 0.0, 100.0 };
constexpr Range<float> lfoFreqModRange { -100.0, 100.0 };
constexpr Range<float> lfoBeatsRange { 0.0, 1000.0 };
constexpr Range<float> lfoBeatsModRange { -1000.0, 1000.0 };
constexpr Range<float> lfoPhaseRange { 0.0, 1.0 };
constexpr Range<float> lfoDelayRange { 0.0, 30.0 };
constexpr Range<float> lfoFadeRange { 0.0, 30.0 };

View file

@ -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 <array>
#include <algorithm>
#include <cmath>
@ -18,10 +21,11 @@
namespace sfz {
struct LFO::Impl {
explicit Impl(NumericId<LFO> id, BufferPool& bufferPool, BeatClock* beatClock)
explicit Impl(NumericId<LFO> 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<LFO> id_;
BufferPool& bufferPool_;
BeatClock* beatClock_ = nullptr;
ModMatrix* modMatrix_ = nullptr;
float sampleRate_ = 0;
// control
@ -43,8 +48,8 @@ struct LFO::Impl {
std::array<int, config::maxLFOSubs> sampleHoldState_ {{}};
};
LFO::LFO(NumericId<LFO> id, BufferPool& bufferPool, BeatClock* beatClock)
: impl_(new Impl(id, bufferPool, beatClock))
LFO::LFO(NumericId<LFO> id, BufferPool& bufferPool, BeatClock* beatClock, ModMatrix* modMatrix)
: impl_(new Impl(id, bufferPool, beatClock, modMatrix))
{
}
@ -212,7 +217,7 @@ void LFO::processSteps(absl::Span<float> out, const float* phaseIn)
}
}
void LFO::process(absl::Span<float> out)
void LFO::process(absl::Span<float> out, NumericId<Region> regionId)
{
Impl& impl = *impl_;
const LFODescription& desc = *impl.desc_;
@ -243,13 +248,13 @@ void LFO::process(absl::Span<float> out)
absl::Span<float> 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<LFOWave::Triangle>(subno, out, phases.data());
@ -306,10 +311,13 @@ void LFO::processFadeIn(absl::Span<float> out)
impl.fadePosition_ = fadePosition;
}
void LFO::generatePhase(unsigned nth, absl::Span<float> phases)
void LFO::generatePhase(unsigned nth, absl::Span<float> phases, NumericId<Region> regionId)
{
Impl& impl = *impl_;
BufferPool& bufferPool = impl.bufferPool_;
BeatClock* beatClock = impl.beatClock_;
ModMatrix* modMatrix = impl.modMatrix_;
const NumericId<LFO> 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<float> 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;

View file

@ -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<LFO> id,
BufferPool& bufferPool,
BeatClock* beatClock = nullptr);
BeatClock* beatClock = nullptr,
ModMatrix* modMatrix = nullptr);
~LFO();
NumericId<LFO> getId() const noexcept;
@ -82,7 +85,7 @@ public:
TODO(jpc) frequency modulations
*/
void process(absl::Span<float> out);
void process(absl::Span<float> out, NumericId<Region> regionId = {});
private:
/**
@ -120,7 +123,7 @@ private:
/**
Generate the phase of the N-th generator
*/
void generatePhase(unsigned nth, absl::Span<float> phases);
void generatePhase(unsigned nth, absl::Span<float> phases, NumericId<Region> regionId);
private:
struct Impl;

View file

@ -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();

View file

@ -1481,7 +1481,7 @@ void Voice::setMaxLFOsPerVoice(size_t numLFOs)
for (size_t i = 0; i < numLFOs; ++i) {
const NumericId<LFO> id { static_cast<int>(i) };
auto lfo = absl::make_unique<LFO>(id, resources.bufferPool, &resources.beatClock);
auto lfo = absl::make_unique<LFO>(id, resources.bufferPool, &resources.beatClock, &resources.modMatrix);
lfo->setSampleRate(impl.sampleRate_);
impl.lfos_[i] = std::move(lfo);
}

View file

@ -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:

View file

@ -49,6 +49,8 @@ enum class ModId : int {
EqBandwidth,
OscillatorDetune,
OscillatorModDepth,
LFOFrequency,
LFOBeats,
_TargetsEnd,
// [/targets] --------------------------------------------------------------

View file

@ -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 {};

View file

@ -59,7 +59,7 @@ void LFOSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl
}
LFO* lfo = voice->getLFO(lfoIndex);
lfo->process(buffer);
lfo->process(buffer, region->getId());
}
} // namespace sfz