You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.6KB

  1. // Based on Will Pirkle's courses & Vadim Zavalishin's book
  2. #include "Bidoo.hpp"
  3. #include "BidooComponents.hpp"
  4. #include "dsp/decimator.hpp"
  5. using namespace std;
  6. namespace rack_plugin_Bidoo {
  7. #define pi 3.14159265359
  8. struct FilterStage
  9. {
  10. float mem = 0.0;
  11. float Filter(float sample, float freq, float smpRate, float gain, int mode)
  12. {
  13. float g = tan(pi*freq/smpRate);
  14. float G = g/(1.0 + g);
  15. float out;
  16. if (mode == 0) {
  17. out = (sample - mem) * G + mem;
  18. } else {
  19. out = (tanh(sample*gain)/tanh(gain) - mem) * G + mem;
  20. }
  21. mem = out + (sample - mem) * G ;
  22. return out;
  23. }
  24. };
  25. struct LadderFilter
  26. {
  27. FilterStage stage1;
  28. FilterStage stage2;
  29. FilterStage stage3;
  30. FilterStage stage4;
  31. float q;
  32. float freq;
  33. float smpRate;
  34. int mode = 0;
  35. float gain = 1.0f;
  36. void setParams(float freq, float q, float smpRate, float gain, int mode) {
  37. this->freq = freq;
  38. this->q=q;
  39. this->smpRate=smpRate;
  40. this->mode = mode;
  41. this->gain = gain;
  42. }
  43. float calcOutput(float sample)
  44. {
  45. float g = tan(pi*freq/smpRate);
  46. float G = g/(1.0f + g);
  47. G = G*G*G*G;
  48. float S1 = stage1.mem/(1.0f + g);
  49. float S2 = stage2.mem/(1.0f + g);
  50. float S3 = stage3.mem/(1.0f + g);
  51. float S4 = stage4.mem/(1.0f + g);
  52. float S = G*G*G*S1 + G*G*S2 + G*S3 + S4;
  53. return stage4.Filter(stage3.Filter(stage2.Filter(stage1.Filter((sample - q*S)/(1.0f + q*G),
  54. freq,smpRate,gain,mode),freq,smpRate,gain,mode),freq,smpRate,gain,mode),freq,smpRate,gain,mode);
  55. }
  56. };
  57. struct LIMBO : Module {
  58. enum ParamIds {
  59. CUTOFF_PARAM,
  60. Q_PARAM,
  61. CMOD_PARAM,
  62. MUG_PARAM,
  63. MODE_PARAM,
  64. NUM_PARAMS
  65. };
  66. enum InputIds {
  67. IN_L,
  68. IN_R,
  69. CUTOFF_INPUT,
  70. Q_INPUT,
  71. MUG_INPUT,
  72. NUM_INPUTS
  73. };
  74. enum OutputIds {
  75. OUT_L,
  76. OUT_R,
  77. NUM_OUTPUTS
  78. };
  79. enum LightIds {
  80. LEARN_LIGHT,
  81. NUM_LIGHTS
  82. };
  83. LadderFilter lFilter,rFilter;
  84. LIMBO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  85. }
  86. void step() override;
  87. };
  88. void LIMBO::step() {
  89. float cfreq = pow(2.0f,rescale(clamp(params[CUTOFF_PARAM].value + params[CMOD_PARAM].value * inputs[CUTOFF_INPUT].value / 5.0f,0.0f,1.0f),0.0f,1.0f,4.5f,13.0f));
  90. float q = 3.5f * clamp(params[Q_PARAM].value + inputs[Q_INPUT].value / 5.0f, 0.0f, 1.0f);
  91. float g = pow(2.0f,rescale(clamp(params[MUG_PARAM].value + inputs[MUG_INPUT].value / 5.0f,0.0f,1.0f),0.0f,1.0f,0.0f,3.0f));
  92. int mode = (int)params[MODE_PARAM].value;
  93. lFilter.setParams(cfreq,q,engineGetSampleRate(),g/3,mode);
  94. rFilter.setParams(cfreq,q,engineGetSampleRate(),g/3,mode);
  95. float inL = inputs[IN_L].value/5.0f; //normalise to -1/+1 we consider VCV Rack standard is #+5/-5V on VCO1
  96. float inR = inputs[IN_R].value/5.0f;
  97. inL = lFilter.calcOutput(inL)*5.0f*(mode == 0 ? g : 1);
  98. inR = rFilter.calcOutput(inR)*5.0f*(mode == 0 ? g : 1);
  99. outputs[OUT_L].value = inL;
  100. outputs[OUT_R].value = inR;
  101. }
  102. struct LIMBOWidget : ModuleWidget {
  103. LIMBOWidget(LIMBO *module) : ModuleWidget(module) {
  104. setPanel(SVG::load(assetPlugin(plugin, "res/LIMBO.svg")));
  105. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  106. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  107. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  108. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  109. addParam(ParamWidget::create<BidooHugeBlueKnob>(Vec(31, 61), module, LIMBO::CUTOFF_PARAM, 0.0f, 1.0f, 1.0f));
  110. addParam(ParamWidget::create<BidooLargeBlueKnob>(Vec(12, 143), module, LIMBO::Q_PARAM, 0.0f, 1.0f, 0.0f));
  111. addParam(ParamWidget::create<BidooLargeBlueKnob>(Vec(71, 143), module, LIMBO::MUG_PARAM, 0.0f, 1.0f, 0.0f));
  112. addParam(ParamWidget::create<BidooLargeBlueKnob>(Vec(12, 208), module, LIMBO::CMOD_PARAM, -1.0f, 1.0f, 0.0f));
  113. addParam(ParamWidget::create<CKSS>(Vec(83, 217), module, LIMBO::MODE_PARAM, 0.0f, 1.0f, 0.0f));
  114. addInput(Port::create<PJ301MPort>(Vec(12, 280), Port::INPUT, module, LIMBO::CUTOFF_INPUT));
  115. addInput(Port::create<PJ301MPort>(Vec(47, 280), Port::INPUT, module, LIMBO::Q_INPUT));
  116. addInput(Port::create<PJ301MPort>(Vec(82, 280), Port::INPUT, module, LIMBO::MUG_INPUT));
  117. addInput(Port::create<TinyPJ301MPort>(Vec(24, 319), Port::INPUT, module, LIMBO::IN_L));
  118. addInput(Port::create<TinyPJ301MPort>(Vec(24, 339), Port::INPUT, module, LIMBO::IN_R));
  119. addOutput(Port::create<TinyPJ301MPort>(Vec(95, 319), Port::OUTPUT, module, LIMBO::OUT_L));
  120. addOutput(Port::create<TinyPJ301MPort>(Vec(95, 339), Port::OUTPUT, module, LIMBO::OUT_R));
  121. }
  122. };
  123. } // namespace rack_plugin_Bidoo
  124. using namespace rack_plugin_Bidoo;
  125. RACK_PLUGIN_MODEL_INIT(Bidoo, LIMBO) {
  126. Model *modelLIMBO = Model::create<LIMBO, LIMBOWidget>("Bidoo", "lIMbO", "lIMbO filter", FILTER_TAG);
  127. return modelLIMBO;
  128. }