commit 745a7b48c9b4066574684472635cf9b7a3d386bf
parent 885997edf9aba82e1a2313dfc41ee897ad18bd8a
Author: Matt Demanett <matt@demanett.net>
Date: Wed, 25 Sep 2019 21:52:42 -0400
Poly: XCO.
Diffstat:
M | src/XCO.cpp | | | 243 | ++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- |
M | src/XCO.hpp | | | 115 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
2 files changed, 204 insertions(+), 154 deletions(-)
diff --git a/src/XCO.cpp b/src/XCO.cpp
@@ -7,12 +7,57 @@ float XCO::XCOFrequencyParamQuantity::offset() {
return xco->_slowMode ? xco->_slowModeOffset : 0.0f;
}
+void XCO::Engine::reset() {
+ syncTrigger.reset();
+}
+
+void XCO::Engine::sampleRateChange(float sampleRate) {
+ phasor.setSampleRate(sampleRate);
+ square.setSampleRate(sampleRate);
+ saw.setSampleRate(sampleRate);
+
+ squareDecimator.setParams(sampleRate, oversample);
+ sawDecimator.setParams(sampleRate, oversample);
+ triangleDecimator.setParams(sampleRate, oversample);
+ sineDecimator.setParams(sampleRate, oversample);
+
+ fmDepthSL.setParams(sampleRate, 5.0f, 1.0f);
+ squarePulseWidthSL.setParams(sampleRate, 0.1f, 2.0f);
+ sawSaturationSL.setParams(sampleRate, 1.0f, 1.0f);
+ triangleSampleWidthSL.setParams(sampleRate, 0.1f, 1.0f);
+ sineFeedbackSL.setParams(sampleRate, 0.1f, 1.0f);
+ squareMixSL.setParams(sampleRate, 5.0f, 1.0f);
+ sawMixSL.setParams(sampleRate, 5.0f, 1.0f);
+ triangleMixSL.setParams(sampleRate, 5.0f, 1.0f);
+ sineMixSL.setParams(sampleRate, 5.0f, 1.0f);
+}
+
+void XCO::Engine::setFrequency(float f) {
+ if (frequency != f && frequency < 0.475f * phasor._sampleRate) {
+ frequency = f;
+ phasor.setFrequency(frequency / (float)oversample);
+ square.setFrequency(frequency);
+ saw.setFrequency(frequency);
+ }
+}
+
void XCO::reset() {
- _syncTrigger.reset();
+ for (int c = 0; c < maxChannels; ++c) {
+ if (_engines[c]) {
+ _engines[c]->reset();
+ }
+ }
}
void XCO::sampleRateChange() {
- setSampleRate(APP->engine->getSampleRate());
+ float sampleRate = APP->engine->getSampleRate();
+ _oversampleThreshold = 0.06f * sampleRate;
+
+ for (int c = 0; c < maxChannels; ++c) {
+ if (_engines[c]) {
+ _engines[c]->sampleRateChange(sampleRate);
+ }
+ }
}
bool XCO::active() {
@@ -25,85 +70,102 @@ bool XCO::active() {
);
}
+int XCO::channels() {
+ return std::max(1, inputs[PITCH_INPUT].getChannels());
+}
+
+void XCO::addEngine(int c) {
+ _engines[c] = new Engine();
+ _engines[c]->reset();
+ _engines[c]->sampleRateChange(APP->engine->getSampleRate());
+}
+
+void XCO::removeEngine(int c) {
+ delete _engines[c];
+ _engines[c] = NULL;
+}
+
void XCO::modulate() {
_fmLinearMode = params[FM_TYPE_PARAM].getValue() < 0.5f;
+}
- _baseVOct = params[FREQUENCY_PARAM].getValue();
- _baseVOct += params[FINE_PARAM].getValue() / 12.0f;;
+void XCO::modulateChannel(int c) {
+ _engines[c]->baseVOct = params[FREQUENCY_PARAM].getValue();
+ _engines[c]->baseVOct += params[FINE_PARAM].getValue() / 12.0f;;
if (inputs[PITCH_INPUT].isConnected()) {
- _baseVOct += clamp(inputs[PITCH_INPUT].getVoltage(), -5.0f, 5.0f);
+ _engines[c]->baseVOct += clamp(inputs[PITCH_INPUT].getVoltage(c), -5.0f, 5.0f);
}
if (_slowMode) {
- _baseVOct += _slowModeOffset;
+ _engines[c]->baseVOct += _slowModeOffset;
}
- _baseHz = cvToFrequency(_baseVOct);
+ _engines[c]->baseHz = cvToFrequency(_engines[c]->baseVOct);
float pw = params[SQUARE_PW_PARAM].getValue();
if (inputs[SQUARE_PW_INPUT].isConnected()) {
- pw *= clamp(inputs[SQUARE_PW_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f);
+ pw *= clamp(inputs[SQUARE_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(_squarePulseWidthSL.next(pw));
+ _engines[c]->square.setPulseWidth(_engines[c]->squarePulseWidthSL.next(pw));
float saturation = params[SAW_SATURATION_PARAM].getValue();
if (inputs[SAW_SATURATION_INPUT].isConnected()) {
- saturation *= clamp(inputs[SAW_SATURATION_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
+ saturation *= clamp(inputs[SAW_SATURATION_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
}
- _saw.setSaturation(_sawSaturationSL.next(saturation) * 10.f);
+ _engines[c]->saw.setSaturation(_engines[c]->sawSaturationSL.next(saturation) * 10.f);
float tsw = params[TRIANGLE_SAMPLE_PARAM].getValue() * Phasor::maxSampleWidth;
if (inputs[TRIANGLE_SAMPLE_INPUT].isConnected()) {
- tsw *= clamp(inputs[TRIANGLE_SAMPLE_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
+ tsw *= clamp(inputs[TRIANGLE_SAMPLE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
}
- _triangleSampleWidth = _triangleSampleWidthSL.next(tsw);
- _triangle.setSampleWidth(_triangleSampleWidth);
+ _engines[c]->triangleSampleWidth = _engines[c]->triangleSampleWidthSL.next(tsw);
+ _engines[c]->triangle.setSampleWidth(_engines[c]->triangleSampleWidth);
float sfb = params[SINE_FEEDBACK_PARAM].getValue();
if (inputs[SINE_FEEDBACK_INPUT].isConnected()) {
- sfb *= clamp(inputs[SINE_FEEDBACK_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
+ sfb *= clamp(inputs[SINE_FEEDBACK_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
}
- _sineFeedback = _sineFeedbackSL.next(sfb);
+ _engines[c]->sineFeedback = _engines[c]->sineFeedbackSL.next(sfb);
- _fmDepth = params[FM_DEPTH_PARAM].getValue();
+ _engines[c]->fmDepth = params[FM_DEPTH_PARAM].getValue();
if (inputs[FM_DEPTH_INPUT].isConnected()) {
- _fmDepth *= clamp(inputs[FM_DEPTH_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
+ _engines[c]->fmDepth *= clamp(inputs[FM_DEPTH_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
}
- _squarePhaseOffset = phaseOffset(params[SQUARE_PHASE_PARAM], inputs[SQUARE_PHASE_INPUT]);
- _sawPhaseOffset = phaseOffset(params[SAW_PHASE_PARAM], inputs[SAW_PHASE_INPUT]);
- _trianglePhaseOffset = phaseOffset(params[TRIANGLE_PHASE_PARAM], inputs[TRIANGLE_PHASE_INPUT]);
- _sinePhaseOffset = phaseOffset(params[SINE_PHASE_PARAM], inputs[SINE_PHASE_INPUT]);
+ _engines[c]->squarePhaseOffset = phaseOffset(c, params[SQUARE_PHASE_PARAM], inputs[SQUARE_PHASE_INPUT]);
+ _engines[c]->sawPhaseOffset = phaseOffset(c, params[SAW_PHASE_PARAM], inputs[SAW_PHASE_INPUT]);
+ _engines[c]->trianglePhaseOffset = phaseOffset(c, params[TRIANGLE_PHASE_PARAM], inputs[TRIANGLE_PHASE_INPUT]);
+ _engines[c]->sinePhaseOffset = phaseOffset(c, params[SINE_PHASE_PARAM], inputs[SINE_PHASE_INPUT]);
- _squareMix = level(params[SQUARE_MIX_PARAM], inputs[SQUARE_MIX_INPUT]);
- _sawMix = level(params[SAW_MIX_PARAM], inputs[SAW_MIX_INPUT]);
- _triangleMix = level(params[TRIANGLE_MIX_PARAM], inputs[TRIANGLE_MIX_INPUT]);
- _sineMix = level(params[SINE_MIX_PARAM], inputs[SINE_MIX_INPUT]);
+ _engines[c]->squareMix = level(c, params[SQUARE_MIX_PARAM], inputs[SQUARE_MIX_INPUT]);
+ _engines[c]->sawMix = level(c, params[SAW_MIX_PARAM], inputs[SAW_MIX_INPUT]);
+ _engines[c]->triangleMix = level(c, params[TRIANGLE_MIX_PARAM], inputs[TRIANGLE_MIX_INPUT]);
+ _engines[c]->sineMix = level(c, params[SINE_MIX_PARAM], inputs[SINE_MIX_INPUT]);
}
void XCO::always(const ProcessArgs& args) {
lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].getValue() > 0.5f;
}
-void XCO::processChannel(const ProcessArgs& args, int _c) {
- if (_syncTrigger.next(inputs[SYNC_INPUT].getVoltage())) {
- _phasor.resetPhase();
+void XCO::processChannel(const ProcessArgs& args, int c) {
+ if (_engines[c]->syncTrigger.next(inputs[SYNC_INPUT].getPolyVoltage(c))) {
+ _engines[c]->phasor.resetPhase();
}
- float frequency = _baseHz;
+ float frequency = _engines[c]->baseHz;
Phasor::phase_delta_t phaseOffset = 0;
- float fmd = _fmDepthSL.next(_fmDepth);
+ float fmd = _engines[c]->fmDepthSL.next(_engines[c]->fmDepth);
if (inputs[FM_INPUT].isConnected() && fmd > 0.01f) {
- float fm = inputs[FM_INPUT].getVoltageSum() * fmd;
+ float fm = inputs[FM_INPUT].getPolyVoltage(c) * fmd;
if (_fmLinearMode) {
phaseOffset = Phasor::radiansToPhase(2.0f * fm);
}
else {
- frequency = cvToFrequency(_baseVOct + fm);
+ frequency = cvToFrequency(_engines[c]->baseVOct + fm);
}
}
- setFrequency(frequency);
+ _engines[c]->setFrequency(frequency);
const float oversampleWidth = 100.0f;
float mix, oMix;
@@ -122,7 +184,7 @@ void XCO::processChannel(const ProcessArgs& args, int _c) {
oMix = 0.0f;
}
- bool triangleSample = _triangleSampleWidth > 0.001f;
+ bool triangleSample = _engines[c]->triangleSampleWidth > 0.001f;
bool squareActive = outputs[MIX_OUTPUT].isConnected() || outputs[SQUARE_OUTPUT].isConnected();
bool sawActive = outputs[MIX_OUTPUT].isConnected() || outputs[SAW_OUTPUT].isConnected();
bool triangleActive = outputs[MIX_OUTPUT].isConnected() || outputs[TRIANGLE_OUTPUT].isConnected();
@@ -140,123 +202,100 @@ void XCO::processChannel(const ProcessArgs& args, int _c) {
Phasor::phase_delta_t sineFeedbackOffset = 0;
if (sineActive) {
- if (_sineFeedback > 0.001f) {
- sineFeedbackOffset = Phasor::radiansToPhase(_sineFeedback * _sineFeedbackDelayedSample);
- if (_sineOMix < 1.0f) {
- _sineOMix += sineOversampleMixIncrement;
+ if (_engines[c]->sineFeedback > 0.001f) {
+ sineFeedbackOffset = Phasor::radiansToPhase(_engines[c]->sineFeedback * _engines[c]->sineFeedbackDelayedSample);
+ if (_engines[c]->sineOMix < 1.0f) {
+ _engines[c]->sineOMix += sineOversampleMixIncrement;
}
}
- else if (_sineOMix > 0.0f) {
- _sineOMix -= sineOversampleMixIncrement;
+ else if (_engines[c]->sineOMix > 0.0f) {
+ _engines[c]->sineOMix -= sineOversampleMixIncrement;
}
}
- if (squareOversample || sawOversample || triangleOversample || _sineOMix > 0.0f) {
- for (int i = 0; i < oversample; ++i) {
- _phasor.advancePhase();
+ if (squareOversample || sawOversample || triangleOversample || _engines[c]->sineOMix > 0.0f) {
+ for (int i = 0; i < Engine::oversample; ++i) {
+ _engines[c]->phasor.advancePhase();
if (squareOversample) {
- _squareBuffer[i] = _square.nextFromPhasor(_phasor, _squarePhaseOffset + phaseOffset);
+ _engines[c]->squareBuffer[i] = _engines[c]->square.nextFromPhasor(_engines[c]->phasor, _engines[c]->squarePhaseOffset + phaseOffset);
}
if (sawOversample) {
- _sawBuffer[i] = _saw.nextFromPhasor(_phasor, _sawPhaseOffset + phaseOffset);
+ _engines[c]->sawBuffer[i] = _engines[c]->saw.nextFromPhasor(_engines[c]->phasor, _engines[c]->sawPhaseOffset + phaseOffset);
}
if (triangleOversample) {
- _triangleBuffer[i] = _triangle.nextFromPhasor(_phasor, _trianglePhaseOffset + phaseOffset);
+ _engines[c]->triangleBuffer[i] = _engines[c]->triangle.nextFromPhasor(_engines[c]->phasor, _engines[c]->trianglePhaseOffset + phaseOffset);
}
- if (_sineOMix > 0.0f) {
- _sineBuffer[i] = _sine.nextFromPhasor(_phasor, sineFeedbackOffset + _sinePhaseOffset + phaseOffset);
+ if (_engines[c]->sineOMix > 0.0f) {
+ _engines[c]->sineBuffer[i] = _engines[c]->sine.nextFromPhasor(_engines[c]->phasor, sineFeedbackOffset + _engines[c]->sinePhaseOffset + phaseOffset);
}
}
if (squareOversample) {
- squareOut += oMix * amplitude * _squareDecimator.next(_squareBuffer);
+ squareOut += oMix * amplitude * _engines[c]->squareDecimator.next(_engines[c]->squareBuffer);
}
if (sawOversample) {
- sawOut += oMix * amplitude * _sawDecimator.next(_sawBuffer);
+ sawOut += oMix * amplitude * _engines[c]->sawDecimator.next(_engines[c]->sawBuffer);
}
if (triangleOversample) {
- triangleOut += amplitude * _triangleDecimator.next(_triangleBuffer);
+ triangleOut += amplitude * _engines[c]->triangleDecimator.next(_engines[c]->triangleBuffer);
if (!triangleSample) {
triangleOut *= oMix;
}
}
- if (_sineOMix > 0.0f) {
- sineOut += amplitude * _sineOMix * _sineDecimator.next(_sineBuffer);
+ if (_engines[c]->sineOMix > 0.0f) {
+ sineOut += amplitude * _engines[c]->sineOMix * _engines[c]->sineDecimator.next(_engines[c]->sineBuffer);
}
}
else {
- _phasor.advancePhase(oversample);
+ _engines[c]->phasor.advancePhase(Engine::oversample);
}
if (squareNormal) {
- squareOut += mix * amplitude * _square.nextFromPhasor(_phasor, _squarePhaseOffset + phaseOffset);
+ squareOut += mix * amplitude * _engines[c]->square.nextFromPhasor(_engines[c]->phasor, _engines[c]->squarePhaseOffset + phaseOffset);
}
if (sawNormal) {
- sawOut += mix * amplitude * _saw.nextFromPhasor(_phasor, _sawPhaseOffset + phaseOffset);
+ sawOut += mix * amplitude * _engines[c]->saw.nextFromPhasor(_engines[c]->phasor, _engines[c]->sawPhaseOffset + phaseOffset);
}
if (triangleNormal) {
- triangleOut += mix * amplitude * _triangle.nextFromPhasor(_phasor, _trianglePhaseOffset + phaseOffset);
+ triangleOut += mix * amplitude * _engines[c]->triangle.nextFromPhasor(_engines[c]->phasor, _engines[c]->trianglePhaseOffset + phaseOffset);
}
- if (_sineOMix < 1.0f) {
- sineOut += amplitude * (1.0f - _sineOMix) * _sine.nextFromPhasor(_phasor, sineFeedbackOffset + _sinePhaseOffset + phaseOffset);
+ if (_engines[c]->sineOMix < 1.0f) {
+ sineOut += amplitude * (1.0f - _engines[c]->sineOMix) * _engines[c]->sine.nextFromPhasor(_engines[c]->phasor, sineFeedbackOffset + _engines[c]->sinePhaseOffset + phaseOffset);
}
- outputs[SQUARE_OUTPUT].setVoltage(squareOut);
- outputs[SAW_OUTPUT].setVoltage(sawOut);
- outputs[TRIANGLE_OUTPUT].setVoltage(triangleOut);
- outputs[SINE_OUTPUT].setVoltage(_sineFeedbackDelayedSample = sineOut);
+ outputs[SQUARE_OUTPUT].setChannels(_channels);
+ outputs[SQUARE_OUTPUT].setVoltage(squareOut, c);
+ outputs[SAW_OUTPUT].setChannels(_channels);
+ outputs[SAW_OUTPUT].setVoltage(sawOut, c);
+ outputs[TRIANGLE_OUTPUT].setChannels(_channels);
+ outputs[TRIANGLE_OUTPUT].setVoltage(triangleOut, c);
+ outputs[SINE_OUTPUT].setChannels(_channels);
+ outputs[SINE_OUTPUT].setVoltage(_engines[c]->sineFeedbackDelayedSample = sineOut, c);
if (outputs[MIX_OUTPUT].isConnected()) {
- outputs[MIX_OUTPUT].setVoltage(_squareMixSL.next(_squareMix) * squareOut + _sawMixSL.next(_sawMix) * sawOut + _triangleMixSL.next(_triangleMix) * triangleOut + _sineMixSL.next(_sineMix) * sineOut);
+ float mix = _engines[c]->squareMixSL.next(_engines[c]->squareMix) * squareOut;
+ mix += _engines[c]->sawMixSL.next(_engines[c]->sawMix) * sawOut;
+ mix += _engines[c]->triangleMixSL.next(_engines[c]->triangleMix) * triangleOut;
+ mix += _engines[c]->sineMixSL.next(_engines[c]->sineMix) * sineOut;
+ outputs[MIX_OUTPUT].setChannels(_channels);
+ outputs[MIX_OUTPUT].setVoltage(mix, c);
}
}
-Phasor::phase_delta_t XCO::phaseOffset(Param& param, Input& input) {
+Phasor::phase_delta_t XCO::phaseOffset(int c, Param& param, Input& input) {
float v = param.getValue();
if (input.isConnected()) {
- v *= clamp(input.getVoltage() / 5.0f, -1.0f, 1.0f);
+ v *= clamp(input.getPolyVoltage(c) / 5.0f, -1.0f, 1.0f);
}
return -v * Phasor::maxPhase / 2.0f;
}
-float XCO::level(Param& param, Input& input) {
+float XCO::level(int c, Param& param, Input& input) {
float v = param.getValue();
if (input.isConnected()) {
- v *= clamp(input.getVoltage() / 10.0f, 0.0f, 1.0f);
+ v *= clamp(input.getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
}
return v;
}
-void XCO::setSampleRate(float sampleRate) {
- _oversampleThreshold = 0.06f * sampleRate;
-
- _phasor.setSampleRate(sampleRate);
- _square.setSampleRate(sampleRate);
- _saw.setSampleRate(sampleRate);
-
- _squareDecimator.setParams(sampleRate, oversample);
- _sawDecimator.setParams(sampleRate, oversample);
- _triangleDecimator.setParams(sampleRate, oversample);
- _sineDecimator.setParams(sampleRate, oversample);
-
- _fmDepthSL.setParams(sampleRate, 5.0f, 1.0f);
- _squarePulseWidthSL.setParams(sampleRate, 0.1f, 2.0f);
- _sawSaturationSL.setParams(sampleRate, 1.0f, 1.0f);
- _triangleSampleWidthSL.setParams(sampleRate, 0.1f, 1.0f);
- _sineFeedbackSL.setParams(sampleRate, 0.1f, 1.0f);
- _squareMixSL.setParams(sampleRate, 5.0f, 1.0f);
- _sawMixSL.setParams(sampleRate, 5.0f, 1.0f);
- _triangleMixSL.setParams(sampleRate, 5.0f, 1.0f);
- _sineMixSL.setParams(sampleRate, 5.0f, 1.0f);
-}
-
-void XCO::setFrequency(float frequency) {
- if (_frequency != frequency && frequency < 0.475f * _phasor._sampleRate) {
- _frequency = frequency;
- _phasor.setFrequency(_frequency / (float)oversample);
- _square.setFrequency(_frequency);
- _saw.setFrequency(_frequency);
- }
-}
-
struct XCOWidget : ModuleWidget {
static constexpr int hp = 20;
diff --git a/src/XCO.hpp b/src/XCO.hpp
@@ -67,54 +67,68 @@ struct XCO : BGModule {
NUM_LIGHTS
};
+ struct Engine {
+ static constexpr int oversample = 8;
+
+ float frequency = 0.0f;
+ float baseVOct = 0.0f;
+ float baseHz = 0.0f;
+ float fmDepth = 0.0f;
+ float triangleSampleWidth = 0.0f;
+ float sineFeedback = 0.0f;
+ float sineOMix = 0.0f;
+ float sineFeedbackDelayedSample = 0.0f;
+ Phasor::phase_delta_t squarePhaseOffset = 0.0f;
+ Phasor::phase_delta_t sawPhaseOffset = 0.0f;
+ Phasor::phase_delta_t trianglePhaseOffset = 0.0f;
+ Phasor::phase_delta_t sinePhaseOffset = 0.0f;
+ float squareMix = 1.0f;
+ float sawMix = 1.0f;
+ float triangleMix = 1.0f;
+ float sineMix = 1.0f;
+
+ Phasor phasor;
+ BandLimitedSquareOscillator square;
+ BandLimitedSawOscillator saw;
+ TriangleOscillator triangle;
+ SineTableOscillator sine;
+ CICDecimator squareDecimator;
+ CICDecimator sawDecimator;
+ CICDecimator triangleDecimator;
+ CICDecimator sineDecimator;
+ float squareBuffer[oversample];
+ float sawBuffer[oversample];
+ float triangleBuffer[oversample];
+ float sineBuffer[oversample];
+ PositiveZeroCrossing syncTrigger;
+
+ bogaudio::dsp::SlewLimiter fmDepthSL;
+ bogaudio::dsp::SlewLimiter squarePulseWidthSL;
+ bogaudio::dsp::SlewLimiter sawSaturationSL;
+ bogaudio::dsp::SlewLimiter triangleSampleWidthSL;
+ bogaudio::dsp::SlewLimiter sineFeedbackSL;
+ bogaudio::dsp::SlewLimiter squareMixSL;
+ bogaudio::dsp::SlewLimiter sawMixSL;
+ bogaudio::dsp::SlewLimiter triangleMixSL;
+ bogaudio::dsp::SlewLimiter sineMixSL;
+
+ Engine() {
+ saw.setQuality(12);
+ square.setQuality(12);
+ }
+
+ void reset();
+ void sampleRateChange(float sampleRate);
+ void setFrequency(float frequency);
+ };
+
const float amplitude = 5.0f;
- static constexpr int oversample = 8;
const float _slowModeOffset = -7.0f;
const float sineOversampleMixIncrement = 0.01f;
float _oversampleThreshold = 0.0f;
- float _frequency = 0.0f;
- float _baseVOct = 0.0f;
- float _baseHz = 0.0f;
bool _slowMode = false;
- float _fmDepth = 0.0f;
bool _fmLinearMode = false;
- float _triangleSampleWidth = 0.0f;
- float _sineFeedback = 0.0f;
- float _sineOMix = 0.0f;
- float _sineFeedbackDelayedSample = 0.0f;
- Phasor::phase_delta_t _squarePhaseOffset = 0.0f;
- Phasor::phase_delta_t _sawPhaseOffset = 0.0f;
- Phasor::phase_delta_t _trianglePhaseOffset = 0.0f;
- Phasor::phase_delta_t _sinePhaseOffset = 0.0f;
- float _squareMix = 1.0f;
- float _sawMix = 1.0f;
- float _triangleMix = 1.0f;
- float _sineMix = 1.0f;
-
- Phasor _phasor;
- BandLimitedSquareOscillator _square;
- BandLimitedSawOscillator _saw;
- TriangleOscillator _triangle;
- SineTableOscillator _sine;
- CICDecimator _squareDecimator;
- CICDecimator _sawDecimator;
- CICDecimator _triangleDecimator;
- CICDecimator _sineDecimator;
- float _squareBuffer[oversample];
- float _sawBuffer[oversample];
- float _triangleBuffer[oversample];
- float _sineBuffer[oversample];
- PositiveZeroCrossing _syncTrigger;
-
- bogaudio::dsp::SlewLimiter _fmDepthSL;
- bogaudio::dsp::SlewLimiter _squarePulseWidthSL;
- bogaudio::dsp::SlewLimiter _sawSaturationSL;
- bogaudio::dsp::SlewLimiter _triangleSampleWidthSL;
- bogaudio::dsp::SlewLimiter _sineFeedbackSL;
- bogaudio::dsp::SlewLimiter _squareMixSL;
- bogaudio::dsp::SlewLimiter _sawMixSL;
- bogaudio::dsp::SlewLimiter _triangleMixSL;
- bogaudio::dsp::SlewLimiter _sineMixSL;
+ Engine* _engines[maxChannels] {};
struct XCOFrequencyParamQuantity : FrequencyParamQuantity {
float offset() override;
@@ -139,23 +153,20 @@ struct XCO : BGModule {
configParam(SINE_FEEDBACK_PARAM, 0.0f, 1.0f, 0.0f, "Sine wave feedback", "%", 0.0f, 100.0f);
configParam(SINE_PHASE_PARAM, -1.0f, 1.0f, 0.0f, "Sine wave phase", "ยบ", 0.0f, 180.0f);
configParam(SINE_MIX_PARAM, 0.0f, 1.0f, 1.0f, "Sine wave mix", "%", 0.0f, 100.0f);
-
- reset();
- setSampleRate(APP->engine->getSampleRate());
- _saw.setQuality(12);
- _square.setQuality(12);
}
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& param, Input& input);
- float level(Param& param, Input& input);
- void setSampleRate(float sampleRate);
- void setFrequency(float frequency);
+ void processChannel(const ProcessArgs& args, int c) override;
+ Phasor::phase_delta_t phaseOffset(int c, Param& param, Input& input);
+ float level(int c, Param& param, Input& input);
};
} // namespace bogaudio