From def1172533d561bd888a92b03802ac7bcc4f526a Mon Sep 17 00:00:00 2001 From: hemmer <915048+hemmer@users.noreply.github.com> Date: Sun, 11 Apr 2021 17:59:41 +0100 Subject: [PATCH] Added Kickall prototype (lots to fix!) --- plugin.json | 6 + res/BefacoTinyKnobGrey.svg | 85 ++ res/Davies1900hLargeGrey.svg | 105 +++ res/Kickall.svg | 1623 ++++++++++++++++++++++++++++++++++ src/Common.hpp | 23 +- src/Kickall.cpp | 187 ++++ src/plugin.cpp | 1 + src/plugin.hpp | 1 + 8 files changed, 2028 insertions(+), 3 deletions(-) create mode 100644 res/BefacoTinyKnobGrey.svg create mode 100644 res/Davies1900hLargeGrey.svg create mode 100644 res/Kickall.svg create mode 100644 src/Kickall.cpp diff --git a/plugin.json b/plugin.json index 489d820..e5615b2 100644 --- a/plugin.json +++ b/plugin.json @@ -116,6 +116,12 @@ "name": "ChoppingKinky", "description": "", "tags": [] + }, + { + "slug": "Kickall", + "name": "Kickall", + "description": "", + "tags": [] } ] } \ No newline at end of file diff --git a/res/BefacoTinyKnobGrey.svg b/res/BefacoTinyKnobGrey.svg new file mode 100644 index 0000000..6954d84 --- /dev/null +++ b/res/BefacoTinyKnobGrey.svg @@ -0,0 +1,85 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/res/Davies1900hLargeGrey.svg b/res/Davies1900hLargeGrey.svg new file mode 100644 index 0000000..0e5c95a --- /dev/null +++ b/res/Davies1900hLargeGrey.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/res/Kickall.svg b/res/Kickall.svg new file mode 100644 index 0000000..b47eba2 --- /dev/null +++ b/res/Kickall.svg @@ -0,0 +1,1623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Common.hpp b/src/Common.hpp index 93078dc..d42e112 100644 --- a/src/Common.hpp +++ b/src/Common.hpp @@ -19,14 +19,28 @@ struct BefacoTinyKnobWhite : app::SvgKnob { } }; +struct BefacoTinyKnobGrey : app::SvgKnob { + BefacoTinyKnobGrey() { + minAngle = -0.8 * M_PI; + maxAngle = 0.8 * M_PI; + setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/BefacoTinyKnobGrey.svg"))); + } +}; + +struct Davies1900hLargeGreyKnob : Davies1900hKnob { + Davies1900hLargeGreyKnob() { + setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Davies1900hLargeGrey.svg"))); + } +}; + struct BefacoOutputPort : app::SvgPort { - BefacoOutputPort() { + BefacoOutputPort() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/BefacoOutputPort.svg"))); } }; struct BefacoInputPort : app::SvgPort { - BefacoInputPort() { + BefacoInputPort() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/BefacoInputPort.svg"))); } }; @@ -36,7 +50,7 @@ struct BefacoInputPort : app::SvgPort { The template parameter N should be 1/2 the desired filter order. Currently uses an 2*N-th order Butterworth filter. - @TODO: implement Chebyshev, Elliptic filter options. + source: https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/AAFilter.hpp */ template class AAFilter { @@ -86,6 +100,7 @@ private: /** * Base class for oversampling of any order + * source: https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/oversampling.hpp */ class BaseOversampling { public: @@ -170,6 +185,8 @@ private: osBuffer[k] = processSample(osBuffer[k]); float y = oversample.downsample(); @endcode + + source (modified): https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/VariableOversampling.hpp */ template class VariableOversampling { diff --git a/src/Kickall.cpp b/src/Kickall.cpp new file mode 100644 index 0000000..2c272e4 --- /dev/null +++ b/src/Kickall.cpp @@ -0,0 +1,187 @@ +#include "plugin.hpp" +#include "Common.hpp" + +struct ADEnvelope { + + enum Stage { + STAGE_OFF, + STAGE_ATTACK, + STAGE_DECAY + }; + + Stage stage = STAGE_OFF; + float env = 0.f; + float attackTime = 0.1, decayTime = 0.1; + + ADEnvelope() { }; + + void process(const float& sampleTime) { + + if (stage == STAGE_OFF) { + env = 0.0f; + } + else if (stage == STAGE_ATTACK) { + env += sampleTime / attackTime; + } + else if (stage == STAGE_DECAY) { + env -= sampleTime / decayTime; + } + + if (env >= 1.0f) { + stage = STAGE_DECAY; + env = 1.0f; + } + else if (env <= 0.0f) { + stage = STAGE_OFF; + env = 0.0f; + } + } +}; + +struct Kickall : Module { + enum ParamIds { + TUNE_PARAM, + TRIGG_BUTTON_PARAM, + SHAPE_PARAM, + DECAY_PARAM, + TIME_PARAM, + BEND_PARAM, + NUM_PARAMS + }; + enum InputIds { + TRIGG_INPUT, + VOLUME_INPUT, + TUNE_INPUT, + SHAPE_INPUT, + DECAY_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT_OUTPUT, + NUM_OUTPUTS + }; + enum LightIds { + ENV_LIGHT, + NUM_LIGHTS + }; + + Kickall() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + // TODO: review this mapping, using displayBase multiplier seems more normal + configParam(TUNE_PARAM, FREQ_A0, FREQ_B2, 0.5 * (FREQ_A0 + FREQ_B2), "Tune", "Hz"); + configParam(TRIGG_BUTTON_PARAM, 0.f, 1.f, 0.f, "Manual trigger"); + configParam(SHAPE_PARAM, 0.f, 1.f, 0.f, ""); + configParam(DECAY_PARAM, 0.01f, 1.f, 0.01f, "VCA Envelope decay time"); + configParam(TIME_PARAM, 0.0079f, 0.076f, 0.0079f, "Pitch envelope decay time", "ms", 0.0f, 1000.f); + configParam(BEND_PARAM, 0.f, 1.f, 0.f, "Pitch envelope attenuator"); + + volume.attackTime = 0.00165; + pitch.attackTime = 0.00165; + + // calculate up/downsampling rates + onSampleRateChange(); + } + + void onSampleRateChange() override { + // SampleRateConverter needs integer value + int sampleRate = APP->engine->getSampleRate(); + oversampler.reset(sampleRate); + } + + float bendRange = 600; + float phase = 0.f; + ADEnvelope volume; + ADEnvelope pitch; + + dsp::SchmittTrigger trigger; + + static constexpr float FREQ_A0 = 27.5f; + static constexpr float FREQ_B2 = 123.471f; + + static const int UPSAMPLE = 4; + Oversampling oversampler; + float shaperBuf[UPSAMPLE]; + + void process(const ProcessArgs& args) override { + + // TODO: check values + if (trigger.process(inputs[TRIGG_INPUT].getVoltage() / 2.0f + params[TRIGG_BUTTON_PARAM].getValue() * 10.0)) { + volume.stage = ADEnvelope::STAGE_ATTACK; + pitch.stage = ADEnvelope::STAGE_ATTACK; + } + + float vcaGain = clamp(inputs[VOLUME_INPUT].getNormalVoltage(10.f) / 10.f, 0.f, 1.0f); + + // pitch envelope + float bend = params[BEND_PARAM].getValue(); + pitch.decayTime = params[TIME_PARAM].getValue(); + pitch.process(args.sampleTime); + + // volume envelope TODO: wire in decay CV + volume.decayTime = params[DECAY_PARAM].getValue(); + volume.process(args.sampleTime); + + float freq = params[TUNE_PARAM].getValue(); + freq *= std::pow(2, inputs[TUNE_INPUT].getVoltage()); + + //float kickFrequency = std::max(50.0f, params[TUNE_PARAM].getValue() + bendRange * bend * pitch.env); + float kickFrequency = std::max(50.0f, freq + bendRange * bend * pitch.env); + + phase += args.sampleTime * kickFrequency; + if (phase > 1.0f) { + phase -= 1.0f; + } + + // TODO: faster approximation + float wave = std::sin(2.0 * M_PI * phase); + + oversampler.upsample(wave); + float* inputBuf = oversampler.getOSBuffer(); + + float shape = clamp(inputs[SHAPE_INPUT].getVoltage() / 2.f + params[SHAPE_PARAM].getValue() * 5.0f, 0.0f, 10.0f); + shape = clamp(shape, 0.f, 5.0f) * 0.2f; + shape *= 0.99f; + const float shapeB = (1.0f - shape) / (1.0f + shape); + const float shapeA = (4.0f * shape) / ((1.0f - shape) * (1.0f + shape)); + + for (int i = 0; i < UPSAMPLE; ++i) { + inputBuf[i] = inputBuf[i] * (shapeA + shapeB) / ((std::abs(inputBuf[i]) * shapeA) + shapeB); + } + + float out = volume.env * oversampler.downsample() * 5.0f * vcaGain; + outputs[OUT_OUTPUT].setVoltage(out); + + lights[ENV_LIGHT].setBrightness(volume.stage); + } +}; + + +struct KickallWidget : ModuleWidget { + KickallWidget(Kickall* module) { + setModule(module); + setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Kickall.svg"))); + + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addParam(createParamCentered(mm2px(Vec(8.76, 29.068)), module, Kickall::TUNE_PARAM)); + addParam(createParamCentered(mm2px(Vec(22.651, 29.068)), module, Kickall::TRIGG_BUTTON_PARAM)); + addParam(createParamCentered(mm2px(Vec(15.781, 49.278)), module, Kickall::SHAPE_PARAM)); + addParam(createParam(mm2px(Vec(19.913, 64.004)), module, Kickall::DECAY_PARAM)); + addParam(createParamCentered(mm2px(Vec(8.977, 71.626)), module, Kickall::TIME_PARAM)); + addParam(createParamCentered(mm2px(Vec(8.977, 93.549)), module, Kickall::BEND_PARAM)); + + addInput(createInputCentered(mm2px(Vec(5.715, 14.651)), module, Kickall::TRIGG_INPUT)); + addInput(createInputCentered(mm2px(Vec(15.748, 14.651)), module, Kickall::VOLUME_INPUT)); + addInput(createInputCentered(mm2px(Vec(5.697, 113.286)), module, Kickall::TUNE_INPUT)); + addInput(createInputCentered(mm2px(Vec(15.794, 113.286)), module, Kickall::SHAPE_INPUT)); + addInput(createInputCentered(mm2px(Vec(25.891, 113.286)), module, Kickall::DECAY_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(25.781, 14.651)), module, Kickall::OUT_OUTPUT)); + + addChild(createLightCentered>(mm2px(Vec(15.723, 34.971)), module, Kickall::ENV_LIGHT)); + } +}; + + +Model* modelKickall = createModel("Kickall"); \ No newline at end of file diff --git a/src/plugin.cpp b/src/plugin.cpp index afea9ad..ec6cdcf 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -16,4 +16,5 @@ void init(rack::Plugin *p) { p->addModel(modelPercall); p->addModel(modelHexmixVCA); p->addModel(modelChoppingKinky); + p->addModel(modelKickall); } diff --git a/src/plugin.hpp b/src/plugin.hpp index 953b870..4bf7ea6 100644 --- a/src/plugin.hpp +++ b/src/plugin.hpp @@ -16,6 +16,7 @@ extern Model *modelDualAtenuverter; extern Model *modelPercall; extern Model *modelHexmixVCA; extern Model *modelChoppingKinky; +extern Model *modelKickall; struct Knurlie : SvgScrew { Knurlie() {