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.

272 lines
8.3KB

  1. //**************************************************************************************
  2. //LaunchGate module for VCV Rack by Alfredo Santamaria - AS - https://github.com/AScustomWorks/AS
  3. //
  4. //Code adapted from Dual Counter - VCV Module, Strum 2017
  5. //**************************************************************************************
  6. #include "AS.hpp"
  7. #include "dsp/digital.hpp"
  8. #include <sstream>
  9. #include <iomanip>
  10. struct LaunchGate : Module {
  11. enum ParamIds {
  12. RST_BUTTON1,
  13. COUNT_NUM_PARAM_1,
  14. RST_BUTTON2,
  15. COUNT_NUM_PARAM_2,
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. INPUT_1,
  20. CLK_IN_1,
  21. RESET_IN_1,
  22. INPUT_2,
  23. CLK_IN_2,
  24. RESET_IN_2,
  25. NUM_INPUTS
  26. };
  27. enum OutputIds {
  28. OUTPUT_1,
  29. OUTPUT_2,
  30. OUTPUT_3,
  31. NUM_OUTPUTS
  32. };
  33. enum LightIds {
  34. RESET_LIGHT1,
  35. RESET_LIGHT2,
  36. NUM_LIGHTS
  37. };
  38. SchmittTrigger clock_trigger_1;
  39. SchmittTrigger reset_trigger_1;
  40. SchmittTrigger reset_ext_trigger_1;
  41. int count_limit1 = 1;
  42. int count1 = 0;
  43. SchmittTrigger clock_trigger_2;
  44. SchmittTrigger reset_trigger_2;
  45. SchmittTrigger reset_ext_trigger_2;
  46. int count_limit_2 = 1;
  47. int count_2 = 0;
  48. const float lightLambda = 0.075f;
  49. float resetLight1 = 0.0f;
  50. float resetLight2 = 0.0f;
  51. bool gate1_open= false;
  52. bool gate2_open= false;
  53. float mute_fade1 = 0.0f;
  54. float mute_fade2 = 0.0f;
  55. const float fade_speed = 0.001f;
  56. LaunchGate() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  57. }
  58. void reset() override {
  59. count_limit1 = 1;
  60. count1 = 0;
  61. count_limit_2 = 1;
  62. count_2 = 0;
  63. gate1_open= false;
  64. gate2_open= false;
  65. }
  66. void step() override;
  67. };
  68. void LaunchGate::step(){
  69. count_limit1 = round(params[COUNT_NUM_PARAM_1].value);
  70. count_limit_2 = round(params[COUNT_NUM_PARAM_2].value);
  71. bool reset1 = false;
  72. bool reset_2 = false;
  73. ///////////// counter 1
  74. if ( reset_trigger_1.process( params[RST_BUTTON1].value ) || reset_ext_trigger_1.process( inputs[RESET_IN_1].value ) ) {
  75. reset1 = true;
  76. count1 = 0;
  77. gate1_open = false;
  78. resetLight1 = 1.0f;
  79. mute_fade1 = 0.0f;
  80. }
  81. resetLight1 -= resetLight1 / lightLambda / engineGetSampleRate();
  82. lights[RESET_LIGHT1].value = resetLight1;
  83. if ( reset1 == false ) {
  84. if ( clock_trigger_1.process( inputs[CLK_IN_1].value ) && count1 <= count_limit1 ) {
  85. if ( !gate1_open ) {
  86. count1++;
  87. }
  88. }
  89. }
  90. if ( count1 == count_limit1 ) {
  91. gate1_open = true;
  92. }
  93. //SOFT MUTE/UNMUTE
  94. mute_fade1 += gate1_open ? fade_speed : -fade_speed;
  95. if ( mute_fade1 < 0.0f ) {
  96. mute_fade1 = 0.0f;
  97. } else if ( mute_fade1 > 1.0f ) {
  98. mute_fade1 = 1.0f;
  99. }
  100. outputs[OUTPUT_1].value = inputs[INPUT_1].value * mute_fade1;
  101. ///////////// counter 2
  102. if ( reset_trigger_2.process( params[RST_BUTTON2].value ) || reset_ext_trigger_2.process( inputs[RESET_IN_2].value ) ) {
  103. reset_2 = true;
  104. count_2 = 0;
  105. gate2_open = false;
  106. resetLight2 = 1.0f;
  107. }
  108. resetLight2 -= resetLight2 / lightLambda / engineGetSampleRate();
  109. lights[RESET_LIGHT2].value = resetLight2;
  110. if ( reset_2 == false ) {
  111. if ( clock_trigger_2.process( inputs[CLK_IN_2].value ) && count_2 <= count_limit_2 ) {
  112. if ( !gate2_open ) {
  113. count_2++;
  114. }
  115. }
  116. }
  117. if ( count_2 == count_limit_2 ) {
  118. gate2_open = true;
  119. }
  120. //SOFT MUTE/UNMUTE
  121. mute_fade2 += gate2_open ? fade_speed : -fade_speed;
  122. if ( mute_fade2 < 0.0f ) {
  123. mute_fade2 = 0.0f;
  124. } else if ( mute_fade2 > 1.0f ) {
  125. mute_fade2 = 1.0f;
  126. }
  127. outputs[OUTPUT_2].value = inputs[INPUT_2].value * mute_fade2;
  128. }
  129. ///////////////////////////////////
  130. struct NumberDisplayWidget : TransparentWidget {
  131. int *value;
  132. std::shared_ptr<Font> font;
  133. NumberDisplayWidget() {
  134. font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf"));
  135. };
  136. void draw(NVGcontext *vg) override
  137. {
  138. // Background
  139. //NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  140. NVGcolor backgroundColor = nvgRGB(0x20, 0x10, 0x10);
  141. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  142. nvgBeginPath(vg);
  143. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0);
  144. nvgFillColor(vg, backgroundColor);
  145. nvgFill(vg);
  146. nvgStrokeWidth(vg, 1.5);
  147. nvgStrokeColor(vg, borderColor);
  148. nvgStroke(vg);
  149. // text
  150. nvgFontSize(vg, 18);
  151. nvgFontFaceId(vg, font->handle);
  152. nvgTextLetterSpacing(vg, 2.5);
  153. std::stringstream to_display;
  154. to_display << std::right << std::setw(2) << *value;
  155. Vec textPos = Vec(4.0f, 17.0f);
  156. NVGcolor textColor = nvgRGB(0xdf, 0xd2, 0x2c);
  157. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  158. nvgText(vg, textPos.x, textPos.y, "~~", NULL);
  159. textColor = nvgRGB(0xda, 0xe9, 0x29);
  160. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  161. nvgText(vg, textPos.x, textPos.y, "\\\\", NULL);
  162. textColor = nvgRGB(0xf0, 0x00, 0x00);
  163. nvgFillColor(vg, textColor);
  164. nvgText(vg, textPos.x, textPos.y, to_display.str().c_str(), NULL);
  165. }
  166. };
  167. ////////////////////////////////////
  168. struct LaunchGateWidget : ModuleWidget
  169. {
  170. LaunchGateWidget(LaunchGate *module);
  171. };
  172. LaunchGateWidget::LaunchGateWidget(LaunchGate *module) : ModuleWidget(module) {
  173. setPanel(SVG::load(assetPlugin(plugin, "res/LaunchGate.svg")));
  174. //SCREWS
  175. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, 0)));
  176. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  177. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  178. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  179. // counter 1
  180. //COUNT DISPLAY
  181. NumberDisplayWidget *display1 = new NumberDisplayWidget();
  182. display1->box.pos = Vec(10,50);
  183. display1->box.size = Vec(30, 20);
  184. display1->value = &module->count1;
  185. addChild(display1);
  186. //LaunchGate DISPLAY
  187. NumberDisplayWidget *display2 = new NumberDisplayWidget();
  188. display2->box.pos = Vec(50,50);
  189. display2->box.size = Vec(30, 20);
  190. display2->value = &module->count_limit1;
  191. addChild(display2);
  192. int group_offset = 160;
  193. addParam(ParamWidget::create<LEDBezel>(Vec(11, 82), module, LaunchGate::RST_BUTTON1 , 0.0f, 1.0f, 0.0f));
  194. addChild(ModuleLightWidget::create<LedLight<RedLight>>(Vec(11+2.2, 82+2.3), module, LaunchGate::RESET_LIGHT1));
  195. addParam(ParamWidget::create<as_KnobBlack>(Vec(43, 73), module, LaunchGate::COUNT_NUM_PARAM_1, 1.0f, 64.0f, 1.0f));
  196. addInput(Port::create<as_PJ301MPort>(Vec(10, 125), Port::INPUT, module, LaunchGate::RESET_IN_1));
  197. addInput(Port::create<as_PJ301MPort>(Vec(55, 125), Port::INPUT, module, LaunchGate::CLK_IN_1));
  198. addInput(Port::create<as_PJ301MPort>(Vec(10, 170), Port::INPUT, module, LaunchGate::INPUT_1));
  199. addOutput(Port::create<as_PJ301MPort>(Vec(55, 170), Port::OUTPUT, module, LaunchGate::OUTPUT_1));
  200. // counter 2
  201. //COUNT DISPLAY
  202. NumberDisplayWidget *display3 = new NumberDisplayWidget();
  203. display3->box.pos = Vec(10,50 + group_offset);
  204. display3->box.size = Vec(30, 20);
  205. display3->value = &module->count_2;
  206. addChild(display3);
  207. //LaunchGate DISPLAY
  208. NumberDisplayWidget *display4 = new NumberDisplayWidget();
  209. display4->box.pos = Vec(50,50 + group_offset);
  210. display4->box.size = Vec(30, 20);
  211. display4->value = &module->count_limit_2;
  212. addChild(display4);
  213. addParam(ParamWidget::create<LEDBezel>(Vec(11, 82+ group_offset), module, LaunchGate::RST_BUTTON2 , 0.0f, 1.0f, 0.0f));
  214. addChild(ModuleLightWidget::create<LedLight<RedLight>>(Vec(11+2.2, 82+2.3+ group_offset), module, LaunchGate::RESET_LIGHT2));
  215. addParam(ParamWidget::create<as_KnobBlack>(Vec(43, 73 + group_offset), module, LaunchGate::COUNT_NUM_PARAM_2, 1.0f, 64.0f, 1.0f));
  216. addInput(Port::create<as_PJ301MPort>(Vec(10, 125 + group_offset), Port::INPUT, module, LaunchGate::RESET_IN_2));
  217. addInput(Port::create<as_PJ301MPort>(Vec(55, 125 + group_offset), Port::INPUT, module, LaunchGate::CLK_IN_2));
  218. addInput(Port::create<as_PJ301MPort>(Vec(10, 170 + group_offset), Port::INPUT, module, LaunchGate::INPUT_2));
  219. addOutput(Port::create<as_PJ301MPort>(Vec(55, 170 + group_offset), Port::OUTPUT, module, LaunchGate::OUTPUT_2));
  220. }
  221. RACK_PLUGIN_MODEL_INIT(AS, LaunchGate) {
  222. Model *modelLaunchGate = Model::create<LaunchGate, LaunchGateWidget>("AS", "LaunchGate", "Launch Gate", SWITCH_TAG, SEQUENCER_TAG, UTILITY_TAG, DELAY_TAG);
  223. return modelLaunchGate;
  224. }