Implement lfoN_beats_onccX, lfoN_freq_onccX
This commit is contained in:
parent
04ff815cab
commit
f06ed50c57
9 changed files with 106 additions and 31 deletions
|
|
@ -222,7 +222,9 @@ namespace Default
|
||||||
constexpr int numLFOSubs { 2 };
|
constexpr int numLFOSubs { 2 };
|
||||||
constexpr int numLFOSteps { 8 };
|
constexpr int numLFOSteps { 8 };
|
||||||
constexpr Range<float> lfoFreqRange { 0.0, 100.0 };
|
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> lfoBeatsRange { 0.0, 1000.0 };
|
||||||
|
constexpr Range<float> lfoBeatsModRange { -1000.0, 1000.0 };
|
||||||
constexpr Range<float> lfoPhaseRange { 0.0, 1.0 };
|
constexpr Range<float> lfoPhaseRange { 0.0, 1.0 };
|
||||||
constexpr Range<float> lfoDelayRange { 0.0, 30.0 };
|
constexpr Range<float> lfoDelayRange { 0.0, 30.0 };
|
||||||
constexpr Range<float> lfoFadeRange { 0.0, 30.0 };
|
constexpr Range<float> lfoFadeRange { 0.0, 30.0 };
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@
|
||||||
#include "MathHelpers.h"
|
#include "MathHelpers.h"
|
||||||
#include "SIMDHelpers.h"
|
#include "SIMDHelpers.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "modulations/ModMatrix.h"
|
||||||
|
#include "modulations/ModKey.h"
|
||||||
|
#include "modulations/ModId.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
@ -18,10 +21,11 @@
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
|
|
||||||
struct LFO::Impl {
|
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),
|
: id_(id),
|
||||||
bufferPool_(bufferPool),
|
bufferPool_(bufferPool),
|
||||||
beatClock_(beatClock),
|
beatClock_(beatClock),
|
||||||
|
modMatrix_(modMatrix),
|
||||||
sampleRate_(config::defaultSampleRate),
|
sampleRate_(config::defaultSampleRate),
|
||||||
desc_(&LFODescription::getDefault())
|
desc_(&LFODescription::getDefault())
|
||||||
{
|
{
|
||||||
|
|
@ -30,6 +34,7 @@ struct LFO::Impl {
|
||||||
NumericId<LFO> id_;
|
NumericId<LFO> id_;
|
||||||
BufferPool& bufferPool_;
|
BufferPool& bufferPool_;
|
||||||
BeatClock* beatClock_ = nullptr;
|
BeatClock* beatClock_ = nullptr;
|
||||||
|
ModMatrix* modMatrix_ = nullptr;
|
||||||
float sampleRate_ = 0;
|
float sampleRate_ = 0;
|
||||||
|
|
||||||
// control
|
// control
|
||||||
|
|
@ -43,8 +48,8 @@ struct LFO::Impl {
|
||||||
std::array<int, config::maxLFOSubs> sampleHoldState_ {{}};
|
std::array<int, config::maxLFOSubs> sampleHoldState_ {{}};
|
||||||
};
|
};
|
||||||
|
|
||||||
LFO::LFO(NumericId<LFO> id, BufferPool& bufferPool, BeatClock* beatClock)
|
LFO::LFO(NumericId<LFO> id, BufferPool& bufferPool, BeatClock* beatClock, ModMatrix* modMatrix)
|
||||||
: impl_(new Impl(id, bufferPool, beatClock))
|
: 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_;
|
Impl& impl = *impl_;
|
||||||
const LFODescription& desc = *impl.desc_;
|
const LFODescription& desc = *impl.desc_;
|
||||||
|
|
@ -243,13 +248,13 @@ void LFO::process(absl::Span<float> out)
|
||||||
absl::Span<float> phases = *phasesTemp;
|
absl::Span<float> phases = *phasesTemp;
|
||||||
|
|
||||||
if (desc.seq) {
|
if (desc.seq) {
|
||||||
generatePhase(0, phases);
|
generatePhase(0, phases, regionId);
|
||||||
processSteps(out, phases.data());
|
processSteps(out, phases.data());
|
||||||
++subno;
|
++subno;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; subno < countSubs; ++subno) {
|
for (; subno < countSubs; ++subno) {
|
||||||
generatePhase(subno, phases);
|
generatePhase(subno, phases, regionId);
|
||||||
switch (desc.sub[subno].wave) {
|
switch (desc.sub[subno].wave) {
|
||||||
case LFOWave::Triangle:
|
case LFOWave::Triangle:
|
||||||
processWave<LFOWave::Triangle>(subno, out, phases.data());
|
processWave<LFOWave::Triangle>(subno, out, phases.data());
|
||||||
|
|
@ -306,10 +311,13 @@ void LFO::processFadeIn(absl::Span<float> out)
|
||||||
impl.fadePosition_ = fadePosition;
|
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_;
|
Impl& impl = *impl_;
|
||||||
|
BufferPool& bufferPool = impl.bufferPool_;
|
||||||
BeatClock* beatClock = impl.beatClock_;
|
BeatClock* beatClock = impl.beatClock_;
|
||||||
|
ModMatrix* modMatrix = impl.modMatrix_;
|
||||||
|
const NumericId<LFO> id { impl.id_ };
|
||||||
const LFODescription& desc = *impl.desc_;
|
const LFODescription& desc = *impl.desc_;
|
||||||
const LFODescription::Sub& sub = desc.sub[nth];
|
const LFODescription::Sub& sub = desc.sub[nth];
|
||||||
const float samplePeriod = 1.0f / impl.sampleRate_;
|
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];
|
float phase = impl.subPhases_[nth];
|
||||||
const size_t numFrames = phases.size();
|
const size_t numFrames = phases.size();
|
||||||
|
|
||||||
|
// TODO(jpc) lfoN_count: number of repetitions
|
||||||
|
|
||||||
if (beatClock && beatClock->isPlaying() && beats > 0) {
|
if (beatClock && beatClock->isPlaying() && beats > 0) {
|
||||||
// generate using the beat clock
|
// generate using the beat clock
|
||||||
float beatRatio = (ratio > 0) ? (1.0f / ratio) : 0.0f;
|
float beatRatio = (ratio > 0) ? (1.0f / ratio) : 0.0f;
|
||||||
beatClock->calculatePhase(beats * beatRatio, phases.data());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < numFrames; ++i) {
|
const float* beatsMod = nullptr;
|
||||||
float withOffset = phase + phaseOffset;
|
if (modMatrix && id && regionId) {
|
||||||
withOffset -= (int)withOffset;
|
ModKey beatsKey = ModKey::createNXYZ(ModId::LFOBeats, regionId, id.number());
|
||||||
|
beatsMod = modMatrix->getModulationByKey(beatsKey);
|
||||||
|
}
|
||||||
|
|
||||||
phases[i] = withOffset;
|
if (!beatsMod)
|
||||||
|
beatClock->calculatePhase(beats * beatRatio, phases.data());
|
||||||
// TODO(jpc) lfoN_count: number of repetitions
|
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 {
|
else {
|
||||||
// generate using the frequency
|
// generate using the frequency
|
||||||
for (size_t i = 0; i < numFrames; ++i) {
|
const float* freqMod = nullptr;
|
||||||
float withOffset = phase + phaseOffset;
|
if (modMatrix && id && regionId) {
|
||||||
withOffset -= (int)withOffset;
|
ModKey freqKey = ModKey::createNXYZ(ModId::LFOFrequency, regionId, id.number());
|
||||||
|
freqMod = modMatrix->getModulationByKey(freqKey);
|
||||||
phases[i] = withOffset;
|
|
||||||
|
|
||||||
// TODO(jpc) lfoN_count: number of repetitions
|
|
||||||
|
|
||||||
float incr = ratio * samplePeriod * baseFreq;
|
|
||||||
phase += incr;
|
|
||||||
int numWraps = (int)phase;
|
|
||||||
phase -= numWraps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
impl.subPhases_[nth] = phase;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
class BufferPool;
|
class BufferPool;
|
||||||
class BeatClock;
|
class BeatClock;
|
||||||
|
class ModMatrix;
|
||||||
|
struct Region;
|
||||||
|
|
||||||
enum class LFOWave : int;
|
enum class LFOWave : int;
|
||||||
struct LFODescription;
|
struct LFODescription;
|
||||||
|
|
@ -55,7 +57,8 @@ public:
|
||||||
explicit LFO(
|
explicit LFO(
|
||||||
NumericId<LFO> id,
|
NumericId<LFO> id,
|
||||||
BufferPool& bufferPool,
|
BufferPool& bufferPool,
|
||||||
BeatClock* beatClock = nullptr);
|
BeatClock* beatClock = nullptr,
|
||||||
|
ModMatrix* modMatrix = nullptr);
|
||||||
~LFO();
|
~LFO();
|
||||||
|
|
||||||
NumericId<LFO> getId() const noexcept;
|
NumericId<LFO> getId() const noexcept;
|
||||||
|
|
@ -82,7 +85,7 @@ public:
|
||||||
|
|
||||||
TODO(jpc) frequency modulations
|
TODO(jpc) frequency modulations
|
||||||
*/
|
*/
|
||||||
void process(absl::Span<float> out);
|
void process(absl::Span<float> out, NumericId<Region> regionId = {});
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
|
@ -120,7 +123,7 @@ private:
|
||||||
/**
|
/**
|
||||||
Generate the phase of the N-th generator
|
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:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
|
|
|
||||||
|
|
@ -866,6 +866,16 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
setValueFromOpcode(opcode, lfos[lfoNumber - 1].freq, Default::lfoFreqRange);
|
setValueFromOpcode(opcode, lfos[lfoNumber - 1].freq, Default::lfoFreqRange);
|
||||||
}
|
}
|
||||||
break;
|
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"):
|
case hash("lfo&_beats"):
|
||||||
{
|
{
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
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);
|
setValueFromOpcode(opcode, lfos[lfoNumber - 1].beats, Default::lfoBeatsRange);
|
||||||
}
|
}
|
||||||
break;
|
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"):
|
case hash("lfo&_phase"):
|
||||||
{
|
{
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
const auto lfoNumber = opcode.parameters.front();
|
||||||
|
|
|
||||||
|
|
@ -1481,7 +1481,7 @@ void Voice::setMaxLFOsPerVoice(size_t numLFOs)
|
||||||
|
|
||||||
for (size_t i = 0; i < numLFOs; ++i) {
|
for (size_t i = 0; i < numLFOs; ++i) {
|
||||||
const NumericId<LFO> id { static_cast<int>(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_);
|
lfo->setSampleRate(impl.sampleRate_);
|
||||||
impl.lfos_[i] = std::move(lfo);
|
impl.lfos_[i] = std::move(lfo);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,10 @@ int ModIds::flags(ModId id) noexcept
|
||||||
return kModIsPerVoice|kModIsAdditive;
|
return kModIsPerVoice|kModIsAdditive;
|
||||||
case ModId::OscillatorModDepth:
|
case ModId::OscillatorModDepth:
|
||||||
return kModIsPerVoice|kModIsPercentMultiplicative;
|
return kModIsPerVoice|kModIsPercentMultiplicative;
|
||||||
|
case ModId::LFOFrequency:
|
||||||
|
return kModIsPerVoice|kModIsAdditive;
|
||||||
|
case ModId::LFOBeats:
|
||||||
|
return kModIsPerVoice|kModIsAdditive;
|
||||||
|
|
||||||
// unknown
|
// unknown
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ enum class ModId : int {
|
||||||
EqBandwidth,
|
EqBandwidth,
|
||||||
OscillatorDetune,
|
OscillatorDetune,
|
||||||
OscillatorModDepth,
|
OscillatorModDepth,
|
||||||
|
LFOFrequency,
|
||||||
|
LFOBeats,
|
||||||
|
|
||||||
_TargetsEnd,
|
_TargetsEnd,
|
||||||
// [/targets] --------------------------------------------------------------
|
// [/targets] --------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,10 @@ std::string ModKey::toString() const
|
||||||
return absl::StrCat("OscillatorDetune {", region_.number(), ", N=", 1 + params_.N, "}");
|
return absl::StrCat("OscillatorDetune {", region_.number(), ", N=", 1 + params_.N, "}");
|
||||||
case ModId::OscillatorModDepth:
|
case ModId::OscillatorModDepth:
|
||||||
return absl::StrCat("OscillatorModDepth {", region_.number(), ", N=", 1 + params_.N, "}");
|
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:
|
default:
|
||||||
return {};
|
return {};
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ void LFOSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl
|
||||||
}
|
}
|
||||||
|
|
||||||
LFO* lfo = voice->getLFO(lfoIndex);
|
LFO* lfo = voice->getLFO(lfoIndex);
|
||||||
lfo->process(buffer);
|
lfo->process(buffer, region->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue