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.

249 lines
9.5KB

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