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.

246 lines
7.5KB

  1. #include "HetrickCV.hpp"
  2. namespace rack_plugin_HetrickCV {
  3. #ifdef USE_VST2
  4. #define plugin "HetrickCV"
  5. #endif // USE_VST2
  6. struct Scanner : Module
  7. {
  8. enum ParamIds
  9. {
  10. SCAN_PARAM,
  11. STAGES_PARAM,
  12. WIDTH_PARAM,
  13. SLOPE_PARAM,
  14. OFFSET_PARAM,
  15. MIXSCALE_PARAM,
  16. NUM_PARAMS
  17. };
  18. enum InputIds
  19. {
  20. IN1_INPUT,
  21. IN2_INPUT,
  22. IN3_INPUT,
  23. IN4_INPUT,
  24. IN5_INPUT,
  25. IN6_INPUT,
  26. IN7_INPUT,
  27. IN8_INPUT,
  28. SCAN_INPUT,
  29. STAGES_INPUT,
  30. WIDTH_INPUT,
  31. SLOPE_INPUT,
  32. ALLIN_INPUT,
  33. NUM_INPUTS
  34. };
  35. enum OutputIds
  36. {
  37. OUT1_OUTPUT,
  38. OUT2_OUTPUT,
  39. OUT3_OUTPUT,
  40. OUT4_OUTPUT,
  41. OUT5_OUTPUT,
  42. OUT6_OUTPUT,
  43. OUT7_OUTPUT,
  44. OUT8_OUTPUT,
  45. MIX_OUTPUT,
  46. NUM_OUTPUTS
  47. };
  48. enum LightIds
  49. {
  50. IN1_LIGHT,
  51. IN2_LIGHT,
  52. IN3_LIGHT,
  53. IN4_LIGHT,
  54. IN5_LIGHT,
  55. IN6_LIGHT,
  56. IN7_LIGHT,
  57. IN8_LIGHT,
  58. OUT1_POS_LIGHT, OUT1_NEG_LIGHT,
  59. OUT2_POS_LIGHT, OUT2_NEG_LIGHT,
  60. OUT3_POS_LIGHT, OUT3_NEG_LIGHT,
  61. OUT4_POS_LIGHT, OUT4_NEG_LIGHT,
  62. OUT5_POS_LIGHT, OUT5_NEG_LIGHT,
  63. OUT6_POS_LIGHT, OUT6_NEG_LIGHT,
  64. OUT7_POS_LIGHT, OUT7_NEG_LIGHT,
  65. OUT8_POS_LIGHT, OUT8_NEG_LIGHT,
  66. NUM_LIGHTS
  67. };
  68. float ins[8] = {};
  69. float outs[8] = {};
  70. float inMults[8] = {};
  71. float widthTable[9] = {0.f, 0.f, 0.f, 0.285f, 0.285f, 0.2608f, 0.23523f, 0.2125f, 0.193f};
  72. Scanner() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS)
  73. {
  74. }
  75. void step() override;
  76. int clampInt(const int _in, const int min = 0, const int max = 7)
  77. {
  78. if (_in > max) return max;
  79. if (_in < min) return min;
  80. return _in;
  81. }
  82. float triShape(float _in)
  83. {
  84. _in = _in - round(_in);
  85. return std::abs(_in + _in);
  86. }
  87. // For more advanced Module features, read Rack's engine.hpp header file
  88. // - toJson, fromJson: serialization of internal data
  89. // - onSampleRateChange: event triggered by a change of sample rate
  90. // - reset, randomize: implements special behavior when user clicks these from the context menu
  91. };
  92. void Scanner::step()
  93. {
  94. float allInValue = 0.0f;
  95. if(inputs[ALLIN_INPUT].active) allInValue = inputs[ALLIN_INPUT].value;
  96. else if(params[OFFSET_PARAM].value != 0.0f) allInValue = 5.0f;
  97. for(int i = 0; i < 8; i++)
  98. {
  99. if(!inputs[IN1_INPUT + i].active) ins[i] = allInValue;
  100. else ins[i] = inputs[IN1_INPUT + i].value;
  101. }
  102. int stages = round(params[STAGES_PARAM].value + inputs[STAGES_INPUT].value);
  103. stages = clampInt(stages, 0, 6) + 2;
  104. const float invStages = 1.0f/stages;
  105. const float halfStages = stages * 0.5f;
  106. const float remainInvStages = 1.0f - invStages;
  107. float widthControl = params[WIDTH_PARAM].value + inputs[WIDTH_INPUT].value;
  108. widthControl = clampf(widthControl, 0.0f, 5.0f) * 0.2f;
  109. widthControl = widthControl * widthControl * widthTable[stages];
  110. float scanControl = params[SCAN_PARAM].value + inputs[SCAN_INPUT].value;
  111. scanControl = clampf(scanControl, 0.0f, 5.0f) * 0.2f;
  112. float slopeControl = params[SLOPE_PARAM].value + inputs[SLOPE_INPUT].value;
  113. slopeControl = clampf(slopeControl, 0.0f, 5.0f) * 0.2f;
  114. float scanFactor1 = LERP(widthControl, halfStages, invStages);
  115. float scanFactor2 = LERP(widthControl, halfStages + remainInvStages, 1.0f);
  116. float scanFinal = LERP(scanControl, scanFactor2, scanFactor1);
  117. float invWidth = 1.0f/(LERP(widthControl, float(stages), invStages+invStages));
  118. float subStage = 0.0f;
  119. for(int i = 0; i < 8; i++)
  120. {
  121. inMults[i] = (scanFinal + subStage) * invWidth;
  122. subStage = subStage - invStages;
  123. }
  124. for(int i = 0; i < 8; i++)
  125. {
  126. inMults[i] = clampf(inMults[i], 0.0f, 1.0f);
  127. inMults[i] = triShape(inMults[i]);
  128. inMults[i] = clampf(inMults[i], 0.0f, 1.0f);
  129. const float shaped = (2.0f - inMults[i]) * inMults[i];
  130. inMults[i] = LERP(slopeControl, shaped, inMults[i]);
  131. }
  132. outputs[MIX_OUTPUT].value = 0.0f;
  133. for(int i = 0; i < 8; i++)
  134. {
  135. outputs[i].value = ins[i] * inMults[i];
  136. lights[IN1_LIGHT + i].setBrightnessSmooth(fmaxf(0.0, inMults[i]));
  137. lights[OUT1_POS_LIGHT + 2*i].setBrightnessSmooth(fmaxf(0.0, outputs[i].value / 5.0));
  138. lights[OUT1_NEG_LIGHT + 2*i].setBrightnessSmooth(fmaxf(0.0, outputs[i].value / -5.0));
  139. outputs[MIX_OUTPUT].value = outputs[MIX_OUTPUT].value + outputs[i].value;
  140. }
  141. outputs[MIX_OUTPUT].value = outputs[MIX_OUTPUT].value * params[MIXSCALE_PARAM].value;
  142. }
  143. struct ScannerWidget : ModuleWidget { ScannerWidget(Scanner *module); };
  144. ScannerWidget::ScannerWidget(Scanner *module) : ModuleWidget(module)
  145. {
  146. box.size = Vec(18 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
  147. {
  148. auto *panel = new SVGPanel();
  149. panel->box.size = box.size;
  150. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Scanner.svg")));
  151. addChild(panel);
  152. }
  153. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  154. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  155. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  156. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  157. const int knobX = 75;
  158. const int jackX = 123;
  159. //////PARAMS//////
  160. addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(knobX, 65), module, Scanner::SCAN_PARAM, 0, 5.0, 0.0));
  161. addInput(Port::create<PJ301MPort>(Vec(jackX, 70), Port::INPUT, module, Scanner::SCAN_INPUT));
  162. addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(knobX, 125), module, Scanner::STAGES_PARAM, 0, 6.0, 6.0));
  163. addInput(Port::create<PJ301MPort>(Vec(jackX, 130), Port::INPUT, module, Scanner::STAGES_INPUT));
  164. addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(knobX, 185), module, Scanner::WIDTH_PARAM, 0, 5.0, 0.0));
  165. addInput(Port::create<PJ301MPort>(Vec(jackX, 190), Port::INPUT, module, Scanner::WIDTH_INPUT));
  166. addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(knobX, 245), module, Scanner::SLOPE_PARAM, 0, 5.0, 0.0));
  167. addInput(Port::create<PJ301MPort>(Vec(jackX, 250), Port::INPUT, module, Scanner::SLOPE_INPUT));
  168. addInput(Port::create<PJ301MPort>(Vec(96, 310), Port::INPUT, module, Scanner::ALLIN_INPUT));
  169. addOutput(Port::create<PJ301MPort>(Vec(141, 310), Port::OUTPUT, module, Scanner::MIX_OUTPUT));
  170. addParam(ParamWidget::create<CKSS>(Vec(75, 312), module, Scanner::OFFSET_PARAM, 0.0, 1.0, 0.0));
  171. addParam(ParamWidget::create<Trimpot>(Vec(180, 313), module, Scanner::MIXSCALE_PARAM, 0.0, 1.0, 0.125));
  172. const int inXPos = 10;
  173. const int inLightX = 50;
  174. const int outXPos = 235;
  175. const int outLightX = 210;
  176. for(int i = 0; i < 8; i++)
  177. {
  178. const int yPos = 50 + (40 * i);
  179. const int lightY = 59 + (40 * i);
  180. //////INPUTS//////
  181. addInput(Port::create<PJ301MPort>(Vec(inXPos, yPos), Port::INPUT, module, i));
  182. //////OUTPUTS//////
  183. addOutput(Port::create<PJ301MPort>(Vec(outXPos, yPos), Port::OUTPUT, module, i));
  184. //////BLINKENLIGHTS//////
  185. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(inLightX, lightY), module, Scanner::IN1_LIGHT + i));
  186. addChild(ModuleLightWidget::create<SmallLight<GreenRedLight>>(Vec(outLightX, lightY), module, Scanner::OUT1_POS_LIGHT + 2*i));
  187. }
  188. }
  189. } // namespace rack_plugin_HetrickCV
  190. using namespace rack_plugin_HetrickCV;
  191. RACK_PLUGIN_MODEL_INIT(HetrickCV, Scanner) {
  192. Model *modelScanner = Model::create<Scanner, ScannerWidget>("HetrickCV", "Scanner", "Scanner", MIXER_TAG);
  193. return modelScanner;
  194. }