Merge pull request #753 from paulfd/lfo-cc
Add lfoN_fade_oncc, lfoN_delay_oncc, lfoN_phase_oncc
This commit is contained in:
commit
40d18f0e23
13 changed files with 111 additions and 52 deletions
|
|
@ -110,14 +110,14 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
constexpr size_t bufferSize = 1024;
|
||||
sfz::BufferPool bufferPool;
|
||||
bufferPool.setBufferSize(bufferSize);
|
||||
sfz::Resources resources;
|
||||
resources.setSamplesPerBlock(bufferSize);
|
||||
|
||||
size_t numLfos = desc.size();
|
||||
std::vector<std::unique_ptr<sfz::LFO>> lfos(numLfos);
|
||||
|
||||
for (size_t l = 0; l < numLfos; ++l) {
|
||||
sfz::LFO* lfo = new sfz::LFO(bufferPool);
|
||||
sfz::LFO* lfo = new sfz::LFO(resources);
|
||||
lfos[l].reset(lfo);
|
||||
lfo->setSampleRate(sampleRate);
|
||||
lfo->configure(&desc[l]);
|
||||
|
|
|
|||
|
|
@ -121,8 +121,11 @@ FloatSpec lfoFreqMod { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds };
|
|||
FloatSpec lfoBeats { 0.0f, {0.0f, 1000.0f}, kPermissiveBounds };
|
||||
FloatSpec lfoBeatsMod { 0.0f, {-1000.0f, 1000.0f}, kPermissiveBounds };
|
||||
FloatSpec lfoPhase { 0.0f, {0.0f, 1.0f}, kWrapPhase|kPermissiveBounds };
|
||||
FloatSpec lfoPhaseMod { 0.0f, {0.0f, 1.0f}, kPermissiveBounds };
|
||||
FloatSpec lfoDelay { 0.0f, {0.0f, 30.0f}, kPermissiveBounds };
|
||||
FloatSpec lfoDelayMod { 0.0f, {0.0f, 30.0f}, kPermissiveBounds };
|
||||
FloatSpec lfoFade { 0.0f, {0.0f, 30.0f}, kPermissiveBounds };
|
||||
FloatSpec lfoFadeMod { 0.0f, {0.0f, 30.0f}, kPermissiveBounds };
|
||||
UInt32Spec lfoCount { 0, {0, 1000}, kEnforceLowerBound|kPermissiveUpperBound };
|
||||
UInt32Spec lfoSteps { 0, {0, static_cast<unsigned>(config::maxLFOSteps)}, kEnforceBounds };
|
||||
FloatSpec lfoStepX { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds };
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ struct OpcodeSpec
|
|||
Range<T> bounds;
|
||||
int flags;
|
||||
|
||||
template <class U>
|
||||
using IsNormalizable = std::integral_constant<
|
||||
bool, std::is_arithmetic<U>::value && !std::is_same<U, bool>::value>;
|
||||
|
||||
/**
|
||||
* @brief Normalizes an input as needed for the spec
|
||||
*
|
||||
|
|
@ -76,7 +80,7 @@ struct OpcodeSpec
|
|||
* @return U
|
||||
*/
|
||||
template<class U=T>
|
||||
typename std::enable_if<std::is_arithmetic<U>::value, U>::type normalizeInput(U input) const
|
||||
typename std::enable_if<IsNormalizable<U>::value, U>::type normalizeInput(U input) const
|
||||
{
|
||||
constexpr int needsOperation {
|
||||
kNormalizePercent |
|
||||
|
|
@ -107,7 +111,7 @@ struct OpcodeSpec
|
|||
* @return U
|
||||
*/
|
||||
template<class U=T>
|
||||
typename std::enable_if<!std::is_arithmetic<U>::value, U>::type normalizeInput(U input) const
|
||||
typename std::enable_if<!IsNormalizable<U>::value, U>::type normalizeInput(U input) const
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
|
@ -221,8 +225,11 @@ namespace Default
|
|||
extern const OpcodeSpec<float> lfoBeats;
|
||||
extern const OpcodeSpec<float> lfoBeatsMod;
|
||||
extern const OpcodeSpec<float> lfoPhase;
|
||||
extern const OpcodeSpec<float> lfoPhaseMod;
|
||||
extern const OpcodeSpec<float> lfoDelay;
|
||||
extern const OpcodeSpec<float> lfoDelayMod;
|
||||
extern const OpcodeSpec<float> lfoFade;
|
||||
extern const OpcodeSpec<float> lfoFadeMod;
|
||||
extern const OpcodeSpec<uint32_t> lfoCount;
|
||||
extern const OpcodeSpec<uint32_t> lfoSteps;
|
||||
extern const OpcodeSpec<float> lfoStepX;
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
#include "LFO.h"
|
||||
#include "LFODescription.h"
|
||||
#include "BeatClock.h"
|
||||
#include "BufferPool.h"
|
||||
#include "MathHelpers.h"
|
||||
#include "SIMDHelpers.h"
|
||||
#include "Config.h"
|
||||
#include "modulations/ModMatrix.h"
|
||||
#include "Resources.h"
|
||||
#include "modulations/ModKey.h"
|
||||
#include "modulations/ModId.h"
|
||||
#include <array>
|
||||
|
|
@ -21,18 +19,14 @@
|
|||
namespace sfz {
|
||||
|
||||
struct LFO::Impl {
|
||||
explicit Impl(BufferPool& bufferPool, BeatClock* beatClock, ModMatrix* modMatrix)
|
||||
: bufferPool_(bufferPool),
|
||||
beatClock_(beatClock),
|
||||
modMatrix_(modMatrix),
|
||||
explicit Impl(Resources& resources)
|
||||
: resources_(resources),
|
||||
sampleRate_(config::defaultSampleRate),
|
||||
desc_(&LFODescription::getDefault())
|
||||
{
|
||||
}
|
||||
|
||||
BufferPool& bufferPool_;
|
||||
BeatClock* beatClock_ = nullptr;
|
||||
ModMatrix* modMatrix_ = nullptr;
|
||||
Resources& resources_;
|
||||
float sampleRate_ = 0;
|
||||
|
||||
// control
|
||||
|
|
@ -40,14 +34,15 @@ struct LFO::Impl {
|
|||
|
||||
// state
|
||||
size_t delayFramesLeft_ = 0;
|
||||
float fadeTime_ = 0;
|
||||
float fadePosition_ = 0;
|
||||
std::array<float, config::maxLFOSubs> subPhases_ {{}};
|
||||
std::array<float, config::maxLFOSubs> sampleHoldMem_ {{}};
|
||||
std::array<int, config::maxLFOSubs> sampleHoldState_ {{}};
|
||||
};
|
||||
|
||||
LFO::LFO(BufferPool& bufferPool, BeatClock* beatClock, ModMatrix* modMatrix)
|
||||
: impl_(new Impl(bufferPool, beatClock, modMatrix))
|
||||
LFO::LFO(Resources& resources)
|
||||
: impl_(new Impl(resources))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -70,16 +65,25 @@ void LFO::start(unsigned triggerDelay)
|
|||
Impl& impl = *impl_;
|
||||
const LFODescription& desc = *impl.desc_;
|
||||
const float sampleRate = impl.sampleRate_;
|
||||
const MidiState& state = impl.resources_.midiState;
|
||||
|
||||
impl.subPhases_.fill(0.0f);
|
||||
impl.sampleHoldMem_.fill(0.0f);
|
||||
impl.sampleHoldState_.fill(0);
|
||||
|
||||
const float delay = desc.delay;
|
||||
float delay = desc.delay;
|
||||
for (const auto& mod: desc.delayCC)
|
||||
delay += mod.data * state.getCCValue(mod.cc);
|
||||
|
||||
size_t delayFrames = (delay > 0) ? static_cast<size_t>(std::ceil(sampleRate * delay)) : 0u;
|
||||
impl.delayFramesLeft_ = triggerDelay + delayFrames;
|
||||
|
||||
impl.fadePosition_ = (desc.fade > 0) ? 0.0f : 1.0f;
|
||||
float fade = desc.fade;
|
||||
for (const auto& mod: desc.fadeCC)
|
||||
fade += mod.data * state.getCCValue(mod.cc);
|
||||
|
||||
impl.fadeTime_ = fade;
|
||||
impl.fadePosition_ = (fade > 0) ? 0.0f : 1.0f;
|
||||
}
|
||||
|
||||
template <>
|
||||
|
|
@ -218,6 +222,7 @@ void LFO::process(absl::Span<float> out)
|
|||
{
|
||||
Impl& impl = *impl_;
|
||||
const LFODescription& desc = *impl.desc_;
|
||||
BufferPool& pool = impl.resources_.bufferPool;
|
||||
size_t numFrames = out.size();
|
||||
|
||||
fill(out, 0.0f);
|
||||
|
|
@ -235,7 +240,7 @@ void LFO::process(absl::Span<float> out)
|
|||
if (countSubs < 1)
|
||||
return;
|
||||
|
||||
auto phasesTemp = impl.bufferPool_.getBuffer(numFrames);
|
||||
auto phasesTemp = pool.getBuffer(numFrames);
|
||||
if (!phasesTemp) {
|
||||
ASSERTFALSE;
|
||||
fill(out, 0.0f);
|
||||
|
|
@ -289,7 +294,6 @@ void LFO::process(absl::Span<float> out)
|
|||
void LFO::processFadeIn(absl::Span<float> out)
|
||||
{
|
||||
Impl& impl = *impl_;
|
||||
const LFODescription& desc = *impl.desc_;
|
||||
const float samplePeriod = 1.0f / impl.sampleRate_;
|
||||
size_t numFrames = out.size();
|
||||
|
||||
|
|
@ -297,7 +301,7 @@ void LFO::processFadeIn(absl::Span<float> out)
|
|||
if (fadePosition >= 1.0f)
|
||||
return;
|
||||
|
||||
const float fadeTime = desc.fade;
|
||||
const float fadeTime = impl.fadeTime_;
|
||||
const float fadeStep = samplePeriod / fadeTime;
|
||||
|
||||
for (size_t i = 0; i < numFrames && fadePosition < 1; ++i) {
|
||||
|
|
@ -311,9 +315,9 @@ void LFO::processFadeIn(absl::Span<float> out)
|
|||
void LFO::generatePhase(unsigned nth, absl::Span<float> phases)
|
||||
{
|
||||
Impl& impl = *impl_;
|
||||
BufferPool& bufferPool = impl.bufferPool_;
|
||||
BeatClock* beatClock = impl.beatClock_;
|
||||
ModMatrix* modMatrix = impl.modMatrix_;
|
||||
BufferPool& bufferPool = impl.resources_.bufferPool;
|
||||
BeatClock& beatClock = impl.resources_.beatClock;
|
||||
ModMatrix& modMatrix = impl.resources_.modMatrix;
|
||||
const LFODescription& desc = *impl.desc_;
|
||||
const LFODescription::Sub& sub = desc.sub[nth];
|
||||
const float samplePeriod = 1.0f / impl.sampleRate_;
|
||||
|
|
@ -329,39 +333,39 @@ void LFO::generatePhase(unsigned nth, absl::Span<float> phases)
|
|||
// modulations
|
||||
const float* beatsMod = nullptr;
|
||||
const float* freqMod = nullptr;
|
||||
if (modMatrix) {
|
||||
// Note(jpc) we might switch between beats and frequency, if host
|
||||
// switches play state on and off; continually generate both.
|
||||
beatsMod = modMatrix->getModulationByKey(desc.beatsKey);
|
||||
freqMod = modMatrix->getModulationByKey(desc.freqKey);
|
||||
}
|
||||
const float* phaseMod = nullptr;
|
||||
// Note(jpc) we might switch between beats and frequency, if host
|
||||
// switches play state on and off; continually generate both.
|
||||
beatsMod = modMatrix.getModulationByKey(desc.beatsKey);
|
||||
freqMod = modMatrix.getModulationByKey(desc.freqKey);
|
||||
phaseMod = modMatrix.getModulationByKey(desc.phaseKey);
|
||||
|
||||
if (beatClock && beatClock->isPlaying() && beats > 0) {
|
||||
if (beatClock.isPlaying() && beats > 0) {
|
||||
// generate using the beat clock
|
||||
float beatRatio = (ratio > 0) ? (1.0f / ratio) : 0.0f;
|
||||
|
||||
if (!beatsMod)
|
||||
beatClock->calculatePhase(beats * beatRatio, phases.data());
|
||||
beatClock.calculatePhase(beats * beatRatio, phases.data());
|
||||
else {
|
||||
auto temp = bufferPool.getBuffer(numFrames);
|
||||
if (!temp) {
|
||||
ASSERTFALSE;
|
||||
beatClock->calculatePhase(beats * beatRatio, phases.data());
|
||||
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());
|
||||
beatClock.calculatePhaseModulated(temp->data(), phases.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// generate using the frequency
|
||||
if (!freqMod) {
|
||||
float incr = ratio * samplePeriod * baseFreq;
|
||||
for (size_t i = 0; i < numFrames; ++i) {
|
||||
phases[i] = phase;
|
||||
float incr = ratio * samplePeriod * baseFreq;
|
||||
phase = wrapPhase(phase + incr);
|
||||
}
|
||||
}
|
||||
|
|
@ -372,13 +376,16 @@ void LFO::generatePhase(unsigned nth, absl::Span<float> phases)
|
|||
phase = wrapPhase(phase + incr);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// apply phase offsets
|
||||
for (size_t i = 0; i < numFrames; ++i) {
|
||||
float withOffset = phases[i] + phaseOffset;
|
||||
withOffset -= (int)withOffset;
|
||||
phases[i] = withOffset;
|
||||
if (!phaseMod) {
|
||||
for (size_t i = 0; i < numFrames; ++i)
|
||||
phases[i] = wrapPhase(phases[i] + phaseOffset);
|
||||
} else {
|
||||
for (size_t i = 0; i < numFrames; ++i)
|
||||
phases[i] = wrapPhase(phases[i] + phaseOffset + phaseMod[i]);
|
||||
}
|
||||
|
||||
impl.subPhases_[nth] = phase;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@
|
|||
#include <memory>
|
||||
|
||||
namespace sfz {
|
||||
class BufferPool;
|
||||
class BeatClock;
|
||||
class ModMatrix;
|
||||
struct Resources;
|
||||
struct Region;
|
||||
|
||||
enum class LFOWave : int;
|
||||
|
|
@ -54,10 +52,7 @@ struct LFODescription;
|
|||
|
||||
class LFO {
|
||||
public:
|
||||
explicit LFO(
|
||||
BufferPool& bufferPool,
|
||||
BeatClock* beatClock = nullptr,
|
||||
ModMatrix* modMatrix = nullptr);
|
||||
explicit LFO(Resources& resources);
|
||||
~LFO();
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "Defaults.h"
|
||||
#include "CCMap.h"
|
||||
#include "modulations/ModKey.h"
|
||||
#include <absl/types/optional.h>
|
||||
#include <vector>
|
||||
|
|
@ -19,8 +20,11 @@ struct LFODescription {
|
|||
float freq { Default::lfoFreq }; // lfoN_freq
|
||||
float beats { Default::lfoBeats }; // lfoN_beats
|
||||
float phase0 { Default::lfoPhase }; // lfoN_phase
|
||||
CCMap<float> phaseCC { Default::lfoPhaseMod }; // lfoN_phase_cc
|
||||
float delay { Default::lfoDelay }; // lfoN_delay
|
||||
CCMap<float> delayCC { Default::lfoDelayMod }; // lfoN_phase_cc
|
||||
float fade { Default::lfoFade }; // lfoN_fade
|
||||
CCMap<float> fadeCC { Default::lfoFadeMod }; // lfoN_phase_cc
|
||||
unsigned count { Default::lfoCount }; // lfoN_count
|
||||
struct Sub {
|
||||
LFOWave wave { Default::lfoWave }; // lfoN_wave[X]
|
||||
|
|
@ -37,6 +41,7 @@ struct LFODescription {
|
|||
// modulations
|
||||
ModKey beatsKey;
|
||||
ModKey freqKey;
|
||||
ModKey phaseKey;
|
||||
};
|
||||
|
||||
} // namespace sfz
|
||||
|
|
|
|||
|
|
@ -1140,6 +1140,7 @@ bool sfz::Region::parseLFOOpcodeV2(const Opcode& opcode)
|
|||
//
|
||||
lfo.beatsKey = ModKey::createNXYZ(ModId::LFOBeats, id, lfoNumber);
|
||||
lfo.freqKey = ModKey::createNXYZ(ModId::LFOFrequency, id, lfoNumber);
|
||||
lfo.phaseKey = ModKey::createNXYZ(ModId::LFOPhase, id, lfoNumber);
|
||||
|
||||
//
|
||||
auto getOrCreateLFOStep = [&opcode, &lfo]() -> float* {
|
||||
|
|
@ -1189,12 +1190,27 @@ bool sfz::Region::parseLFOOpcodeV2(const Opcode& opcode)
|
|||
case hash("lfo&_phase"):
|
||||
lfo.phase0 = opcode.read(Default::lfoPhase);
|
||||
break;
|
||||
case_any_ccN("lfo&_phase"):
|
||||
processGenericCc(opcode, Default::lfoPhaseMod, ModKey::createNXYZ(ModId::LFOPhase, id, lfoNumber));
|
||||
break;
|
||||
case hash("lfo&_delay"):
|
||||
lfo.delay = opcode.read(Default::lfoDelay);
|
||||
break;
|
||||
case hash("lfo&_delay_oncc&"):
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
|
||||
lfo.delayCC[opcode.parameters.back()] = opcode.read(Default::lfoDelayMod);
|
||||
break;
|
||||
case hash("lfo&_fade"):
|
||||
lfo.fade = opcode.read(Default::lfoFade);
|
||||
break;
|
||||
case hash("lfo&_fade_oncc&"):
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
|
||||
lfo.fadeCC[opcode.parameters.back()] = opcode.read(Default::lfoFadeMod);
|
||||
break;
|
||||
case hash("lfo&_count"):
|
||||
lfo.count = opcode.read(Default::lfoCount);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1677,7 +1677,7 @@ void Voice::setMaxLFOsPerVoice(size_t numLFOs)
|
|||
impl.lfos_.resize(numLFOs);
|
||||
|
||||
for (size_t i = 0; i < numLFOs; ++i) {
|
||||
auto lfo = absl::make_unique<LFO>(resources.bufferPool, &resources.beatClock, &resources.modMatrix);
|
||||
auto lfo = absl::make_unique<LFO>(resources);
|
||||
lfo->setSampleRate(impl.sampleRate_);
|
||||
impl.lfos_[i] = std::move(lfo);
|
||||
}
|
||||
|
|
@ -1718,7 +1718,7 @@ void Voice::setAmplitudeLFOEnabledPerVoice(bool haveAmplitudeLFO)
|
|||
Impl& impl = *impl_;
|
||||
Resources& res = impl.resources_;
|
||||
if (haveAmplitudeLFO) {
|
||||
LFO* lfo = new LFO(res.bufferPool, &res.beatClock, &res.modMatrix);
|
||||
LFO* lfo = new LFO(res);
|
||||
impl.lfoAmplitude_.reset(lfo);
|
||||
lfo->setSampleRate(impl.sampleRate_);
|
||||
}
|
||||
|
|
@ -1731,7 +1731,7 @@ void Voice::setPitchLFOEnabledPerVoice(bool havePitchLFO)
|
|||
Impl& impl = *impl_;
|
||||
Resources& res = impl.resources_;
|
||||
if (havePitchLFO) {
|
||||
LFO* lfo = new LFO(res.bufferPool, &res.beatClock, &res.modMatrix);
|
||||
LFO* lfo = new LFO(res);
|
||||
impl.lfoPitch_.reset(lfo);
|
||||
lfo->setSampleRate(impl.sampleRate_);
|
||||
}
|
||||
|
|
@ -1744,7 +1744,7 @@ void Voice::setFilterLFOEnabledPerVoice(bool haveFilterLFO)
|
|||
Impl& impl = *impl_;
|
||||
Resources& res = impl.resources_;
|
||||
if (haveFilterLFO) {
|
||||
LFO* lfo = new LFO(res.bufferPool, &res.beatClock, &res.modMatrix);
|
||||
LFO* lfo = new LFO(res);
|
||||
impl.lfoFilter_.reset(lfo);
|
||||
lfo->setSampleRate(impl.sampleRate_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ int ModIds::flags(ModId id) noexcept
|
|||
return kModIsPerVoice|kModIsAdditive;
|
||||
case ModId::LFOBeats:
|
||||
return kModIsPerVoice|kModIsAdditive;
|
||||
case ModId::LFOPhase:
|
||||
return kModIsPerVoice|kModIsAdditive;
|
||||
|
||||
// unknown
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ enum class ModId : int {
|
|||
FilLFOFrequency,
|
||||
LFOFrequency,
|
||||
LFOBeats,
|
||||
LFOPhase,
|
||||
|
||||
_TargetsEnd,
|
||||
// [/targets] --------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -164,6 +164,8 @@ std::string ModKey::toString() const
|
|||
return absl::StrCat("LFOFrequency {", region_.number(), ", N=", 1 + params_.N, "}");
|
||||
case ModId::LFOBeats:
|
||||
return absl::StrCat("LFOBeats {", region_.number(), ", N=", 1 + params_.N, "}");
|
||||
case ModId::LFOPhase:
|
||||
return absl::StrCat("LFOPhase {", region_.number(), ", N=", 1 + params_.N, "}");
|
||||
|
||||
default:
|
||||
return {};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ static bool computeLFO(DataPoints& dp, const fs::path& sfzPath, double sampleRat
|
|||
std::vector<std::unique_ptr<sfz::LFO>> lfos(numLfos);
|
||||
|
||||
for (size_t l = 0; l < numLfos; ++l) {
|
||||
sfz::LFO* lfo = new sfz::LFO(resources.bufferPool);
|
||||
sfz::LFO* lfo = new sfz::LFO(resources);
|
||||
lfos[l].reset(lfo);
|
||||
lfo->setSampleRate(sampleRate);
|
||||
lfo->configure(&desc[l]);
|
||||
|
|
|
|||
|
|
@ -408,3 +408,24 @@ TEST_CASE("[Modulations] EG v1 CC connections")
|
|||
R"("FilterEG {1}" -> "FilterCutoff {1, N=1}")",
|
||||
}, 2));
|
||||
}
|
||||
|
||||
TEST_CASE("[Modulations] LFO CC connections")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.loadSfzString("/modulation.sfz", R"(
|
||||
<region> sample=*sine
|
||||
lfo1_freq=2 lfo1_freq_cc1=0.1 lfo1_volume=0.5
|
||||
lfo2_freq=0.1 lfo2_freq_cc1=2 lfo2_freq_smoothcc1=10 lfo2_freq_stepcc1=0.2 lfo2_freq_curvecc1=1 lfo2_pitch=1200
|
||||
lfo3_freq=0.1 lfo3_phase_cc1=2 lfo3_phase_smoothcc1=10 lfo3_phase_stepcc1=0.2 lfo3_phase_curvecc1=1 lfo3_amplitude=50
|
||||
)");
|
||||
|
||||
const std::string graph = synth.getResources().modMatrix.toDotGraph();
|
||||
REQUIRE(graph == createDefaultGraph({
|
||||
R"("LFO 1 {0}" -> "Volume {0}")",
|
||||
R"("LFO 2 {0}" -> "Pitch {0}")",
|
||||
R"("LFO 3 {0}" -> "Amplitude {0}")",
|
||||
R"("Controller 1 {curve=0, smooth=0, step=0}" -> "LFOFrequency {0, N=1}")",
|
||||
R"("Controller 1 {curve=1, smooth=10, step=0.1}" -> "LFOFrequency {0, N=2}")",
|
||||
R"("Controller 1 {curve=1, smooth=10, step=0.1}" -> "LFOPhase {0, N=3}")",
|
||||
}, 1));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue