diff --git a/src/ADSR.cpp b/src/ADSR.cpp index f63db26..b42a804 100644 --- a/src/ADSR.cpp +++ b/src/ADSR.cpp @@ -28,24 +28,18 @@ struct ADSR : Module { SchmittTrigger trigger; float lights[4] = {}; - ADSR(); + ADSR() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { + trigger.setThresholds(0.0, 1.0); + } void step(); }; -ADSR::ADSR() { - params.resize(NUM_PARAMS); - inputs.resize(NUM_INPUTS); - outputs.resize(NUM_OUTPUTS); - - trigger.setThresholds(0.0, 1.0); -} - void ADSR::step() { - float attack = clampf(params[ATTACK_INPUT] + getf(inputs[ATTACK_INPUT]) / 10.0, 0.0, 1.0); - float decay = clampf(params[DECAY_PARAM] + getf(inputs[DECAY_INPUT]) / 10.0, 0.0, 1.0); - float sustain = clampf(params[SUSTAIN_PARAM] + getf(inputs[SUSTAIN_INPUT]) / 10.0, 0.0, 1.0); - float release = clampf(params[RELEASE_PARAM] + getf(inputs[RELEASE_PARAM]) / 10.0, 0.0, 1.0); + float attack = clampf(params[ATTACK_INPUT].value + inputs[ATTACK_INPUT].value / 10.0, 0.0, 1.0); + float decay = clampf(params[DECAY_PARAM].value + inputs[DECAY_INPUT].value / 10.0, 0.0, 1.0); + float sustain = clampf(params[SUSTAIN_PARAM].value + inputs[SUSTAIN_INPUT].value / 10.0, 0.0, 1.0); + float release = clampf(params[RELEASE_PARAM].value + inputs[RELEASE_PARAM].value / 10.0, 0.0, 1.0); // Lights lights[0] = 2.0*attack - 1.0; @@ -54,8 +48,8 @@ void ADSR::step() { lights[3] = 2.0*release - 1.0; // Gate and trigger - bool gated = getf(inputs[GATE_INPUT]) >= 1.0; - if (trigger.process(getf(inputs[TRIG_INPUT]))) + bool gated = inputs[GATE_INPUT].value >= 1.0; + if (trigger.process(inputs[TRIG_INPUT].value)) decaying = false; const float base = 20000.0; @@ -63,7 +57,12 @@ void ADSR::step() { if (gated) { if (decaying) { // Decay - env += powf(base, 1 - decay) / maxTime * (sustain - env) / gSampleRate; + if (decay < 1e-4) { + env = sustain; + } + else { + env += powf(base, 1 - decay) / maxTime * (sustain - env) / gSampleRate; + } } else { // Attack @@ -82,11 +81,16 @@ void ADSR::step() { } else { // Release - env += powf(base, 1 - release) / maxTime * (0.0 - env) / gSampleRate; + if (release < 1e-4) { + env = 0.0; + } + else { + env += powf(base, 1 - release) / maxTime * (0.0 - env) / gSampleRate; + } decaying = false; } - setf(outputs[ENVELOPE_OUTPUT], 10.0 * env); + outputs[ENVELOPE_OUTPUT].value = 10.0 * env; } diff --git a/src/Delay.cpp b/src/Delay.cpp index 822220c..57398e1 100644 --- a/src/Delay.cpp +++ b/src/Delay.cpp @@ -31,26 +31,20 @@ struct Delay : Module { RCFilter lowpassFilter; RCFilter highpassFilter; - Delay(); + Delay() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); }; -Delay::Delay() { - params.resize(NUM_PARAMS); - inputs.resize(NUM_INPUTS); - outputs.resize(NUM_OUTPUTS); -} - void Delay::step() { // Get input to delay block - float in = getf(inputs[IN_INPUT]); - float feedback = clampf(params[FEEDBACK_PARAM] + getf(inputs[FEEDBACK_INPUT]) / 10.0, 0.0, 0.99); + float in = inputs[IN_INPUT].value; + float feedback = clampf(params[FEEDBACK_PARAM].value + inputs[FEEDBACK_INPUT].value / 10.0, 0.0, 0.99); float dry = in + lastWet * feedback; // Compute delay time in seconds - float delay = 1e-3 * powf(10.0 / 1e-3, clampf(params[TIME_PARAM] + getf(inputs[TIME_INPUT]) / 10.0, 0.0, 1.0)); + float delay = 1e-3 * powf(10.0 / 1e-3, clampf(params[TIME_PARAM].value + inputs[TIME_INPUT].value / 10.0, 0.0, 1.0)); // Number of delay samples float index = delay * gSampleRate; @@ -93,7 +87,7 @@ void Delay::step() { // Apply color to delay wet output // TODO Make it sound better - float color = clampf(params[COLOR_PARAM] + getf(inputs[COLOR_INPUT]) / 10.0, 0.0, 1.0); + float color = clampf(params[COLOR_PARAM].value + inputs[COLOR_INPUT].value / 10.0, 0.0, 1.0); float lowpassFreq = 10000.0 * powf(10.0, clampf(2.0*color, 0.0, 1.0)); lowpassFilter.setCutoff(lowpassFreq / gSampleRate); lowpassFilter.process(wet); @@ -105,9 +99,9 @@ void Delay::step() { lastWet = wet; - float mix = clampf(params[MIX_PARAM] + getf(inputs[MIX_INPUT]) / 10.0, 0.0, 1.0); + float mix = clampf(params[MIX_PARAM].value + inputs[MIX_INPUT].value / 10.0, 0.0, 1.0); float out = crossf(in, wet, mix); - setf(outputs[OUT_OUTPUT], out); + outputs[OUT_OUTPUT].value = out; } diff --git a/src/Fundamental.hpp b/src/Fundamental.hpp index 550bd8d..08c89c5 100644 --- a/src/Fundamental.hpp +++ b/src/Fundamental.hpp @@ -40,6 +40,5 @@ struct ScopeWidget : ModuleWidget { struct SEQ3Widget : ModuleWidget { SEQ3Widget(); - json_t *toJsonData(); - void fromJsonData(json_t *root); + Menu *createContextMenu(); }; diff --git a/src/SEQ3.cpp b/src/SEQ3.cpp index 90dfa9e..c38fb9b 100644 --- a/src/SEQ3.cpp +++ b/src/SEQ3.cpp @@ -39,6 +39,13 @@ struct SEQ3 : Module { bool gateState[8] = {}; float stepLights[8] = {}; + enum GateMode { + TRIGGER, + RETRIGGER, + CONTINUOUS, + }; + GateMode gateMode = TRIGGER; + // Lights float runningLight = 0.0; float resetLight = 0.0; @@ -46,12 +53,13 @@ struct SEQ3 : Module { float rowLights[3] = {}; float gateLights[8] = {}; - SEQ3(); + SEQ3() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); json_t *toJson() { json_t *rootJ = json_object(); + // gates json_t *gatesJ = json_array(); for (int i = 0; i < 8; i++) { json_t *gateJ = json_integer((int) gateState[i]); @@ -59,15 +67,28 @@ struct SEQ3 : Module { } json_object_set_new(rootJ, "gates", gatesJ); + // gateMode + json_t *gateModeJ = json_integer((int) gateMode); + json_object_set_new(rootJ, "gateMode", gateModeJ); + return rootJ; } void fromJson(json_t *rootJ) { + // gates json_t *gatesJ = json_object_get(rootJ, "gates"); - for (int i = 0; i < 8; i++) { - json_t *gateJ = json_array_get(gatesJ, i); - gateState[i] = !!json_integer_value(gateJ); + if (gatesJ) { + for (int i = 0; i < 8; i++) { + json_t *gateJ = json_array_get(gatesJ, i); + if (gateJ) + gateState[i] = !!json_integer_value(gateJ); + } } + + // gateMode + json_t *gateModeJ = json_object_get(rootJ, "gateMode"); + if (gateModeJ) + gateMode = (GateMode)json_integer_value(gateModeJ); } void initialize() { @@ -84,16 +105,10 @@ struct SEQ3 : Module { }; -SEQ3::SEQ3() { - params.resize(NUM_PARAMS); - inputs.resize(NUM_INPUTS); - outputs.resize(NUM_OUTPUTS); -} - void SEQ3::step() { const float lightLambda = 0.075; // Run - if (runningTrigger.process(params[RUN_PARAM])) { + if (runningTrigger.process(params[RUN_PARAM].value)) { running = !running; } runningLight = running ? 1.0 : 0.0; @@ -101,16 +116,16 @@ void SEQ3::step() { bool nextStep = false; if (running) { - if (inputs[EXT_CLOCK_INPUT]) { + if (inputs[EXT_CLOCK_INPUT].active) { // External clock - if (clockTrigger.process(*inputs[EXT_CLOCK_INPUT])) { + if (clockTrigger.process(inputs[EXT_CLOCK_INPUT].value)) { phase = 0.0; nextStep = true; } } else { // Internal clock - float clockTime = powf(2.0, params[CLOCK_PARAM] + getf(inputs[CLOCK_INPUT])); + float clockTime = powf(2.0, params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value); phase += clockTime / gSampleRate; if (phase >= 1.0) { phase -= 1.0; @@ -120,7 +135,7 @@ void SEQ3::step() { } // Reset - if (resetTrigger.process(params[RESET_PARAM] + getf(inputs[RESET_INPUT]))) { + if (resetTrigger.process(params[RESET_PARAM].value + inputs[RESET_INPUT].value)) { phase = 0.0; index = 999; nextStep = true; @@ -129,7 +144,7 @@ void SEQ3::step() { if (nextStep) { // Advance step - int numSteps = clampi(roundf(params[STEPS_PARAM] + getf(inputs[STEPS_INPUT])), 1, 8); + int numSteps = clampi(roundf(params[STEPS_PARAM].value + inputs[STEPS_INPUT].value), 1, 8); index += 1; if (index >= numSteps) { index = 0; @@ -141,24 +156,31 @@ void SEQ3::step() { // Gate buttons for (int i = 0; i < 8; i++) { - if (gateTriggers[i].process(params[GATE_PARAM + i])) { + if (gateTriggers[i].process(params[GATE_PARAM + i].value)) { gateState[i] = !gateState[i]; } float gate = (i == index && gateState[i] >= 1.0) ? 10.0 : 0.0; - setf(outputs[GATE_OUTPUT + i], gate); + outputs[GATE_OUTPUT + i].value = gate; stepLights[i] -= stepLights[i] / lightLambda / gSampleRate; gateLights[i] = (gateState[i] >= 1.0) ? 1.0 - stepLights[i] : stepLights[i]; } // Rows - float row1 = params[ROW1_PARAM + index]; - float row2 = params[ROW2_PARAM + index]; - float row3 = params[ROW3_PARAM + index]; - float gates = (gateState[index] >= 1.0) ? 10.0 : 0.0; - setf(outputs[ROW1_OUTPUT], row1); - setf(outputs[ROW2_OUTPUT], row2); - setf(outputs[ROW3_OUTPUT], row3); - setf(outputs[GATES_OUTPUT], gates); + float row1 = params[ROW1_PARAM + index].value; + float row2 = params[ROW2_PARAM + index].value; + float row3 = params[ROW3_PARAM + index].value; + bool gatesOn = gateState[index]; + if (gateMode == TRIGGER) + gatesOn = gatesOn && nextStep; + else if (gateMode == RETRIGGER) + gatesOn = gatesOn && !nextStep; + float gates = gatesOn ? 10.0 : 0.0; + + // Outputs + outputs[ROW1_OUTPUT].value = row1; + outputs[ROW2_OUTPUT].value = row2; + outputs[ROW3_OUTPUT].value = row3; + outputs[GATES_OUTPUT].value = gates; gatesLight = (gateState[index] >= 1.0) ? 1.0 : 0.0; rowLights[0] = row1; rowLights[1] = row2; @@ -213,3 +235,48 @@ SEQ3Widget::SEQ3Widget() { addOutput(createOutput(Vec(portX[i]-1, 308-1), module, SEQ3::GATE_OUTPUT + i)); } } + +struct SEQ3GateModeItem : MenuItem { + SEQ3 *seq3; + SEQ3::GateMode gateMode; + void onAction() { + seq3->gateMode = gateMode; + } + void step() { + rightText = (seq3->gateMode == gateMode) ? "✔" : ""; + } +}; + +Menu *SEQ3Widget::createContextMenu() { + Menu *menu = ModuleWidget::createContextMenu(); + + MenuLabel *spacerLabel = new MenuLabel(); + menu->pushChild(spacerLabel); + + SEQ3 *seq3 = dynamic_cast(module); + assert(seq3); + + MenuLabel *modeLabel = new MenuLabel(); + modeLabel->text = "Gate Mode"; + menu->pushChild(modeLabel); + + SEQ3GateModeItem *triggerItem = new SEQ3GateModeItem(); + triggerItem->text = "Trigger"; + triggerItem->seq3 = seq3; + triggerItem->gateMode = SEQ3::TRIGGER; + menu->pushChild(triggerItem); + + SEQ3GateModeItem *retriggerItem = new SEQ3GateModeItem(); + retriggerItem->text = "Retrigger"; + retriggerItem->seq3 = seq3; + retriggerItem->gateMode = SEQ3::RETRIGGER; + menu->pushChild(retriggerItem); + + SEQ3GateModeItem *continuousItem = new SEQ3GateModeItem(); + continuousItem->text = "Continuous"; + continuousItem->seq3 = seq3; + continuousItem->gateMode = SEQ3::CONTINUOUS; + menu->pushChild(continuousItem); + + return menu; +} diff --git a/src/Scope.cpp b/src/Scope.cpp index eded236..ad7bd5a 100644 --- a/src/Scope.cpp +++ b/src/Scope.cpp @@ -38,7 +38,7 @@ struct Scope : Module { float lights[4] = {}; SchmittTrigger resetTrigger; - Scope(); + Scope() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); json_t *toJson() { @@ -65,36 +65,30 @@ struct Scope : Module { }; -Scope::Scope() { - params.resize(NUM_PARAMS); - inputs.resize(NUM_INPUTS); - outputs.resize(NUM_OUTPUTS); -} - void Scope::step() { // Modes - if (sumTrigger.process(params[MODE_PARAM])) { + if (sumTrigger.process(params[MODE_PARAM].value)) { sum = !sum; } lights[0] = sum ? 0.0 : 1.0; lights[1] = sum ? 1.0 : 0.0; - if (extTrigger.process(params[EXT_PARAM])) { + if (extTrigger.process(params[EXT_PARAM].value)) { ext = !ext; } lights[2] = ext ? 0.0 : 1.0; lights[3] = ext ? 1.0 : 0.0; // Compute time - float deltaTime = powf(2.0, params[TIME_PARAM]); + float deltaTime = powf(2.0, params[TIME_PARAM].value); int frameCount = (int)ceilf(deltaTime * gSampleRate); // Add frame to buffer if (bufferIndex < BUFFER_SIZE) { if (++frameIndex > frameCount) { frameIndex = 0; - bufferX[bufferIndex] = getf(inputs[X_INPUT]); - bufferY[bufferIndex] = getf(inputs[Y_INPUT]); + bufferX[bufferIndex] = inputs[X_INPUT].value; + bufferY[bufferIndex] = inputs[Y_INPUT].value; bufferIndex++; } } @@ -102,7 +96,7 @@ void Scope::step() { // Are we waiting on the next trigger? if (bufferIndex >= BUFFER_SIZE) { // Trigger immediately if external but nothing plugged in - if (ext && !inputs[TRIG_INPUT]) { + if (ext && !inputs[TRIG_INPUT].active) { bufferIndex = 0; frameIndex = 0; return; } @@ -113,8 +107,8 @@ void Scope::step() { frameIndex++; // Must go below 0.1V to trigger - resetTrigger.setThresholds(params[TRIG_PARAM] - 0.1, params[TRIG_PARAM]); - float gate = ext ? getf(inputs[TRIG_INPUT]) : getf(inputs[X_INPUT]); + resetTrigger.setThresholds(params[TRIG_PARAM].value - 0.1, params[TRIG_PARAM].value); + float gate = ext ? inputs[TRIG_INPUT].value : inputs[X_INPUT].value; // Reset if triggered float holdTime = 0.1; @@ -232,10 +226,10 @@ struct ScopeDisplay : TransparentWidget { } void draw(NVGcontext *vg) { - float gainX = powf(2.0, roundf(module->params[Scope::X_SCALE_PARAM])) / 12.0; - float gainY = powf(2.0, roundf(module->params[Scope::Y_SCALE_PARAM])) / 12.0; - float posX = module->params[Scope::X_POS_PARAM]; - float posY = module->params[Scope::Y_POS_PARAM]; + float gainX = powf(2.0, roundf(module->params[Scope::X_SCALE_PARAM].value)) / 12.0; + float gainY = powf(2.0, roundf(module->params[Scope::Y_SCALE_PARAM].value)) / 12.0; + float posX = module->params[Scope::X_POS_PARAM].value; + float posY = module->params[Scope::Y_POS_PARAM].value; // Draw waveforms if (module->sum) { @@ -249,18 +243,18 @@ struct ScopeDisplay : TransparentWidget { } else { // Y - if (module->inputs[Scope::Y_INPUT]) { + if (module->inputs[Scope::Y_INPUT].active) { nvgStrokeColor(vg, nvgRGBA(0xe1, 0x02, 0x78, 0xc0)); drawWaveform(vg, module->bufferY, gainY, posY); } // X - if (module->inputs[Scope::X_INPUT]) { + if (module->inputs[Scope::X_INPUT].active) { nvgStrokeColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xc0)); drawWaveform(vg, module->bufferX, gainX, posX); } } - drawTrig(vg, module->params[Scope::TRIG_PARAM], gainX, posX); + drawTrig(vg, module->params[Scope::TRIG_PARAM].value, gainX, posX); // Calculate and draw stats if (++frame >= 4) { diff --git a/src/VCA.cpp b/src/VCA.cpp index 4224455..f9bc912 100644 --- a/src/VCA.cpp +++ b/src/VCA.cpp @@ -22,25 +22,19 @@ struct VCA : Module { NUM_OUTPUTS }; - VCA(); + VCA() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); }; -VCA::VCA() { - params.resize(NUM_PARAMS); - inputs.resize(NUM_INPUTS); - outputs.resize(NUM_OUTPUTS); -} - -static void stepChannel(const float *in, float level, const float *lin, const float *exp, float *out) { - float v = getf(in) * level; - if (lin) - v *= clampf(*lin / 10.0, 0.0, 1.0); +static void stepChannel(Input &in, Param &level, Input &lin, Input &exp, Output &out) { + float v = in.value * level.value; + if (lin.active) + v *= clampf(lin.value / 10.0, 0.0, 1.0); const float expBase = 50.0; - if (exp) - v *= rescalef(powf(expBase, clampf(*exp / 10.0, 0.0, 1.0)), 1.0, expBase, 0.0, 1.0); - setf(out, v); + if (exp.active) + v *= rescalef(powf(expBase, clampf(exp.value / 10.0, 0.0, 1.0)), 1.0, expBase, 0.0, 1.0); + out.value = v; } void VCA::step() { diff --git a/src/VCF.cpp b/src/VCF.cpp index 86e94d0..9318237 100644 --- a/src/VCF.cpp +++ b/src/VCF.cpp @@ -102,32 +102,26 @@ struct VCF : Module { LadderFilter filter; - VCF(); + VCF() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); }; -VCF::VCF() { - params.resize(NUM_PARAMS); - inputs.resize(NUM_INPUTS); - outputs.resize(NUM_OUTPUTS); -} - void VCF::step() { - float input = getf(inputs[IN_INPUT]) / 5.0; - float drive = params[DRIVE_PARAM] + getf(inputs[DRIVE_INPUT]) / 10.0; + float input = inputs[IN_INPUT].value / 5.0; + float drive = params[DRIVE_PARAM].value + inputs[DRIVE_INPUT].value / 10.0; float gain = powf(100.0, drive); input *= gain; // Add -60dB noise to bootstrap self-oscillation input += 1.0e-6 * (2.0*randomf() - 1.0); // Set resonance - float res = params[RES_PARAM] + getf(inputs[RES_INPUT]) / 5.0; + float res = params[RES_PARAM].value + inputs[RES_INPUT].value / 5.0; res = 5.5 * clampf(res, 0.0, 1.0); filter.resonance = res; // Set cutoff frequency - float cutoffExp = params[FREQ_PARAM] + params[FREQ_CV_PARAM] * getf(inputs[FREQ_INPUT]) / 5.0; + float cutoffExp = params[FREQ_PARAM].value + params[FREQ_CV_PARAM].value * inputs[FREQ_INPUT].value / 5.0; cutoffExp = clampf(cutoffExp, 0.0, 1.0); const float minCutoff = 15.0; const float maxCutoff = 8400.0; @@ -136,9 +130,9 @@ void VCF::step() { // Push a sample to the state filter filter.process(input, 1.0/gSampleRate); - // Extract outputs - setf(outputs[LPF_OUTPUT], 5.0 * filter.state[3]); - setf(outputs[HPF_OUTPUT], 5.0 * (input - filter.state[3])); + // Set outputs + outputs[LPF_OUTPUT].value = 5.0 * filter.state[3]; + outputs[HPF_OUTPUT].value = 5.0 * (input - filter.state[3]); } diff --git a/src/VCMixer.cpp b/src/VCMixer.cpp index 11ea90f..53ffb65 100644 --- a/src/VCMixer.cpp +++ b/src/VCMixer.cpp @@ -27,28 +27,22 @@ struct VCMixer : Module { NUM_OUTPUTS }; - VCMixer(); + VCMixer() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); }; -VCMixer::VCMixer() { - params.resize(NUM_PARAMS); - inputs.resize(NUM_INPUTS); - outputs.resize(NUM_OUTPUTS); -} - - void VCMixer::step() { - float ch1 = getf(inputs[CH1_INPUT]) * params[CH1_PARAM] * clampf(getf(inputs[CH1_CV_INPUT], 10.0) / 10.0, 0.0, 1.0); - float ch2 = getf(inputs[CH2_INPUT]) * params[CH2_PARAM] * clampf(getf(inputs[CH2_CV_INPUT], 10.0) / 10.0, 0.0, 1.0); - float ch3 = getf(inputs[CH3_INPUT]) * params[CH3_PARAM] * clampf(getf(inputs[CH3_CV_INPUT], 10.0) / 10.0, 0.0, 1.0); - float mix = (ch1 + ch2 + ch3) * params[MIX_PARAM] * getf(inputs[MIX_CV_INPUT], 10.0) / 10.0; + float ch1 = inputs[CH1_INPUT].value * params[CH1_PARAM].value * clampf(inputs[CH1_CV_INPUT].normalize(10.0) / 10.0, 0.0, 1.0); + float ch2 = inputs[CH2_INPUT].value * params[CH2_PARAM].value * clampf(inputs[CH2_CV_INPUT].normalize(10.0) / 10.0, 0.0, 1.0); + float ch3 = inputs[CH3_INPUT].value * params[CH3_PARAM].value * clampf(inputs[CH3_CV_INPUT].normalize(10.0) / 10.0, 0.0, 1.0); + float cv = inputs[MIX_CV_INPUT].normalize(10.0); + float mix = (ch1 + ch2 + ch3) * params[MIX_PARAM].value * cv / 10.0; - setf(outputs[CH1_OUTPUT], ch1); - setf(outputs[CH2_OUTPUT], ch2); - setf(outputs[CH3_OUTPUT], ch3); - setf(outputs[MIX_OUTPUT], mix); + outputs[CH1_OUTPUT].value = ch1; + outputs[CH2_OUTPUT].value = ch2; + outputs[CH3_OUTPUT].value = ch3; + outputs[MIX_OUTPUT].value = mix; } diff --git a/src/VCO.cpp b/src/VCO.cpp index 5ed4b01..f7264e1 100644 --- a/src/VCO.cpp +++ b/src/VCO.cpp @@ -51,20 +51,15 @@ struct VCO : Module { float pitchSlew = 0.0; int pitchSlewIndex = 0; - VCO(); + VCO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); }; -VCO::VCO() { - params.resize(NUM_PARAMS); - inputs.resize(NUM_INPUTS); - outputs.resize(NUM_OUTPUTS); -} void VCO::step() { - bool analog = params[MODE_PARAM] < 1.0; + bool analog = params[MODE_PARAM].value < 1.0; // TODO Soft sync features - bool soft = params[SYNC_PARAM] < 1.0; + bool soft = params[SYNC_PARAM].value < 1.0; if (analog) { // Adjust pitch slew @@ -76,7 +71,7 @@ void VCO::step() { } // Compute frequency - float pitch = params[FREQ_PARAM]; + float pitch = params[FREQ_PARAM].value; if (analog) { // Apply pitch slew const float pitchSlewAmount = 3.0; @@ -86,16 +81,16 @@ void VCO::step() { // Quantize coarse knob if digital mode pitch = roundf(pitch); } - pitch += 12.0 * getf(inputs[PITCH_INPUT]); - pitch += 3.0 * quadraticBipolar(params[FINE_PARAM]); - if (inputs[FM_INPUT]) { - pitch += quadraticBipolar(params[FM_PARAM]) * 12.0 * *inputs[FM_INPUT]; + pitch += 12.0 * inputs[PITCH_INPUT].value; + pitch += 3.0 * quadraticBipolar(params[FINE_PARAM].value); + if (inputs[FM_INPUT].active) { + pitch += quadraticBipolar(params[FM_PARAM].value) * 12.0 * inputs[FM_INPUT].value; } float freq = 261.626 * powf(2.0, pitch / 12.0); // Pulse width const float pwMin = 0.01; - float pw = clampf(params[PW_PARAM] + params[PW_CV_PARAM] * getf(inputs[PW_INPUT]) / 10.0, pwMin, 1.0 - pwMin); + float pw = clampf(params[PW_PARAM].value + params[PW_CV_PARAM].value * inputs[PW_INPUT].value / 10.0, pwMin, 1.0 - pwMin); // Advance phase float deltaPhase = clampf(freq / gSampleRate, 1e-6, 0.5); @@ -103,8 +98,8 @@ void VCO::step() { // Detect sync int syncIndex = -1; // Index in the oversample loop where sync occurs [0, OVERSAMPLE) float syncCrossing = 0.0; // Offset that sync occurs [0.0, 1.0) - if (inputs[SYNC_INPUT]) { - float sync = *inputs[SYNC_INPUT] - 0.01; + if (inputs[SYNC_INPUT].active) { + float sync = inputs[SYNC_INPUT].value - 0.01; if (sync > 0.0 && lastSync <= 0.0) { float deltaSync = sync - lastSync; syncCrossing = 1.0 - sync / deltaSync; @@ -137,26 +132,26 @@ void VCO::step() { } } - if (outputs[SIN_OUTPUT]) { + if (outputs[SIN_OUTPUT].active) { if (analog) // Quadratic approximation of sine, slightly richer harmonics sin[i] = 1.08 * ((phase < 0.25) ? (-1.0 + (4*phase)*(4*phase)) : (phase < 0.75) ? (1.0 - (4*phase-2)*(4*phase-2)) : (-1.0 + (4*phase-4)*(4*phase-4))); else sin[i] = -cosf(2*M_PI * phase); } - if (outputs[TRI_OUTPUT]) { + if (outputs[TRI_OUTPUT].active) { if (analog) tri[i] = 1.35 * interpf(triTable, phase * 2047.0); else tri[i] = (phase < 0.5) ? (-1.0 + 4.0*phase) : (1.0 - 4.0*(phase - 0.5)); } - if (outputs[SAW_OUTPUT]) { + if (outputs[SAW_OUTPUT].active) { if (analog) saw[i] = 1.5 * interpf(sawTable, phase * 2047.0); else saw[i] = -1.0 + 2.0*phase; } - if (outputs[SQR_OUTPUT]) { + if (outputs[SQR_OUTPUT].active) { sqr[i] = (phase < 1.0 - pw) ? -1.0 : 1.0; if (analog) { // Simply filter here @@ -171,14 +166,14 @@ void VCO::step() { } // Set output - if (outputs[SIN_OUTPUT]) - *outputs[SIN_OUTPUT] = 5.0 * sinDecimator.process(sin); - if (outputs[TRI_OUTPUT]) - *outputs[TRI_OUTPUT] = 5.0 * triDecimator.process(tri); - if (outputs[SAW_OUTPUT]) - *outputs[SAW_OUTPUT] = 5.0 * sawDecimator.process(saw); - if (outputs[SQR_OUTPUT]) - *outputs[SQR_OUTPUT] = 5.0 * sqrDecimator.process(sqr); + if (outputs[SIN_OUTPUT].active) + outputs[SIN_OUTPUT].value = 5.0 * sinDecimator.process(sin); + if (outputs[TRI_OUTPUT].active) + outputs[TRI_OUTPUT].value = 5.0 * triDecimator.process(tri); + if (outputs[SAW_OUTPUT].active) + outputs[SAW_OUTPUT].value = 5.0 * sawDecimator.process(saw); + if (outputs[SQR_OUTPUT].active) + outputs[SQR_OUTPUT].value = 5.0 * sqrDecimator.process(sqr); lights[0] = rescalef(pitch, -48.0, 48.0, -1.0, 1.0); }