Browse Source

Modify params, inputs, and outputs for Rack engine update

tags/v0.4.0
Andrew Belt 7 years ago
parent
commit
1b7b9e5066
9 changed files with 188 additions and 153 deletions
  1. +22
    -18
      src/ADSR.cpp
  2. +7
    -13
      src/Delay.cpp
  3. +1
    -2
      src/Fundamental.hpp
  4. +93
    -26
      src/SEQ3.cpp
  5. +16
    -22
      src/Scope.cpp
  6. +8
    -14
      src/VCA.cpp
  7. +8
    -14
      src/VCF.cpp
  8. +10
    -16
      src/VCMixer.cpp
  9. +23
    -28
      src/VCO.cpp

+ 22
- 18
src/ADSR.cpp View File

@@ -28,24 +28,18 @@ struct ADSR : Module {
SchmittTrigger trigger; SchmittTrigger trigger;
float lights[4] = {}; float lights[4] = {};


ADSR();
ADSR() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
trigger.setThresholds(0.0, 1.0);
}
void step(); 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() { 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
lights[0] = 2.0*attack - 1.0; lights[0] = 2.0*attack - 1.0;
@@ -54,8 +48,8 @@ void ADSR::step() {
lights[3] = 2.0*release - 1.0; lights[3] = 2.0*release - 1.0;


// Gate and trigger // 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; decaying = false;


const float base = 20000.0; const float base = 20000.0;
@@ -63,7 +57,12 @@ void ADSR::step() {
if (gated) { if (gated) {
if (decaying) { if (decaying) {
// Decay // 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 { else {
// Attack // Attack
@@ -82,11 +81,16 @@ void ADSR::step() {
} }
else { else {
// Release // 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; decaying = false;
} }


setf(outputs[ENVELOPE_OUTPUT], 10.0 * env);
outputs[ENVELOPE_OUTPUT].value = 10.0 * env;
} }






+ 7
- 13
src/Delay.cpp View File

@@ -31,26 +31,20 @@ struct Delay : Module {
RCFilter lowpassFilter; RCFilter lowpassFilter;
RCFilter highpassFilter; RCFilter highpassFilter;


Delay();
Delay() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}


void step(); void step();
}; };




Delay::Delay() {
params.resize(NUM_PARAMS);
inputs.resize(NUM_INPUTS);
outputs.resize(NUM_OUTPUTS);
}

void Delay::step() { void Delay::step() {
// Get input to delay block // 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; float dry = in + lastWet * feedback;


// Compute delay time in seconds // 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 // Number of delay samples
float index = delay * gSampleRate; float index = delay * gSampleRate;


@@ -93,7 +87,7 @@ void Delay::step() {


// Apply color to delay wet output // Apply color to delay wet output
// TODO Make it sound better // 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)); float lowpassFreq = 10000.0 * powf(10.0, clampf(2.0*color, 0.0, 1.0));
lowpassFilter.setCutoff(lowpassFreq / gSampleRate); lowpassFilter.setCutoff(lowpassFreq / gSampleRate);
lowpassFilter.process(wet); lowpassFilter.process(wet);
@@ -105,9 +99,9 @@ void Delay::step() {


lastWet = wet; 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); float out = crossf(in, wet, mix);
setf(outputs[OUT_OUTPUT], out);
outputs[OUT_OUTPUT].value = out;
} }






+ 1
- 2
src/Fundamental.hpp View File

@@ -40,6 +40,5 @@ struct ScopeWidget : ModuleWidget {


struct SEQ3Widget : ModuleWidget { struct SEQ3Widget : ModuleWidget {
SEQ3Widget(); SEQ3Widget();
json_t *toJsonData();
void fromJsonData(json_t *root);
Menu *createContextMenu();
}; };

+ 93
- 26
src/SEQ3.cpp View File

@@ -39,6 +39,13 @@ struct SEQ3 : Module {
bool gateState[8] = {}; bool gateState[8] = {};
float stepLights[8] = {}; float stepLights[8] = {};


enum GateMode {
TRIGGER,
RETRIGGER,
CONTINUOUS,
};
GateMode gateMode = TRIGGER;

// Lights // Lights
float runningLight = 0.0; float runningLight = 0.0;
float resetLight = 0.0; float resetLight = 0.0;
@@ -46,12 +53,13 @@ struct SEQ3 : Module {
float rowLights[3] = {}; float rowLights[3] = {};
float gateLights[8] = {}; float gateLights[8] = {};


SEQ3();
SEQ3() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
void step(); void step();


json_t *toJson() { json_t *toJson() {
json_t *rootJ = json_object(); json_t *rootJ = json_object();


// gates
json_t *gatesJ = json_array(); json_t *gatesJ = json_array();
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
json_t *gateJ = json_integer((int) gateState[i]); json_t *gateJ = json_integer((int) gateState[i]);
@@ -59,15 +67,28 @@ struct SEQ3 : Module {
} }
json_object_set_new(rootJ, "gates", gatesJ); json_object_set_new(rootJ, "gates", gatesJ);


// gateMode
json_t *gateModeJ = json_integer((int) gateMode);
json_object_set_new(rootJ, "gateMode", gateModeJ);

return rootJ; return rootJ;
} }


void fromJson(json_t *rootJ) { void fromJson(json_t *rootJ) {
// gates
json_t *gatesJ = json_object_get(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() { 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() { void SEQ3::step() {
const float lightLambda = 0.075; const float lightLambda = 0.075;
// Run // Run
if (runningTrigger.process(params[RUN_PARAM])) {
if (runningTrigger.process(params[RUN_PARAM].value)) {
running = !running; running = !running;
} }
runningLight = running ? 1.0 : 0.0; runningLight = running ? 1.0 : 0.0;
@@ -101,16 +116,16 @@ void SEQ3::step() {
bool nextStep = false; bool nextStep = false;


if (running) { if (running) {
if (inputs[EXT_CLOCK_INPUT]) {
if (inputs[EXT_CLOCK_INPUT].active) {
// External clock // External clock
if (clockTrigger.process(*inputs[EXT_CLOCK_INPUT])) {
if (clockTrigger.process(inputs[EXT_CLOCK_INPUT].value)) {
phase = 0.0; phase = 0.0;
nextStep = true; nextStep = true;
} }
} }
else { else {
// Internal clock // 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; phase += clockTime / gSampleRate;
if (phase >= 1.0) { if (phase >= 1.0) {
phase -= 1.0; phase -= 1.0;
@@ -120,7 +135,7 @@ void SEQ3::step() {
} }


// Reset // 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; phase = 0.0;
index = 999; index = 999;
nextStep = true; nextStep = true;
@@ -129,7 +144,7 @@ void SEQ3::step() {


if (nextStep) { if (nextStep) {
// Advance step // 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; index += 1;
if (index >= numSteps) { if (index >= numSteps) {
index = 0; index = 0;
@@ -141,24 +156,31 @@ void SEQ3::step() {


// Gate buttons // Gate buttons
for (int i = 0; i < 8; i++) { 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]; gateState[i] = !gateState[i];
} }
float gate = (i == index && gateState[i] >= 1.0) ? 10.0 : 0.0; 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; stepLights[i] -= stepLights[i] / lightLambda / gSampleRate;
gateLights[i] = (gateState[i] >= 1.0) ? 1.0 - stepLights[i] : stepLights[i]; gateLights[i] = (gateState[i] >= 1.0) ? 1.0 - stepLights[i] : stepLights[i];
} }


// Rows // 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; gatesLight = (gateState[index] >= 1.0) ? 1.0 : 0.0;
rowLights[0] = row1; rowLights[0] = row1;
rowLights[1] = row2; rowLights[1] = row2;
@@ -213,3 +235,48 @@ SEQ3Widget::SEQ3Widget() {
addOutput(createOutput<PJ301MPort>(Vec(portX[i]-1, 308-1), module, SEQ3::GATE_OUTPUT + i)); addOutput(createOutput<PJ301MPort>(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<SEQ3*>(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;
}

+ 16
- 22
src/Scope.cpp View File

@@ -38,7 +38,7 @@ struct Scope : Module {
float lights[4] = {}; float lights[4] = {};
SchmittTrigger resetTrigger; SchmittTrigger resetTrigger;


Scope();
Scope() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
void step(); void step();


json_t *toJson() { 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() { void Scope::step() {
// Modes // Modes
if (sumTrigger.process(params[MODE_PARAM])) {
if (sumTrigger.process(params[MODE_PARAM].value)) {
sum = !sum; sum = !sum;
} }
lights[0] = sum ? 0.0 : 1.0; lights[0] = sum ? 0.0 : 1.0;
lights[1] = sum ? 1.0 : 0.0; lights[1] = sum ? 1.0 : 0.0;


if (extTrigger.process(params[EXT_PARAM])) {
if (extTrigger.process(params[EXT_PARAM].value)) {
ext = !ext; ext = !ext;
} }
lights[2] = ext ? 0.0 : 1.0; lights[2] = ext ? 0.0 : 1.0;
lights[3] = ext ? 1.0 : 0.0; lights[3] = ext ? 1.0 : 0.0;


// Compute time // 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); int frameCount = (int)ceilf(deltaTime * gSampleRate);


// Add frame to buffer // Add frame to buffer
if (bufferIndex < BUFFER_SIZE) { if (bufferIndex < BUFFER_SIZE) {
if (++frameIndex > frameCount) { if (++frameIndex > frameCount) {
frameIndex = 0; 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++; bufferIndex++;
} }
} }
@@ -102,7 +96,7 @@ void Scope::step() {
// Are we waiting on the next trigger? // Are we waiting on the next trigger?
if (bufferIndex >= BUFFER_SIZE) { if (bufferIndex >= BUFFER_SIZE) {
// Trigger immediately if external but nothing plugged in // Trigger immediately if external but nothing plugged in
if (ext && !inputs[TRIG_INPUT]) {
if (ext && !inputs[TRIG_INPUT].active) {
bufferIndex = 0; frameIndex = 0; return; bufferIndex = 0; frameIndex = 0; return;
} }


@@ -113,8 +107,8 @@ void Scope::step() {
frameIndex++; frameIndex++;


// Must go below 0.1V to trigger // 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 // Reset if triggered
float holdTime = 0.1; float holdTime = 0.1;
@@ -232,10 +226,10 @@ struct ScopeDisplay : TransparentWidget {
} }


void draw(NVGcontext *vg) { 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 // Draw waveforms
if (module->sum) { if (module->sum) {
@@ -249,18 +243,18 @@ struct ScopeDisplay : TransparentWidget {
} }
else { else {
// Y // Y
if (module->inputs[Scope::Y_INPUT]) {
if (module->inputs[Scope::Y_INPUT].active) {
nvgStrokeColor(vg, nvgRGBA(0xe1, 0x02, 0x78, 0xc0)); nvgStrokeColor(vg, nvgRGBA(0xe1, 0x02, 0x78, 0xc0));
drawWaveform(vg, module->bufferY, gainY, posY); drawWaveform(vg, module->bufferY, gainY, posY);
} }


// X // X
if (module->inputs[Scope::X_INPUT]) {
if (module->inputs[Scope::X_INPUT].active) {
nvgStrokeColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xc0)); nvgStrokeColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xc0));
drawWaveform(vg, module->bufferX, gainX, posX); 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 // Calculate and draw stats
if (++frame >= 4) { if (++frame >= 4) {


+ 8
- 14
src/VCA.cpp View File

@@ -22,25 +22,19 @@ struct VCA : Module {
NUM_OUTPUTS NUM_OUTPUTS
}; };


VCA();
VCA() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
void step(); 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; 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() { void VCA::step() {


+ 8
- 14
src/VCF.cpp View File

@@ -102,32 +102,26 @@ struct VCF : Module {


LadderFilter filter; LadderFilter filter;


VCF();
VCF() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
void step(); void step();
}; };




VCF::VCF() {
params.resize(NUM_PARAMS);
inputs.resize(NUM_INPUTS);
outputs.resize(NUM_OUTPUTS);
}

void VCF::step() { 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); float gain = powf(100.0, drive);
input *= gain; input *= gain;
// Add -60dB noise to bootstrap self-oscillation // Add -60dB noise to bootstrap self-oscillation
input += 1.0e-6 * (2.0*randomf() - 1.0); input += 1.0e-6 * (2.0*randomf() - 1.0);


// Set resonance // 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); res = 5.5 * clampf(res, 0.0, 1.0);
filter.resonance = res; filter.resonance = res;


// Set cutoff frequency // 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); cutoffExp = clampf(cutoffExp, 0.0, 1.0);
const float minCutoff = 15.0; const float minCutoff = 15.0;
const float maxCutoff = 8400.0; const float maxCutoff = 8400.0;
@@ -136,9 +130,9 @@ void VCF::step() {
// Push a sample to the state filter // Push a sample to the state filter
filter.process(input, 1.0/gSampleRate); 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]);
} }






+ 10
- 16
src/VCMixer.cpp View File

@@ -27,28 +27,22 @@ struct VCMixer : Module {
NUM_OUTPUTS NUM_OUTPUTS
}; };


VCMixer();
VCMixer() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
void step(); void step();
}; };




VCMixer::VCMixer() {
params.resize(NUM_PARAMS);
inputs.resize(NUM_INPUTS);
outputs.resize(NUM_OUTPUTS);
}


void VCMixer::step() { 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;
} }






+ 23
- 28
src/VCO.cpp View File

@@ -51,20 +51,15 @@ struct VCO : Module {
float pitchSlew = 0.0; float pitchSlew = 0.0;
int pitchSlewIndex = 0; int pitchSlewIndex = 0;


VCO();
VCO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
void step(); void step();
}; };


VCO::VCO() {
params.resize(NUM_PARAMS);
inputs.resize(NUM_INPUTS);
outputs.resize(NUM_OUTPUTS);
}


void VCO::step() { void VCO::step() {
bool analog = params[MODE_PARAM] < 1.0;
bool analog = params[MODE_PARAM].value < 1.0;
// TODO Soft sync features // TODO Soft sync features
bool soft = params[SYNC_PARAM] < 1.0;
bool soft = params[SYNC_PARAM].value < 1.0;


if (analog) { if (analog) {
// Adjust pitch slew // Adjust pitch slew
@@ -76,7 +71,7 @@ void VCO::step() {
} }


// Compute frequency // Compute frequency
float pitch = params[FREQ_PARAM];
float pitch = params[FREQ_PARAM].value;
if (analog) { if (analog) {
// Apply pitch slew // Apply pitch slew
const float pitchSlewAmount = 3.0; const float pitchSlewAmount = 3.0;
@@ -86,16 +81,16 @@ void VCO::step() {
// Quantize coarse knob if digital mode // Quantize coarse knob if digital mode
pitch = roundf(pitch); 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); float freq = 261.626 * powf(2.0, pitch / 12.0);


// Pulse width // Pulse width
const float pwMin = 0.01; 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 // Advance phase
float deltaPhase = clampf(freq / gSampleRate, 1e-6, 0.5); float deltaPhase = clampf(freq / gSampleRate, 1e-6, 0.5);
@@ -103,8 +98,8 @@ void VCO::step() {
// Detect sync // Detect sync
int syncIndex = -1; // Index in the oversample loop where sync occurs [0, OVERSAMPLE) 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) 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) { if (sync > 0.0 && lastSync <= 0.0) {
float deltaSync = sync - lastSync; float deltaSync = sync - lastSync;
syncCrossing = 1.0 - sync / deltaSync; syncCrossing = 1.0 - sync / deltaSync;
@@ -137,26 +132,26 @@ void VCO::step() {
} }
} }


if (outputs[SIN_OUTPUT]) {
if (outputs[SIN_OUTPUT].active) {
if (analog) if (analog)
// Quadratic approximation of sine, slightly richer harmonics // 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))); 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 else
sin[i] = -cosf(2*M_PI * phase); sin[i] = -cosf(2*M_PI * phase);
} }
if (outputs[TRI_OUTPUT]) {
if (outputs[TRI_OUTPUT].active) {
if (analog) if (analog)
tri[i] = 1.35 * interpf(triTable, phase * 2047.0); tri[i] = 1.35 * interpf(triTable, phase * 2047.0);
else else
tri[i] = (phase < 0.5) ? (-1.0 + 4.0*phase) : (1.0 - 4.0*(phase - 0.5)); 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) if (analog)
saw[i] = 1.5 * interpf(sawTable, phase * 2047.0); saw[i] = 1.5 * interpf(sawTable, phase * 2047.0);
else else
saw[i] = -1.0 + 2.0*phase; 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; sqr[i] = (phase < 1.0 - pw) ? -1.0 : 1.0;
if (analog) { if (analog) {
// Simply filter here // Simply filter here
@@ -171,14 +166,14 @@ void VCO::step() {
} }


// Set output // 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); lights[0] = rescalef(pitch, -48.0, 48.0, -1.0, 1.0);
} }


Loading…
Cancel
Save