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 @@
+
+
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 @@
+
+
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 @@
+
+
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() {