diff --git a/src/Bandit.cpp b/src/Bandit.cpp index 19ef647..09ad573 100644 --- a/src/Bandit.cpp +++ b/src/Bandit.cpp @@ -43,35 +43,57 @@ struct Bandit : Module { // float_4 * [4] give 16 polyphony channels, [2] is for cascading biquads dsp::TBiquadFilter filterLow[4][2], filterLowMid[4][2], filterHighMid[4][2], filterHigh[4][2]; - + float clipTimer = 0.f; + const float clipTime = 0.25f; + dsp::ClockDivider ledUpdateClock; + const int ledUpdateRate = 64; Bandit() { config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); - configParam(LOW_GAIN_PARAM, 0.f, 1.f, 0.f, "Low gain"); - configParam(LOW_MID_GAIN_PARAM, 0.f, 1.f, 0.f, "Low mid gain"); - configParam(HIGH_MID_GAIN_PARAM, 0.f, 1.f, 0.f, "High mid gain"); - configParam(HIGH_GAIN_PARAM, 0.f, 1.f, 0.f, "High gain"); - + auto lowGainParam = configParam(LOW_GAIN_PARAM, 0.f, 1.f, 0.75f, "Low gain"); + lowGainParam->description = "Lowpass <300 Hz"; + auto lowMidGainParam = configParam(LOW_MID_GAIN_PARAM, 0.f, 1.f, 0.75f, "Low mid gain"); + lowMidGainParam->description = "Bandpass ~750 Hz"; + auto highMidGainParam = configParam(HIGH_MID_GAIN_PARAM, 0.f, 1.f, 0.75f, "High mid gain"); + highMidGainParam->description = "Bandpass ~1.5 kHz"; + auto highGainParam = configParam(HIGH_GAIN_PARAM, 0.f, 1.f, 0.75f, "High gain"); + highGainParam->description = "Highpass >3 kHz"; + + // band inputs configInput(LOW_INPUT, "Low"); configInput(LOW_MID_INPUT, "Low mid"); configInput(HIGH_MID_INPUT, "High mid"); configInput(HIGH_INPUT, "High"); + + // band send outputs + auto outLowSend = configOutput(LOW_OUTPUT, "Low"); + outLowSend->description = "Normalled to Low band return"; + auto outLowMidSend = configOutput(LOW_MID_OUTPUT, "Low mid"); + outLowMidSend->description = "Normalled to Low Mid band return"; + auto outHighMidSend = configOutput(HIGH_MID_OUTPUT, "High mid"); + outHighMidSend->description = "Normalled to High Mid band return"; + auto outHighSend = configOutput(HIGH_OUTPUT, "High"); + outHighSend->description = "Normalled to High band return"; + + // band return inputs configInput(LOW_RETURN_INPUT, "Low return"); configInput(LOW_MID_RETURN_INPUT, "Low mid return"); configInput(HIGH_MID_RETURN_INPUT, "High mid return"); configInput(HIGH_RETURN_INPUT, "High return"); + + // band gain CVs configInput(LOW_CV_INPUT, "Low CV"); configInput(LOW_MID_CV_INPUT, "Low mid CV"); configInput(HIGH_MID_CV_INPUT, "High mid CV"); configInput(HIGH_CV_INPUT, "High CV"); configInput(ALL_INPUT, "All"); - configInput(ALL_CV_INPUT, "All CV"); + auto allCvInput = configInput(ALL_CV_INPUT, "All CV"); + allCvInput->description = "Mix VCA, 10V to fully open"; - configOutput(LOW_OUTPUT, "Low"); - configOutput(LOW_MID_OUTPUT, "Low mid"); - configOutput(HIGH_MID_OUTPUT, "High mid"); - configOutput(HIGH_OUTPUT, "High"); + // mix out configOutput(MIX_OUTPUT, "Mix"); + + ledUpdateClock.setDivision(ledUpdateRate); } void onSampleRateChange() override { @@ -125,7 +147,7 @@ struct Bandit : Module { const bool allReturnsActiveAndMonophonic = inputs[LOW_RETURN_INPUT].isMonophonic() && inputs[LOW_MID_RETURN_INPUT].isMonophonic() && inputs[HIGH_MID_RETURN_INPUT].isMonophonic() && inputs[HIGH_RETURN_INPUT].isMonophonic(); - float_4 mixOutput; + float_4 mixOutput[4] = {}; for (int c = 0; c < maxPolyphony; c += 4) { const float_4 inLow = inputs[LOW_INPUT].getPolyVoltageSimd(c); @@ -151,13 +173,13 @@ struct Bandit : Module { outputs[HIGH_OUTPUT].setVoltageSimd(outHigh, c); // the fx return input is normalled to the fx send output - mixOutput = inputs[LOW_RETURN_INPUT].getNormalPolyVoltageSimd(outLow, c); - mixOutput += inputs[LOW_MID_RETURN_INPUT].getNormalPolyVoltageSimd(outLowMid, c); - mixOutput += inputs[HIGH_MID_RETURN_INPUT].getNormalPolyVoltageSimd(outHighMid, c); - mixOutput += inputs[HIGH_RETURN_INPUT].getNormalPolyVoltageSimd(outHigh, c); - mixOutput = mixOutput * clamp(inputs[ALL_CV_INPUT].getNormalPolyVoltageSimd(10.f, c) / 10.f, 0.f, 1.f); + mixOutput[c / 4] = inputs[LOW_RETURN_INPUT].getNormalPolyVoltageSimd(outLow * !outputs[LOW_OUTPUT].isConnected(), c); + mixOutput[c / 4] += inputs[LOW_MID_RETURN_INPUT].getNormalPolyVoltageSimd(outLowMid * !outputs[LOW_MID_OUTPUT].isConnected(), c); + mixOutput[c / 4] += inputs[HIGH_MID_RETURN_INPUT].getNormalPolyVoltageSimd(outHighMid * !outputs[HIGH_MID_OUTPUT].isConnected(), c); + mixOutput[c / 4] += inputs[HIGH_RETURN_INPUT].getNormalPolyVoltageSimd(outHigh * !outputs[HIGH_OUTPUT].isConnected(), c); + mixOutput[c / 4] = mixOutput[c / 4] * clamp(inputs[ALL_CV_INPUT].getNormalPolyVoltageSimd(10.f, c) / 10.f, 0.f, 1.f); - outputs[MIX_OUTPUT].setVoltageSimd(mixOutput, c); + outputs[MIX_OUTPUT].setVoltageSimd(mixOutput[c / 4], c); } outputs[LOW_OUTPUT].setChannels(maxPolyphony); @@ -166,7 +188,7 @@ struct Bandit : Module { outputs[HIGH_OUTPUT].setChannels(maxPolyphony); if (allReturnsActiveAndMonophonic) { - // special case: if all return paths are connected and monophonic, then output mix should be monophonic + // special case: if all return paths are connected and monophonic, then output mix should be monophonic outputs[MIX_OUTPUT].setChannels(1); } else { @@ -174,15 +196,57 @@ struct Bandit : Module { outputs[MIX_OUTPUT].setChannels(maxPolyphony); } + if (ledUpdateClock.process()) { + processLEDs(mixOutput, args.sampleTime * ledUpdateRate); + } + } + + void processLEDs(const float_4* output, const float sampleTime) { + + const int maxPolyphony = outputs[MIX_OUTPUT].getChannels(); + if (maxPolyphony == 1) { + const float rmsOut = std::fabs(output[0][0]); lights[MIX_LIGHT + 0].setBrightness(0.f); - lights[MIX_LIGHT + 1].setBrightnessSmooth(outputs[MIX_OUTPUT].getVoltageRMS(), args.sampleTime); + lights[MIX_LIGHT + 1].setBrightnessSmooth(rmsOut / 5.f, sampleTime); lights[MIX_LIGHT + 2].setBrightness(0.f); + + if (rmsOut > 10.f) { + clipTimer = clipTime; + } + + const bool clip = clipTimer > 0.f; + if (clip) { + clipTimer -= sampleTime; + } + + lights[MIX_CLIP_LIGHT + 0].setBrightnessSmooth(clip, sampleTime); + lights[MIX_CLIP_LIGHT + 1].setBrightness(0.f); + lights[MIX_CLIP_LIGHT + 2].setBrightness(0.f); } else { + + float maxRmsOut = 0.f; + for (int c = 0; c < maxPolyphony; c++) { + maxRmsOut = std::max(maxRmsOut, std::fabs(output[c / 4][c % 4])); + } + lights[MIX_LIGHT + 0].setBrightness(0.f); lights[MIX_LIGHT + 1].setBrightness(0.f); - lights[MIX_LIGHT + 2].setBrightnessSmooth(outputs[MIX_OUTPUT].getVoltageRMS(), args.sampleTime); + lights[MIX_LIGHT + 2].setBrightnessSmooth(maxRmsOut / 5.f, sampleTime); + + // if any channel peaks above 10V, turn the clip light on for the next clipTime seconds + if (maxRmsOut > 10.f) { + clipTimer = clipTime; + } + + const bool clip = clipTimer > 0.f; + if (clip) { + clipTimer -= sampleTime; + } + lights[MIX_CLIP_LIGHT + 0].setBrightnessSmooth(clip, sampleTime); + lights[MIX_CLIP_LIGHT + 1].setBrightness(0.f); + lights[MIX_CLIP_LIGHT + 2].setBrightness(0.f); } } };