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.

236 lines
9.3KB

  1. #include "Befaco.hpp"
  2. #include "dsp/digital.hpp"
  3. struct Rampage : Module {
  4. enum ParamIds {
  5. RANGE_A_PARAM,
  6. RANGE_B_PARAM,
  7. SHAPE_A_PARAM,
  8. SHAPE_B_PARAM,
  9. TRIGG_A_PARAM,
  10. TRIGG_B_PARAM,
  11. RISE_A_PARAM,
  12. RISE_B_PARAM,
  13. FALL_A_PARAM,
  14. FALL_B_PARAM,
  15. CYCLE_A_PARAM,
  16. CYCLE_B_PARAM,
  17. BALANCE_PARAM,
  18. NUM_PARAMS
  19. };
  20. enum InputIds {
  21. IN_A_INPUT,
  22. IN_B_INPUT,
  23. TRIGG_A_INPUT,
  24. TRIGG_B_INPUT,
  25. RISE_CV_A_INPUT,
  26. RISE_CV_B_INPUT,
  27. FALL_CV_A_INPUT,
  28. FALL_CV_B_INPUT,
  29. EXP_CV_A_INPUT,
  30. EXP_CV_B_INPUT,
  31. CYCLE_A_INPUT,
  32. CYCLE_B_INPUT,
  33. NUM_INPUTS
  34. };
  35. enum OutputIds {
  36. RISING_A_OUTPUT,
  37. RISING_B_OUTPUT,
  38. FALLING_A_OUTPUT,
  39. FALLING_B_OUTPUT,
  40. EOC_A_OUTPUT,
  41. EOC_B_OUTPUT,
  42. OUT_A_OUTPUT,
  43. OUT_B_OUTPUT,
  44. COMPARATOR_OUTPUT,
  45. MIN_OUTPUT,
  46. MAX_OUTPUT,
  47. NUM_OUTPUTS
  48. };
  49. enum LightIds {
  50. COMPARATOR_LIGHT,
  51. MIN_LIGHT,
  52. MAX_LIGHT,
  53. OUT_A_LIGHT,
  54. OUT_B_LIGHT,
  55. RISING_A_LIGHT,
  56. RISING_B_LIGHT,
  57. FALLING_A_LIGHT,
  58. FALLING_B_LIGHT,
  59. NUM_LIGHTS
  60. };
  61. float out[2] = {};
  62. bool gate[2] = {};
  63. SchmittTrigger trigger[2];
  64. PulseGenerator endOfCyclePulse[2];
  65. Rampage() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  66. void step() override;
  67. };
  68. static float shapeDelta(float delta, float tau, float shape) {
  69. float lin = sgn(delta) * 10.0 / tau;
  70. if (shape < 0.0) {
  71. float log = sgn(delta) * 40.0 / tau / (fabsf(delta) + 1.0);
  72. return crossfade(lin, log, -shape * 0.95f);
  73. }
  74. else {
  75. float exp = M_E * delta / tau;
  76. return crossfade(lin, exp, shape * 0.90f);
  77. }
  78. }
  79. void Rampage::step() {
  80. for (int c = 0; c < 2; c++) {
  81. float in = inputs[IN_A_INPUT + c].value;
  82. if (trigger[c].process(params[TRIGG_A_PARAM + c].value * 10.0 + inputs[TRIGG_A_INPUT + c].value / 2.0)) {
  83. gate[c] = true;
  84. }
  85. if (gate[c]) {
  86. in = 10.0;
  87. }
  88. float shape = params[SHAPE_A_PARAM + c].value;
  89. float delta = in - out[c];
  90. // Integrator
  91. float minTime;
  92. switch ((int) params[RANGE_A_PARAM + c].value) {
  93. case 0: minTime = 1e-2; break;
  94. case 1: minTime = 1e-3; break;
  95. default: minTime = 1e-1; break;
  96. }
  97. bool rising = false;
  98. bool falling = false;
  99. if (delta > 0) {
  100. // Rise
  101. float riseCv = params[RISE_A_PARAM + c].value - inputs[EXP_CV_A_INPUT + c].value / 10.0 + inputs[RISE_CV_A_INPUT + c].value / 10.0;
  102. riseCv = clamp(riseCv, 0.0f, 1.0f);
  103. float rise = minTime * powf(2.0, riseCv * 10.0);
  104. out[c] += shapeDelta(delta, rise, shape) * engineGetSampleTime();
  105. rising = (in - out[c] > 1e-3);
  106. if (!rising) {
  107. gate[c] = false;
  108. }
  109. }
  110. else if (delta < 0) {
  111. // Fall
  112. float fallCv = params[FALL_A_PARAM + c].value - inputs[EXP_CV_A_INPUT + c].value / 10.0 + inputs[FALL_CV_A_INPUT + c].value / 10.0;
  113. fallCv = clamp(fallCv, 0.0f, 1.0f);
  114. float fall = minTime * powf(2.0, fallCv * 10.0);
  115. out[c] += shapeDelta(delta, fall, shape) * engineGetSampleTime();
  116. falling = (in - out[c] < -1e-3);
  117. if (!falling) {
  118. // End of cycle, check if we should turn the gate back on (cycle mode)
  119. endOfCyclePulse[c].trigger(1e-3);
  120. if (params[CYCLE_A_PARAM + c].value * 10.0 + inputs[CYCLE_A_INPUT + c].value >= 4.0) {
  121. gate[c] = true;
  122. }
  123. }
  124. }
  125. else {
  126. gate[c] = false;
  127. }
  128. if (!rising && !falling) {
  129. out[c] = in;
  130. }
  131. outputs[RISING_A_OUTPUT + c].value = (rising ? 10.0 : 0.0);
  132. outputs[FALLING_A_OUTPUT + c].value = (falling ? 10.0 : 0.0);
  133. lights[RISING_A_LIGHT + c].value = (rising ? 1.0 : 0.0);
  134. lights[FALLING_A_LIGHT + c].value = (falling ? 1.0 : 0.0);
  135. outputs[EOC_A_OUTPUT + c].value = (endOfCyclePulse[c].process(engineGetSampleTime()) ? 10.0 : 0.0);
  136. outputs[OUT_A_OUTPUT + c].value = out[c];
  137. lights[OUT_A_LIGHT + c].value = out[c] / 10.0;
  138. }
  139. // Logic
  140. float balance = params[BALANCE_PARAM].value;
  141. float a = out[0];
  142. float b = out[1];
  143. if (balance < 0.5)
  144. b *= 2.0 * balance;
  145. else if (balance > 0.5)
  146. a *= 2.0 * (1.0 - balance);
  147. outputs[COMPARATOR_OUTPUT].value = (b > a ? 10.0 : 0.0);
  148. outputs[MIN_OUTPUT].value = fminf(a, b);
  149. outputs[MAX_OUTPUT].value = fmaxf(a, b);
  150. // Lights
  151. lights[COMPARATOR_LIGHT].value = outputs[COMPARATOR_OUTPUT].value / 10.0;
  152. lights[MIN_LIGHT].value = outputs[MIN_OUTPUT].value / 10.0;
  153. lights[MAX_LIGHT].value = outputs[MAX_OUTPUT].value / 10.0;
  154. }
  155. struct RampageWidget : ModuleWidget {
  156. RampageWidget(Rampage *module) : ModuleWidget(module) {
  157. setPanel(SVG::load(assetPlugin(plugin, "res/Rampage.svg")));
  158. addChild(Widget::create<Knurlie>(Vec(15, 0)));
  159. addChild(Widget::create<Knurlie>(Vec(box.size.x-30, 0)));
  160. addChild(Widget::create<Knurlie>(Vec(15, 365)));
  161. addChild(Widget::create<Knurlie>(Vec(box.size.x-30, 365)));
  162. addInput(Port::create<PJ301MPort>(Vec(14, 30), Port::INPUT, module, Rampage::IN_A_INPUT));
  163. addInput(Port::create<PJ301MPort>(Vec(52, 37), Port::INPUT, module, Rampage::TRIGG_A_INPUT));
  164. addInput(Port::create<PJ301MPort>(Vec(8, 268), Port::INPUT, module, Rampage::RISE_CV_A_INPUT));
  165. addInput(Port::create<PJ301MPort>(Vec(67, 268), Port::INPUT, module, Rampage::FALL_CV_A_INPUT));
  166. addInput(Port::create<PJ301MPort>(Vec(38, 297), Port::INPUT, module, Rampage::EXP_CV_A_INPUT));
  167. addInput(Port::create<PJ301MPort>(Vec(102, 290), Port::INPUT, module, Rampage::CYCLE_A_INPUT));
  168. addInput(Port::create<PJ301MPort>(Vec(229, 30), Port::INPUT, module, Rampage::IN_B_INPUT));
  169. addInput(Port::create<PJ301MPort>(Vec(192, 37), Port::INPUT, module, Rampage::TRIGG_B_INPUT));
  170. addInput(Port::create<PJ301MPort>(Vec(176, 268), Port::INPUT, module, Rampage::RISE_CV_B_INPUT));
  171. addInput(Port::create<PJ301MPort>(Vec(237, 268), Port::INPUT, module, Rampage::FALL_CV_B_INPUT));
  172. addInput(Port::create<PJ301MPort>(Vec(207, 297), Port::INPUT, module, Rampage::EXP_CV_B_INPUT));
  173. addInput(Port::create<PJ301MPort>(Vec(143, 290), Port::INPUT, module, Rampage::CYCLE_B_INPUT));
  174. addParam(ParamWidget::create<BefacoSwitch>(Vec(94, 32), module, Rampage::RANGE_A_PARAM, 0.0, 2.0, 0.0));
  175. addParam(ParamWidget::create<BefacoTinyKnob>(Vec(27, 90), module, Rampage::SHAPE_A_PARAM, -1.0, 1.0, 0.0));
  176. addParam(ParamWidget::create<BefacoPush>(Vec(72, 82), module, Rampage::TRIGG_A_PARAM, 0.0, 1.0, 0.0));
  177. addParam(ParamWidget::create<BefacoSlidePot>(Vec(16, 135), module, Rampage::RISE_A_PARAM, 0.0, 1.0, 0.0));
  178. addParam(ParamWidget::create<BefacoSlidePot>(Vec(57, 135), module, Rampage::FALL_A_PARAM, 0.0, 1.0, 0.0));
  179. addParam(ParamWidget::create<BefacoSwitch>(Vec(101, 238), module, Rampage::CYCLE_A_PARAM, 0.0, 1.0, 0.0));
  180. addParam(ParamWidget::create<BefacoSwitch>(Vec(147, 32), module, Rampage::RANGE_B_PARAM, 0.0, 2.0, 0.0));
  181. addParam(ParamWidget::create<BefacoTinyKnob>(Vec(217, 90), module, Rampage::SHAPE_B_PARAM, -1.0, 1.0, 0.0));
  182. addParam(ParamWidget::create<BefacoPush>(Vec(170, 82), module, Rampage::TRIGG_B_PARAM, 0.0, 1.0, 0.0));
  183. addParam(ParamWidget::create<BefacoSlidePot>(Vec(197, 135), module, Rampage::RISE_B_PARAM, 0.0, 1.0, 0.0));
  184. addParam(ParamWidget::create<BefacoSlidePot>(Vec(238, 135), module, Rampage::FALL_B_PARAM, 0.0, 1.0, 0.0));
  185. addParam(ParamWidget::create<BefacoSwitch>(Vec(141, 238), module, Rampage::CYCLE_B_PARAM, 0.0, 1.0, 0.0));
  186. addParam(ParamWidget::create<Davies1900hWhiteKnob>(Vec(117, 76), module, Rampage::BALANCE_PARAM, 0.0, 1.0, 0.5));
  187. addOutput(Port::create<PJ301MPort>(Vec(8, 326), Port::OUTPUT, module, Rampage::RISING_A_OUTPUT));
  188. addOutput(Port::create<PJ301MPort>(Vec(68, 326), Port::OUTPUT, module, Rampage::FALLING_A_OUTPUT));
  189. addOutput(Port::create<PJ301MPort>(Vec(104, 326), Port::OUTPUT, module, Rampage::EOC_A_OUTPUT));
  190. addOutput(Port::create<PJ301MPort>(Vec(102, 195), Port::OUTPUT, module, Rampage::OUT_A_OUTPUT));
  191. addOutput(Port::create<PJ301MPort>(Vec(177, 326), Port::OUTPUT, module, Rampage::RISING_B_OUTPUT));
  192. addOutput(Port::create<PJ301MPort>(Vec(237, 326), Port::OUTPUT, module, Rampage::FALLING_B_OUTPUT));
  193. addOutput(Port::create<PJ301MPort>(Vec(140, 326), Port::OUTPUT, module, Rampage::EOC_B_OUTPUT));
  194. addOutput(Port::create<PJ301MPort>(Vec(142, 195), Port::OUTPUT, module, Rampage::OUT_B_OUTPUT));
  195. addOutput(Port::create<PJ301MPort>(Vec(122, 133), Port::OUTPUT, module, Rampage::COMPARATOR_OUTPUT));
  196. addOutput(Port::create<PJ301MPort>(Vec(89, 157), Port::OUTPUT, module, Rampage::MIN_OUTPUT));
  197. addOutput(Port::create<PJ301MPort>(Vec(155, 157), Port::OUTPUT, module, Rampage::MAX_OUTPUT));
  198. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(132, 167), module, Rampage::COMPARATOR_LIGHT));
  199. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(123, 174), module, Rampage::MIN_LIGHT));
  200. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(141, 174), module, Rampage::MAX_LIGHT));
  201. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(126, 185), module, Rampage::OUT_A_LIGHT));
  202. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(138, 185), module, Rampage::OUT_B_LIGHT));
  203. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(18, 312), module, Rampage::RISING_A_LIGHT));
  204. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(78, 312), module, Rampage::FALLING_A_LIGHT));
  205. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(187, 312), module, Rampage::RISING_B_LIGHT));
  206. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(247, 312), module, Rampage::FALLING_B_LIGHT));
  207. }
  208. };
  209. RACK_PLUGIN_MODEL_INIT(Befaco, Rampage) {
  210. Model *modelRampage = Model::create<Rampage, RampageWidget>("Befaco", "Rampage", "Rampage", FUNCTION_GENERATOR_TAG, LOGIC_TAG, SLEW_LIMITER_TAG, ENVELOPE_FOLLOWER_TAG, DUAL_TAG);
  211. return modelRampage;
  212. }