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.

202 lines
6.4KB

  1. #include "Gratrix.hpp"
  2. #include "dsp/digital.hpp"
  3. namespace rack_plugin_Gratrix {
  4. //============================================================================================================
  5. struct ADSR : MicroModule {
  6. enum ParamIds {
  7. ATTACK_PARAM,
  8. DECAY_PARAM,
  9. SUSTAIN_PARAM,
  10. RELEASE_PARAM,
  11. NUM_PARAMS
  12. };
  13. enum InputIds {
  14. ATTACK_INPUT, // 1
  15. DECAY_INPUT, // 1
  16. SUSTAIN_INPUT, // 1
  17. RELEASE_INPUT, // 1
  18. GATE_INPUT, // N+1
  19. TRIG_INPUT, // N+1
  20. NUM_INPUTS,
  21. OFF_INPUTS = GATE_INPUT
  22. };
  23. enum OutputIds {
  24. ENVELOPE_OUTPUT, // N
  25. INVERTED_OUTPUT, // N
  26. NUM_OUTPUTS,
  27. OFF_OUTPUTS = ENVELOPE_OUTPUT
  28. };
  29. enum LightIds {
  30. NUM_LIGHTS
  31. };
  32. bool decaying = false;
  33. float env = 0.0f;
  34. SchmittTrigger trigger;
  35. ADSR() : MicroModule(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  36. void step();
  37. };
  38. //============================================================================================================
  39. void ADSR::step() {
  40. float attack = clamp(params[ATTACK_INPUT].value + inputs[ATTACK_INPUT].value / 10.0f, 0.0f, 1.0f);
  41. float decay = clamp(params[DECAY_PARAM].value + inputs[DECAY_INPUT].value / 10.0f, 0.0f, 1.0f);
  42. float sustain = clamp(params[SUSTAIN_PARAM].value + inputs[SUSTAIN_INPUT].value / 10.0f, 0.0f, 1.0f);
  43. float release = clamp(params[RELEASE_PARAM].value + inputs[RELEASE_PARAM].value / 10.0f, 0.0f, 1.0f);
  44. // Gate and trigger
  45. bool gated = inputs[GATE_INPUT].value >= 1.0f;
  46. if (trigger.process(inputs[TRIG_INPUT].value))
  47. decaying = false;
  48. const float base = 20000.0f;
  49. const float maxTime = 10.0f;
  50. if (gated) {
  51. if (decaying) {
  52. // Decay
  53. if (decay < 1e-4) {
  54. env = sustain;
  55. }
  56. else {
  57. env += powf(base, 1 - decay) / maxTime * (sustain - env) * engineGetSampleTime();
  58. }
  59. }
  60. else {
  61. // Attack
  62. // Skip ahead if attack is all the way down (infinitely fast)
  63. if (attack < 1e-4) {
  64. env = 1.0f;
  65. }
  66. else {
  67. env += powf(base, 1 - attack) / maxTime * (1.01f - env) * engineGetSampleTime();
  68. }
  69. if (env >= 1.0f) {
  70. env = 1.0f;
  71. decaying = true;
  72. }
  73. }
  74. }
  75. else {
  76. // Release
  77. if (release < 1e-4) {
  78. env = 0.0f;
  79. }
  80. else {
  81. env += powf(base, 1 - release) / maxTime * (0.0f - env) * engineGetSampleTime();
  82. }
  83. decaying = false;
  84. }
  85. outputs[ENVELOPE_OUTPUT].value = 10.0 * env;
  86. outputs[INVERTED_OUTPUT].value = 10.0 * (1.0 - env);
  87. }
  88. //============================================================================================================
  89. struct GtxModule_ADSR : Module
  90. {
  91. std::array<ADSR, GTX__N> inst;
  92. GtxModule_ADSR()
  93. :
  94. Module(ADSR::NUM_PARAMS,
  95. (GTX__N+1) * (ADSR::NUM_INPUTS - ADSR::OFF_INPUTS ) + ADSR::OFF_INPUTS,
  96. (GTX__N ) * (ADSR::NUM_OUTPUTS - ADSR::OFF_OUTPUTS) + ADSR::OFF_OUTPUTS)
  97. {}
  98. static constexpr std::size_t imap(std::size_t port, std::size_t bank)
  99. {
  100. return (port < ADSR::OFF_INPUTS) ? port : port + bank * (ADSR::NUM_INPUTS - ADSR::OFF_INPUTS);
  101. }
  102. static constexpr std::size_t omap(std::size_t port, std::size_t bank)
  103. {
  104. // return (port < ADSR::OFF_OUTPUTS) ? port : port + bank * (ADSR::NUM_OUTPUTS - ADSR::OFF_OUTPUTS);
  105. return port + bank * ADSR::NUM_OUTPUTS;
  106. }
  107. void step() override
  108. {
  109. for (std::size_t i=0; i<GTX__N; ++i)
  110. {
  111. for (std::size_t p=0; p<ADSR::NUM_PARAMS; ++p) inst[i].params[p] = params[p];
  112. for (std::size_t p=0; p<ADSR::NUM_INPUTS; ++p) inst[i].inputs[p] = inputs[imap(p, i)].active ? inputs[imap(p, i)] : inputs[imap(p, GTX__N)];
  113. for (std::size_t p=0; p<ADSR::NUM_OUTPUTS; ++p) inst[i].outputs[p] = outputs[omap(p, i)];
  114. inst[i].step();
  115. for (std::size_t p=0; p<ADSR::NUM_OUTPUTS; ++p) outputs[omap(p, i)].value = inst[i].outputs[p].value;
  116. }
  117. }
  118. };
  119. //============================================================================================================
  120. struct GtxWidget_ADSR : ModuleWidget
  121. {
  122. GtxWidget_ADSR(GtxModule_ADSR *module) : ModuleWidget(module)
  123. {
  124. GTX__WIDGET();
  125. box.size = Vec(12*15, 380);
  126. #if GTX__SAVE_SVG
  127. {
  128. PanelGen pg(assetPlugin(plugin, "build/res/ADSR-F1.svg"), box.size, "ADSR-F1");
  129. pg.nob_med(0, -0.28, "ATTACK"); pg.nob_med(1, -0.28, "DECAY");
  130. pg.nob_med(0, +0.28, "SUSTAIN"); pg.nob_med(1, +0.28, "RELEASE");
  131. pg.bus_in(0, 1, "GATE"); pg.bus_out(1, 1, "OUT");
  132. pg.bus_in(0, 2, "TRIG"); pg.bus_out(1, 2, "INV OUT");
  133. }
  134. #endif
  135. setPanel(SVG::load(assetPlugin(plugin, "res/ADSR-F1.svg")));
  136. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  137. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  138. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  139. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  140. addParam(createParamGTX<KnobFreeMed>(Vec(fx(0+0.18), fy(-0.28)), module, ADSR::ATTACK_PARAM, 0.0, 1.0, 0.5));
  141. addParam(createParamGTX<KnobFreeMed>(Vec(fx(1+0.18), fy(-0.28)), module, ADSR::DECAY_PARAM, 0.0, 1.0, 0.5));
  142. addParam(createParamGTX<KnobFreeMed>(Vec(fx(0+0.18), fy(+0.28)), module, ADSR::SUSTAIN_PARAM, 0.0, 1.0, 0.5));
  143. addParam(createParamGTX<KnobFreeMed>(Vec(fx(1+0.18), fy(+0.28)), module, ADSR::RELEASE_PARAM, 0.0, 1.0, 0.5));
  144. addInput(createInputGTX<PortInMed>(Vec(fx(0-0.28), fy(-0.28)), module, ADSR::ATTACK_INPUT));
  145. addInput(createInputGTX<PortInMed>(Vec(fx(1-0.28), fy(-0.28)), module, ADSR::DECAY_INPUT));
  146. addInput(createInputGTX<PortInMed>(Vec(fx(0-0.28), fy(+0.28)), module, ADSR::SUSTAIN_INPUT));
  147. addInput(createInputGTX<PortInMed>(Vec(fx(1-0.28), fy(+0.28)), module, ADSR::RELEASE_INPUT));
  148. for (std::size_t i=0; i<GTX__N; ++i)
  149. {
  150. addInput(createInputGTX<PortInMed>(Vec(px(0, i), py(1, i)), module, GtxModule_ADSR::imap(ADSR::GATE_INPUT, i)));
  151. addInput(createInputGTX<PortInMed>(Vec(px(0, i), py(2, i)), module, GtxModule_ADSR::imap(ADSR::TRIG_INPUT, i)));
  152. addOutput(createOutputGTX<PortOutMed>(Vec(px(1, i), py(1, i)), module, GtxModule_ADSR::omap(ADSR::ENVELOPE_OUTPUT, i)));
  153. addOutput(createOutputGTX<PortOutMed>(Vec(px(1, i), py(2, i)), module, GtxModule_ADSR::omap(ADSR::INVERTED_OUTPUT, i)));
  154. }
  155. addInput(createInputGTX<PortInMed>(Vec(gx(0), gy(1)), module, GtxModule_ADSR::imap(ADSR::GATE_INPUT, GTX__N)));
  156. addInput(createInputGTX<PortInMed>(Vec(gx(0), gy(2)), module, GtxModule_ADSR::imap(ADSR::TRIG_INPUT, GTX__N)));
  157. }
  158. };
  159. } // namespace rack_plugin_Gratrix
  160. using namespace rack_plugin_Gratrix;
  161. RACK_PLUGIN_MODEL_INIT(Gratrix, ADSR_F1) {
  162. Model *model = Model::create<GtxModule_ADSR, GtxWidget_ADSR>("Gratrix", "ADSR-F1", "ADSR-F1", ENVELOPE_GENERATOR_TAG);
  163. return model;
  164. }