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.

205 lines
6.6KB

  1. #include "Southpole.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "VAStateVariableFilter.h"
  4. namespace rack_plugin_Southpole {
  5. struct Piste : Module {
  6. enum ParamIds {
  7. FREQ_PARAM,
  8. RESO_PARAM,
  9. DECAY1_PARAM,
  10. DECAY2_PARAM,
  11. SCALE1_PARAM,
  12. SCALE2_PARAM,
  13. DRIVE_PARAM,
  14. NUM_PARAMS
  15. };
  16. enum InputIds {
  17. IN_INPUT,
  18. DECAY1_INPUT,
  19. DECAY2_INPUT,
  20. TRIG1_INPUT,
  21. TRIG2_INPUT,
  22. SCALE1_INPUT,
  23. SCALE2_INPUT,
  24. MUTE_INPUT,
  25. NUM_INPUTS
  26. };
  27. enum OutputIds {
  28. ENV1_OUTPUT,
  29. ENV2_OUTPUT,
  30. OUT_OUTPUT,
  31. NUM_OUTPUTS
  32. };
  33. enum LightIds {
  34. DECAY1_LIGHT,
  35. DECAY2_LIGHT,
  36. NUM_LIGHTS
  37. };
  38. VAStateVariableFilter lpFilter;
  39. VAStateVariableFilter hpFilter;
  40. float env1 = 0.0;
  41. float env2 = 0.0;
  42. SchmittTrigger trigger1;
  43. SchmittTrigger trigger2;
  44. SchmittTrigger mute;
  45. Piste() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  46. params.resize(NUM_PARAMS);
  47. inputs.resize(NUM_INPUTS);
  48. outputs.resize(NUM_OUTPUTS);
  49. lights.resize(NUM_LIGHTS);
  50. //trigger1.setThresholds(0.0, 2.0);
  51. //trigger1.setThresholds(0.0, 2.0);
  52. lpFilter.setFilterType(SVFLowpass);
  53. hpFilter.setFilterType(SVFHighpass);
  54. }
  55. void step() override;
  56. unsigned timer;
  57. };
  58. void Piste::step() {
  59. float drive = clamp(params[DRIVE_PARAM].value, 0.0f, 1.0f);
  60. float freq = clamp(params[FREQ_PARAM].value, -1.0f, 1.0f);
  61. float reso = clamp(params[RESO_PARAM].value, 0.0f, 1.0f);
  62. float decay1 = clamp(params[DECAY1_PARAM].value + inputs[DECAY1_INPUT].value / 10.0f, 0.0f, 1.0f);
  63. float decay2 = decay1 * clamp(params[DECAY2_PARAM].value + inputs[DECAY2_INPUT].value / 10.0f, 0.0f, 1.0f);
  64. float scale1 = clamp(params[SCALE1_PARAM].value, 0.0, 1.0);
  65. float scale2 = scale1 * clamp(params[SCALE2_PARAM].value, 0.0, 1.0);
  66. bool muted = inputs[MUTE_INPUT].normalize(0.) >= 1.0;
  67. if (!muted) {
  68. if (trigger1.process(inputs[TRIG1_INPUT].value)) {
  69. env1 = 1.;
  70. }
  71. if (trigger2.process(inputs[TRIG2_INPUT].value)) {
  72. env2 = 1.;
  73. }
  74. }
  75. const float base = 20000.0;
  76. const float maxTime = 1.0;
  77. if (decay1 < 1e-4) { env1 = 0.;
  78. } else {
  79. env1 += powf(base, 1. - decay1) / maxTime * ( - env1) / engineGetSampleRate();
  80. }
  81. if (decay2 < 1e-4) { env2 = 0.;
  82. } else {
  83. env2 += powf(base, 1. - decay2) / maxTime * ( - env2) / engineGetSampleRate();
  84. }
  85. outputs[ENV1_OUTPUT].value = 10.*scale1 * env1;
  86. outputs[ENV2_OUTPUT].value = 10.*scale2 * env2;
  87. float v = inputs[IN_INPUT].value;
  88. // DRIVE
  89. v = (1.-drive)*v + drive * 10.*tanhf(10.*drive*v);
  90. const float f0 = 261.626;
  91. const float rmax = 0.9995; // Qmax = 1000
  92. float fout = v;
  93. // FILTER
  94. if (freq < 0.) {
  95. float lp_cutoff = f0 * powf(2.f, 8.*(freq+1.)-4.);
  96. lpFilter.setResonance(reso*rmax);
  97. lpFilter.setSampleRate(engineGetSampleRate());
  98. lpFilter.setCutoffFreq(lp_cutoff);
  99. fout = lpFilter.processAudioSample( v, 1);
  100. } else if ( freq > 0.) {
  101. float hp_cutoff = f0 * powf(2.f, 8.*freq-3.);
  102. hpFilter.setResonance(reso*rmax);
  103. hpFilter.setSampleRate(engineGetSampleRate());
  104. hpFilter.setCutoffFreq(hp_cutoff);
  105. fout = hpFilter.processAudioSample( v, 1);
  106. }
  107. // VCA
  108. v = fout * 10.*scale1 * env1 * (1. + 10* scale2 * env2);
  109. outputs[OUT_OUTPUT].value = v;
  110. // Lights
  111. lights[DECAY1_LIGHT].value = env1;
  112. lights[DECAY2_LIGHT].value = env2;
  113. }
  114. struct PisteWidget : ModuleWidget {
  115. PisteWidget(Module *module) : ModuleWidget(module) {
  116. box.size = Vec(15*4, 380);
  117. {
  118. SVGPanel *panel = new SVGPanel();
  119. panel->box.size = box.size;
  120. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Piste.svg")));
  121. addChild(panel);
  122. }
  123. const float x1 = 5.;
  124. const float x2 = 36.;
  125. const float y1 = 47.;
  126. const float yh = 31.;
  127. addInput(Port::create<sp_Port>(Vec(x1, y1), Port::INPUT, module, Piste::IN_INPUT));
  128. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x2, y1), module, Piste::DRIVE_PARAM, 0.0, 1.0, 0.0));
  129. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x1, y1+1*yh), module, Piste::FREQ_PARAM, -1.0, 1.0, 0.));
  130. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x2, y1+1*yh), module, Piste::RESO_PARAM, .0, 1.0, 0.));
  131. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(x1+6, y1+2*yh+5), module, Piste::DECAY1_LIGHT));
  132. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(x2+6, y1+2*yh+5), module, Piste::DECAY2_LIGHT));
  133. addInput(Port::create<sp_Port>(Vec(x1, y1+2.5*yh), Port::INPUT, module, Piste::TRIG1_INPUT));
  134. addInput(Port::create<sp_Port>(Vec(x2, y1+2.5*yh), Port::INPUT, module, Piste::TRIG2_INPUT));
  135. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x1, y1+3.5*yh), module, Piste::SCALE1_PARAM, 0.0, 1.0, .5));
  136. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x2, y1+3.5*yh), module, Piste::SCALE2_PARAM, 0.0, 1.0, 1.));
  137. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x1, y1+4.5*yh), module, Piste::DECAY1_PARAM, 0.0, 1.0, 0.5));
  138. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x2, y1+4.5*yh), module, Piste::DECAY2_PARAM, 0.0, 1.0, 1.));
  139. addInput(Port::create<sp_Port>(Vec(x1, y1+5.25*yh), Port::INPUT, module, Piste::DECAY1_INPUT));
  140. addInput(Port::create<sp_Port>(Vec(x2, y1+5.25*yh), Port::INPUT, module, Piste::DECAY2_INPUT));
  141. addOutput(Port::create<sp_Port>(Vec(x1, y1+6.5*yh), Port::OUTPUT, module, Piste::ENV1_OUTPUT));
  142. addOutput(Port::create<sp_Port>(Vec(x2, y1+6.5*yh), Port::OUTPUT, module, Piste::ENV2_OUTPUT));
  143. addInput(Port::create<sp_Port>(Vec(0.5*(x1+x2), 7.75*yh+y1), Port::INPUT, module, Piste::MUTE_INPUT));
  144. addOutput(Port::create<sp_Port>(Vec(0.5*(x1+x2), y1+9*yh), Port::OUTPUT, module, Piste::OUT_OUTPUT));
  145. }
  146. };
  147. } // namespace rack_plugin_Southpole
  148. using namespace rack_plugin_Southpole;
  149. RACK_PLUGIN_MODEL_INIT(Southpole, Piste) {
  150. Model *modelPiste = Model::create<Piste,PisteWidget>( "Southpole", "Piste", "Piste - drum processor", ENVELOPE_GENERATOR_TAG, EFFECT_TAG);
  151. return modelPiste;
  152. }