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.

197 lines
5.8KB

  1. #include "plugin.hpp"
  2. struct SlewFilter {
  3. float value = 0.f;
  4. float process(float in, float slew) {
  5. value += math::clamp(in - value, -slew, slew);
  6. return value;
  7. }
  8. float jump(float in) {
  9. value = in;
  10. return value;
  11. }
  12. float getValue() {
  13. return value;
  14. }
  15. };
  16. struct Process : Module {
  17. enum ParamId {
  18. SLEW_PARAM,
  19. GATE_PARAM,
  20. PARAMS_LEN
  21. };
  22. enum InputId {
  23. SLEW_INPUT,
  24. IN_INPUT,
  25. GATE_INPUT,
  26. INPUTS_LEN
  27. };
  28. enum OutputId {
  29. SH1_OUTPUT,
  30. SH2_OUTPUT,
  31. TH_OUTPUT,
  32. HT_OUTPUT,
  33. SLEW_OUTPUT,
  34. GLIDE_OUTPUT,
  35. OUTPUTS_LEN
  36. };
  37. enum LightId {
  38. GATE_LIGHT,
  39. LIGHTS_LEN
  40. };
  41. struct Engine {
  42. bool state = false;
  43. // For glide to turn on after 1ms
  44. float onTime = 0.f;
  45. float sample1 = 0.f;
  46. float sample2 = 0.f;
  47. SlewFilter sample1Filter;
  48. SlewFilter sample2Filter;
  49. float holdValue = 0.f;
  50. SlewFilter slewFilter;
  51. SlewFilter glideFilter;
  52. };
  53. Engine engines[16];
  54. Process() {
  55. config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
  56. struct SlewQuantity : ParamQuantity {
  57. float getDisplayValue() override {
  58. if (getValue() <= getMinValue())
  59. return 0.f;
  60. return ParamQuantity::getDisplayValue();
  61. }
  62. };
  63. configParam<SlewQuantity>(SLEW_PARAM, std::log2(1e-3f), std::log2(10.f), std::log2(1e-3f), "Slew", " ms/V", 2, 1000);
  64. configButton(GATE_PARAM, "Gate");
  65. configInput(SLEW_INPUT, "Slew");
  66. configInput(IN_INPUT, "Voltage");
  67. configInput(GATE_INPUT, "Gate");
  68. configOutput(SH1_OUTPUT, "Sample & hold");
  69. configOutput(SH2_OUTPUT, "Sample & hold 2");
  70. configOutput(TH_OUTPUT, "Track & hold");
  71. configOutput(HT_OUTPUT, "Hold & track");
  72. configOutput(SLEW_OUTPUT, "Slew");
  73. configOutput(GLIDE_OUTPUT, "Glide");
  74. }
  75. void process(const ProcessArgs& args) override {
  76. int channels = inputs[IN_INPUT].getChannels();
  77. bool gateButton = params[GATE_PARAM].getValue() > 0.f;
  78. float slewParam = params[SLEW_PARAM].getValue();
  79. // Hard-left param means infinite slew
  80. if (slewParam <= std::log2(1e-3f))
  81. slewParam = -INFINITY;
  82. for (int c = 0; c < channels; c++) {
  83. Engine& e = engines[c];
  84. float in = inputs[IN_INPUT].getVoltage(c);
  85. float gateValue = inputs[GATE_INPUT].getPolyVoltage(c);
  86. // Slew rate in V/s
  87. float slew = INFINITY;
  88. if (std::isfinite(slewParam)) {
  89. float slewPitch = slewParam + inputs[SLEW_INPUT].getPolyVoltage(c);
  90. slew = dsp::exp2_taylor5(-slewPitch + 30.f) / std::exp2(30.f);
  91. }
  92. float slewDelta = slew * args.sampleTime;
  93. // Gate trigger/untrigger
  94. if (!e.state) {
  95. if (gateValue >= 2.f || gateButton) {
  96. // Triggered
  97. e.state = true;
  98. e.onTime = 0.f;
  99. // Hold and track
  100. e.holdValue = in;
  101. // Sample and hold
  102. e.sample2 = e.sample1;
  103. e.sample1 = in;
  104. }
  105. }
  106. else {
  107. if (gateValue <= 0.1f && !gateButton) {
  108. // Untriggered
  109. e.state = false;
  110. // Track and hold
  111. e.holdValue = in;
  112. }
  113. }
  114. // Track & hold
  115. float tr = e.state ? e.holdValue : in;
  116. float ht = e.state ? in : e.holdValue;
  117. // Slew
  118. if (e.state) {
  119. e.slewFilter.jump(in);
  120. e.onTime += args.sampleTime;
  121. }
  122. else {
  123. e.slewFilter.process(in, slewDelta);
  124. }
  125. // Glide
  126. // Wait 1ms before considering gate as legato
  127. if (e.state && e.onTime > 1e-3f) {
  128. e.glideFilter.process(in, slewDelta);
  129. }
  130. else {
  131. e.glideFilter.jump(in);
  132. }
  133. outputs[SH1_OUTPUT].setVoltage(e.sample1Filter.process(e.sample1, slewDelta), c);
  134. outputs[SH2_OUTPUT].setVoltage(e.sample2Filter.process(e.sample2, slewDelta), c);
  135. outputs[TH_OUTPUT].setVoltage(tr, c);
  136. outputs[HT_OUTPUT].setVoltage(ht, c);
  137. outputs[SLEW_OUTPUT].setVoltage(e.slewFilter.getValue(), c);
  138. outputs[GLIDE_OUTPUT].setVoltage(e.glideFilter.getValue(), c);
  139. }
  140. outputs[SH1_OUTPUT].setChannels(channels);
  141. outputs[SH2_OUTPUT].setChannels(channels);
  142. outputs[TH_OUTPUT].setChannels(channels);
  143. outputs[HT_OUTPUT].setChannels(channels);
  144. outputs[SLEW_OUTPUT].setChannels(channels);
  145. outputs[GLIDE_OUTPUT].setChannels(channels);
  146. lights[GATE_LIGHT].setBrightness(gateButton);
  147. }
  148. };
  149. struct ProcessWidget : ModuleWidget {
  150. ProcessWidget(Process* module) {
  151. setModule(module);
  152. setPanel(createPanel(asset::plugin(pluginInstance, "res/Process.svg"), asset::plugin(pluginInstance, "res/Process-dark.svg")));
  153. addChild(createWidget<ThemedScrew>(Vec(RACK_GRID_WIDTH, 0)));
  154. addChild(createWidget<ThemedScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  155. addChild(createWidget<ThemedScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  156. addChild(createWidget<ThemedScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  157. addParam(createParamCentered<RoundLargeBlackKnob>(mm2px(Vec(12.646, 26.755)), module, Process::SLEW_PARAM));
  158. addParam(createLightParamCentered<VCVLightBezel<>>(mm2px(Vec(18.136, 52.31)), module, Process::GATE_PARAM, Process::GATE_LIGHT));
  159. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(7.299, 52.31)), module, Process::SLEW_INPUT));
  160. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(7.297, 67.53)), module, Process::IN_INPUT));
  161. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(18.122, 67.53)), module, Process::GATE_INPUT));
  162. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(7.297, 82.732)), module, Process::SH1_OUTPUT));
  163. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(18.134, 82.732)), module, Process::SH2_OUTPUT));
  164. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(7.297, 97.958)), module, Process::TH_OUTPUT));
  165. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(18.134, 97.923)), module, Process::HT_OUTPUT));
  166. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(7.297, 113.115)), module, Process::SLEW_OUTPUT));
  167. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(18.134, 113.115)), module, Process::GLIDE_OUTPUT));
  168. }
  169. };
  170. Model* modelProcess = createModel<Process, ProcessWidget>("Process");