From 87f3fe0934a7c51c1892e7bec7d2324894cd2205 Mon Sep 17 00:00:00 2001 From: hemmer <915048+hemmer@users.noreply.github.com> Date: Fri, 11 Jun 2021 07:44:40 +0100 Subject: [PATCH] Few bits of cleanup for Chopping Kinky, normal CV A -> B --- src/ChoppingKinky.cpp | 70 ++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 47 deletions(-) diff --git a/src/ChoppingKinky.cpp b/src/ChoppingKinky.cpp index 3b56d54..fa3ef6b 100644 --- a/src/ChoppingKinky.cpp +++ b/src/ChoppingKinky.cpp @@ -2,7 +2,6 @@ #include "Common.hpp" #include "ChowDSP.hpp" -static const size_t BUF_LEN = 32; struct ChoppingKinky : Module { enum ParamIds { @@ -40,7 +39,7 @@ struct ChoppingKinky : Module { NUM_CHANNELS }; - static const int WAVESHAPE_CACHE_SIZE = 128; + static const int WAVESHAPE_CACHE_SIZE = 256; float waveshapeA[WAVESHAPE_CACHE_SIZE + 1] = {0.f}; float waveshapeBPositive[WAVESHAPE_CACHE_SIZE + 1] = {0.f}; float waveshapeBNegative[WAVESHAPE_CACHE_SIZE + 1] = {0.f}; @@ -48,9 +47,9 @@ struct ChoppingKinky : Module { dsp::SchmittTrigger trigger; bool outputAToChopp; float previousA = 0.0; - + chowdsp::VariableOversampling<> oversampler[NUM_CHANNELS]; - int oversamplingIndex = 2; + int oversamplingIndex = 2; // default is 2^oversamplingIndex == x4 oversampling dsp::BiquadFilter blockDCFilter; bool blockDC = false; @@ -68,9 +67,8 @@ struct ChoppingKinky : Module { onSampleRateChange(); } - void onSampleRateChange() override { - // SampleRateConverter needs integer value - int sampleRate = APP->engine->getSampleRate(); + void onSampleRateChange() override { + float sampleRate = APP->engine->getSampleRate(); blockDCFilter.setParameters(dsp::BiquadFilter::HIGHPASS, 10.3f / sampleRate, M_SQRT1_2, 1.0f); @@ -80,43 +78,27 @@ struct ChoppingKinky : Module { } } - void process(const ProcessArgs& args) override { float gainA = params[FOLD_A_PARAM].getValue(); - if (inputs[CV_A_INPUT].isConnected()) { - gainA += params[CV_A_PARAM].getValue() * inputs[CV_A_INPUT].getVoltage() / 10.f; - } - if (inputs[VCA_CV_A_INPUT].isConnected()) { - gainA += inputs[VCA_CV_A_INPUT].getVoltage() / 10.f; - } + gainA += params[CV_A_PARAM].getValue() * inputs[CV_A_INPUT].getVoltage() / 10.f; + gainA += inputs[VCA_CV_A_INPUT].getVoltage() / 10.f; + // CV_B_INPUT is normalled to CV_A_INPUT (input with attenuverter) float gainB = params[FOLD_B_PARAM].getValue(); - if (inputs[CV_B_INPUT].isConnected()) { - gainB += params[CV_B_PARAM].getValue() * inputs[CV_B_INPUT].getVoltage() / 10.f; - } - if (inputs[VCA_CV_B_INPUT].isConnected()) { - gainB += inputs[VCA_CV_B_INPUT].getVoltage() / 10.f; - } + gainB += params[CV_B_PARAM].getValue() * inputs[CV_B_INPUT].getNormalVoltage(inputs[CV_A_INPUT].getVoltage()) / 10.f; + gainB += inputs[VCA_CV_B_INPUT].getVoltage() / 10.f; - float inA = 0; - float inB = 0; - if (inputs[IN_A_INPUT].isConnected()) { - inA = inputs[IN_A_INPUT].getVoltage(); - } - if (inputs[IN_B_INPUT].isConnected()) { - inB = inputs[IN_B_INPUT].getVoltage(); - } - else if (inputs[IN_A_INPUT].isConnected()) { - inB = inA; - } + const float inA = inputs[IN_A_INPUT].getVoltage(); + const float inB = inputs[IN_B_INPUT].getNormalVoltage(inputs[IN_A_INPUT].getVoltage()); // if the CHOPP gate is wired in, do chop logic if (inputs[IN_GATE_INPUT].isConnected()) { // TODO: check rescale? trigger.process(rescale(inputs[IN_GATE_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f)); outputAToChopp = trigger.isHigh(); - } + } + // else zero-crossing detector on input A switches between A and B else { if (previousA > 0 && inA < 0) { outputAToChopp = false; @@ -127,9 +109,9 @@ struct ChoppingKinky : Module { } previousA = inA; - bool choppIsRequired = outputs[OUT_CHOPP_OUTPUT].isConnected(); - bool aIsRequired = outputs[OUT_A_OUTPUT].isConnected() || choppIsRequired; - bool bIsRequired = outputs[OUT_B_OUTPUT].isConnected() || choppIsRequired; + const bool choppIsRequired = outputs[OUT_CHOPP_OUTPUT].isConnected(); + const bool aIsRequired = outputs[OUT_A_OUTPUT].isConnected() || choppIsRequired; + const bool bIsRequired = outputs[OUT_B_OUTPUT].isConnected() || choppIsRequired; if (aIsRequired) { oversampler[CHANNEL_A].upsample(inA * gainA); @@ -138,8 +120,7 @@ struct ChoppingKinky : Module { oversampler[CHANNEL_B].upsample(inB * gainB); } if (choppIsRequired) { - float inChopp = outputAToChopp ? 1.f : 0.f; - oversampler[CHANNEL_CHOPP].upsample(inChopp); + oversampler[CHANNEL_CHOPP].upsample(outputAToChopp ? 1.f : 0.f); } float* osBufferA = oversampler[CHANNEL_A].getOSBuffer(); @@ -148,12 +129,10 @@ struct ChoppingKinky : Module { for (int i = 0; i < oversampler[0].getOversamplingRatio(); i++) { if (aIsRequired) { - // TODO: replace with LUT of measured wavefolder response //osBufferA[i] = wavefolderAResponse(osBufferA[i]); osBufferA[i] = wavefolderAResponseCached(osBufferA[i]); } if (bIsRequired) { - // TODO: replace with LUT of measured wavefolder response //osBufferB[i] = wavefolderBResponse(osBufferB[i]); osBufferB[i] = wavefolderBResponseCached(osBufferB[i]); } @@ -279,20 +258,17 @@ struct ChoppingKinky : Module { } } + // functional form for waveshapers uses a lot of transcendental functions, so we cache + // the response in a LUT void cacheWaveshaperResponses() { for (int i = 0; i < WAVESHAPE_CACHE_SIZE; ++i) { float x = rescale(i, 0, WAVESHAPE_CACHE_SIZE - 1, 0.0, 10.f); waveshapeA[i] = wavefolderAResponse(x); - - float j = rescale(x, 0.0, 10., 0, WAVESHAPE_CACHE_SIZE - 1); - float lookupResult = math::interpolateLinear(waveshapeA, j); - waveshapeBPositive[i] = wavefolderBResponse(+x); waveshapeBNegative[i] = wavefolderBResponse(-x); } } - json_t* dataToJson() override { json_t* rootJ = json_object(); json_object_set_new(rootJ, "filterDC", json_boolean(blockDC)); @@ -357,9 +333,9 @@ struct ChoppingKinkyWidget : ModuleWidget { struct ModeItem : MenuItem { ChoppingKinky* module; - int mode; + int oversamplingIndex; void onAction(const event::Action& e) override { - module->oversamplingIndex = mode; + module->oversamplingIndex = oversamplingIndex; module->onSampleRateChange(); } }; @@ -380,7 +356,7 @@ struct ChoppingKinkyWidget : ModuleWidget { ModeItem* modeItem = createMenuItem(std::to_string(int (1 << i)) + "x"); modeItem->rightText = CHECKMARK(module->oversamplingIndex == i); modeItem->module = module; - modeItem->mode = i; + modeItem->oversamplingIndex = i; menu->addChild(modeItem); } }