diff --git a/dpf.mk b/dpf.mk index 4060db8d4..ec3038fd2 100644 --- a/dpf.mk +++ b/dpf.mk @@ -1,3 +1,4 @@ + # # A build file to help using sfizz with the DISTRHO Plugin Framework (DPF) # ------------------------------------------------------------------------ @@ -65,6 +66,7 @@ SFIZZ_SOURCES = \ src/sfizz/modulations/ModKeyHash.cpp \ src/sfizz/modulations/ModMatrix.cpp \ src/sfizz/modulations/sources/Controller.cpp \ + src/sfizz/modulations/sources/LFO.cpp \ src/sfizz/effects/Compressor.cpp \ src/sfizz/effects/Disto.cpp \ src/sfizz/effects/Eq.cpp \ @@ -91,6 +93,8 @@ SFIZZ_SOURCES = \ src/sfizz/FilterPool.cpp \ src/sfizz/FloatEnvelopes.cpp \ src/sfizz/Logger.cpp \ + src/sfizz/LFO.cpp \ + src/sfizz/LFODescription.cpp \ src/sfizz/MidiState.cpp \ src/sfizz/OpcodeCleanup.cpp \ src/sfizz/Opcode.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a11991577..63cdd25ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ set (SFIZZ_HEADERS sfizz/modulations/ModMatrix.h sfizz/modulations/ModGenerator.h sfizz/modulations/sources/Controller.h + sfizz/modulations/sources/LFO.h sfizz/effects/impl/ResonantArray.h sfizz/effects/impl/ResonantArrayAVX.h sfizz/effects/impl/ResonantArraySSE.h @@ -70,6 +71,8 @@ set (SFIZZ_HEADERS sfizz/Interpolators.h sfizz/Interpolators.hpp sfizz/Logger.h + sfizz/LFO.h + sfizz/LFODescription.h sfizz/MathHelpers.h sfizz/MidiState.h sfizz/ModifierHelpers.h @@ -133,11 +136,14 @@ set (SFIZZ_SOURCES sfizz/RTSemaphore.cpp sfizz/Panning.cpp sfizz/Effects.cpp + sfizz/LFO.cpp + sfizz/LFODescription.cpp sfizz/modulations/ModId.cpp sfizz/modulations/ModKey.cpp sfizz/modulations/ModKeyHash.cpp sfizz/modulations/ModMatrix.cpp sfizz/modulations/sources/Controller.cpp + sfizz/modulations/sources/LFO.cpp sfizz/effects/Nothing.cpp sfizz/effects/Filter.cpp sfizz/effects/Eq.cpp diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 03f7f3784..d3592bc57 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -64,6 +64,8 @@ namespace config { constexpr unsigned int defaultAlignment { 16 }; constexpr int filtersInPool { maxVoices * 2 }; constexpr int excessFileFrames { 8 }; + constexpr int maxLFOSubs { 8 }; + constexpr int maxLFOSteps { 128 }; /** * @brief The threshold for age stealing. * In percentage of the voice's max age. diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 8be2fc96e..c30d4192a 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -207,6 +207,22 @@ namespace Default constexpr int bendStep { 1 }; constexpr uint8_t bendSmooth { 0 }; + // Modulation: LFO + constexpr int numLFOs { 4 }; + constexpr int numLFOSubs { 2 }; + constexpr int numLFOSteps { 8 }; + constexpr Range lfoFreqRange { 0.0, 100.0 }; + constexpr Range lfoPhaseRange { 0.0, 360.0 }; + constexpr Range lfoDelayRange { 0.0, 30.0 }; + constexpr Range lfoFadeRange { 0.0, 30.0 }; + constexpr Range lfoCountRange { 0, 1000 }; + constexpr Range lfoStepsRange { 0, static_cast(config::maxLFOSteps) }; + constexpr Range lfoStepXRange { -100.0, 100.0 }; + constexpr Range lfoWaveRange { 0, 15 }; + constexpr Range lfoOffsetRange { -1.0, 1.0 }; + constexpr Range lfoRatioRange { 0.0, 100.0 }; + constexpr Range lfoScaleRange { 0.0, 1.0 }; + // Envelope generators constexpr float attack { 0 }; constexpr float decay { 0 }; diff --git a/src/sfizz/LFO.cpp b/src/sfizz/LFO.cpp new file mode 100644 index 000000000..33d31f704 --- /dev/null +++ b/src/sfizz/LFO.cpp @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "LFO.h" +#include "LFODescription.h" +#include "MathHelpers.h" +#include "SIMDHelpers.h" +#include "Config.h" +#include +#include +#include + +namespace sfz { + +struct LFO::Impl { + float sampleRate_ = 0; + + // control + const LFODescription* desc_ = nullptr; + + // state + size_t delayFramesLeft_ = 0; + float fadePosition_ = 0; + std::array subPhases_ {{}}; + std::array sampleHoldMem_ {{}}; +}; + +LFO::LFO() + : impl_(new Impl) +{ + impl_->sampleRate_ = config::defaultSampleRate; + impl_->desc_ = &LFODescription::getDefault(); +} + +LFO::~LFO() +{ +} + +void LFO::setSampleRate(double sampleRate) +{ + impl_->sampleRate_ = sampleRate; +} + +void LFO::configure(const LFODescription* desc) +{ + impl_->desc_ = desc ? desc : &LFODescription::getDefault(); +} + +void LFO::start() +{ + Impl& impl = *impl_; + const LFODescription& desc = *impl.desc_; + const float sampleRate = impl.sampleRate_; + + impl.subPhases_.fill(desc.phase0); + impl.sampleHoldMem_.fill(0.0f); + + const float delay = desc.delay; + impl.delayFramesLeft_ = (delay > 0) ? static_cast(std::ceil(sampleRate * delay)) : 0u; + + impl.fadePosition_ = (desc.fade > 0) ? 0.0f : 1.0f; +} + +template <> +inline float LFO::eval(float phase) +{ + float y = -4 * phase + 2; + y = (phase < 0.25f) ? (4 * phase) : y; + y = (phase > 0.75f) ? (4 * phase - 4) : y; + return y; +} + +template <> +inline float LFO::eval(float phase) +{ + float x = phase + phase - 1; + return 4 * x * (1 - std::fabs(x)); +} + +template <> +inline float LFO::eval(float phase) +{ + return (phase < 0.75f) ? +1.0f : -1.0f; +} + +template <> +inline float LFO::eval(float phase) +{ + return (phase < 0.5f) ? +1.0f : -1.0f; +} + +template <> +inline float LFO::eval(float phase) +{ + return (phase < 0.25f) ? +1.0f : -1.0f; +} + +template <> +inline float LFO::eval(float phase) +{ + return (phase < 0.125f) ? +1.0f : -1.0f; +} + +template <> +inline float LFO::eval(float phase) +{ + return 2 * phase - 1; +} + +template <> +inline float LFO::eval(float phase) +{ + return 1 - 2 * phase; +} + +template +void LFO::processWave(unsigned nth, absl::Span out) +{ + Impl& impl = *impl_; + const LFODescription& desc = *impl.desc_; + const LFODescription::Sub& sub = desc.sub[nth]; + const size_t numFrames = out.size(); + + const float samplePeriod = 1.0f / impl.sampleRate_; + const float baseFreq = desc.freq; + const float offset = sub.offset; + const float ratio = sub.ratio; + const float scale = sub.scale; + float phase = impl.subPhases_[nth]; + + for (size_t i = 0; i < numFrames; ++i) { + out[i] += offset + scale * eval(phase); + + // TODO(jpc) lfoN_count: number of repetitions + + float incrPhase = ratio * samplePeriod * baseFreq; + phase += incrPhase; + int numWraps = (int)phase; + phase -= numWraps; + } + + impl.subPhases_[nth] = phase; +} + +template +void LFO::processSH(unsigned nth, absl::Span out) +{ + Impl& impl = *impl_; + const LFODescription& desc = *impl.desc_; + const LFODescription::Sub& sub = desc.sub[nth]; + const size_t numFrames = out.size(); + + const float samplePeriod = 1.0f / impl.sampleRate_; + const float baseFreq = desc.freq; + const float offset = sub.offset; + const float ratio = sub.ratio; + const float scale = sub.scale; + float sampleHoldValue = impl.sampleHoldMem_[nth]; + float phase = impl.subPhases_[nth]; + + for (size_t i = 0; i < numFrames; ++i) { + out[i] += offset + scale * sampleHoldValue; + + // TODO(jpc) lfoN_count: number of repetitions + + float incrPhase = ratio * samplePeriod * baseFreq; + + // value updates twice every period + bool updateValue = (int)(phase * 2.0) != (int)((phase + incrPhase) * 2.0); + + phase += incrPhase; + int numWraps = (int)phase; + phase -= numWraps; + + if (updateValue) { + std::uniform_real_distribution dist(-1.0f, +1.0f); + sampleHoldValue = dist(Random::randomGenerator); + } + } + + impl.subPhases_[nth] = phase; + impl.sampleHoldMem_[nth] = sampleHoldValue; +} + +void LFO::processSteps(absl::Span out) +{ + unsigned nth = 0; + Impl& impl = *impl_; + const LFODescription& desc = *impl.desc_; + const LFODescription::Sub& sub = desc.sub[nth]; + const size_t numFrames = out.size(); + + const LFODescription::StepSequence& seq = *desc.seq; + const float* steps = seq.steps.data(); + unsigned numSteps = seq.steps.size(); + + if (numSteps <= 0) + return; + + const float samplePeriod = 1.0f / impl.sampleRate_; + const float baseFreq = desc.freq; + const float offset = sub.offset; + const float ratio = sub.ratio; + const float scale = sub.scale; + float phase = impl.subPhases_[nth]; + + for (size_t i = 0; i < numFrames; ++i) { + float step = steps[static_cast(phase * numSteps)]; + out[i] += offset + scale * step; + + // TODO(jpc) lfoN_count: number of repetitions + + float incrPhase = ratio * samplePeriod * baseFreq; + phase += incrPhase; + int numWraps = (int)phase; + phase -= numWraps; + } + + impl.subPhases_[nth] = phase; +} + +void LFO::process(absl::Span out) +{ + Impl& impl = *impl_; + const LFODescription& desc = *impl.desc_; + size_t numFrames = out.size(); + + fill(out, 0.0f); + + size_t skipFrames = std::min(numFrames, impl.delayFramesLeft_); + if (skipFrames > 0) { + impl.delayFramesLeft_ -= skipFrames; + out.remove_prefix(skipFrames); + numFrames -= skipFrames; + } + + unsigned subno = 0; + const unsigned countSubs = desc.sub.size(); + + if (countSubs < 1) + return; + + if (desc.seq) { + processSteps(out); + ++subno; + } + + for (; subno < countSubs; ++subno) { + switch (desc.sub[subno].wave) { + case LFOWave::Triangle: + processWave(subno, out); + break; + case LFOWave::Sine: + processWave(subno, out); + break; + case LFOWave::Pulse75: + processWave(subno, out); + break; + case LFOWave::Square: + processWave(subno, out); + break; + case LFOWave::Pulse25: + processWave(subno, out); + break; + case LFOWave::Pulse12_5: + processWave(subno, out); + break; + case LFOWave::Ramp: + processWave(subno, out); + break; + case LFOWave::Saw: + processWave(subno, out); + break; + case LFOWave::RandomSH: + processSH(subno, out); + break; + } + } + + processFadeIn(out); +} + +void LFO::processFadeIn(absl::Span out) +{ + Impl& impl = *impl_; + const LFODescription& desc = *impl.desc_; + const float samplePeriod = 1.0f / impl.sampleRate_; + size_t numFrames = out.size(); + + float fadePosition = impl.fadePosition_; + if (fadePosition >= 1.0f) + return; + + const float fadeTime = desc.fade; + const float fadeStep = samplePeriod / fadeTime; + + for (size_t i = 0; i < numFrames && fadePosition < 1; ++i) { + out[i] *= fadePosition; + fadePosition = std::min(1.0f, fadePosition + fadeStep); + } + + impl.fadePosition_ = fadePosition; +} + +} // namespace sfz diff --git a/src/sfizz/LFO.h b/src/sfizz/LFO.h new file mode 100644 index 000000000..3d75cad3e --- /dev/null +++ b/src/sfizz/LFO.h @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include + +namespace sfz { + +enum class LFOWave : int; +struct LFODescription; + +/* + * General + + lfoN_freq: Base frequency - Allow modulations at A-rate + lfoN_phase: Initial phase + lfoN_delay: Delay + lfoN_fade: Time to fade-in + lfoN_count: Number of repetitions - not implemented in ARIA + lfoN_steps: Length of the step sequence - 1 to 128 + lfoN_steps_onccX: ??? TODO(jpc) seen in Rapture + lfoN_stepX: Value of the Xth step of the sequence - -100% to +100% + lfoN_stepX_onccY: ??? TODO(jpc) check this. override/modulate step in sequence? + + note: LFO evaluates between -1 to +1 + + note: make the step sequencer override the main wave when present. + subwaves are ARIA, step sequencer is Cakewalk, so do our own thing + which makes the most sense. + + * Subwaveforms + X: - #1/omitted: the main wave + - #2-#8: a subwave + + note: if there are gaps in subwaveforms, these subwaveforms which are gaps + will be initialized and processed. + + example: lfo1_ratio4=1.0 // instantiate implicitly the subs #2 and #3 + + lfoN_wave[X]: Wave + lfoN_offset[X]: DC offset - Add to LFO output; not affected by scale. + lfoN_ratio[X]: Sub ratio - Frequency = (Ratio * Base Frequency) + lfoN_scale[X]: Sub scale - Amplitude of sub +*/ + +class LFO { +public: + LFO(); + ~LFO(); + + /** + Sets the sample rate. + */ + void setSampleRate(double sampleRate); + + /** + Attach some control parameters to this LFO. + The control structure is owned by the caller. + */ + void configure(const LFODescription* desc); + + /** + Start processing a LFO as a region is triggered. + Prepares the delay, phases, fade-in, etc.. + */ + void start(); + + /** + Process a cycle of the oscillator. + + TODO(jpc) frequency modulations + */ + void process(absl::Span out); + +private: + /** + Evaluate the wave at a given phase. + Phase must be in the range 0 to 1 excluded. + */ + template + static float eval(float phase); + + /** + Process the nth subwaveform, adding to the buffer. + + This definition is duplicated per each wave, a strategy to avoid a switch + on wave type inside the frame loop. + */ + template + void processWave(unsigned nth, absl::Span out); + + /** + Process a sample-and-hold subwaveform, adding to the buffer. + */ + template + void processSH(unsigned nth, absl::Span out); + + /** + Process the step sequencer, adding to the buffer. + */ + void processSteps(absl::Span out); + + /** + Process the fade in gain, and apply it to the buffer. + */ + void processFadeIn(absl::Span out); + +private: + struct Impl; + std::unique_ptr impl_; +}; + +} // namespace sfz diff --git a/src/sfizz/LFODescription.cpp b/src/sfizz/LFODescription.cpp new file mode 100644 index 000000000..4e846ef23 --- /dev/null +++ b/src/sfizz/LFODescription.cpp @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "LFODescription.h" + +namespace sfz { + +LFODescription::LFODescription() +{ + sub.resize(1); +} + +LFODescription::~LFODescription() +{ +} + +const LFODescription& LFODescription::getDefault() +{ + static LFODescription desc = []() -> LFODescription + { + LFODescription desc; + desc.sub.resize(1); + return desc; + }(); + return desc; +} + +} // namespace sfz diff --git a/src/sfizz/LFODescription.h b/src/sfizz/LFODescription.h new file mode 100644 index 000000000..8f5344c97 --- /dev/null +++ b/src/sfizz/LFODescription.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include + +namespace sfz { + +enum class LFOWave : int { + Triangle, + Sine, + Pulse75, + Square, + Pulse25, + Pulse12_5, + Ramp, + Saw, + // ARIA extra + RandomSH = 12, +}; + +struct LFODescription { + LFODescription(); + ~LFODescription(); + static const LFODescription& getDefault(); + float freq = 0; // lfoN_freq + float phase0 = 0; // lfoN_phase + float delay = 0; // lfoN_delay + float fade = 0; // lfoN_fade + unsigned count = 0; // lfoN_count + struct Sub { + LFOWave wave = LFOWave::Triangle; // lfoN_wave[X] + float offset = 0; // lfoN_offset[X] + float ratio = 1; // lfoN_ratio[X] + float scale = 1; // lfoN_scale[X] + }; + struct StepSequence { + std::vector steps {}; // lfoN_stepX - normalized to unity + }; + absl::optional seq; + std::vector sub; +}; + +} // namespace sfz diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 25d38f40e..83086730e 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -786,6 +786,228 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) setValueFromOpcode(opcode, bendSmooth, Default::smoothCCRange); break; + // Modulation: LFO + case hash("lfo&_freq"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + setValueFromOpcode(opcode, lfos[lfoNumber - 1].freq, Default::lfoFreqRange); + } + break; + case hash("lfo&_phase"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + 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; + } + } + break; + case hash("lfo&_delay"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + setValueFromOpcode(opcode, lfos[lfoNumber - 1].delay, Default::lfoDelayRange); + } + break; + case hash("lfo&_fade"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + setValueFromOpcode(opcode, lfos[lfoNumber - 1].fade, Default::lfoFadeRange); + } + break; + case hash("lfo&_count"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + setValueFromOpcode(opcode, lfos[lfoNumber - 1].count, Default::lfoCountRange); + } + break; + case hash("lfo&_steps"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + if (auto value = readOpcode(opcode.value, Default::lfoStepsRange)) { + if (!lfos[lfoNumber - 1].seq) + lfos[lfoNumber - 1].seq = LFODescription::StepSequence(); + lfos[lfoNumber - 1].seq->steps.resize(*value); + } + } + break; + case hash("lfo&_step&"): + { + const auto lfoNumber = opcode.parameters.front(); + const auto stepNumber = opcode.parameters[1]; + if (lfoNumber == 0 || stepNumber == 0 || stepNumber > config::maxLFOSteps) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + if (auto value = readOpcode(opcode.value, Default::lfoStepXRange)) { + if (!lfos[lfoNumber - 1].seq) + lfos[lfoNumber - 1].seq = LFODescription::StepSequence(); + if (!extendIfNecessary(lfos[lfoNumber - 1].seq->steps, stepNumber, Default::numLFOSteps)) + return false; + lfos[lfoNumber - 1].seq->steps[stepNumber - 1] = *value * 0.01f; + } + } + break; + case hash("lfo&_wave&"): // also lfo&_wave + { + const auto lfoNumber = opcode.parameters.front(); + const auto subNumber = opcode.parameters[1]; + if (lfoNumber == 0 || subNumber == 0 || subNumber > config::maxLFOSubs) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + if (auto value = readOpcode(opcode.value, Default::lfoWaveRange)) { + if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) + return false; + lfos[lfoNumber - 1].sub[subNumber - 1].wave = static_cast(*value); + } + } + break; + case hash("lfo&_offset&"): // also lfo&_offset + { + const auto lfoNumber = opcode.parameters.front(); + const auto subNumber = opcode.parameters[1]; + if (lfoNumber == 0 || subNumber == 0 || subNumber > config::maxLFOSubs) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + if (auto value = readOpcode(opcode.value, Default::lfoOffsetRange)) { + if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) + return false; + lfos[lfoNumber - 1].sub[subNumber - 1].offset = *value; + } + } + break; + case hash("lfo&_ratio&"): // also lfo&_ratio + { + const auto lfoNumber = opcode.parameters.front(); + const auto subNumber = opcode.parameters[1]; + if (lfoNumber == 0 || subNumber == 0 || subNumber > config::maxLFOSubs) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + if (auto value = readOpcode(opcode.value, Default::lfoRatioRange)) { + if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) + return false; + lfos[lfoNumber - 1].sub[subNumber - 1].ratio = *value; + } + } + break; + case hash("lfo&_scale&"): // also lfo&_scale + { + const auto lfoNumber = opcode.parameters.front(); + const auto subNumber = opcode.parameters[1]; + if (lfoNumber == 0 || subNumber == 0 || subNumber > config::maxLFOSubs) + return false; + if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs)) + return false; + if (auto value = readOpcode(opcode.value, Default::lfoScaleRange)) { + if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs)) + return false; + lfos[lfoNumber - 1].sub[subNumber - 1].scale = *value; + } + } + break; + + // Modulation: LFO (targets) + case hash("lfo&_amplitude"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) { + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id); + getOrCreateConnection(source, target).sourceDepth = *value; + } + } + break; + case hash("lfo&_pan"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (auto value = readOpcode(opcode.value, Default::panCCRange)) { + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Pan, id); + getOrCreateConnection(source, target).sourceDepth = *value; + } + } + break; + case hash("lfo&_width"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (auto value = readOpcode(opcode.value, Default::widthCCRange)) { + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Width, id); + getOrCreateConnection(source, target).sourceDepth = *value; + } + } + break; + case hash("lfo&_position"): // sfizz extension + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (auto value = readOpcode(opcode.value, Default::positionCCRange)) { + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Position, id); + getOrCreateConnection(source, target).sourceDepth = *value; + } + } + break; + case hash("lfo&_pitch"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (auto value = readOpcode(opcode.value, Default::tuneCCRange)) { + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Pitch, id); + getOrCreateConnection(source, target).sourceDepth = *value; + } + } + break; + case hash("lfo&_volume"): + { + const auto lfoNumber = opcode.parameters.front(); + if (lfoNumber == 0) + return false; + if (auto value = readOpcode(opcode.value, Default::volumeCCRange)) { + const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1); + const ModKey target = ModKey::createNXYZ(ModId::Volume, id); + getOrCreateConnection(source, target).sourceDepth = *value; + } + } + break; + // Amplitude Envelope case hash("ampeg_attack"): setValueFromOpcode(opcode, amplitudeEG.attack, Default::egTimeRange); @@ -1324,3 +1546,22 @@ float sfz::Region::getBendInCents(float bend) const noexcept { return bend > 0.0f ? bend * static_cast(bendUp) : -bend * static_cast(bendDown); } + +sfz::Region::Connection& sfz::Region::getOrCreateConnection(const ModKey& source, const ModKey& target) +{ + auto pred = [&source, &target](const Connection& c) + { + return c.source == source && c.target == target; + }; + + auto it = std::find_if(connections.begin(), connections.end(), pred); + if (it != connections.end()) + return *it; + + sfz::Region::Connection c; + c.source = source; + c.target = target; + + connections.push_back(c); + return connections.back(); +} diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index f226a3319..a4c4f291b 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -12,6 +12,7 @@ #include "EGDescription.h" #include "EQDescription.h" #include "FilterDescription.h" +#include "LFODescription.h" #include "Opcode.h" #include "AudioBuffer.h" #include "MidiState.h" @@ -364,6 +365,9 @@ struct Region { EGDescription pitchEG; EGDescription filterEG; + // LFOs + std::vector lfos; + bool hasStereoSample { false }; // Effects @@ -379,6 +383,7 @@ struct Region { float sourceDepth = 1.0f; }; std::vector connections; + Connection& getOrCreateConnection(const ModKey& source, const ModKey& target); // Parent RegionSet* parent { nullptr }; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 71f9f500f..8a268b641 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -16,6 +16,7 @@ #include "modulations/ModKey.h" #include "modulations/ModId.h" #include "modulations/sources/Controller.h" +#include "modulations/sources/LFO.h" #include "pugixml.hpp" #include "absl/algorithm/container.h" #include "absl/memory/memory.h" @@ -42,6 +43,7 @@ sfz::Synth::Synth(int numVoices) // modulation sources genController.reset(new ControllerSource(resources)); + genLFO.reset(new LFOSource(*this)); } sfz::Synth::~Synth() @@ -452,6 +454,7 @@ void sfz::Synth::finalizeSfzLoad() size_t maxFilters { 0 }; size_t maxEQs { 0 }; + size_t maxLFOs { 0 }; while (currentRegionIndex < currentRegionCount) { auto region = regions[currentRegionIndex].get(); @@ -562,6 +565,7 @@ void sfz::Synth::finalizeSfzLoad() region->registerTempo(2.0f); maxFilters = max(maxFilters, region->filters.size()); maxEQs = max(maxEQs, region->equalizers.size()); + maxLFOs = max(maxLFOs, region->lfos.size()); ++currentRegionIndex; } @@ -571,6 +575,7 @@ void sfz::Synth::finalizeSfzLoad() settingsPerVoice.maxFilters = maxFilters; settingsPerVoice.maxEQs = maxEQs; + settingsPerVoice.maxLFOs = maxLFOs; applySettingsPerVoice(); @@ -1346,6 +1351,7 @@ void sfz::Synth::applySettingsPerVoice() for (auto& voice : voices) { voice->setMaxFiltersPerVoice(settingsPerVoice.maxFilters); voice->setMaxEQsPerVoice(settingsPerVoice.maxEQs); + voice->setMaxLFOsPerVoice(settingsPerVoice.maxLFOs); } } @@ -1361,6 +1367,9 @@ void sfz::Synth::setupModMatrix() case ModId::Controller: gen = genController.get(); break; + case ModId::LFO: + gen = genLFO.get(); + break; default: DBG("[sfizz] Have unknown type of source generator"); break; diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 8133b676e..5b49ddd1e 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -27,6 +27,7 @@ namespace sfz { class ControllerSource; +class LFOSource; /** * @brief This class is the core of the sfizz library. In C++ it is the main point @@ -767,11 +768,13 @@ class Synth final : public Voice::StateListener, public Parser::Listener { // Modulation source generators std::unique_ptr genController; + std::unique_ptr genLFO; // Settings per voice struct SettingsPerVoice { size_t maxFilters { 0 }; size_t maxEQs { 0 }; + size_t maxLFOs { 0 }; }; SettingsPerVoice settingsPerVoice; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 97fa044da..03194ecec 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -12,6 +12,7 @@ #include "SIMDHelpers.h" #include "Panning.h" #include "SfzHelpers.h" +#include "LFO.h" #include "modulations/ModId.h" #include "modulations/ModKey.h" #include "modulations/ModMatrix.h" @@ -34,6 +35,10 @@ sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources) filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); } +sfz::Voice::~Voice() +{ +} + void sfz::Voice::startVoice(Region* region, int delay, int number, float value, sfz::Voice::TriggerType triggerType) noexcept { ASSERT(value >= 0.0f && value <= 1.0f); @@ -235,6 +240,9 @@ void sfz::Voice::setSampleRate(float sampleRate) noexcept for (WavetableOscillator& osc : waveOscillators) osc.init(sampleRate); + + for (auto& lfo : lfos) + lfo->setSampleRate(sampleRate); } void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept @@ -773,6 +781,17 @@ void sfz::Voice::setMaxEQsPerVoice(size_t numFilters) equalizers.reserve(numFilters); } +void sfz::Voice::setMaxLFOsPerVoice(size_t numLFOs) +{ + lfos.resize(numLFOs); + + for (size_t i = 0; i < numLFOs; ++i) { + auto lfo = absl::make_unique(); + lfo->setSampleRate(sampleRate); + lfos[i] = std::move(lfo); + } +} + void sfz::Voice::setupOscillatorUnison() { int m = region->oscillatorMulti; diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 52a7f0c8d..17913bb31 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -22,6 +22,7 @@ namespace sfz { enum InterpolatorModel : int; +class LFO; /** * @brief The SFZ voice are the polyphony holders. They get activated by the synth * and tasked to play a given region until the end, stopping on note-offs, off-groups @@ -38,6 +39,9 @@ class Voice { * @param midiState */ Voice(int voiceNumber, Resources& resources); + + ~Voice(); + enum class TriggerType { NoteOn, NoteOff, @@ -270,6 +274,12 @@ class Voice { * @return */ const Region* getRegion() const noexcept { return region; } + /** + * @brief Get the LFO designated by the given index + * + * @param index + */ + LFO* getLFO(size_t index) { return lfos[index].get(); } /** * @brief Set the max number of filters per voice * @@ -279,9 +289,15 @@ class Voice { /** * @brief Set the max number of EQs per voice * - * @param numFilters + * @param numEQs */ void setMaxEQsPerVoice(size_t numEQs); + /** + * @brief Set the max number of LFOs per voice + * + * @param numLFOs + */ + void setMaxLFOsPerVoice(size_t numLFOs); /** * @brief Release the voice after a given delay * @@ -433,6 +449,7 @@ class Voice { std::vector filters; std::vector equalizers; + std::vector> lfos; ADSREnvelope egEnvelope; float bendStepFactor { centsFactor(1) }; diff --git a/src/sfizz/modulations/sources/LFO.cpp b/src/sfizz/modulations/sources/LFO.cpp new file mode 100644 index 000000000..0f6048982 --- /dev/null +++ b/src/sfizz/modulations/sources/LFO.cpp @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "LFO.h" +#include "../../LFO.h" +#include "../../Synth.h" +#include "../../Voice.h" +#include "../../SIMDHelpers.h" +#include "../../Config.h" +#include "../../Debug.h" + +namespace sfz { + +LFOSource::LFOSource(Synth &synth) + : synth_(&synth) +{ +} + +void LFOSource::init(const ModKey& sourceKey, NumericId voiceId) +{ + Synth& synth = *synth_; + unsigned lfoIndex = sourceKey.parameters().N; + + Voice* voice = synth.getVoiceById(voiceId); + if (!voice) { + ASSERTFALSE; + return; + } + + const Region* region = voice->getRegion(); + if (lfoIndex >= region->lfos.size()) { + ASSERTFALSE; + return; + } + + LFO* lfo = voice->getLFO(lfoIndex); + lfo->configure(®ion->lfos[lfoIndex]); + lfo->start(); +} + +void LFOSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) +{ + Synth& synth = *synth_; + const unsigned lfoIndex = sourceKey.parameters().N; + + Voice* voice = synth.getVoiceById(voiceId); + if (!voice) { + ASSERTFALSE; + fill(buffer, 0.0f); + return; + } + + const Region* region = voice->getRegion(); + if (lfoIndex >= region->lfos.size()) { + ASSERTFALSE; + fill(buffer, 0.0f); + return; + } + + LFO* lfo = voice->getLFO(lfoIndex); + lfo->process(buffer); +} + +} // namespace sfz diff --git a/src/sfizz/modulations/sources/LFO.h b/src/sfizz/modulations/sources/LFO.h new file mode 100644 index 000000000..83a1e30f0 --- /dev/null +++ b/src/sfizz/modulations/sources/LFO.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "../ModGenerator.h" + +namespace sfz { +class Synth; + +class LFOSource : public ModGenerator { +public: + explicit LFOSource(Synth &synth); + void init(const ModKey& sourceKey, NumericId voiceId) override; + void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; + +private: + Synth* synth_ = nullptr; +}; + +} // namespace sfz diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4018edee0..b9926ab96 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,9 @@ set(SFIZZ_TEST_SOURCES TuningT.cpp ConcurrencyT.cpp ModulationsT.cpp + LFOT.cpp + DataHelpers.h + DataHelpers.cpp ) add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES}) @@ -94,6 +97,9 @@ target_link_libraries(sfizz_plot_curve PRIVATE sfizz::sfizz) add_executable(sfizz_plot_wavetables PlotWavetables.cpp) target_link_libraries(sfizz_plot_wavetables PRIVATE sfizz::sfizz) +add_executable(sfizz_plot_lfo PlotLFO.cpp) +target_link_libraries(sfizz_plot_lfo PRIVATE sfizz::sfizz) + add_executable(sfizz_file_instrument FileInstrument.cpp) target_link_libraries(sfizz_file_instrument PRIVATE sfizz::sfizz) diff --git a/tests/DataHelpers.cpp b/tests/DataHelpers.cpp new file mode 100644 index 000000000..1f0bd949f --- /dev/null +++ b/tests/DataHelpers.cpp @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "DataHelpers.h" +#include +#include +#include +#include +#include + +void load_txt(DataPoints& dp, std::istream& in) +{ + struct RawValue { + bool rowJump; + float value; + }; + + std::vector raw; + raw.reserve(1024); + + // read raw value data + { + std::string line; + line.reserve(256); + + while (std::getline(in, line)) { + size_t commentPos = line.find('#'); + if (commentPos == line.npos) + line = line.substr(0, commentPos); + + std::istringstream lineIn(line); + + RawValue rv; + rv.rowJump = true; + while (lineIn >> rv.value) { + raw.push_back(rv); + rv.rowJump = false; + } + } + } + + if (raw.empty()) { + dp.rows = 0; + dp.cols = 0; + dp.data.reset(); + return; + } + + // count rows and columns + size_t numRows = 0; + size_t numCols = 0; + { + size_t c = 0; + for (const RawValue& rv : raw) { + if (!rv.rowJump) + ++c; + else { + numRows += c != 0; + c = 1; + } + numCols = std::max(numCols, c); + } + numRows += c != 0; + } + + // fill the data + float* data = new float[numRows * numCols]; + dp.rows = numRows; + dp.cols = numCols; + dp.data.reset(data); + for (size_t i = 0, j = 0; i < numRows * numCols; ) { + size_t c = 1; + data[i++] = raw[j++].value; + for (; j < raw.size() && !raw[j].rowJump; ++c) + data[i++] = raw[j++].value; + for ( ; c < numCols; ++c) + data[i++] = 0.0f; + } +} + +bool load_txt_file(DataPoints& dp, const fs::path& path) +{ + fs::ifstream in(path); + load_txt(dp, in); + return !in.bad(); +} diff --git a/tests/DataHelpers.h b/tests/DataHelpers.h new file mode 100644 index 000000000..7f337a494 --- /dev/null +++ b/tests/DataHelpers.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include +#include +#include + +struct DataPoints { + size_t rows = 0; + size_t cols = 0; + std::unique_ptr data; + + const float& operator()(size_t r, size_t c) const noexcept + { + return data[r * cols + c]; + } + float& operator()(size_t r, size_t c) noexcept + { + return data[r * cols + c]; + } +}; + +void load_txt(DataPoints& dp, std::istream& in); +bool load_txt_file(DataPoints& dp, const fs::path& path); diff --git a/tests/LFOT.cpp b/tests/LFOT.cpp new file mode 100644 index 000000000..84c2795d5 --- /dev/null +++ b/tests/LFOT.cpp @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "DataHelpers.h" +#include "sfizz/Synth.h" +#include "sfizz/LFO.h" +#include "catch2/catch.hpp" + +static bool computeLFO(DataPoints& dp, const fs::path& sfzPath, double sampleRate, size_t numFrames) +{ + sfz::Synth synth; + + if (!synth.loadSfzFile(sfzPath)) + return false; + + if (synth.getNumRegions() != 1) + return false; + + const std::vector& desc = synth.getRegionView(0)->lfos; + size_t numLfos = desc.size(); + std::vector lfos(numLfos); + + for (size_t l = 0; l < numLfos; ++l) { + lfos[l].setSampleRate(sampleRate); + lfos[l].configure(&desc[l]); + } + + std::vector outputMemory(numLfos * numFrames); + + for (size_t l = 0; l < numLfos; ++l) { + lfos[l].start(); + } + + std::vector> lfoOutputs(numLfos); + for (size_t l = 0; l < numLfos; ++l) { + lfoOutputs[l] = absl::MakeSpan(&outputMemory[l * numFrames], numFrames); + lfos[l].process(lfoOutputs[l]); + } + + dp.rows = numFrames; + dp.cols = numLfos + 1; + dp.data.reset(new float[dp.rows * dp.cols]); + + for (size_t i = 0; i < numFrames; ++i) { + dp(i, 0) = i / sampleRate; + for (size_t l = 0; l < numLfos; ++l) + dp(i, 1 + l) = lfoOutputs[l][i]; + } + + return true; +} + +double meanSquareError(const float* a, const float* b, size_t count, size_t step) +{ + double sum = 0; + for (size_t i = 0; i < count; ++i) { + double diff = a[i * step] - b[i * step]; + sum += diff * diff; + } + return sum / count; +} + +static constexpr double mseThreshold = 1e-3; + +TEST_CASE("[LFO] Waves") +{ + DataPoints ref; + REQUIRE(load_txt_file(ref, "tests/lfo/lfo_waves_reference.dat")); + + DataPoints cur; + REQUIRE(computeLFO(cur, "tests/lfo/lfo_waves.sfz", 100.0, ref.rows)); + + REQUIRE(ref.rows == cur.rows); + REQUIRE(ref.cols == cur.cols); + + for (size_t l = 1; l < cur.cols; ++l) { + double mse = meanSquareError(&ref.data[l], &cur.data[l], ref.rows, ref.cols); + REQUIRE(mse < mseThreshold); + } +} + +TEST_CASE("[LFO] Subwave") +{ + DataPoints ref; + REQUIRE(load_txt_file(ref, "tests/lfo/lfo_subwave_reference.dat")); + + DataPoints cur; + REQUIRE(computeLFO(cur, "tests/lfo/lfo_subwave.sfz", 100.0, ref.rows)); + + REQUIRE(ref.rows == cur.rows); + REQUIRE(ref.cols == cur.cols); + + for (size_t l = 1; l < cur.cols; ++l) { + double mse = meanSquareError(&ref.data[l], &cur.data[l], ref.rows, ref.cols); + REQUIRE(mse < mseThreshold); + } +} + +TEST_CASE("[LFO] Fade and delay") +{ + DataPoints ref; + REQUIRE(load_txt_file(ref, "tests/lfo/lfo_fade_and_delay_reference.dat")); + + DataPoints cur; + REQUIRE(computeLFO(cur, "tests/lfo/lfo_fade_and_delay.sfz", 100.0, ref.rows)); + + REQUIRE(ref.rows == cur.rows); + REQUIRE(ref.cols == cur.cols); + + for (size_t l = 1; l < cur.cols; ++l) { + double mse = meanSquareError(&ref.data[l], &cur.data[l], ref.rows, ref.cols); + REQUIRE(mse < mseThreshold); + } +} diff --git a/tests/PlotLFO.cpp b/tests/PlotLFO.cpp new file mode 100644 index 000000000..06ccc5531 --- /dev/null +++ b/tests/PlotLFO.cpp @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/** + This program generates the data file of a LFO output recorded for a fixed + duration. The file contains columns for each LFO in the SFZ region. + The columns are: Time, Lfo1, ... LfoN + One can use Gnuplot to display this data. + Example: + sfizz_plot_lfo file.sfz > lfo.dat + gnuplot + plot "lfo.dat" using 1:2 with lines + */ + +#include "sfizz/Synth.h" +#include "sfizz/LFO.h" +#include "sfizz/LFODescription.h" +#include "sfizz/MathHelpers.h" +#include "cxxopts.hpp" +#include +#include +#include +#include +#ifdef _WIN32 +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#endif +#include + +//============================================================================== + +static double sampleRate = 1000.0; // sample rate used to compute +static double duration = 5.0; // length in seconds +static std::string outputFilename; +static bool saveFlac = false; + +static std::vector lfoDescriptionFromSfzFile(const fs::path &sfzPath, bool &success) +{ + sfz::Synth synth; + + if (!synth.loadSfzFile(sfzPath)) { + std::cerr << "Cannot load the SFZ file.\n"; + success = false; + return {}; + } + + if (synth.getNumRegions() != 1) { + std::cerr << "The SFZ file must contain exactly one region.\n"; + success = false; + return {}; + } + + success = true; + return synth.getRegionView(0)->lfos; +} + +/** + Program which loads LFO configuration and generates plot data for the given duration. + */ +int main(int argc, char* argv[]) +{ + cxxopts::Options options("sfizz_plot_lfo", "Compute LFO and generate plot data"); + + options.add_options() + ("s,samplerate", "Sample rate", cxxopts::value(sampleRate)) + ("d,duration", "Duration", cxxopts::value(duration)) + ("o,output", "Output file", cxxopts::value(outputFilename)) + ("F,flac", "Save output as FLAC", cxxopts::value(saveFlac)) + ("h,help", "Print usage") + ; + options.positional_help("sfz-file"); + + fs::path sfzPath; + + try { + cxxopts::ParseResult result = options.parse(argc, argv); + options.parse_positional({ "sfz-file" }); + + if (result.count("help")) { + std::cout << options.help() << std::endl; + return 0; + } + + if (argc != 2) { + std::cerr << "Please indicate the SFZ file to process.\n"; + return 1; + } + + sfzPath = argv[1]; + } + catch (cxxopts::OptionException& ex) { + std::cerr << ex.what() << "\n"; + return 1; + } + + bool success = false; + const std::vector desc = lfoDescriptionFromSfzFile(sfzPath, success); + if (!success){ + std::cerr << "Could not extract LFO descriptions from SFZ file.\n"; + return 1; + } + + if (sampleRate <= 0) { + std::cerr << "The sample rate provided is invalid.\n"; + return 1; + } + + size_t numLfos = desc.size(); + std::vector lfos(numLfos); + + for (size_t l = 0; l < numLfos; ++l) { + lfos[l].setSampleRate(sampleRate); + lfos[l].configure(&desc[l]); + } + + size_t numFrames = (size_t)std::ceil(sampleRate * duration); + std::vector outputMemory(numLfos * numFrames); + + for (size_t l = 0; l < numLfos; ++l) { + lfos[l].start(); + } + + std::vector> lfoOutputs(numLfos); + for (size_t l = 0; l < numLfos; ++l) { + lfoOutputs[l] = absl::MakeSpan(&outputMemory[l * numFrames], numFrames); + lfos[l].process(lfoOutputs[l]); + } + + if (saveFlac) { + if (outputFilename.empty()) { + std::cerr << "Please indicate the audio file to save.\n"; + return 1; + } + + fs::path outputPath = fs::u8path(outputFilename); + SndfileHandle snd( +#ifndef _WIN32 + outputPath.c_str(), +#else + outputPath.wstring().c_str(), +#endif + SFM_WRITE, SF_FORMAT_FLAC|SF_FORMAT_PCM_16, numLfos, sampleRate); + + std::unique_ptr frame(new float[numLfos]); + size_t numClips = 0; + + for (size_t i = 0; i < numFrames; ++i) { + for (size_t l = 0; l < numLfos; ++l) { + float orig = lfoOutputs[l][i]; + float clamped = clamp(orig, -1.0f, 1.0f); + numClips += clamped != orig; + frame[l] = clamped; + } + snd.writef(frame.get(), 1); + } + snd.writeSync(); + + if (snd.error()) { + std::error_code ec; + fs::remove(outputPath, ec); + std::cerr << "Could not save audio to the output file.\n"; + return 1; + } + + if (numClips > 0) + std::cerr << "Warning: the audio output has been clipped on " << numClips << " frames.\n"; + } + else { + std::ostream* os = &std::cout; + fs::ofstream of; + + fs::path outputPath; + if (!outputFilename.empty()) { + outputPath = fs::u8path(outputFilename); + of.open(outputPath); + os = &of; + } + + for (size_t i = 0; i < numFrames; ++i) { + *os << (i / sampleRate); + for (size_t l = 0; l < numLfos; ++l) + *os << ' ' << lfoOutputs[l][i]; + *os << '\n'; + } + + if (os == &of) { + of.flush(); + if (!of) { + std::error_code ec; + fs::remove(outputPath, ec); + std::cerr << "Could not save data to the output file.\n"; + return 1; + } + } + } + + return 0; +} diff --git a/tests/lfo/compare_lfo.py b/tests/lfo/compare_lfo.py new file mode 100755 index 000000000..31fd7e122 --- /dev/null +++ b/tests/lfo/compare_lfo.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +import numpy as np +from argparse import ArgumentParser +import os + +parser = ArgumentParser(usage="Compare 2 files as outputted by sfizz_plot_lfo") +parser.add_argument("file", help="The file to test") +parser.add_argument("reference", help="The reference file") +parser.add_argument("--threshold", type=float, default=0.001, help="Mean squared error threshold") +args = parser.parse_args() + +assert os.path.exists(args.file), "The file to test does not exist" +assert os.path.exists(args.reference), "The reference file does not exist" + +reference_data = np.loadtxt(args.reference) +data = np.loadtxt(args.file) + +assert reference_data.shape == data.shape, "The shapes of the data and reference are different" + +mean_squared_error = np.mean((data - reference_data) ** 2) +print("MSE difference:", mean_squared_error) + +if (mean_squared_error > args.threshold): + exit(-1) + diff --git a/tests/lfo/lfo_fade_and_delay.sfz b/tests/lfo/lfo_fade_and_delay.sfz new file mode 100644 index 000000000..122e7e942 --- /dev/null +++ b/tests/lfo/lfo_fade_and_delay.sfz @@ -0,0 +1,7 @@ + +sample=*sine +// +lfo1_freq=1 +lfo1_wave=3 +lfo1_delay=0.5 +lfo1_fade=1 diff --git a/tests/lfo/lfo_fade_and_delay_reference.dat b/tests/lfo/lfo_fade_and_delay_reference.dat new file mode 100644 index 000000000..fa6a155f9 --- /dev/null +++ b/tests/lfo/lfo_fade_and_delay_reference.dat @@ -0,0 +1,500 @@ +0 0 +0.01 0 +0.02 0 +0.03 0 +0.04 0 +0.05 0 +0.06 0 +0.07 0 +0.08 0 +0.09 0 +0.1 0 +0.11 0 +0.12 0 +0.13 0 +0.14 0 +0.15 0 +0.16 0 +0.17 0 +0.18 0 +0.19 0 +0.2 0 +0.21 0 +0.22 0 +0.23 0 +0.24 0 +0.25 0 +0.26 0 +0.27 0 +0.28 0 +0.29 0 +0.3 0 +0.31 0 +0.32 0 +0.33 0 +0.34 0 +0.35 0 +0.36 0 +0.37 0 +0.38 0 +0.39 0 +0.4 0 +0.41 0 +0.42 0 +0.43 0 +0.44 0 +0.45 0 +0.46 0 +0.47 0 +0.48 0 +0.49 0 +0.5 0 +0.51 0.01 +0.52 0.02 +0.53 0.03 +0.54 0.04 +0.55 0.05 +0.56 0.06 +0.57 0.07 +0.58 0.08 +0.59 0.09 +0.6 0.1 +0.61 0.11 +0.62 0.12 +0.63 0.13 +0.64 0.14 +0.65 0.15 +0.66 0.16 +0.67 0.17 +0.68 0.18 +0.69 0.19 +0.7 0.2 +0.71 0.21 +0.72 0.22 +0.73 0.23 +0.74 0.24 +0.75 0.25 +0.76 0.26 +0.77 0.27 +0.78 0.28 +0.79 0.29 +0.8 0.3 +0.81 0.31 +0.82 0.32 +0.83 0.33 +0.84 0.34 +0.85 0.35 +0.86 0.36 +0.87 0.37 +0.88 0.38 +0.89 0.39 +0.9 0.4 +0.91 0.41 +0.92 0.42 +0.93 0.43 +0.94 0.44 +0.95 0.45 +0.96 0.46 +0.97 0.47 +0.98 0.48 +0.99 0.49 +1 0.5 +1.01 -0.51 +1.02 -0.52 +1.03 -0.53 +1.04 -0.54 +1.05 -0.55 +1.06 -0.56 +1.07 -0.57 +1.08 -0.58 +1.09 -0.59 +1.1 -0.6 +1.11 -0.61 +1.12 -0.62 +1.13 -0.63 +1.14 -0.64 +1.15 -0.65 +1.16 -0.66 +1.17 -0.67 +1.18 -0.68 +1.19 -0.69 +1.2 -0.7 +1.21 -0.71 +1.22 -0.72 +1.23 -0.73 +1.24 -0.74 +1.25 -0.75 +1.26 -0.76 +1.27 -0.77 +1.28 -0.78 +1.29 -0.79 +1.3 -0.8 +1.31 -0.81 +1.32 -0.82 +1.33 -0.83 +1.34 -0.839999 +1.35 -0.849999 +1.36 -0.859999 +1.37 -0.869999 +1.38 -0.879999 +1.39 -0.889999 +1.4 -0.899999 +1.41 -0.909999 +1.42 -0.919999 +1.43 -0.929999 +1.44 -0.939999 +1.45 -0.949999 +1.46 -0.959999 +1.47 -0.969999 +1.48 -0.979999 +1.49 -0.989999 +1.5 -0.999999 +1.51 1 +1.52 1 +1.53 1 +1.54 1 +1.55 1 +1.56 1 +1.57 1 +1.58 1 +1.59 1 +1.6 1 +1.61 1 +1.62 1 +1.63 1 +1.64 1 +1.65 1 +1.66 1 +1.67 1 +1.68 1 +1.69 1 +1.7 1 +1.71 1 +1.72 1 +1.73 1 +1.74 1 +1.75 1 +1.76 1 +1.77 1 +1.78 1 +1.79 1 +1.8 1 +1.81 1 +1.82 1 +1.83 1 +1.84 1 +1.85 1 +1.86 1 +1.87 1 +1.88 1 +1.89 1 +1.9 1 +1.91 1 +1.92 1 +1.93 1 +1.94 1 +1.95 1 +1.96 1 +1.97 1 +1.98 1 +1.99 1 +2 1 +2.01 -1 +2.02 -1 +2.03 -1 +2.04 -1 +2.05 -1 +2.06 -1 +2.07 -1 +2.08 -1 +2.09 -1 +2.1 -1 +2.11 -1 +2.12 -1 +2.13 -1 +2.14 -1 +2.15 -1 +2.16 -1 +2.17 -1 +2.18 -1 +2.19 -1 +2.2 -1 +2.21 -1 +2.22 -1 +2.23 -1 +2.24 -1 +2.25 -1 +2.26 -1 +2.27 -1 +2.28 -1 +2.29 -1 +2.3 -1 +2.31 -1 +2.32 -1 +2.33 -1 +2.34 -1 +2.35 -1 +2.36 -1 +2.37 -1 +2.38 -1 +2.39 -1 +2.4 -1 +2.41 -1 +2.42 -1 +2.43 -1 +2.44 -1 +2.45 -1 +2.46 -1 +2.47 -1 +2.48 -1 +2.49 -1 +2.5 -1 +2.51 1 +2.52 1 +2.53 1 +2.54 1 +2.55 1 +2.56 1 +2.57 1 +2.58 1 +2.59 1 +2.6 1 +2.61 1 +2.62 1 +2.63 1 +2.64 1 +2.65 1 +2.66 1 +2.67 1 +2.68 1 +2.69 1 +2.7 1 +2.71 1 +2.72 1 +2.73 1 +2.74 1 +2.75 1 +2.76 1 +2.77 1 +2.78 1 +2.79 1 +2.8 1 +2.81 1 +2.82 1 +2.83 1 +2.84 1 +2.85 1 +2.86 1 +2.87 1 +2.88 1 +2.89 1 +2.9 1 +2.91 1 +2.92 1 +2.93 1 +2.94 1 +2.95 1 +2.96 1 +2.97 1 +2.98 1 +2.99 1 +3 1 +3.01 -1 +3.02 -1 +3.03 -1 +3.04 -1 +3.05 -1 +3.06 -1 +3.07 -1 +3.08 -1 +3.09 -1 +3.1 -1 +3.11 -1 +3.12 -1 +3.13 -1 +3.14 -1 +3.15 -1 +3.16 -1 +3.17 -1 +3.18 -1 +3.19 -1 +3.2 -1 +3.21 -1 +3.22 -1 +3.23 -1 +3.24 -1 +3.25 -1 +3.26 -1 +3.27 -1 +3.28 -1 +3.29 -1 +3.3 -1 +3.31 -1 +3.32 -1 +3.33 -1 +3.34 -1 +3.35 -1 +3.36 -1 +3.37 -1 +3.38 -1 +3.39 -1 +3.4 -1 +3.41 -1 +3.42 -1 +3.43 -1 +3.44 -1 +3.45 -1 +3.46 -1 +3.47 -1 +3.48 -1 +3.49 -1 +3.5 -1 +3.51 1 +3.52 1 +3.53 1 +3.54 1 +3.55 1 +3.56 1 +3.57 1 +3.58 1 +3.59 1 +3.6 1 +3.61 1 +3.62 1 +3.63 1 +3.64 1 +3.65 1 +3.66 1 +3.67 1 +3.68 1 +3.69 1 +3.7 1 +3.71 1 +3.72 1 +3.73 1 +3.74 1 +3.75 1 +3.76 1 +3.77 1 +3.78 1 +3.79 1 +3.8 1 +3.81 1 +3.82 1 +3.83 1 +3.84 1 +3.85 1 +3.86 1 +3.87 1 +3.88 1 +3.89 1 +3.9 1 +3.91 1 +3.92 1 +3.93 1 +3.94 1 +3.95 1 +3.96 1 +3.97 1 +3.98 1 +3.99 1 +4 1 +4.01 -1 +4.02 -1 +4.03 -1 +4.04 -1 +4.05 -1 +4.06 -1 +4.07 -1 +4.08 -1 +4.09 -1 +4.1 -1 +4.11 -1 +4.12 -1 +4.13 -1 +4.14 -1 +4.15 -1 +4.16 -1 +4.17 -1 +4.18 -1 +4.19 -1 +4.2 -1 +4.21 -1 +4.22 -1 +4.23 -1 +4.24 -1 +4.25 -1 +4.26 -1 +4.27 -1 +4.28 -1 +4.29 -1 +4.3 -1 +4.31 -1 +4.32 -1 +4.33 -1 +4.34 -1 +4.35 -1 +4.36 -1 +4.37 -1 +4.38 -1 +4.39 -1 +4.4 -1 +4.41 -1 +4.42 -1 +4.43 -1 +4.44 -1 +4.45 -1 +4.46 -1 +4.47 -1 +4.48 -1 +4.49 -1 +4.5 -1 +4.51 1 +4.52 1 +4.53 1 +4.54 1 +4.55 1 +4.56 1 +4.57 1 +4.58 1 +4.59 1 +4.6 1 +4.61 1 +4.62 1 +4.63 1 +4.64 1 +4.65 1 +4.66 1 +4.67 1 +4.68 1 +4.69 1 +4.7 1 +4.71 1 +4.72 1 +4.73 1 +4.74 1 +4.75 1 +4.76 1 +4.77 1 +4.78 1 +4.79 1 +4.8 1 +4.81 1 +4.82 1 +4.83 1 +4.84 1 +4.85 1 +4.86 1 +4.87 1 +4.88 1 +4.89 1 +4.9 1 +4.91 1 +4.92 1 +4.93 1 +4.94 1 +4.95 1 +4.96 1 +4.97 1 +4.98 1 +4.99 1 diff --git a/tests/lfo/lfo_subwave.sfz b/tests/lfo/lfo_subwave.sfz new file mode 100644 index 000000000..eb493764b --- /dev/null +++ b/tests/lfo/lfo_subwave.sfz @@ -0,0 +1,31 @@ + +sample=*noise +lokey=0 +hikey=127 +cutoff=1000.0 +fil_type=brf_2p +lfo1_cutoff=1200.0 +// +lfo1_freq=1 +lfo1_phase=180 +lfo1_wave=3 +// +lfo2_freq=1 +lfo2_phase=180 +lfo2_wave=3 +lfo2_wave2=1 +// +lfo3_freq=1 +lfo3_phase=180 +lfo3_wave=3 +lfo3_wave2=1 +lfo3_ratio2=2 + +// +lfo4_freq=1 +lfo4_phase=180 +lfo4_wave=3 +lfo4_wave2=1 +lfo4_ratio2=2 +lfo4_offset2=0.5 +lfo4_scale2=0.5 diff --git a/tests/lfo/lfo_subwave_reference.dat b/tests/lfo/lfo_subwave_reference.dat new file mode 100644 index 000000000..c4dd0ef1a --- /dev/null +++ b/tests/lfo/lfo_subwave_reference.dat @@ -0,0 +1,500 @@ +0 -1 -1 -1 -0.5 +0.01 -1 -0.9216 -0.8464 -0.4232 +0.02 -1 -0.8464 -0.7056 -0.3528 +0.03 -1 -0.7744 -0.5776 -0.2888 +0.04 -1 -0.7056 -0.4624 -0.2312 +0.05 -1 -0.64 -0.36 -0.18 +0.06 -1 -0.5776 -0.2704 -0.1352 +0.07 -1 -0.5184 -0.1936 -0.0968002 +0.08 -1 -0.4624 -0.1296 -0.0648002 +0.09 -1 -0.4096 -0.0784004 -0.0392002 +0.1 -1 -0.36 -0.0400003 -0.0200002 +0.11 -1 -0.3136 -0.0144002 -0.00720009 +0.12 -1 -0.2704 -0.00160009 -0.000800043 +0.13 -1 -0.230401 -0.00159991 -0.000799954 +0.14 -1 -0.1936 -0.0143998 -0.00719988 +0.15 -1 -0.16 -0.0399995 -0.0199998 +0.16 -1 -0.1296 -0.0783993 -0.0391997 +0.17 -1 -0.1024 -0.129599 -0.0647995 +0.18 -1 -0.0784004 -0.193599 -0.0967994 +0.19 -1 -0.0576003 -0.270398 -0.135199 +0.2 -1 -0.0400003 -0.359998 -0.179999 +0.21 -1 -0.0256003 -0.462398 -0.231199 +0.22 -1 -0.0144002 -0.577597 -0.288799 +0.23 -1 -0.00640017 -0.705597 -0.352799 +0.24 -1 -0.00160009 -0.846397 -0.423198 +0.25 -1 0 -0.999996 -0.499998 +0.26 -1 -0.00159991 -1.1536 -0.576798 +0.27 -1 -0.00639981 -1.2944 -0.647198 +0.28 -1 -0.0143998 -1.4224 -0.711198 +0.29 -1 -0.0255997 -1.5376 -0.768799 +0.3 -1 -0.0399995 -1.64 -0.819999 +0.31 -1 -0.0575994 -1.7296 -0.864799 +0.32 -1 -0.0783993 -1.8064 -0.903199 +0.33 -1 -0.102399 -1.8704 -0.935199 +0.34 -1 -0.129599 -1.9216 -0.960799 +0.35 -1 -0.159999 -1.96 -0.98 +0.36 -1 -0.193599 -1.9856 -0.9928 +0.37 -1 -0.230399 -1.9984 -0.9992 +0.38 -1 -0.270398 -1.9984 -0.9992 +0.39 -1 -0.313598 -1.9856 -0.9928 +0.4 -1 -0.359998 -1.96 -0.98 +0.41 -1 -0.409598 -1.9216 -0.960801 +0.42 -1 -0.462398 -1.8704 -0.935201 +0.43 -1 -0.518398 -1.8064 -0.903201 +0.44 -1 -0.577597 -1.7296 -0.864801 +0.45 -1 -0.639997 -1.64 -0.820001 +0.46 -1 -0.705597 -1.5376 -0.768801 +0.47 -1 -0.774397 -1.4224 -0.711201 +0.48 -1 -0.846397 -1.2944 -0.647201 +0.49 -1 -0.921596 -1.1536 -0.576801 +0.5 -1 -0.999996 -1 -0.500002 +0.51 1 0.921604 1.1536 1.5768 +0.52 1 0.846404 1.2944 1.6472 +0.53 1 0.774403 1.4224 1.7112 +0.54 1 0.705603 1.5376 1.7688 +0.55 1 0.640003 1.64 1.82 +0.56 1 0.577603 1.7296 1.8648 +0.57 1 0.518403 1.8064 1.9032 +0.58 1 0.462403 1.8704 1.9352 +0.59 1 0.409603 1.9216 1.9608 +0.6 1 0.360002 1.96 1.98 +0.61 1 0.313602 1.9856 1.9928 +0.62 1 0.270402 1.9984 1.9992 +0.63 1 0.230402 1.9984 1.9992 +0.64 1 0.193602 1.9856 1.9928 +0.65 1 0.160002 1.96 1.98 +0.66 1 0.129601 1.9216 1.9608 +0.67 1 0.102401 1.8704 1.9352 +0.68 1 0.078401 1.8064 1.9032 +0.69 1 0.0576009 1.7296 1.8648 +0.7 1 0.0400007 1.64 1.82 +0.71 1 0.0256006 1.5376 1.7688 +0.72 1 0.0144004 1.4224 1.7112 +0.73 1 0.00640029 1.29441 1.6472 +0.74 1 0.00160015 1.15361 1.5768 +0.75 1 0 1.00001 1.5 +0.76 1 0.00159985 0.846406 1.4232 +0.77 1 0.00639969 0.705606 1.3528 +0.78 1 0.0143996 0.577605 1.2888 +0.79 1 0.0255994 0.462405 1.2312 +0.8 1 0.0399992 0.360004 1.18 +0.81 1 0.0575991 0.270404 1.1352 +0.82 1 0.0783989 0.193603 1.0968 +0.83 1 0.102399 0.129602 1.0648 +0.84 1 0.129599 0.078402 1.0392 +0.85 1 0.159998 0.0400014 1.02 +0.86 1 0.193598 0.0144008 1.0072 +0.87 1 0.230398 0.00160027 1.0008 +0.88 1 0.270398 0.00159973 1.0008 +0.89 1 0.313598 0.0143992 1.0072 +0.9 1 0.359997 0.0399987 1.02 +0.91 1 0.409597 0.0783981 1.0392 +0.92 1 0.462397 0.129598 1.0648 +0.93 1 0.518397 0.193597 1.0968 +0.94 1 0.577596 0.270397 1.1352 +0.95 1 0.639996 0.359996 1.18 +0.96 1 0.705596 0.462396 1.2312 +0.97 1 0.774396 0.577595 1.2888 +0.98 1 0.846395 0.705595 1.3528 +0.99 1 0.921595 0.846394 1.4232 +1 1 0.999995 0.999994 1.5 +1.01 -1 -0.921605 -0.846405 -0.423203 +1.02 -1 -0.846405 -0.705605 -0.352803 +1.03 -1 -0.774405 -0.577605 -0.288802 +1.04 -1 -0.705605 -0.462404 -0.231202 +1.05 -1 -0.640005 -0.360004 -0.180002 +1.06 -1 -0.577604 -0.270403 -0.135202 +1.07 -1 -0.518404 -0.193603 -0.0968015 +1.08 -1 -0.462404 -0.129602 -0.0648012 +1.09 -1 -0.409604 -0.078402 -0.039201 +1.1 -1 -0.360004 -0.0400015 -0.0200007 +1.11 -1 -0.313603 -0.0144009 -0.00720045 +1.12 -1 -0.270403 -0.00160033 -0.000800163 +1.13 -1 -0.230403 -0.00159967 -0.000799835 +1.14 -1 -0.193603 -0.0143991 -0.00719953 +1.15 -1 -0.160003 -0.0399984 -0.0199992 +1.16 -1 -0.129602 -0.0783977 -0.0391988 +1.17 -1 -0.102402 -0.129597 -0.0647985 +1.18 -1 -0.0784019 -0.193596 -0.0967982 +1.19 -1 -0.0576016 -0.270396 -0.135198 +1.2 -1 -0.0400013 -0.359995 -0.179997 +1.21 -1 -0.0256011 -0.462394 -0.231197 +1.22 -1 -0.0144008 -0.577593 -0.288797 +1.23 -1 -0.00640059 -0.705592 -0.352796 +1.24 -1 -0.00160027 -0.846391 -0.423196 +1.25 -1 0 -0.99999 -0.499995 +1.26 -1 -0.00159973 -1.15359 -0.576796 +1.27 -1 -0.00639939 -1.29439 -0.647196 +1.28 -1 -0.0143991 -1.42239 -0.711196 +1.29 -1 -0.0255988 -1.53759 -0.768797 +1.3 -1 -0.0399985 -1.63999 -0.819997 +1.31 -1 -0.0575982 -1.72959 -0.864797 +1.32 -1 -0.0783979 -1.8064 -0.903198 +1.33 -1 -0.102398 -1.8704 -0.935198 +1.34 -1 -0.129597 -1.9216 -0.960799 +1.35 -1 -0.159997 -1.96 -0.979999 +1.36 -1 -0.193596 -1.9856 -0.992799 +1.37 -1 -0.230396 -1.9984 -0.9992 +1.38 -1 -0.270396 -1.9984 -0.9992 +1.39 -1 -0.313595 -1.9856 -0.992801 +1.4 -1 -0.359995 -1.96 -0.980001 +1.41 -1 -0.409595 -1.9216 -0.960801 +1.42 -1 -0.462394 -1.8704 -0.935202 +1.43 -1 -0.518394 -1.8064 -0.903202 +1.44 -1 -0.577593 -1.7296 -0.864802 +1.45 -1 -0.639993 -1.64001 -0.820003 +1.46 -1 -0.705593 -1.53761 -0.768803 +1.47 -1 -0.774392 -1.42241 -0.711203 +1.48 -1 -0.846392 -1.29441 -0.647204 +1.49 -1 -0.921591 -1.15361 -0.576804 +1.5 -1 -0.999991 -1.00001 -0.500004 +1.51 1 0.921608 1.15359 1.5768 +1.52 1 0.846408 1.29439 1.6472 +1.53 1 0.774408 1.42239 1.7112 +1.54 1 0.705607 1.53759 1.7688 +1.55 1 0.640007 1.63999 1.82 +1.56 1 0.577606 1.7296 1.8648 +1.57 1 0.518406 1.8064 1.9032 +1.58 1 0.462406 1.8704 1.9352 +1.59 1 0.409606 1.9216 1.9608 +1.6 1 0.360005 1.96 1.98 +1.61 1 0.313605 1.9856 1.9928 +1.62 1 0.270405 1.9984 1.9992 +1.63 1 0.230404 1.9984 1.9992 +1.64 1 0.193604 1.9856 1.9928 +1.65 1 0.160003 1.96 1.98 +1.66 1 0.129603 1.9216 1.9608 +1.67 1 0.102403 1.8704 1.9352 +1.68 1 0.0784024 1.80641 1.9032 +1.69 1 0.057602 1.72961 1.8648 +1.7 1 0.0400017 1.64001 1.82 +1.71 1 0.0256013 1.53761 1.7688 +1.72 1 0.014401 1.42241 1.7112 +1.73 1 0.00640064 1.29441 1.64721 +1.74 1 0.00160033 1.15361 1.57681 +1.75 1 0 1.00001 1.50001 +1.76 1 0.00159967 0.846412 1.42321 +1.77 1 0.00639933 0.705611 1.35281 +1.78 1 0.014399 0.57761 1.2888 +1.79 1 0.0255986 0.462409 1.2312 +1.8 1 0.0399983 0.360008 1.18 +1.81 1 0.0575979 0.270407 1.1352 +1.82 1 0.0783976 0.193605 1.0968 +1.83 1 0.102397 0.129605 1.0648 +1.84 1 0.129597 0.0784036 1.0392 +1.85 1 0.159996 0.0400025 1.02 +1.86 1 0.193596 0.0144015 1.0072 +1.87 1 0.230396 0.0016005 1.0008 +1.88 1 0.270395 0.00159949 1.0008 +1.89 1 0.313595 0.0143985 1.0072 +1.9 1 0.359994 0.0399975 1.02 +1.91 1 0.409594 0.0783965 1.0392 +1.92 1 0.462394 0.129596 1.0648 +1.93 1 0.518393 0.193595 1.0968 +1.94 1 0.577593 0.270394 1.1352 +1.95 1 0.639992 0.359993 1.18 +1.96 1 0.705592 0.462392 1.2312 +1.97 1 0.774391 0.577591 1.2888 +1.98 1 0.846391 0.70559 1.3528 +1.99 1 0.92159 0.846389 1.42319 +2 1 0.99999 0.999988 1.49999 +2.01 -1 -0.92161 -0.846411 -0.423205 +2.02 -1 -0.846409 -0.70561 -0.352805 +2.03 -1 -0.774409 -0.577609 -0.288805 +2.04 -1 -0.705609 -0.462408 -0.231204 +2.05 -1 -0.640008 -0.360007 -0.180004 +2.06 -1 -0.577608 -0.270406 -0.135203 +2.07 -1 -0.518408 -0.193605 -0.0968027 +2.08 -1 -0.462407 -0.129605 -0.0648023 +2.09 -1 -0.409607 -0.0784036 -0.0392018 +2.1 -1 -0.360006 -0.0400026 -0.0200013 +2.11 -1 -0.313606 -0.0144016 -0.00720078 +2.12 -1 -0.270406 -0.0016005 -0.000800252 +2.13 -1 -0.230405 -0.00159949 -0.000799745 +2.14 -1 -0.193605 -0.0143984 -0.0071992 +2.15 -1 -0.160004 -0.0399973 -0.0199986 +2.16 -1 -0.129604 -0.0783961 -0.0391981 +2.17 -1 -0.102404 -0.129595 -0.0647975 +2.18 -1 -0.0784032 -0.193594 -0.0967969 +2.19 -1 -0.0576028 -0.270393 -0.135196 +2.2 -1 -0.0400023 -0.359991 -0.179996 +2.21 -1 -0.0256019 -0.46239 -0.231195 +2.22 -1 -0.0144014 -0.577589 -0.288794 +2.23 -1 -0.00640094 -0.705587 -0.352794 +2.24 -1 -0.00160044 -0.846386 -0.423193 +2.25 -1 0 -0.999985 -0.499992 +2.26 -1 -0.00159949 -1.15359 -0.576793 +2.27 -1 -0.00639904 -1.29439 -0.647194 +2.28 -1 -0.0143985 -1.42239 -0.711194 +2.29 -1 -0.025598 -1.53759 -0.768795 +2.3 -1 -0.0399975 -1.63999 -0.819995 +2.31 -1 -0.057597 -1.72959 -0.864796 +2.32 -1 -0.0783965 -1.80639 -0.903197 +2.33 -1 -0.102396 -1.87039 -0.935197 +2.34 -1 -0.129595 -1.9216 -0.960798 +2.35 -1 -0.159995 -1.96 -0.979998 +2.36 -1 -0.193594 -1.9856 -0.992799 +2.37 -1 -0.230394 -1.9984 -0.9992 +2.38 -1 -0.270393 -1.9984 -0.9992 +2.39 -1 -0.313593 -1.9856 -0.992801 +2.4 -1 -0.359992 -1.96 -0.980002 +2.41 -1 -0.409592 -1.9216 -0.960802 +2.42 -1 -0.462391 -1.87041 -0.935203 +2.43 -1 -0.51839 -1.80641 -0.903203 +2.44 -1 -0.57759 -1.72961 -0.864804 +2.45 -1 -0.639989 -1.64001 -0.820004 +2.46 -1 -0.705589 -1.53761 -0.768805 +2.47 -1 -0.774388 -1.42241 -0.711206 +2.48 -1 -0.846387 -1.29441 -0.647206 +2.49 -1 -0.921587 -1.15361 -0.576807 +2.5 -1 -0.999986 -1.00001 -0.500007 +2.51 1 0.921613 1.15359 1.57679 +2.52 1 0.846412 1.29439 1.64719 +2.53 1 0.774412 1.42239 1.71119 +2.54 1 0.705611 1.53759 1.76879 +2.55 1 0.640011 1.63999 1.82 +2.56 1 0.57761 1.72959 1.8648 +2.57 1 0.51841 1.80639 1.9032 +2.58 1 0.462409 1.87039 1.9352 +2.59 1 0.409609 1.9216 1.9608 +2.6 1 0.360008 1.96 1.98 +2.61 1 0.313608 1.9856 1.9928 +2.62 1 0.270407 1.9984 1.9992 +2.63 1 0.230406 1.9984 1.9992 +2.64 1 0.193606 1.9856 1.9928 +2.65 1 0.160005 1.96 1.98 +2.66 1 0.129605 1.9216 1.9608 +2.67 1 0.102404 1.87041 1.9352 +2.68 1 0.0784037 1.80641 1.9032 +2.69 1 0.0576032 1.72961 1.8648 +2.7 1 0.0400026 1.64001 1.82001 +2.71 1 0.0256021 1.53761 1.76881 +2.72 1 0.0144016 1.42241 1.71121 +2.73 1 0.00640106 1.29441 1.64721 +2.74 1 0.0016005 1.15362 1.57681 +2.75 1 0 1.00002 1.50001 +2.76 1 0.00159949 0.846417 1.42321 +2.77 1 0.00639898 0.705615 1.35281 +2.78 1 0.0143985 0.577614 1.28881 +2.79 1 0.0255979 0.462412 1.23121 +2.8 1 0.0399973 0.360011 1.18001 +2.81 1 0.0575968 0.27041 1.1352 +2.82 1 0.0783963 0.193608 1.0968 +2.83 1 0.102396 0.129607 1.0648 +2.84 1 0.129595 0.0784052 1.0392 +2.85 1 0.159995 0.0400037 1.02 +2.86 1 0.193594 0.0144022 1.0072 +2.87 1 0.230393 0.00160074 1.0008 +2.88 1 0.270393 0.00159925 1.0008 +2.89 1 0.313592 0.0143978 1.0072 +2.9 1 0.359992 0.0399963 1.02 +2.91 1 0.409591 0.0783949 1.0392 +2.92 1 0.46239 0.129593 1.0648 +2.93 1 0.51839 0.193592 1.0968 +2.94 1 0.577589 0.270391 1.1352 +2.95 1 0.639988 0.359989 1.17999 +2.96 1 0.705588 0.462388 1.23119 +2.97 1 0.774387 0.577587 1.28879 +2.98 1 0.846387 0.705585 1.35279 +2.99 1 0.921586 0.846384 1.42319 +3 1 0.999985 0.999983 1.49999 +3.01 -1 -0.921614 -0.846416 -0.423208 +3.02 -1 -0.846414 -0.705615 -0.352807 +3.03 -1 -0.774413 -0.577613 -0.288807 +3.04 -1 -0.705613 -0.462412 -0.231206 +3.05 -1 -0.640012 -0.360011 -0.180005 +3.06 -1 -0.577612 -0.270409 -0.135205 +3.07 -1 -0.518411 -0.193608 -0.096804 +3.08 -1 -0.46241 -0.129607 -0.0648033 +3.09 -1 -0.40961 -0.0784052 -0.0392026 +3.1 -1 -0.360009 -0.0400037 -0.0200019 +3.11 -1 -0.313609 -0.0144023 -0.00720114 +3.12 -1 -0.270408 -0.00160074 -0.000800371 +3.13 -1 -0.230408 -0.00159925 -0.000799626 +3.14 -1 -0.193607 -0.0143977 -0.00719884 +3.15 -1 -0.160006 -0.0399961 -0.019998 +3.16 -1 -0.129606 -0.0783945 -0.0391973 +3.17 -1 -0.102405 -0.129593 -0.0647964 +3.18 -1 -0.0784045 -0.193591 -0.0967956 +3.19 -1 -0.0576039 -0.27039 -0.135195 +3.2 -1 -0.0400032 -0.359988 -0.179994 +3.21 -1 -0.0256026 -0.462386 -0.231193 +3.22 -1 -0.014402 -0.577584 -0.288792 +3.23 -1 -0.0064013 -0.705583 -0.352791 +3.24 -1 -0.00160068 -0.846381 -0.42319 +3.25 -1 0 -0.999979 -0.49999 +3.26 -1 -0.00159931 -1.15358 -0.57679 +3.27 -1 -0.00639868 -1.29438 -0.647191 +3.28 -1 -0.014398 -1.42238 -0.711192 +3.29 -1 -0.0255973 -1.53759 -0.768793 +3.3 -1 -0.0399966 -1.63999 -0.819994 +3.31 -1 -0.0575959 -1.72959 -0.864794 +3.32 -1 -0.0783952 -1.80639 -0.903195 +3.33 -1 -0.102394 -1.87039 -0.935196 +3.34 -1 -0.129594 -1.92159 -0.960797 +3.35 -1 -0.159993 -1.96 -0.979998 +3.36 -1 -0.193592 -1.9856 -0.992799 +3.37 -1 -0.230392 -1.9984 -0.9992 +3.38 -1 -0.270391 -1.9984 -0.9992 +3.39 -1 -0.31359 -1.9856 -0.992801 +3.4 -1 -0.359989 -1.96 -0.980002 +3.41 -1 -0.409589 -1.92161 -0.960803 +3.42 -1 -0.462388 -1.87041 -0.935204 +3.43 -1 -0.518387 -1.80641 -0.903205 +3.44 -1 -0.577586 -1.72961 -0.864805 +3.45 -1 -0.639985 -1.64001 -0.820006 +3.46 -1 -0.705585 -1.53761 -0.768807 +3.47 -1 -0.774384 -1.42242 -0.711208 +3.48 -1 -0.846383 -1.29442 -0.647209 +3.49 -1 -0.921582 -1.15362 -0.576809 +3.5 -1 -0.999981 -1.00002 -0.50001 +3.51 1 0.921617 1.15358 1.57679 +3.52 1 0.846417 1.29438 1.64719 +3.53 1 0.774416 1.42238 1.71119 +3.54 1 0.705615 1.53759 1.76879 +3.55 1 0.640015 1.63999 1.81999 +3.56 1 0.577614 1.72959 1.86479 +3.57 1 0.518413 1.80639 1.9032 +3.58 1 0.462412 1.87039 1.9352 +3.59 1 0.409612 1.92159 1.9608 +3.6 1 0.360011 1.96 1.98 +3.61 1 0.31361 1.9856 1.9928 +3.62 1 0.27041 1.9984 1.9992 +3.63 1 0.230409 1.9984 1.9992 +3.64 1 0.193608 1.9856 1.9928 +3.65 1 0.160007 1.96 1.98 +3.66 1 0.129607 1.92161 1.9608 +3.67 1 0.102406 1.87041 1.9352 +3.68 1 0.0784051 1.80641 1.90321 +3.69 1 0.0576044 1.72961 1.86481 +3.7 1 0.0400036 1.64001 1.82001 +3.71 1 0.0256029 1.53762 1.76881 +3.72 1 0.0144022 1.42242 1.71121 +3.73 1 0.00640142 1.29442 1.64721 +3.74 1 0.00160068 1.15362 1.57681 +3.75 1 0 1.00002 1.50001 +3.76 1 0.00159931 0.846422 1.42321 +3.77 1 0.00639856 0.70562 1.35281 +3.78 1 0.0143979 0.577618 1.28881 +3.79 1 0.0255972 0.462416 1.23121 +3.8 1 0.0399964 0.360014 1.18001 +3.81 1 0.0575957 0.270413 1.13521 +3.82 1 0.0783949 0.193611 1.09681 +3.83 1 0.102394 0.129609 1.0648 +3.84 1 0.129593 0.0784068 1.0392 +3.85 1 0.159993 0.0400048 1.02 +3.86 1 0.193592 0.0144029 1.0072 +3.87 1 0.230391 0.00160098 1.0008 +3.88 1 0.27039 0.00159901 1.0008 +3.89 1 0.31359 0.0143971 1.0072 +3.9 1 0.359989 0.0399952 1.02 +3.91 1 0.409588 0.0783933 1.0392 +3.92 1 0.462387 0.129591 1.0648 +3.93 1 0.518386 0.19359 1.09679 +3.94 1 0.577585 0.270388 1.13519 +3.95 1 0.639985 0.359986 1.17999 +3.96 1 0.705584 0.462384 1.23119 +3.97 1 0.774383 0.577582 1.28879 +3.98 1 0.846382 0.70558 1.35279 +3.99 1 0.921581 0.846379 1.42319 +4 1 0.99998 0.999977 1.49999 +4.01 -1 -0.921619 -0.846421 -0.423211 +4.02 -1 -0.846418 -0.705619 -0.35281 +4.03 -1 -0.774417 -0.577618 -0.288809 +4.04 -1 -0.705617 -0.462416 -0.231208 +4.05 -1 -0.640016 -0.360014 -0.180007 +4.06 -1 -0.577615 -0.270412 -0.135206 +4.07 -1 -0.518414 -0.193611 -0.0968053 +4.08 -1 -0.462414 -0.129609 -0.0648043 +4.09 -1 -0.409613 -0.0784068 -0.0392034 +4.1 -1 -0.360012 -0.0400049 -0.0200025 +4.11 -1 -0.313611 -0.0144029 -0.00720146 +4.12 -1 -0.270411 -0.00160098 -0.00080049 +4.13 -1 -0.23041 -0.00159901 -0.000799507 +4.14 -1 -0.193609 -0.014397 -0.00719851 +4.15 -1 -0.160008 -0.039995 -0.0199975 +4.16 -1 -0.129607 -0.0783929 -0.0391965 +4.17 -1 -0.102407 -0.129591 -0.0647954 +4.18 -1 -0.0784059 -0.193589 -0.0967944 +4.19 -1 -0.057605 -0.270387 -0.135193 +4.2 -1 -0.0400042 -0.359984 -0.179992 +4.21 -1 -0.0256034 -0.462382 -0.231191 +4.22 -1 -0.0144026 -0.57758 -0.28879 +4.23 -1 -0.00640172 -0.705578 -0.352789 +4.24 -1 -0.00160086 -0.846376 -0.423188 +4.25 -1 0 -0.999973 -0.499987 +4.26 -1 -0.00159913 -1.15358 -0.576788 +4.27 -1 -0.00639826 -1.29438 -0.647189 +4.28 -1 -0.0143974 -1.42238 -0.71119 +4.29 -1 -0.0255965 -1.53758 -0.768791 +4.3 -1 -0.0399956 -1.63998 -0.819992 +4.31 -1 -0.0575947 -1.72959 -0.864793 +4.32 -1 -0.0783938 -1.80639 -0.903194 +4.33 -1 -0.102393 -1.87039 -0.935195 +4.34 -1 -0.129592 -1.92159 -0.960796 +4.35 -1 -0.159991 -1.95999 -0.979997 +4.36 -1 -0.19359 -1.9856 -0.992798 +4.37 -1 -0.230389 -1.9984 -0.999199 +4.38 -1 -0.270388 -1.9984 -0.999201 +4.39 -1 -0.313587 -1.9856 -0.992802 +4.4 -1 -0.359986 -1.96001 -0.980003 +4.41 -1 -0.409585 -1.92161 -0.960804 +4.42 -1 -0.462385 -1.87041 -0.935205 +4.43 -1 -0.518384 -1.80641 -0.903206 +4.44 -1 -0.577583 -1.72961 -0.864807 +4.45 -1 -0.639982 -1.64002 -0.820008 +4.46 -1 -0.705581 -1.53762 -0.768809 +4.47 -1 -0.77438 -1.42242 -0.71121 +4.48 -1 -0.846379 -1.29442 -0.647211 +4.49 -1 -0.921578 -1.15362 -0.576812 +4.5 -1 -0.999977 -1.00003 -0.500013 +4.51 1 0.921622 1.15358 1.57679 +4.52 1 0.846421 1.29438 1.64719 +4.53 1 0.77442 1.42238 1.71119 +4.54 1 0.705619 1.53758 1.76879 +4.55 1 0.640018 1.63998 1.81999 +4.56 1 0.577617 1.72959 1.86479 +4.57 1 0.518417 1.80639 1.90319 +4.58 1 0.462416 1.87039 1.9352 +4.59 1 0.409615 1.92159 1.9608 +4.6 1 0.360014 1.95999 1.98 +4.61 1 0.313613 1.9856 1.9928 +4.62 1 0.270412 1.9984 1.9992 +4.63 1 0.230411 1.9984 1.9992 +4.64 1 0.19361 1.9856 1.9928 +4.65 1 0.160009 1.96001 1.98 +4.66 1 0.129608 1.92161 1.9608 +4.67 1 0.102407 1.87041 1.93521 +4.68 1 0.0784064 1.80641 1.90321 +4.69 1 0.0576055 1.72961 1.86481 +4.7 1 0.0400046 1.64002 1.82001 +4.71 1 0.0256036 1.53762 1.76881 +4.72 1 0.0144027 1.42242 1.71121 +4.73 1 0.00640184 1.29442 1.64721 +4.74 1 0.00160092 1.15363 1.57681 +4.75 1 0 1.00003 1.50001 +4.76 1 0.00159907 0.846427 1.42321 +4.77 1 0.0063982 0.705625 1.35281 +4.78 1 0.0143973 0.577623 1.28881 +4.79 1 0.0255964 0.46242 1.23121 +4.8 1 0.0399954 0.360018 1.18001 +4.81 1 0.0575945 0.270415 1.13521 +4.82 1 0.0783936 0.193613 1.09681 +4.83 1 0.102393 0.129611 1.06481 +4.84 1 0.129592 0.0784084 1.0392 +4.85 1 0.159991 0.040006 1.02 +4.86 1 0.19359 0.0144036 1.0072 +4.87 1 0.230389 0.00160122 1.0008 +4.88 1 0.270388 0.00159878 1.0008 +4.89 1 0.313587 0.0143964 1.0072 +4.9 1 0.359986 0.0399941 1.02 +4.91 1 0.409585 0.0783917 1.0392 +4.92 1 0.462384 0.129589 1.06479 +4.93 1 0.518383 0.193587 1.09679 +4.94 1 0.577582 0.270385 1.13519 +4.95 1 0.639981 0.359982 1.17999 +4.96 1 0.70558 0.46238 1.23119 +4.97 1 0.774379 0.577578 1.28879 +4.98 1 0.846378 0.705576 1.35279 +4.99 1 0.921577 0.846373 1.42319 diff --git a/tests/lfo/lfo_waves.sfz b/tests/lfo/lfo_waves.sfz new file mode 100644 index 000000000..091245dd7 --- /dev/null +++ b/tests/lfo/lfo_waves.sfz @@ -0,0 +1,23 @@ + +sample=*sine +lfo1_wave=0 +lfo1_freq=1.0 +lfo2_wave=1 +lfo2_freq=2.0 +lfo2_scale=0.8 +lfo3_wave=2 +lfo3_freq=1.0 +lfo3_scale=0.5 +lfo4_wave=3 +lfo4_freq=2.0 +lfo4_ratio=2 +lfo5_wave=4 +lfo5_freq=1.0 +lfo5_offset=0.5 +lfo6_wave=5 +lfo6_freq=2.0 +lfo7_wave=6 +lfo7_freq=1.0 +lfo7_phase=180 +lfo8_wave=7 +lfo8_freq=2.0 diff --git a/tests/lfo/lfo_waves_reference.dat b/tests/lfo/lfo_waves_reference.dat new file mode 100644 index 000000000..9a65cdf25 --- /dev/null +++ b/tests/lfo/lfo_waves_reference.dat @@ -0,0 +1,500 @@ +0 0 0 0.5 1 1.5 1 0 1 +0.01 0.04 -0.12288 0.5 1 1.5 1 0.02 0.96 +0.02 0.08 -0.23552 0.5 1 1.5 1 0.04 0.92 +0.03 0.12 -0.33792 0.5 1 1.5 1 0.0599999 0.88 +0.04 0.16 -0.43008 0.5 1 1.5 1 0.0799999 0.84 +0.05 0.2 -0.512 0.5 1 1.5 1 0.0999999 0.8 +0.06 0.24 -0.58368 0.5 1 1.5 1 0.12 0.76 +0.07 0.28 -0.64512 0.5 1 1.5 -1 0.14 0.72 +0.08 0.32 -0.69632 0.5 1 1.5 -1 0.16 0.68 +0.09 0.36 -0.73728 0.5 1 1.5 -1 0.18 0.64 +0.1 0.4 -0.768 0.5 1 1.5 -1 0.2 0.6 +0.11 0.44 -0.78848 0.5 1 1.5 -1 0.22 0.56 +0.12 0.48 -0.79872 0.5 1 1.5 -1 0.24 0.52 +0.13 0.52 -0.79872 0.5 -1 1.5 -1 0.26 0.48 +0.14 0.56 -0.78848 0.5 -1 1.5 -1 0.28 0.44 +0.15 0.6 -0.768 0.5 -1 1.5 -1 0.3 0.4 +0.16 0.64 -0.73728 0.5 -1 1.5 -1 0.32 0.36 +0.17 0.68 -0.69632 0.5 -1 1.5 -1 0.34 0.32 +0.18 0.72 -0.64512 0.5 -1 1.5 -1 0.36 0.28 +0.19 0.76 -0.58368 0.5 -1 1.5 -1 0.38 0.24 +0.2 0.8 -0.512 0.5 -1 1.5 -1 0.4 0.2 +0.21 0.84 -0.43008 0.5 -1 1.5 -1 0.42 0.16 +0.22 0.88 -0.33792 0.5 -1 1.5 -1 0.44 0.12 +0.23 0.92 -0.23552 0.5 -1 1.5 -1 0.46 0.0799999 +0.24 0.96 -0.12288 0.5 -1 1.5 -1 0.48 0.0399998 +0.25 1 3.8147e-07 0.5 1 -0.5 -1 0.5 -1.19209e-07 +0.26 0.96 0.12288 0.5 1 -0.5 -1 0.52 -0.0400001 +0.27 0.92 0.23552 0.5 1 -0.5 -1 0.539999 -0.08 +0.28 0.88 0.33792 0.5 1 -0.5 -1 0.559999 -0.12 +0.29 0.84 0.43008 0.5 1 -0.5 -1 0.579999 -0.16 +0.3 0.8 0.512 0.5 1 -0.5 -1 0.599999 -0.2 +0.31 0.76 0.58368 0.5 1 -0.5 -1 0.619999 -0.24 +0.32 0.72 0.64512 0.5 1 -0.5 -1 0.639999 -0.28 +0.33 0.68 0.69632 0.5 1 -0.5 -1 0.659999 -0.32 +0.34 0.64 0.73728 0.5 1 -0.5 -1 0.679999 -0.36 +0.35 0.6 0.768 0.5 1 -0.5 -1 0.699999 -0.4 +0.36 0.56 0.78848 0.5 1 -0.5 -1 0.719999 -0.44 +0.37 0.52 0.79872 0.5 1 -0.5 -1 0.739999 -0.48 +0.38 0.48 0.79872 0.5 -1 -0.5 -1 0.759999 -0.52 +0.39 0.44 0.78848 0.5 -1 -0.5 -1 0.779999 -0.56 +0.4 0.4 0.768 0.5 -1 -0.5 -1 0.799999 -0.6 +0.41 0.36 0.73728 0.5 -1 -0.5 -1 0.819999 -0.64 +0.42 0.320001 0.696321 0.5 -1 -0.5 -1 0.839999 -0.679999 +0.43 0.280001 0.645121 0.5 -1 -0.5 -1 0.859999 -0.719999 +0.44 0.240001 0.583681 0.5 -1 -0.5 -1 0.879999 -0.759999 +0.45 0.200001 0.512001 0.5 -1 -0.5 -1 0.899999 -0.799999 +0.46 0.160001 0.430081 0.5 -1 -0.5 -1 0.919999 -0.839999 +0.47 0.120001 0.337922 0.5 -1 -0.5 -1 0.939999 -0.879999 +0.48 0.0800008 0.235522 0.5 -1 -0.5 -1 0.959999 -0.919999 +0.49 0.0400008 0.122882 0.5 -1 -0.5 -1 0.979999 -0.959999 +0.5 8.34465e-07 2.67029e-06 0.5 1 -0.5 -1 0.999999 -0.999999 +0.51 -0.0399992 -0.122878 0.5 1 -0.5 1 -0.980001 0.960001 +0.52 -0.0799992 -0.235518 0.5 1 -0.5 1 -0.960001 0.920001 +0.53 -0.119999 -0.337918 0.5 1 -0.5 1 -0.940001 0.880001 +0.54 -0.159999 -0.430078 0.5 1 -0.5 1 -0.920001 0.840001 +0.55 -0.199999 -0.511998 0.5 1 -0.5 1 -0.900001 0.800001 +0.56 -0.239999 -0.583679 0.5 1 -0.5 1 -0.880001 0.760001 +0.57 -0.279999 -0.645119 0.5 1 -0.5 -1 -0.860001 0.720001 +0.58 -0.319999 -0.696319 0.5 1 -0.5 -1 -0.840001 0.680001 +0.59 -0.359999 -0.737279 0.5 1 -0.5 -1 -0.820001 0.640001 +0.6 -0.399999 -0.767999 0.5 1 -0.5 -1 -0.800001 0.600001 +0.61 -0.439999 -0.78848 0.5 1 -0.5 -1 -0.780001 0.560001 +0.62 -0.479999 -0.79872 0.5 1 -0.5 -1 -0.760001 0.520001 +0.63 -0.519999 -0.79872 0.5 -1 -0.5 -1 -0.740001 0.480001 +0.64 -0.559999 -0.78848 0.5 -1 -0.5 -1 -0.720001 0.440001 +0.65 -0.599999 -0.768001 0.5 -1 -0.5 -1 -0.700001 0.400001 +0.66 -0.639999 -0.737281 0.5 -1 -0.5 -1 -0.680001 0.360001 +0.67 -0.679999 -0.696321 0.5 -1 -0.5 -1 -0.660001 0.320001 +0.68 -0.719999 -0.645121 0.5 -1 -0.5 -1 -0.640001 0.280001 +0.69 -0.759999 -0.583681 0.5 -1 -0.5 -1 -0.620001 0.240001 +0.7 -0.799999 -0.512001 0.5 -1 -0.5 -1 -0.600001 0.200001 +0.71 -0.839998 -0.430081 0.5 -1 -0.5 -1 -0.580001 0.160001 +0.72 -0.879998 -0.337921 0.5 -1 -0.5 -1 -0.560001 0.120001 +0.73 -0.919998 -0.235522 0.5 -1 -0.5 -1 -0.540001 0.0800006 +0.74 -0.959998 -0.122882 0.5 -1 -0.5 -1 -0.520001 0.0400006 +0.75 -0.999998 -1.71661e-06 0.5 1 -0.5 -1 -0.500001 5.36442e-07 +0.76 -0.960002 0.122878 -0.5 1 -0.5 -1 -0.480001 -0.0399995 +0.77 -0.920002 0.235519 -0.5 1 -0.5 -1 -0.460001 -0.0799994 +0.78 -0.880002 0.337919 -0.5 1 -0.5 -1 -0.440001 -0.119999 +0.79 -0.840002 0.430079 -0.5 1 -0.5 -1 -0.420001 -0.159999 +0.8 -0.800002 0.511999 -0.5 1 -0.5 -1 -0.400001 -0.199999 +0.81 -0.760002 0.583679 -0.5 1 -0.5 -1 -0.380001 -0.239999 +0.82 -0.720002 0.645119 -0.5 1 -0.5 -1 -0.360001 -0.279999 +0.83 -0.680002 0.696319 -0.5 1 -0.5 -1 -0.340001 -0.319999 +0.84 -0.640002 0.737279 -0.5 1 -0.5 -1 -0.320001 -0.359999 +0.85 -0.600002 0.767999 -0.5 1 -0.5 -1 -0.300001 -0.399999 +0.86 -0.560002 0.78848 -0.5 1 -0.5 -1 -0.280001 -0.439999 +0.87 -0.520002 0.79872 -0.5 1 -0.5 -1 -0.260001 -0.479999 +0.88 -0.480002 0.79872 -0.5 -1 -0.5 -1 -0.240001 -0.519999 +0.89 -0.440002 0.78848 -0.5 -1 -0.5 -1 -0.220001 -0.559999 +0.9 -0.400002 0.768001 -0.5 -1 -0.5 -1 -0.200001 -0.599999 +0.91 -0.360002 0.737281 -0.5 -1 -0.5 -1 -0.180001 -0.639999 +0.92 -0.320002 0.696321 -0.5 -1 -0.5 -1 -0.160001 -0.679999 +0.93 -0.280002 0.645122 -0.5 -1 -0.5 -1 -0.140001 -0.719999 +0.94 -0.240002 0.583682 -0.5 -1 -0.5 -1 -0.120001 -0.759999 +0.95 -0.200002 0.512002 -0.5 -1 -0.5 -1 -0.100001 -0.799999 +0.96 -0.160002 0.430083 -0.5 -1 -0.5 -1 -0.0800012 -0.839999 +0.97 -0.120003 0.337923 -0.5 -1 -0.5 -1 -0.0600013 -0.879999 +0.98 -0.0800025 0.235524 -0.5 -1 -0.5 -1 -0.0400013 -0.919999 +0.99 -0.0400026 0.122884 -0.5 -1 -0.5 -1 -0.0200013 -0.959999 +1 -2.6226e-06 4.57763e-06 -0.5 1 -0.5 -1 -1.3113e-06 -0.999999 +1.01 0.0399976 -0.122876 0.5 1 1.5 1 0.0199987 0.960001 +1.02 0.0799976 -0.235516 0.5 1 1.5 1 0.0399987 0.920001 +1.03 0.119998 -0.337916 0.5 1 1.5 1 0.0599986 0.880001 +1.04 0.159998 -0.430077 0.5 1 1.5 1 0.0799986 0.840001 +1.05 0.199998 -0.511997 0.5 1 1.5 1 0.0999986 0.800002 +1.06 0.239998 -0.583678 0.5 1 1.5 1 0.119999 0.760001 +1.07 0.279998 -0.645118 0.5 1 1.5 -1 0.139999 0.720001 +1.08 0.319998 -0.696318 0.5 1 1.5 -1 0.159999 0.680001 +1.09 0.359998 -0.737279 0.5 1 1.5 -1 0.179999 0.640002 +1.1 0.399998 -0.767999 0.5 1 1.5 -1 0.199998 0.600002 +1.11 0.439998 -0.788479 0.5 1 1.5 -1 0.219998 0.560001 +1.12 0.479998 -0.79872 0.5 1 1.5 -1 0.239998 0.520002 +1.13 0.519998 -0.79872 0.5 -1 1.5 -1 0.259998 0.480002 +1.14 0.559998 -0.788481 0.5 -1 1.5 -1 0.279998 0.440001 +1.15 0.599998 -0.768001 0.5 -1 1.5 -1 0.299998 0.400001 +1.16 0.639998 -0.737281 0.5 -1 1.5 -1 0.319998 0.360001 +1.17 0.679998 -0.696322 0.5 -1 1.5 -1 0.339998 0.320001 +1.18 0.719998 -0.645122 0.5 -1 1.5 -1 0.359998 0.280001 +1.19 0.759998 -0.583682 0.5 -1 1.5 -1 0.379998 0.240001 +1.2 0.799998 -0.512003 0.5 -1 1.5 -1 0.399998 0.200001 +1.21 0.839998 -0.430083 0.5 -1 1.5 -1 0.419998 0.160001 +1.22 0.879998 -0.337923 0.5 -1 1.5 -1 0.439998 0.120001 +1.23 0.919998 -0.235523 0.5 -1 1.5 -1 0.459998 0.0800013 +1.24 0.959998 -0.122884 0.5 -1 1.5 -1 0.479998 0.0400013 +1.25 0.999998 -4.00543e-06 0.5 1 1.5 -1 0.499998 1.2517e-06 +1.26 0.960002 0.122876 0.5 1 -0.5 -1 0.519998 -0.0399988 +1.27 0.920002 0.235517 0.5 1 -0.5 -1 0.539998 -0.0799987 +1.28 0.880002 0.337917 0.5 1 -0.5 -1 0.559998 -0.119999 +1.29 0.840002 0.430077 0.5 1 -0.5 -1 0.579998 -0.159999 +1.3 0.800002 0.511997 0.5 1 -0.5 -1 0.599998 -0.199999 +1.31 0.760002 0.583678 0.5 1 -0.5 -1 0.619998 -0.239999 +1.32 0.720002 0.645118 0.5 1 -0.5 -1 0.639998 -0.279999 +1.33 0.680002 0.696318 0.5 1 -0.5 -1 0.659998 -0.319999 +1.34 0.640002 0.737279 0.5 1 -0.5 -1 0.679998 -0.359998 +1.35 0.600003 0.767999 0.5 1 -0.5 -1 0.699998 -0.399998 +1.36 0.560003 0.788479 0.5 1 -0.5 -1 0.719998 -0.439998 +1.37 0.520003 0.79872 0.5 1 -0.5 -1 0.739998 -0.479998 +1.38 0.480003 0.79872 0.5 -1 -0.5 -1 0.759998 -0.519998 +1.39 0.440003 0.788481 0.5 -1 -0.5 -1 0.779998 -0.559998 +1.4 0.400003 0.768001 0.5 -1 -0.5 -1 0.799998 -0.599998 +1.41 0.360003 0.737282 0.5 -1 -0.5 -1 0.819998 -0.639998 +1.42 0.320003 0.696322 0.5 -1 -0.5 -1 0.839998 -0.679998 +1.43 0.280003 0.645123 0.5 -1 -0.5 -1 0.859998 -0.719998 +1.44 0.240003 0.583683 0.5 -1 -0.5 -1 0.879998 -0.759998 +1.45 0.200003 0.512004 0.5 -1 -0.5 -1 0.899998 -0.799998 +1.46 0.160003 0.430084 0.5 -1 -0.5 -1 0.919998 -0.839998 +1.47 0.120003 0.337925 0.5 -1 -0.5 -1 0.939998 -0.879998 +1.48 0.080003 0.235526 0.5 -1 -0.5 -1 0.959998 -0.919998 +1.49 0.0400031 0.122886 0.5 -1 -0.5 -1 0.979998 -0.959998 +1.5 3.09944e-06 6.86644e-06 0.5 1 -0.5 -1 0.999998 -0.999998 +1.51 -0.0399969 -0.122874 0.5 1 -0.5 1 -0.980002 0.960002 +1.52 -0.0799968 -0.235514 0.5 1 -0.5 1 -0.960002 0.920002 +1.53 -0.119997 -0.337915 0.5 1 -0.5 1 -0.940002 0.880002 +1.54 -0.159997 -0.430075 0.5 1 -0.5 1 -0.920002 0.840002 +1.55 -0.199997 -0.511996 0.5 1 -0.5 1 -0.900002 0.800002 +1.56 -0.239997 -0.583676 0.5 1 -0.5 1 -0.880002 0.760002 +1.57 -0.279997 -0.645117 0.5 1 -0.5 -1 -0.860002 0.720002 +1.58 -0.319997 -0.696317 0.5 1 -0.5 -1 -0.840002 0.680002 +1.59 -0.359997 -0.737278 0.5 1 -0.5 -1 -0.820002 0.640002 +1.6 -0.399997 -0.767999 0.5 1 -0.5 -1 -0.800002 0.600002 +1.61 -0.439996 -0.788479 0.5 1 -0.5 -1 -0.780002 0.560002 +1.62 -0.479996 -0.79872 0.5 1 -0.5 -1 -0.760002 0.520002 +1.63 -0.519996 -0.79872 0.5 -1 -0.5 -1 -0.740002 0.480002 +1.64 -0.559996 -0.788481 0.5 -1 -0.5 -1 -0.720002 0.440002 +1.65 -0.599996 -0.768001 0.5 -1 -0.5 -1 -0.700002 0.400002 +1.66 -0.639996 -0.737282 0.5 -1 -0.5 -1 -0.680002 0.360002 +1.67 -0.679996 -0.696322 0.5 -1 -0.5 -1 -0.660002 0.320002 +1.68 -0.719996 -0.645123 0.5 -1 -0.5 -1 -0.640002 0.280002 +1.69 -0.759996 -0.583683 0.5 -1 -0.5 -1 -0.620002 0.240002 +1.7 -0.799996 -0.512004 0.5 -1 -0.5 -1 -0.600002 0.200002 +1.71 -0.839996 -0.430084 0.5 -1 -0.5 -1 -0.580002 0.160002 +1.72 -0.879996 -0.337925 0.5 -1 -0.5 -1 -0.560002 0.120002 +1.73 -0.919996 -0.235525 0.5 -1 -0.5 -1 -0.540002 0.080002 +1.74 -0.959996 -0.122886 0.5 -1 -0.5 -1 -0.520002 0.040002 +1.75 -0.999996 -6.29424e-06 0.5 1 -0.5 -1 -0.500002 1.96695e-06 +1.76 -0.960004 0.122874 -0.5 1 -0.5 -1 -0.480002 -0.0399981 +1.77 -0.920004 0.235515 -0.5 1 -0.5 -1 -0.460002 -0.079998 +1.78 -0.880004 0.337915 -0.5 1 -0.5 -1 -0.440002 -0.119998 +1.79 -0.840004 0.430076 -0.5 1 -0.5 -1 -0.420002 -0.159998 +1.8 -0.800004 0.511996 -0.5 1 -0.5 -1 -0.400002 -0.199998 +1.81 -0.760004 0.583676 -0.5 1 -0.5 -1 -0.380002 -0.239998 +1.82 -0.720004 0.645117 -0.5 1 -0.5 -1 -0.360002 -0.279998 +1.83 -0.680004 0.696317 -0.5 1 -0.5 -1 -0.340002 -0.319998 +1.84 -0.640004 0.737278 -0.5 1 -0.5 -1 -0.320002 -0.359998 +1.85 -0.600004 0.767999 -0.5 1 -0.5 -1 -0.300002 -0.399998 +1.86 -0.560004 0.788479 -0.5 1 -0.5 -1 -0.280002 -0.439998 +1.87 -0.520005 0.79872 -0.5 1 -0.5 -1 -0.260002 -0.479998 +1.88 -0.480005 0.79872 -0.5 -1 -0.5 -1 -0.240002 -0.519998 +1.89 -0.440005 0.788481 -0.5 -1 -0.5 -1 -0.220002 -0.559998 +1.9 -0.400005 0.768002 -0.5 -1 -0.5 -1 -0.200002 -0.599998 +1.91 -0.360005 0.737282 -0.5 -1 -0.5 -1 -0.180002 -0.639997 +1.92 -0.320005 0.696323 -0.5 -1 -0.5 -1 -0.160002 -0.679997 +1.93 -0.280005 0.645124 -0.5 -1 -0.5 -1 -0.140002 -0.719997 +1.94 -0.240005 0.583684 -0.5 -1 -0.5 -1 -0.120002 -0.759997 +1.95 -0.200005 0.512005 -0.5 -1 -0.5 -1 -0.100002 -0.799997 +1.96 -0.160005 0.430086 -0.5 -1 -0.5 -1 -0.0800024 -0.839997 +1.97 -0.120005 0.337927 -0.5 -1 -0.5 -1 -0.0600024 -0.879997 +1.98 -0.0800049 0.235527 -0.5 -1 -0.5 -1 -0.0400025 -0.919997 +1.99 -0.040005 0.122888 -0.5 -1 -0.5 -1 -0.0200025 -0.959997 +2 -5.00679e-06 9.15525e-06 -0.5 1 -0.5 -1 -2.5034e-06 -0.999997 +2.01 0.0399952 -0.122871 0.5 1 1.5 1 0.0199975 0.960003 +2.02 0.0799952 -0.235512 0.5 1 1.5 1 0.0399975 0.920003 +2.03 0.119995 -0.337913 0.5 1 1.5 1 0.0599974 0.880003 +2.04 0.159995 -0.430074 0.5 1 1.5 1 0.0799974 0.840003 +2.05 0.199995 -0.511994 0.5 1 1.5 1 0.0999974 0.800003 +2.06 0.239995 -0.583675 0.5 1 1.5 1 0.119997 0.760003 +2.07 0.279995 -0.645116 0.5 1 1.5 -1 0.139997 0.720003 +2.08 0.319995 -0.696317 0.5 1 1.5 -1 0.159997 0.680003 +2.09 0.359995 -0.737277 0.5 1 1.5 -1 0.179997 0.640003 +2.1 0.399995 -0.767998 0.5 1 1.5 -1 0.199997 0.600003 +2.11 0.439995 -0.788479 0.5 1 1.5 -1 0.219997 0.560003 +2.12 0.479995 -0.79872 0.5 1 1.5 -1 0.239997 0.520003 +2.13 0.519995 -0.79872 0.5 -1 1.5 -1 0.259997 0.480003 +2.14 0.559995 -0.788481 0.5 -1 1.5 -1 0.279997 0.440003 +2.15 0.599995 -0.768002 0.5 -1 1.5 -1 0.299997 0.400003 +2.16 0.639995 -0.737283 0.5 -1 1.5 -1 0.319997 0.360003 +2.17 0.679995 -0.696323 0.5 -1 1.5 -1 0.339997 0.320003 +2.18 0.719995 -0.645124 0.5 -1 1.5 -1 0.359997 0.280003 +2.19 0.759995 -0.583685 0.5 -1 1.5 -1 0.379997 0.240003 +2.2 0.799995 -0.512005 0.5 -1 1.5 -1 0.399997 0.200003 +2.21 0.839995 -0.430086 0.5 -1 1.5 -1 0.419997 0.160003 +2.22 0.879995 -0.337927 0.5 -1 1.5 -1 0.439997 0.120003 +2.23 0.919995 -0.235527 0.5 -1 1.5 -1 0.459997 0.0800027 +2.24 0.959995 -0.122888 0.5 -1 1.5 -1 0.479997 0.0400027 +2.25 0.999995 -8.58305e-06 0.5 1 1.5 -1 0.499997 2.68221e-06 +2.26 0.960005 0.122872 0.5 1 -0.5 -1 0.519997 -0.0399973 +2.27 0.920005 0.235513 0.5 1 -0.5 -1 0.539997 -0.0799973 +2.28 0.880005 0.337913 0.5 1 -0.5 -1 0.559997 -0.119997 +2.29 0.840005 0.430074 0.5 1 -0.5 -1 0.579997 -0.159997 +2.3 0.800005 0.511995 0.5 1 -0.5 -1 0.599997 -0.199997 +2.31 0.760005 0.583675 0.5 1 -0.5 -1 0.619997 -0.239997 +2.32 0.720005 0.645116 0.5 1 -0.5 -1 0.639997 -0.279997 +2.33 0.680005 0.696317 0.5 1 -0.5 -1 0.659997 -0.319997 +2.34 0.640005 0.737277 0.5 1 -0.5 -1 0.679997 -0.359997 +2.35 0.600005 0.767998 0.5 1 -0.5 -1 0.699997 -0.399997 +2.36 0.560005 0.788479 0.5 1 -0.5 -1 0.719997 -0.439997 +2.37 0.520005 0.79872 0.5 1 -0.5 -1 0.739997 -0.479997 +2.38 0.480005 0.79872 0.5 -1 -0.5 -1 0.759997 -0.519997 +2.39 0.440005 0.788481 0.5 -1 -0.5 -1 0.779997 -0.559997 +2.4 0.400005 0.768002 0.5 -1 -0.5 -1 0.799997 -0.599997 +2.41 0.360005 0.737283 0.5 -1 -0.5 -1 0.819997 -0.639997 +2.42 0.320005 0.696324 0.5 -1 -0.5 -1 0.839997 -0.679997 +2.43 0.280005 0.645125 0.5 -1 -0.5 -1 0.859997 -0.719997 +2.44 0.240005 0.583686 0.5 -1 -0.5 -1 0.879997 -0.759997 +2.45 0.200005 0.512007 0.5 -1 -0.5 -1 0.899997 -0.799997 +2.46 0.160005 0.430087 0.5 -1 -0.5 -1 0.919997 -0.839997 +2.47 0.120005 0.337928 0.5 -1 -0.5 -1 0.939997 -0.879997 +2.48 0.0800054 0.235529 0.5 -1 -0.5 -1 0.959997 -0.919997 +2.49 0.0400054 0.12289 0.5 -1 -0.5 -1 0.979997 -0.959996 +2.5 5.48363e-06 1.14441e-05 0.5 1 -0.5 -1 0.999997 -0.999996 +2.51 -0.0399945 -0.122869 0.5 1 -0.5 1 -0.980003 0.960004 +2.52 -0.0799944 -0.23551 0.5 1 -0.5 1 -0.960003 0.920004 +2.53 -0.119994 -0.337911 0.5 1 -0.5 1 -0.940003 0.880004 +2.54 -0.159994 -0.430072 0.5 1 -0.5 1 -0.920003 0.840004 +2.55 -0.199994 -0.511993 0.5 1 -0.5 1 -0.900003 0.800004 +2.56 -0.239994 -0.583674 0.5 1 -0.5 1 -0.880003 0.760004 +2.57 -0.279994 -0.645115 0.5 1 -0.5 -1 -0.860003 0.720004 +2.58 -0.319994 -0.696316 0.5 1 -0.5 -1 -0.840003 0.680004 +2.59 -0.359994 -0.737277 0.5 1 -0.5 -1 -0.820003 0.640004 +2.6 -0.399994 -0.767998 0.5 1 -0.5 -1 -0.800003 0.600004 +2.61 -0.439994 -0.788479 0.5 1 -0.5 -1 -0.780003 0.560004 +2.62 -0.479994 -0.79872 0.5 1 -0.5 -1 -0.760003 0.520004 +2.63 -0.519994 -0.79872 0.5 -1 -0.5 -1 -0.740003 0.480004 +2.64 -0.559994 -0.788481 0.5 -1 -0.5 -1 -0.720003 0.440004 +2.65 -0.599994 -0.768002 0.5 -1 -0.5 -1 -0.700003 0.400004 +2.66 -0.639994 -0.737283 0.5 -1 -0.5 -1 -0.680003 0.360004 +2.67 -0.679994 -0.696324 0.5 -1 -0.5 -1 -0.660003 0.320004 +2.68 -0.719994 -0.645125 0.5 -1 -0.5 -1 -0.640003 0.280004 +2.69 -0.759994 -0.583686 0.5 -1 -0.5 -1 -0.620003 0.240004 +2.7 -0.799994 -0.512007 0.5 -1 -0.5 -1 -0.600003 0.200004 +2.71 -0.839994 -0.430088 0.5 -1 -0.5 -1 -0.580003 0.160003 +2.72 -0.879994 -0.337928 0.5 -1 -0.5 -1 -0.560003 0.120003 +2.73 -0.919994 -0.235529 0.5 -1 -0.5 -1 -0.540003 0.0800034 +2.74 -0.959994 -0.12289 0.5 -1 -0.5 -1 -0.520003 0.0400034 +2.75 -0.999994 -1.08719e-05 0.5 1 -0.5 -1 -0.500003 3.39746e-06 +2.76 -0.960006 0.12287 -0.5 1 -0.5 -1 -0.480003 -0.0399966 +2.77 -0.920007 0.235511 -0.5 1 -0.5 -1 -0.460003 -0.0799966 +2.78 -0.880007 0.337912 -0.5 1 -0.5 -1 -0.440003 -0.119997 +2.79 -0.840007 0.430072 -0.5 1 -0.5 -1 -0.420003 -0.159997 +2.8 -0.800007 0.511993 -0.5 1 -0.5 -1 -0.400003 -0.199996 +2.81 -0.760007 0.583674 -0.5 1 -0.5 -1 -0.380003 -0.239996 +2.82 -0.720007 0.645115 -0.5 1 -0.5 -1 -0.360003 -0.279996 +2.83 -0.680007 0.696316 -0.5 1 -0.5 -1 -0.340003 -0.319996 +2.84 -0.640007 0.737277 -0.5 1 -0.5 -1 -0.320003 -0.359996 +2.85 -0.600007 0.767998 -0.5 1 -0.5 -1 -0.300003 -0.399996 +2.86 -0.560007 0.788479 -0.5 1 -0.5 -1 -0.280003 -0.439996 +2.87 -0.520007 0.79872 -0.5 1 -0.5 -1 -0.260003 -0.479996 +2.88 -0.480007 0.79872 -0.5 -1 -0.5 -1 -0.240003 -0.519996 +2.89 -0.440007 0.788482 -0.5 -1 -0.5 -1 -0.220003 -0.559996 +2.9 -0.400007 0.768003 -0.5 -1 -0.5 -1 -0.200004 -0.599996 +2.91 -0.360007 0.737284 -0.5 -1 -0.5 -1 -0.180004 -0.639996 +2.92 -0.320007 0.696325 -0.5 -1 -0.5 -1 -0.160004 -0.679996 +2.93 -0.280007 0.645126 -0.5 -1 -0.5 -1 -0.140004 -0.719996 +2.94 -0.240007 0.583687 -0.5 -1 -0.5 -1 -0.120004 -0.759996 +2.95 -0.200007 0.512008 -0.5 -1 -0.5 -1 -0.100004 -0.799996 +2.96 -0.160007 0.430089 -0.5 -1 -0.5 -1 -0.0800036 -0.839996 +2.97 -0.120007 0.33793 -0.5 -1 -0.5 -1 -0.0600036 -0.879996 +2.98 -0.0800073 0.235531 -0.5 -1 -0.5 -1 -0.0400037 -0.919996 +2.99 -0.0400074 0.122893 -0.5 -1 -0.5 -1 -0.0200037 -0.959996 +3 -7.39098e-06 1.37329e-05 -0.5 1 -0.5 -1 -3.69549e-06 -0.999996 +3.01 0.0399928 -0.122867 0.5 1 1.5 1 0.0199963 0.960004 +3.02 0.0799928 -0.235508 0.5 1 1.5 1 0.0399963 0.920004 +3.03 0.119993 -0.337909 0.5 1 1.5 1 0.0599962 0.880004 +3.04 0.159993 -0.430071 0.5 1 1.5 1 0.0799962 0.840004 +3.05 0.199993 -0.511992 0.5 1 1.5 1 0.0999962 0.800004 +3.06 0.239993 -0.583673 0.5 1 1.5 1 0.119996 0.760004 +3.07 0.279993 -0.645114 0.5 1 1.5 -1 0.139996 0.720004 +3.08 0.319993 -0.696315 0.5 1 1.5 -1 0.159996 0.680004 +3.09 0.359993 -0.737276 0.5 1 1.5 -1 0.179996 0.640004 +3.1 0.399993 -0.767997 0.5 1 1.5 -1 0.199996 0.600004 +3.11 0.439993 -0.788478 0.5 1 1.5 -1 0.219996 0.560004 +3.12 0.479993 -0.798719 0.5 1 1.5 -1 0.239996 0.520004 +3.13 0.519993 -0.798721 0.5 -1 1.5 -1 0.259996 0.480004 +3.14 0.559993 -0.788482 0.5 -1 1.5 -1 0.279996 0.440004 +3.15 0.599993 -0.768003 0.5 -1 1.5 -1 0.299996 0.400004 +3.16 0.639993 -0.737284 0.5 -1 1.5 -1 0.319996 0.360004 +3.17 0.679993 -0.696325 0.5 -1 1.5 -1 0.339996 0.320004 +3.18 0.719993 -0.645126 0.5 -1 1.5 -1 0.359996 0.280004 +3.19 0.759993 -0.583687 0.5 -1 1.5 -1 0.379996 0.240004 +3.2 0.799993 -0.512008 0.5 -1 1.5 -1 0.399996 0.200004 +3.21 0.839993 -0.430089 0.5 -1 1.5 -1 0.419996 0.160004 +3.22 0.879993 -0.33793 0.5 -1 1.5 -1 0.439996 0.120004 +3.23 0.919993 -0.235531 0.5 -1 1.5 -1 0.459996 0.0800042 +3.24 0.959993 -0.122892 0.5 -1 1.5 -1 0.479996 0.0400041 +3.25 0.999993 -1.31607e-05 0.5 1 1.5 -1 0.499996 4.11272e-06 +3.26 0.960007 0.122868 0.5 1 -0.5 -1 0.519996 -0.0399959 +3.27 0.920007 0.235509 0.5 1 -0.5 -1 0.539996 -0.0799959 +3.28 0.880007 0.33791 0.5 1 -0.5 -1 0.559996 -0.119996 +3.29 0.840007 0.430071 0.5 1 -0.5 -1 0.579996 -0.159996 +3.3 0.800007 0.511992 0.5 1 -0.5 -1 0.599996 -0.199996 +3.31 0.760007 0.583673 0.5 1 -0.5 -1 0.619996 -0.239996 +3.32 0.720007 0.645114 0.5 1 -0.5 -1 0.639996 -0.279996 +3.33 0.680007 0.696315 0.5 1 -0.5 -1 0.659996 -0.319996 +3.34 0.640007 0.737276 0.5 1 -0.5 -1 0.679996 -0.359996 +3.35 0.600007 0.767997 0.5 1 -0.5 -1 0.699996 -0.399996 +3.36 0.560007 0.788478 0.5 1 -0.5 -1 0.719996 -0.439996 +3.37 0.520007 0.798719 0.5 1 -0.5 -1 0.739996 -0.479995 +3.38 0.480007 0.798721 0.5 -1 -0.5 -1 0.759996 -0.519995 +3.39 0.440007 0.788482 0.5 -1 -0.5 -1 0.779996 -0.559995 +3.4 0.400007 0.768003 0.5 -1 -0.5 -1 0.799996 -0.599995 +3.41 0.360008 0.737284 0.5 -1 -0.5 -1 0.819996 -0.639995 +3.42 0.320008 0.696325 0.5 -1 -0.5 -1 0.839996 -0.679995 +3.43 0.280008 0.645127 0.5 -1 -0.5 -1 0.859995 -0.719995 +3.44 0.240008 0.583688 0.5 -1 -0.5 -1 0.879995 -0.759995 +3.45 0.200008 0.512009 0.5 -1 -0.5 -1 0.899995 -0.799995 +3.46 0.160008 0.430091 0.5 -1 -0.5 -1 0.919995 -0.839995 +3.47 0.120008 0.337932 0.5 -1 -0.5 -1 0.939995 -0.879995 +3.48 0.0800078 0.235533 0.5 -1 -0.5 -1 0.959995 -0.919995 +3.49 0.0400078 0.122895 0.5 -1 -0.5 -1 0.979995 -0.959995 +3.5 7.86781e-06 1.60216e-05 0.5 1 -0.5 -1 0.999995 -0.999995 +3.51 -0.0399921 -0.122865 0.5 1 -0.5 1 -0.980005 0.960005 +3.52 -0.0799921 -0.235507 0.5 1 -0.5 1 -0.960005 0.920005 +3.53 -0.119992 -0.337908 0.5 1 -0.5 1 -0.940005 0.880005 +3.54 -0.159992 -0.430069 0.5 1 -0.5 1 -0.920005 0.840005 +3.55 -0.199992 -0.51199 0.5 1 -0.5 1 -0.900005 0.800005 +3.56 -0.239992 -0.583672 0.5 1 -0.5 1 -0.880005 0.760005 +3.57 -0.279992 -0.645113 0.5 1 -0.5 -1 -0.860005 0.720005 +3.58 -0.319992 -0.696314 0.5 1 -0.5 -1 -0.840005 0.680005 +3.59 -0.359992 -0.737275 0.5 1 -0.5 -1 -0.820005 0.640005 +3.6 -0.399992 -0.767997 0.5 1 -0.5 -1 -0.800005 0.600005 +3.61 -0.439992 -0.788478 0.5 1 -0.5 -1 -0.780005 0.560005 +3.62 -0.479992 -0.798719 0.5 1 -0.5 -1 -0.760005 0.520005 +3.63 -0.519992 -0.798721 0.5 -1 -0.5 -1 -0.740005 0.480005 +3.64 -0.559992 -0.788482 0.5 -1 -0.5 -1 -0.720005 0.440005 +3.65 -0.599992 -0.768003 0.5 -1 -0.5 -1 -0.700005 0.400005 +3.66 -0.639992 -0.737284 0.5 -1 -0.5 -1 -0.680005 0.360005 +3.67 -0.679991 -0.696326 0.5 -1 -0.5 -1 -0.660004 0.320005 +3.68 -0.719991 -0.645127 0.5 -1 -0.5 -1 -0.640005 0.280005 +3.69 -0.759991 -0.583688 0.5 -1 -0.5 -1 -0.620005 0.240005 +3.7 -0.799991 -0.512009 0.5 -1 -0.5 -1 -0.600004 0.200005 +3.71 -0.839991 -0.430091 0.5 -1 -0.5 -1 -0.580004 0.160005 +3.72 -0.879991 -0.337932 0.5 -1 -0.5 -1 -0.560004 0.120005 +3.73 -0.919991 -0.235533 0.5 -1 -0.5 -1 -0.540004 0.0800049 +3.74 -0.959991 -0.122894 0.5 -1 -0.5 -1 -0.520004 0.0400048 +3.75 -0.999991 -1.54495e-05 0.5 1 -0.5 -1 -0.500004 4.82798e-06 +3.76 -0.960009 0.122866 -0.5 1 -0.5 -1 -0.480004 -0.0399952 +3.77 -0.920009 0.235507 -0.5 1 -0.5 -1 -0.460004 -0.0799952 +3.78 -0.880009 0.337908 -0.5 1 -0.5 -1 -0.440004 -0.119995 +3.79 -0.840009 0.430069 -0.5 1 -0.5 -1 -0.420004 -0.159995 +3.8 -0.800009 0.51199 -0.5 1 -0.5 -1 -0.400005 -0.199995 +3.81 -0.760009 0.583672 -0.5 1 -0.5 -1 -0.380005 -0.239995 +3.82 -0.720009 0.645113 -0.5 1 -0.5 -1 -0.360005 -0.279995 +3.83 -0.680009 0.696314 -0.5 1 -0.5 -1 -0.340005 -0.319995 +3.84 -0.640009 0.737275 -0.5 1 -0.5 -1 -0.320005 -0.359995 +3.85 -0.600009 0.767997 -0.5 1 -0.5 -1 -0.300005 -0.399995 +3.86 -0.560009 0.788478 -0.5 1 -0.5 -1 -0.280005 -0.439995 +3.87 -0.520009 0.798719 -0.5 1 -0.5 -1 -0.260005 -0.479995 +3.88 -0.480009 0.798721 -0.5 -1 -0.5 -1 -0.240005 -0.519995 +3.89 -0.440009 0.788482 -0.5 -1 -0.5 -1 -0.220005 -0.559995 +3.9 -0.400009 0.768003 -0.5 -1 -0.5 -1 -0.200005 -0.599995 +3.91 -0.360009 0.737285 -0.5 -1 -0.5 -1 -0.180005 -0.639995 +3.92 -0.320009 0.696326 -0.5 -1 -0.5 -1 -0.160005 -0.679995 +3.93 -0.28001 0.645128 -0.5 -1 -0.5 -1 -0.140005 -0.719995 +3.94 -0.24001 0.583689 -0.5 -1 -0.5 -1 -0.120005 -0.759995 +3.95 -0.20001 0.512011 -0.5 -1 -0.5 -1 -0.100005 -0.799994 +3.96 -0.16001 0.430092 -0.5 -1 -0.5 -1 -0.0800048 -0.839994 +3.97 -0.12001 0.337934 -0.5 -1 -0.5 -1 -0.0600048 -0.879994 +3.98 -0.0800097 0.235535 -0.5 -1 -0.5 -1 -0.0400048 -0.919994 +3.99 -0.0400097 0.122897 -0.5 -1 -0.5 -1 -0.0200049 -0.959994 +4 -9.77516e-06 1.83104e-05 -0.5 1 -0.5 -1 -4.88758e-06 -0.999994 +4.01 0.0399904 -0.122863 0.5 1 1.5 1 0.0199951 0.960006 +4.02 0.0799904 -0.235505 0.5 1 1.5 1 0.0399951 0.920006 +4.03 0.11999 -0.337906 0.5 1 1.5 1 0.0599951 0.880006 +4.04 0.15999 -0.430068 0.5 1 1.5 1 0.079995 0.840006 +4.05 0.19999 -0.511989 0.5 1 1.5 1 0.099995 0.800006 +4.06 0.23999 -0.58367 0.5 1 1.5 1 0.119995 0.760006 +4.07 0.27999 -0.645112 0.5 1 1.5 -1 0.139995 0.720006 +4.08 0.31999 -0.696313 0.5 1 1.5 -1 0.159995 0.680006 +4.09 0.35999 -0.737275 0.5 1 1.5 -1 0.179995 0.640006 +4.1 0.39999 -0.767996 0.5 1 1.5 -1 0.199995 0.600006 +4.11 0.43999 -0.788478 0.5 1 1.5 -1 0.219995 0.560006 +4.12 0.47999 -0.798719 0.5 1 1.5 -1 0.239995 0.520006 +4.13 0.51999 -0.798721 0.5 -1 1.5 -1 0.259995 0.480006 +4.14 0.55999 -0.788482 0.5 -1 1.5 -1 0.279995 0.440006 +4.15 0.59999 -0.768004 0.5 -1 1.5 -1 0.299995 0.400006 +4.16 0.63999 -0.737285 0.5 -1 1.5 -1 0.319995 0.360006 +4.17 0.67999 -0.696327 0.5 -1 1.5 -1 0.339995 0.320006 +4.18 0.71999 -0.645128 0.5 -1 1.5 -1 0.359995 0.280006 +4.19 0.759991 -0.583689 0.5 -1 1.5 -1 0.379995 0.240006 +4.2 0.799991 -0.512011 0.5 -1 1.5 -1 0.399995 0.200006 +4.21 0.839991 -0.430092 0.5 -1 1.5 -1 0.419995 0.160006 +4.22 0.879991 -0.337934 0.5 -1 1.5 -1 0.439995 0.120006 +4.23 0.919991 -0.235535 0.5 -1 1.5 -1 0.459995 0.0800056 +4.24 0.959991 -0.122896 0.5 -1 1.5 -1 0.479995 0.0400056 +4.25 0.999991 -1.77382e-05 0.5 1 1.5 -1 0.499995 5.54323e-06 +4.26 0.960009 0.122864 0.5 1 -0.5 -1 0.519995 -0.0399945 +4.27 0.920009 0.235505 0.5 1 -0.5 -1 0.539995 -0.0799944 +4.28 0.880009 0.337906 0.5 1 -0.5 -1 0.559995 -0.119994 +4.29 0.840009 0.430068 0.5 1 -0.5 -1 0.579995 -0.159994 +4.3 0.800009 0.511989 0.5 1 -0.5 -1 0.599995 -0.199994 +4.31 0.76001 0.58367 0.5 1 -0.5 -1 0.619995 -0.239994 +4.32 0.72001 0.645112 0.5 1 -0.5 -1 0.639995 -0.279994 +4.33 0.68001 0.696313 0.5 1 -0.5 -1 0.659994 -0.319994 +4.34 0.64001 0.737275 0.5 1 -0.5 -1 0.679994 -0.359994 +4.35 0.60001 0.767996 0.5 1 -0.5 -1 0.699994 -0.399994 +4.36 0.56001 0.788478 0.5 1 -0.5 -1 0.719994 -0.439994 +4.37 0.52001 0.798719 0.5 1 -0.5 -1 0.739994 -0.479994 +4.38 0.48001 0.798721 0.5 -1 -0.5 -1 0.759994 -0.519994 +4.39 0.44001 0.788482 0.5 -1 -0.5 -1 0.779994 -0.559994 +4.4 0.40001 0.768004 0.5 -1 -0.5 -1 0.799994 -0.599994 +4.41 0.36001 0.737285 0.5 -1 -0.5 -1 0.819994 -0.639994 +4.42 0.32001 0.696327 0.5 -1 -0.5 -1 0.839994 -0.679994 +4.43 0.28001 0.645129 0.5 -1 -0.5 -1 0.859994 -0.719994 +4.44 0.24001 0.58369 0.5 -1 -0.5 -1 0.879994 -0.759994 +4.45 0.20001 0.512012 0.5 -1 -0.5 -1 0.899994 -0.799994 +4.46 0.16001 0.430094 0.5 -1 -0.5 -1 0.919994 -0.839994 +4.47 0.12001 0.337935 0.5 -1 -0.5 -1 0.939994 -0.879994 +4.48 0.0800102 0.235537 0.5 -1 -0.5 -1 0.959994 -0.919994 +4.49 0.0400102 0.122899 0.5 -1 -0.5 -1 0.979994 -0.959994 +4.5 1.0252e-05 2.05992e-05 0.5 1 -0.5 -1 0.999994 -0.999994 +4.51 -0.0399897 -0.122861 0.5 1 -0.5 1 -0.980006 0.960006 +4.52 -0.0799897 -0.235503 0.5 1 -0.5 1 -0.960006 0.920006 +4.53 -0.11999 -0.337904 0.5 1 -0.5 1 -0.940006 0.880006 +4.54 -0.15999 -0.430066 0.5 1 -0.5 1 -0.920006 0.840006 +4.55 -0.19999 -0.511988 0.5 1 -0.5 1 -0.900006 0.800007 +4.56 -0.23999 -0.583669 0.5 1 -0.5 1 -0.880006 0.760006 +4.57 -0.279989 -0.645111 0.5 1 -0.5 -1 -0.860006 0.720006 +4.58 -0.319989 -0.696313 0.5 1 -0.5 -1 -0.840006 0.680007 +4.59 -0.359989 -0.737274 0.5 1 -0.5 -1 -0.820006 0.640007 +4.6 -0.399989 -0.767996 0.5 1 -0.5 -1 -0.800006 0.600007 +4.61 -0.439989 -0.788477 0.5 1 -0.5 -1 -0.780006 0.560006 +4.62 -0.479989 -0.798719 0.5 1 -0.5 -1 -0.760006 0.520007 +4.63 -0.519989 -0.798721 0.5 -1 -0.5 -1 -0.740006 0.480007 +4.64 -0.559989 -0.788483 0.5 -1 -0.5 -1 -0.720006 0.440006 +4.65 -0.599989 -0.768004 0.5 -1 -0.5 -1 -0.700006 0.400006 +4.66 -0.639989 -0.737286 0.5 -1 -0.5 -1 -0.680006 0.360006 +4.67 -0.679989 -0.696327 0.5 -1 -0.5 -1 -0.660006 0.320006 +4.68 -0.719989 -0.645129 0.5 -1 -0.5 -1 -0.640006 0.280006 +4.69 -0.759989 -0.583691 0.5 -1 -0.5 -1 -0.620006 0.240006 +4.7 -0.799989 -0.512012 0.5 -1 -0.5 -1 -0.600006 0.200006 +4.71 -0.839989 -0.430094 0.5 -1 -0.5 -1 -0.580006 0.160006 +4.72 -0.879989 -0.337935 0.5 -1 -0.5 -1 -0.560006 0.120006 +4.73 -0.919989 -0.235537 0.5 -1 -0.5 -1 -0.540006 0.0800063 +4.74 -0.959989 -0.122898 0.5 -1 -0.5 -1 -0.520006 0.0400063 +4.75 -0.999989 -2.0027e-05 0.5 1 -0.5 -1 -0.500006 6.25849e-06 +4.76 -0.960011 0.122862 -0.5 1 -0.5 -1 -0.480006 -0.0399938 +4.77 -0.920011 0.235503 -0.5 1 -0.5 -1 -0.460006 -0.0799937 +4.78 -0.880011 0.337905 -0.5 1 -0.5 -1 -0.440006 -0.119994 +4.79 -0.840011 0.430066 -0.5 1 -0.5 -1 -0.420006 -0.159994 +4.8 -0.800011 0.511988 -0.5 1 -0.5 -1 -0.400006 -0.199994 +4.81 -0.760011 0.583669 -0.5 1 -0.5 -1 -0.380006 -0.239994 +4.82 -0.720011 0.645111 -0.5 1 -0.5 -1 -0.360006 -0.279994 +4.83 -0.680012 0.696312 -0.5 1 -0.5 -1 -0.340006 -0.319993 +4.84 -0.640012 0.737274 -0.5 1 -0.5 -1 -0.320006 -0.359993 +4.85 -0.600012 0.767996 -0.5 1 -0.5 -1 -0.300006 -0.399993 +4.86 -0.560012 0.788477 -0.5 1 -0.5 -1 -0.280006 -0.439993 +4.87 -0.520012 0.798719 -0.5 1 -0.5 -1 -0.260006 -0.479993 +4.88 -0.480012 0.798721 -0.5 -1 -0.5 -1 -0.240006 -0.519993 +4.89 -0.440012 0.788483 -0.5 -1 -0.5 -1 -0.220006 -0.559993 +4.9 -0.400012 0.768004 -0.5 -1 -0.5 -1 -0.200006 -0.599993 +4.91 -0.360012 0.737286 -0.5 -1 -0.5 -1 -0.180006 -0.639993 +4.92 -0.320012 0.696328 -0.5 -1 -0.5 -1 -0.160006 -0.679993 +4.93 -0.280012 0.64513 -0.5 -1 -0.5 -1 -0.140006 -0.719993 +4.94 -0.240012 0.583692 -0.5 -1 -0.5 -1 -0.120006 -0.759993 +4.95 -0.200012 0.512013 -0.5 -1 -0.5 -1 -0.100006 -0.799993 +4.96 -0.160012 0.430095 -0.5 -1 -0.5 -1 -0.080006 -0.839993 +4.97 -0.120012 0.337937 -0.5 -1 -0.5 -1 -0.060006 -0.879993 +4.98 -0.0800121 0.235539 -0.5 -1 -0.5 -1 -0.040006 -0.919993 +4.99 -0.0400121 0.122901 -0.5 -1 -0.5 -1 -0.0200061 -0.959993 diff --git a/tests/lfo/plot_lfo.py b/tests/lfo/plot_lfo.py new file mode 100755 index 000000000..34c03df83 --- /dev/null +++ b/tests/lfo/plot_lfo.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +import numpy as np +import matplotlib.pyplot as plt +from argparse import ArgumentParser +import os + +parser = ArgumentParser(usage="Compare 2 files as outputted by sfizz_plot_lfo") +parser.add_argument("file", help="The file to test") +parser.add_argument("reference", help="The reference file") +args = parser.parse_args() + +assert os.path.exists(args.file), "The file to test does not exist" +assert os.path.exists(args.reference), "The reference file does not exist" + +reference_data = np.loadtxt(args.reference) +data = np.loadtxt(args.file) + +assert reference_data.shape == data.shape, "The shapes of the data and reference are different" + +n_samples, n_lfos = reference_data.shape +n_lfos -= 1 # The first column is the time + +if n_lfos == 1: + fig, ax = plt.subplots(figsize=(20, 10)) + ax.plot(reference_data[:, 0], reference_data[:, 1]) + ax.plot(data[:, 0], data[:, 1]) + ax.grid() +elif n_lfos > 4: + n_cols = 4 + n_rows = n_lfos // n_cols + print(n_rows, n_cols) + fig, ax = plt.subplots(n_rows, n_cols, figsize=(20, 10)) + for i in range(n_rows): + for j in range(n_cols): + lfo_index = 1 + i * 4 + j + ax[i, j].plot(reference_data[:, 0], reference_data[:, lfo_index]) + ax[i, j].plot(data[:, 0], data[:, lfo_index]) + ax[i, j].set_title("LFO {}".format(lfo_index)) + ax[i, j].grid() +else: + fig, ax = plt.subplots(1, n_lfos, figsize=(20, 10)) + for i in range(n_lfos): + lfo_index = i + 1 + ax[i].plot(reference_data[:, 0], reference_data[:, lfo_index]) + ax[i].plot(data[:, 0], data[:, lfo_index]) + ax[i].set_title("LFO {}".format(lfo_index)) + ax[i].grid() + +plt.show()