commit d26ec5da70dd74f3e8df4326105ee14044cee5e2
parent 079511c3a3ab67b54aba42ab6d17153e91d4e402
Author: Matt Demanett <matt@demanett.net>
Date: Tue, 24 Sep 2019 22:29:56 -0400
Poly: *FO.
Diffstat:
9 files changed, 276 insertions(+), 181 deletions(-)
diff --git a/src/EightFO.cpp b/src/EightFO.cpp
@@ -10,14 +10,30 @@ const Phasor::phase_delta_t basePhase2Offset = Phasor::radiansToPhase(0.5f * M_P
const Phasor::phase_delta_t basePhase1Offset = Phasor::radiansToPhase(0.25f * M_PI);
const Phasor::phase_delta_t basePhase0Offset = Phasor::radiansToPhase(0.0f);
+void EightFO::Engine::reset() {
+ resetTrigger.reset();
+ sampleStep = phasor._sampleRate;
+}
+
+void EightFO::Engine::sampleRateChange() {
+ phasor.setSampleRate(APP->engine->getSampleRate());
+ sampleStep = phasor._sampleRate;
+}
+
void EightFO::reset() {
- _resetTrigger.reset();
- _sampleStep = _phasor._sampleRate;
+ for (int c = 0; c < maxChannels; ++c) {
+ if (_engines[c]) {
+ _engines[c]->reset();
+ }
+ }
}
void EightFO::sampleRateChange() {
- _phasor.setSampleRate(APP->engine->getSampleRate());
- _sampleStep = _phasor._sampleRate;
+ for (int c = 0; c < maxChannels; ++c) {
+ if (_engines[c]) {
+ _engines[c]->sampleRateChange();
+ }
+ }
}
bool EightFO::active() {
@@ -33,93 +49,112 @@ bool EightFO::active() {
);
}
-void EightFO::modulate() {
- setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _phasor);
+int EightFO::channels() {
+ return std::max(1, inputs[PITCH_INPUT].getChannels());
+}
+
+void EightFO::addEngine(int c) {
+ _engines[c] = new Engine();
+ _engines[c]->reset();
+ _engines[c]->sampleRateChange();
+}
+
+void EightFO::removeEngine(int c) {
+ delete _engines[c];
+ _engines[c] = NULL;
+}
+void EightFO::modulate() {
_wave = (Wave)roundf(params[WAVE_PARAM].getValue());
+}
+
+void EightFO::modulateChannel(int c) {
+ setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _engines[c]->phasor, c);
+
if (_wave == SQUARE_WAVE) {
float pw = params[SAMPLE_PWM_PARAM].getValue();
if (inputs[SAMPLE_PWM_INPUT].isConnected()) {
- pw *= clamp(inputs[SAMPLE_PWM_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f);
+ pw *= clamp(inputs[SAMPLE_PWM_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f);
}
- pw *= 1.0f - 2.0f * _square.minPulseWidth;
+ pw *= 1.0f - 2.0f * _engines[c]->square.minPulseWidth;
pw *= 0.5f;
pw += 0.5f;
- _square.setPulseWidth(pw);
- _sampleSteps = 1;
+ _engines[c]->square.setPulseWidth(pw);
+ _engines[c]->sampleSteps = 1;
}
else {
float sample = fabsf(params[SAMPLE_PWM_PARAM].getValue());
if (inputs[SAMPLE_PWM_INPUT].isConnected()) {
- sample *= clamp(fabsf(inputs[SAMPLE_PWM_INPUT].getVoltage()) / 5.0f, 0.0f, 1.0f);
+ sample *= clamp(fabsf(inputs[SAMPLE_PWM_INPUT].getPolyVoltage(c)) / 5.0f, 0.0f, 1.0f);
}
- float maxSampleSteps = (_phasor._sampleRate / _phasor._frequency) / 4.0f;
- _sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps);
- _square.setPulseWidth(SquareOscillator::defaultPulseWidth);
+ float maxSampleSteps = (_engines[c]->phasor._sampleRate / _engines[c]->phasor._frequency) / 4.0f;
+ _engines[c]->sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps);
+ _engines[c]->square.setPulseWidth(SquareOscillator::defaultPulseWidth);
}
- _offset = params[OFFSET_PARAM].getValue();
+ _engines[c]->offset = params[OFFSET_PARAM].getValue();
if (inputs[OFFSET_INPUT].isConnected()) {
- _offset *= clamp(inputs[OFFSET_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f);
+ _engines[c]->offset *= clamp(inputs[OFFSET_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f);
}
- _offset *= 5.0f;
- _scale = params[SCALE_PARAM].getValue();
+ _engines[c]->offset *= 5.0f;
+ _engines[c]->scale = params[SCALE_PARAM].getValue();
if (inputs[SCALE_INPUT].isConnected()) {
- _scale *= clamp(inputs[SCALE_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
+ _engines[c]->scale *= clamp(inputs[SCALE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
}
- _phase7Offset = phaseOffset(params[PHASE7_PARAM], inputs[PHASE7_INPUT], basePhase7Offset);
- _phase6Offset = phaseOffset(params[PHASE6_PARAM], inputs[PHASE6_INPUT], basePhase6Offset);
- _phase5Offset = phaseOffset(params[PHASE5_PARAM], inputs[PHASE5_INPUT], basePhase5Offset);
- _phase4Offset = phaseOffset(params[PHASE4_PARAM], inputs[PHASE4_INPUT], basePhase4Offset);
- _phase3Offset = phaseOffset(params[PHASE3_PARAM], inputs[PHASE3_INPUT], basePhase3Offset);
- _phase2Offset = phaseOffset(params[PHASE2_PARAM], inputs[PHASE2_INPUT], basePhase2Offset);
- _phase1Offset = phaseOffset(params[PHASE1_PARAM], inputs[PHASE1_INPUT], basePhase1Offset);
- _phase0Offset = phaseOffset(params[PHASE0_PARAM], inputs[PHASE0_INPUT], basePhase0Offset);
+ _engines[c]->phase7Offset = phaseOffset(c, params[PHASE7_PARAM], inputs[PHASE7_INPUT], basePhase7Offset);
+ _engines[c]->phase6Offset = phaseOffset(c, params[PHASE6_PARAM], inputs[PHASE6_INPUT], basePhase6Offset);
+ _engines[c]->phase5Offset = phaseOffset(c, params[PHASE5_PARAM], inputs[PHASE5_INPUT], basePhase5Offset);
+ _engines[c]->phase4Offset = phaseOffset(c, params[PHASE4_PARAM], inputs[PHASE4_INPUT], basePhase4Offset);
+ _engines[c]->phase3Offset = phaseOffset(c, params[PHASE3_PARAM], inputs[PHASE3_INPUT], basePhase3Offset);
+ _engines[c]->phase2Offset = phaseOffset(c, params[PHASE2_PARAM], inputs[PHASE2_INPUT], basePhase2Offset);
+ _engines[c]->phase1Offset = phaseOffset(c, params[PHASE1_PARAM], inputs[PHASE1_INPUT], basePhase1Offset);
+ _engines[c]->phase0Offset = phaseOffset(c, params[PHASE0_PARAM], inputs[PHASE0_INPUT], basePhase0Offset);
}
void EightFO::always(const ProcessArgs& args) {
lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].getValue() > 0.5f;
}
-void EightFO::processChannel(const ProcessArgs& args, int _c) {
- if (_resetTrigger.next(inputs[RESET_INPUT].getVoltage())) {
- _phasor.resetPhase();
+void EightFO::processChannel(const ProcessArgs& args, int c) {
+ if (_engines[c]->resetTrigger.next(inputs[RESET_INPUT].getPolyVoltage(c))) {
+ _engines[c]->phasor.resetPhase();
}
- _phasor.advancePhase();
+ _engines[c]->phasor.advancePhase();
bool useSample = false;
- if (_sampleSteps > 1) {
- ++_sampleStep;
- if (_sampleStep >= _sampleSteps) {
- _sampleStep = 0;
+ if (_engines[c]->sampleSteps > 1) {
+ ++_engines[c]->sampleStep;
+ if (_engines[c]->sampleStep >= _engines[c]->sampleSteps) {
+ _engines[c]->sampleStep = 0;
}
else {
useSample = true;
}
}
- updateOutput(useSample, outputs[PHASE7_OUTPUT], _phase7Offset, _phase7Sample, _phase7Active);
- updateOutput(useSample, outputs[PHASE6_OUTPUT], _phase6Offset, _phase6Sample, _phase6Active);
- updateOutput(useSample, outputs[PHASE5_OUTPUT], _phase5Offset, _phase5Sample, _phase5Active);
- updateOutput(useSample, outputs[PHASE4_OUTPUT], _phase4Offset, _phase4Sample, _phase4Active);
- updateOutput(useSample, outputs[PHASE3_OUTPUT], _phase3Offset, _phase3Sample, _phase3Active);
- updateOutput(useSample, outputs[PHASE2_OUTPUT], _phase2Offset, _phase2Sample, _phase2Active);
- updateOutput(useSample, outputs[PHASE1_OUTPUT], _phase1Offset, _phase1Sample, _phase1Active);
- updateOutput(useSample, outputs[PHASE0_OUTPUT], _phase0Offset, _phase0Sample, _phase0Active);
+ updateOutput(c, useSample, outputs[PHASE7_OUTPUT], _engines[c]->phase7Offset, _engines[c]->phase7Sample, _engines[c]->phase7Active);
+ updateOutput(c, useSample, outputs[PHASE6_OUTPUT], _engines[c]->phase6Offset, _engines[c]->phase6Sample, _engines[c]->phase6Active);
+ updateOutput(c, useSample, outputs[PHASE5_OUTPUT], _engines[c]->phase5Offset, _engines[c]->phase5Sample, _engines[c]->phase5Active);
+ updateOutput(c, useSample, outputs[PHASE4_OUTPUT], _engines[c]->phase4Offset, _engines[c]->phase4Sample, _engines[c]->phase4Active);
+ updateOutput(c, useSample, outputs[PHASE3_OUTPUT], _engines[c]->phase3Offset, _engines[c]->phase3Sample, _engines[c]->phase3Active);
+ updateOutput(c, useSample, outputs[PHASE2_OUTPUT], _engines[c]->phase2Offset, _engines[c]->phase2Sample, _engines[c]->phase2Active);
+ updateOutput(c, useSample, outputs[PHASE1_OUTPUT], _engines[c]->phase1Offset, _engines[c]->phase1Sample, _engines[c]->phase1Active);
+ updateOutput(c, useSample, outputs[PHASE0_OUTPUT], _engines[c]->phase0Offset, _engines[c]->phase0Sample, _engines[c]->phase0Active);
}
-Phasor::phase_delta_t EightFO::phaseOffset(Param& p, Input& i, Phasor::phase_delta_t baseOffset) {
+Phasor::phase_delta_t EightFO::phaseOffset(int c, Param& p, Input& i, Phasor::phase_delta_t baseOffset) {
float o = p.getValue() * Phasor::maxPhase / 2.0f;
if (i.isConnected()) {
- o *= clamp(i.getVoltage() / 5.0f, -1.0f, 1.0f);
+ o *= clamp(i.getPolyVoltage(c) / 5.0f, -1.0f, 1.0f);
}
return baseOffset - o;
}
-void EightFO::updateOutput(bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active) {
+void EightFO::updateOutput(int c, bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active) {
if (output.isConnected()) {
+ output.setChannels(_channels);
if (useSample && active) {
- output.setVoltage(sample);
+ output.setVoltage(sample, c);
}
else {
float v = 0.0f;
@@ -128,27 +163,27 @@ void EightFO::updateOutput(bool useSample, Output& output, Phasor::phase_delta_t
assert(false);
}
case SINE_WAVE: {
- v = _sine.nextFromPhasor(_phasor, offset);
+ v = _engines[c]->sine.nextFromPhasor(_engines[c]->phasor, offset);
break;
}
case TRIANGLE_WAVE: {
- v = _triangle.nextFromPhasor(_phasor, offset);
+ v = _engines[c]->triangle.nextFromPhasor(_engines[c]->phasor, offset);
break;
}
case RAMP_UP_WAVE: {
- v = _ramp.nextFromPhasor(_phasor, offset);
+ v = _engines[c]->ramp.nextFromPhasor(_engines[c]->phasor, offset);
break;
}
case RAMP_DOWN_WAVE: {
- v = -_ramp.nextFromPhasor(_phasor, offset);
+ v = -_engines[c]->ramp.nextFromPhasor(_engines[c]->phasor, offset);
break;
}
case SQUARE_WAVE: {
- v = _square.nextFromPhasor(_phasor, offset);
+ v = _engines[c]->square.nextFromPhasor(_engines[c]->phasor, offset);
break;
}
}
- output.setVoltage(sample = amplitude * _scale * v + _offset);
+ output.setVoltage(sample = amplitude * _engines[c]->scale * v + _engines[c]->offset, c);
}
active = true;
}
diff --git a/src/EightFO.hpp b/src/EightFO.hpp
@@ -71,46 +71,53 @@ struct EightFO : LFOBase {
SQUARE_WAVE
};
+ struct Engine {
+ int sampleSteps = 1;
+ int sampleStep = 0;
+ float offset = 0.0f;
+ float scale = 0.0f;
+ PositiveZeroCrossing resetTrigger;
+
+ Phasor phasor;
+ SineTableOscillator sine;
+ TriangleOscillator triangle;
+ SawOscillator ramp;
+ SquareOscillator square;
+
+ Phasor::phase_delta_t phase7Offset = 0.0f;
+ Phasor::phase_delta_t phase6Offset = 0.0f;
+ Phasor::phase_delta_t phase5Offset = 0.0f;
+ Phasor::phase_delta_t phase4Offset = 0.0f;
+ Phasor::phase_delta_t phase3Offset = 0.0f;
+ Phasor::phase_delta_t phase2Offset = 0.0f;
+ Phasor::phase_delta_t phase1Offset = 0.0f;
+ Phasor::phase_delta_t phase0Offset = 0.0f;
+
+ float phase7Sample = 0.0f;
+ float phase6Sample = 0.0f;
+ float phase5Sample = 0.0f;
+ float phase4Sample = 0.0f;
+ float phase3Sample = 0.0f;
+ float phase2Sample = 0.0f;
+ float phase1Sample = 0.0f;
+ float phase0Sample = 0.0f;
+
+ bool phase7Active = false;
+ bool phase6Active = false;
+ bool phase5Active = false;
+ bool phase4Active = false;
+ bool phase3Active = false;
+ bool phase2Active = false;
+ bool phase1Active = false;
+ bool phase0Active = false;
+
+ void reset();
+ void sampleRateChange();
+ };
+
const float amplitude = 5.0f;
Wave _wave = NO_WAVE;
- int _sampleSteps = 1;
- int _sampleStep = 0;
- float _offset = 0.0f;
- float _scale = 0.0f;
- PositiveZeroCrossing _resetTrigger;
-
- Phasor _phasor;
- SineTableOscillator _sine;
- TriangleOscillator _triangle;
- SawOscillator _ramp;
- SquareOscillator _square;
-
- Phasor::phase_delta_t _phase7Offset = 0.0f;
- Phasor::phase_delta_t _phase6Offset = 0.0f;
- Phasor::phase_delta_t _phase5Offset = 0.0f;
- Phasor::phase_delta_t _phase4Offset = 0.0f;
- Phasor::phase_delta_t _phase3Offset = 0.0f;
- Phasor::phase_delta_t _phase2Offset = 0.0f;
- Phasor::phase_delta_t _phase1Offset = 0.0f;
- Phasor::phase_delta_t _phase0Offset = 0.0f;
-
- float _phase7Sample = 0.0f;
- float _phase6Sample = 0.0f;
- float _phase5Sample = 0.0f;
- float _phase4Sample = 0.0f;
- float _phase3Sample = 0.0f;
- float _phase2Sample = 0.0f;
- float _phase1Sample = 0.0f;
- float _phase0Sample = 0.0f;
-
- bool _phase7Active = false;
- bool _phase6Active = false;
- bool _phase5Active = false;
- bool _phase4Active = false;
- bool _phase3Active = false;
- bool _phase2Active = false;
- bool _phase1Active = false;
- bool _phase0Active = false;
+ Engine* _engines[maxChannels] {};
EightFO() : LFOBase(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
configParam<LFOFrequencyParamQuantity>(FREQUENCY_PARAM, -5.0f, 8.0f, 0.0, "Frequency", " Hz");
@@ -127,19 +134,20 @@ struct EightFO : LFOBase {
configParam(PHASE2_PARAM, -1.0, 1.0, 0.0, "Phase 90", "º", 0.0f, 180.0f);
configParam(PHASE1_PARAM, -1.0, 1.0, 0.0, "Phase 45", "º", 0.0f, 180.0f);
configParam(PHASE0_PARAM, -1.0, 1.0, 0.0f, "Phase 0", "º", 0.0f, 180.0f);
-
- reset();
- sampleRateChange();
}
void reset() override;
void sampleRateChange() override;
bool active() override;
+ int channels() override;
+ void addEngine(int c) override;
+ void removeEngine(int c) override;
void modulate() override;
+ void modulateChannel(int c) override;
void always(const ProcessArgs& args) override;
- void processChannel(const ProcessArgs& args, int _c) override;
- Phasor::phase_delta_t phaseOffset(Param& p, Input& i, Phasor::phase_delta_t baseOffset);
- void updateOutput(bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active);
+ void processChannel(const ProcessArgs& args, int c) override;
+ Phasor::phase_delta_t phaseOffset(int c, Param& p, Input& i, Phasor::phase_delta_t baseOffset);
+ void updateOutput(int c, bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active);
};
} // namespace bogaudio
diff --git a/src/LFO.cpp b/src/LFO.cpp
@@ -1,14 +1,30 @@
#include "LFO.hpp"
+void LFO::Engine::reset() {
+ resetTrigger.reset();
+ sampleStep = phasor._sampleRate;
+}
+
+void LFO::Engine::sampleRateChange() {
+ phasor.setSampleRate(APP->engine->getSampleRate());
+ sampleStep = phasor._sampleRate;
+}
+
void LFO::reset() {
- _resetTrigger.reset();
- _sampleStep = _phasor._sampleRate;
+ for (int c = 0; c < maxChannels; ++c) {
+ if (_engines[c]) {
+ _engines[c]->reset();
+ }
+ }
}
void LFO::sampleRateChange() {
- _phasor.setSampleRate(APP->engine->getSampleRate());
- _sampleStep = _phasor._sampleRate;
+ for (int c = 0; c < maxChannels; ++c) {
+ if (_engines[c]) {
+ _engines[c]->sampleRateChange();
+ }
+ }
}
bool LFO::active() {
@@ -21,34 +37,49 @@ bool LFO::active() {
);
}
-void LFO::modulate() {
- setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _phasor);
+int LFO::channels() {
+ return std::max(1, inputs[PITCH_INPUT].getChannels());
+}
+
+void LFO::addEngine(int c) {
+ _engines[c] = new Engine();
+ _engines[c]->reset();
+ _engines[c]->sampleRateChange();
+}
+
+void LFO::removeEngine(int c) {
+ delete _engines[c];
+ _engines[c] = NULL;
+}
+
+void LFO::modulateChannel(int c) {
+ setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _engines[c]->phasor, c);
float pw = params[PW_PARAM].getValue();
if (inputs[PW_INPUT].isConnected()) {
- pw *= clamp(inputs[PW_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f);
+ pw *= clamp(inputs[PW_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f);
}
- pw *= 1.0f - 2.0f * _square.minPulseWidth;
+ pw *= 1.0f - 2.0f * _engines[c]->square.minPulseWidth;
pw *= 0.5f;
pw += 0.5f;
- _square.setPulseWidth(pw);
+ _engines[c]->square.setPulseWidth(pw);
float sample = params[SAMPLE_PARAM].getValue();
if (inputs[SAMPLE_INPUT].isConnected()) {
- sample *= clamp(inputs[SAMPLE_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
+ sample *= clamp(inputs[SAMPLE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
}
- float maxSampleSteps = (_phasor._sampleRate / _phasor._frequency) / 4.0f;
- _sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps);
+ float maxSampleSteps = (_engines[c]->phasor._sampleRate / _engines[c]->phasor._frequency) / 4.0f;
+ _engines[c]->sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps);
- _offset = params[OFFSET_PARAM].getValue();
+ _engines[c]->offset = params[OFFSET_PARAM].getValue();
if (inputs[OFFSET_INPUT].isConnected()) {
- _offset *= clamp(inputs[OFFSET_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f);
+ _engines[c]->offset *= clamp(inputs[OFFSET_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f);
}
- _offset *= 5.0f;
+ _engines[c]->offset *= 5.0f;
- _scale = params[SCALE_PARAM].getValue();
+ _engines[c]->scale = params[SCALE_PARAM].getValue();
if (inputs[SCALE_INPUT].isConnected()) {
- _scale *= clamp(inputs[SCALE_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
+ _engines[c]->scale *= clamp(inputs[SCALE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
}
}
@@ -56,41 +87,42 @@ void LFO::always(const ProcessArgs& args) {
lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].getValue() > 0.5f;
}
-void LFO::processChannel(const ProcessArgs& args, int _c) {
- if (_resetTrigger.next(inputs[RESET_INPUT].getVoltage())) {
- _phasor.resetPhase();
+void LFO::processChannel(const ProcessArgs& args, int c) {
+ if (_engines[c]->resetTrigger.next(inputs[RESET_INPUT].getPolyVoltage(c))) {
+ _engines[c]->phasor.resetPhase();
}
- _phasor.advancePhase();
+ _engines[c]->phasor.advancePhase();
bool useSample = false;
- if (_sampleSteps > 1) {
- ++_sampleStep;
- if (_sampleStep >= _sampleSteps) {
- _sampleStep = 0;
+ if (_engines[c]->sampleSteps > 1) {
+ ++_engines[c]->sampleStep;
+ if (_engines[c]->sampleStep >= _engines[c]->sampleSteps) {
+ _engines[c]->sampleStep = 0;
}
else {
useSample = true;
}
}
- updateOutput(_sine, useSample, false, outputs[SINE_OUTPUT], _sineSample, _sineActive);
- updateOutput(_triangle, useSample, false, outputs[TRIANGLE_OUTPUT], _triangleSample, _triangleActive);
- updateOutput(_ramp, useSample, false, outputs[RAMP_UP_OUTPUT], _rampUpSample, _rampUpActive);
- updateOutput(_ramp, useSample, true, outputs[RAMP_DOWN_OUTPUT], _rampDownSample, _rampDownActive);
- updateOutput(_square, false, false, outputs[SQUARE_OUTPUT], _squareSample, _squareActive);
+ updateOutput(c, _engines[c]->sine, useSample, false, outputs[SINE_OUTPUT], _engines[c]->sineSample, _engines[c]->sineActive);
+ updateOutput(c, _engines[c]->triangle, useSample, false, outputs[TRIANGLE_OUTPUT], _engines[c]->triangleSample, _engines[c]->triangleActive);
+ updateOutput(c, _engines[c]->ramp, useSample, false, outputs[RAMP_UP_OUTPUT], _engines[c]->rampUpSample, _engines[c]->rampUpActive);
+ updateOutput(c, _engines[c]->ramp, useSample, true, outputs[RAMP_DOWN_OUTPUT], _engines[c]->rampDownSample, _engines[c]->rampDownActive);
+ updateOutput(c, _engines[c]->square, false, false, outputs[SQUARE_OUTPUT], _engines[c]->squareSample, _engines[c]->squareActive);
}
-void LFO::updateOutput(Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active) {
+void LFO::updateOutput(int c, Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active) {
if (output.isConnected()) {
+ output.setChannels(_channels);
if (useSample && active) {
- output.setVoltage(sample);
+ output.setVoltage(sample, c);
}
else {
- sample = wave.nextFromPhasor(_phasor) * amplitude * _scale;
+ sample = wave.nextFromPhasor(_engines[c]->phasor) * amplitude * _engines[c]->scale;
if (invert) {
sample = -sample;
}
- sample += _offset;
- output.setVoltage(sample);
+ sample += _engines[c]->offset;
+ output.setVoltage(sample, c);
}
active = true;
}
diff --git a/src/LFO.hpp b/src/LFO.hpp
@@ -44,50 +44,57 @@ struct LFO : LFOBase {
NUM_LIGHTS
};
+ struct Engine {
+ int sampleSteps = 1;
+ int sampleStep = 0;
+ float offset = 0.0f;
+ float scale = 0.0f;
+ PositiveZeroCrossing resetTrigger;
+
+ Phasor phasor;
+ SineTableOscillator sine;
+ TriangleOscillator triangle;
+ SawOscillator ramp;
+ SquareOscillator square;
+
+ float sineSample = 0.0f;
+ float triangleSample = 0.0f;
+ float rampUpSample = 0.0f;
+ float rampDownSample = 0.0f;
+ float squareSample = 0.0f;
+
+ bool sineActive = false;
+ bool triangleActive = false;
+ bool rampUpActive = false;
+ bool rampDownActive = false;
+ bool squareActive = false;
+
+ void reset();
+ void sampleRateChange();
+ };
+
const float amplitude = 5.0f;
- int _sampleSteps = 1;
- int _sampleStep = 0;
- float _offset = 0.0f;
- float _scale = 0.0f;
- PositiveZeroCrossing _resetTrigger;
-
- Phasor _phasor;
- SineTableOscillator _sine;
- TriangleOscillator _triangle;
- SawOscillator _ramp;
- SquareOscillator _square;
-
- float _sineSample = 0.0f;
- float _triangleSample = 0.0f;
- float _rampUpSample = 0.0f;
- float _rampDownSample = 0.0f;
- float _squareSample = 0.0f;
-
- bool _sineActive = false;
- bool _triangleActive = false;
- bool _rampUpActive = false;
- bool _rampDownActive = false;
- bool _squareActive = false;
+ Engine* _engines[maxChannels] {};
LFO() : LFOBase(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
configParam<LFOFrequencyParamQuantity>(FREQUENCY_PARAM, -5.0f, 8.0f, 0.0f, "Frequency", " Hz");
configParam(SLOW_PARAM, 0.0f, 1.0f, 0.0f, "Slow");
configParam(SAMPLE_PARAM, 0.0f, 1.0f, 0.0f, "Output sampling", "%", 0.0f, 100.0f);
- configParam(PW_PARAM, -1.0f, 1.0f, 0.0f, "Pulse width", "%", 0.0f, 100.0f*0.5f*(1.0f - 2.0f * _square.minPulseWidth), 50.0f);
+ configParam(PW_PARAM, -1.0f, 1.0f, 0.0f, "Pulse width", "%", 0.0f, 100.0f*0.5f*(1.0f - 2.0f * SquareOscillator::minPulseWidth), 50.0f);
configParam(OFFSET_PARAM, -1.0f, 1.0f, 0.0f, "Offset", " V", 0.0f, 5.0f);
configParam(SCALE_PARAM, 0.0f, 1.0f, 1.0f, "Scale", "%", 0.0f, 100.0f);
-
- reset();
- sampleRateChange();
}
void reset() override;
void sampleRateChange() override;
bool active() override;
- void modulate() override;
+ int channels() override;
+ void addEngine(int c) override;
+ void removeEngine(int c) override;
+ void modulateChannel(int c) override;
void always(const ProcessArgs& args) override;
- void processChannel(const ProcessArgs& args, int _c) override;
- void updateOutput(Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active);
+ void processChannel(const ProcessArgs& args, int c) override;
+ void updateOutput(int c, Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active);
};
} // namespace bogaudio
diff --git a/src/LLFO.cpp b/src/LLFO.cpp
@@ -2,20 +2,26 @@
#include "LLFO.hpp"
void LLFO::reset() {
- _resetTrigger.reset();
+ for (int c = 0; c < maxChannels; ++c) {
+ _resetTrigger[c].reset();
+ }
}
void LLFO::sampleRateChange() {
- _phasor.setSampleRate(APP->engine->getSampleRate());
+ for (int c = 0; c < maxChannels; ++c) {
+ _phasor[c].setSampleRate(APP->engine->getSampleRate());
+ }
}
bool LLFO::active() {
return outputs[OUT_OUTPUT].isConnected();
}
-void LLFO::modulate() {
- setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _phasor);
+int LLFO::channels() {
+ return std::max(1, inputs[PITCH_INPUT].getChannels());
+}
+void LLFO::modulate() {
_invert = false;
Wave wave = (Wave)params[WAVE_PARAM].getValue();
switch (wave) {
@@ -52,6 +58,10 @@ void LLFO::modulate() {
_scale = params[SCALE_PARAM].getValue();
}
+void LLFO::modulateChannel(int c) {
+ setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _phasor[c], c);
+}
+
void LLFO::always(const ProcessArgs& args) {
lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].getValue() > 0.5f;
@@ -64,17 +74,18 @@ void LLFO::always(const ProcessArgs& args) {
lights[PULSE_LIGHT].value = wave == PULSE_WAVE;
}
-void LLFO::processChannel(const ProcessArgs& args, int _c) {
- if (_resetTrigger.next(inputs[RESET_INPUT].getVoltage())) {
- _phasor.resetPhase();
+void LLFO::processChannel(const ProcessArgs& args, int c) {
+ if (_resetTrigger[c].next(inputs[RESET_INPUT].getPolyVoltage(c))) {
+ _phasor[c].resetPhase();
}
- _phasor.advancePhase();
- float sample = _oscillator->nextFromPhasor(_phasor) * amplitude * _scale;
+ _phasor[c].advancePhase();
+ float sample = _oscillator->nextFromPhasor(_phasor[c]) * amplitude * _scale;
if (_invert) {
sample = -sample;
}
sample += _offset;
- outputs[OUT_OUTPUT].setVoltage(sample);
+ outputs[OUT_OUTPUT].setChannels(_channels);
+ outputs[OUT_OUTPUT].setVoltage(sample, c);
}
struct LLFOWidget : ModuleWidget {
diff --git a/src/LLFO.hpp b/src/LLFO.hpp
@@ -53,14 +53,14 @@ struct LLFO : LFOBase {
const float amplitude = 5.0f;
float _offset = 0.0f;
float _scale = 0.0f;
- PositiveZeroCrossing _resetTrigger;
- Phasor _phasor;
+ PositiveZeroCrossing _resetTrigger[maxChannels];
+ Phasor _phasor[maxChannels];
+
SineTableOscillator _sine;
TriangleOscillator _triangle;
SawOscillator _ramp;
SquareOscillator _square;
-
bool _invert;
Phasor* _oscillator;
@@ -82,9 +82,11 @@ struct LLFO : LFOBase {
void reset() override;
void sampleRateChange() override;
bool active() override;
+ int channels() override;
void modulate() override;
+ void modulateChannel(int c) override;
void always(const ProcessArgs& args) override;
- void processChannel(const ProcessArgs& args, int _c) override;
+ void processChannel(const ProcessArgs& args, int c) override;
};
} // namespace bogaudio
diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp
@@ -217,8 +217,8 @@ struct BandLimitedSawOscillator : SaturatingSawOscillator {
};
struct SquareOscillator : Phasor {
- const float minPulseWidth = 0.03f;
- const float maxPulseWidth = 1.0f - minPulseWidth;
+ static constexpr float minPulseWidth = 0.03f;
+ static constexpr float maxPulseWidth = 1.0f - minPulseWidth;
static constexpr float defaultPulseWidth = 0.5f;
float _pulseWidthInput;
phase_t _pulseWidth = maxPhase * defaultPulseWidth;
diff --git a/src/lfo_base.cpp b/src/lfo_base.cpp
@@ -18,10 +18,10 @@ float LFOBase::getPitchOffset() {
return offset;
}
-void LFOBase::setFrequency(Param& frequency, Input& pitch, Phasor& phasor) {
+void LFOBase::setFrequency(Param& frequency, Input& pitch, Phasor& phasor, int c) {
float f = frequency.getValue();
if (pitch.isConnected()) {
- f += pitch.getVoltage();
+ f += pitch.getVoltage(c);
}
f += getPitchOffset();
diff --git a/src/lfo_base.hpp b/src/lfo_base.hpp
@@ -26,7 +26,7 @@ struct LFOBase : BGModule {
}
float getPitchOffset();
- void setFrequency(Param& frequency, Input& pitch, Phasor& phasor);
+ void setFrequency(Param& frequency, Input& pitch, Phasor& phasor, int c = 0); // FIXME
};