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.

275 lines
8.2KB

  1. //**************************************************************************************
  2. //KillGate 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 KillGate : 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.075;
  49. float resetLight1 = 0.0f;
  50. float resetLight2 = 0.0f;
  51. bool gate1_open= true;
  52. bool gate2_open= true;
  53. float mute_fade1 = 0.0f;
  54. float mute_fade2 = 0.0f;
  55. const float fade_speed = 0.001f;
  56. KillGate() : 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= true;
  64. gate2_open= true;
  65. }
  66. void step() override;
  67. };
  68. void KillGate::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=true;
  78. resetLight1 = 1.0;
  79. }
  80. resetLight1 -= resetLight1 / lightLambda / engineGetSampleRate();
  81. lights[RESET_LIGHT1].value = resetLight1;
  82. if ( reset1 == false ) {
  83. if ( clock_trigger_1.process( inputs[CLK_IN_1].value ) && count1 <= count_limit1 ) {
  84. if ( gate1_open ) {
  85. count1++;
  86. }
  87. }
  88. }
  89. if ( count1 == count_limit1 ) {
  90. gate1_open = false;
  91. }
  92. //SOFT MUTE/UNMUTE
  93. mute_fade1 -= !gate1_open ? fade_speed : -fade_speed;
  94. if ( mute_fade1 < 0.0f ) {
  95. mute_fade1 = 0.0f;
  96. } else if ( mute_fade1 > 1.0f ) {
  97. mute_fade1 = 1.0f;
  98. }
  99. outputs[OUTPUT_1].value = inputs[INPUT_1].value * mute_fade1;
  100. ///////////// counter 2
  101. if ( reset_trigger_2.process( params[RST_BUTTON2].value ) || reset_ext_trigger_2.process( inputs[RESET_IN_2].value ) ) {
  102. reset_2 = true;
  103. count_2 = 0;
  104. gate2_open = true;
  105. resetLight2 = 1.0f;
  106. }
  107. resetLight2 -= resetLight2 / lightLambda / engineGetSampleRate();
  108. lights[RESET_LIGHT2].value = resetLight2;
  109. if ( reset_2 == false ) {
  110. if ( clock_trigger_2.process( inputs[CLK_IN_2].value ) && count_2 <= count_limit_2 ) {
  111. if ( gate2_open ) {
  112. count_2++;
  113. }
  114. }
  115. }
  116. if ( count_2 == count_limit_2 ) {
  117. gate2_open = false;
  118. }
  119. //SOFT MUTE/UNMUTE
  120. mute_fade2 -= !gate2_open ? fade_speed : -fade_speed;
  121. if ( mute_fade2 < 0.0f ) {
  122. mute_fade2 = 0.0f;
  123. } else if ( mute_fade2 > 1.0f ) {
  124. mute_fade2 = 1.0f;
  125. }
  126. outputs[OUTPUT_2].value = inputs[INPUT_2].value * mute_fade2;
  127. }
  128. ///////////////////////////////////
  129. struct NumberDisplayWidget : TransparentWidget {
  130. int *value;
  131. std::shared_ptr<Font> font;
  132. NumberDisplayWidget() {
  133. font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf"));
  134. };
  135. void draw(NVGcontext *vg) override
  136. {
  137. // Background
  138. //NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  139. NVGcolor backgroundColor = nvgRGB(0x20, 0x10, 0x10);
  140. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  141. nvgBeginPath(vg);
  142. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0);
  143. nvgFillColor(vg, backgroundColor);
  144. nvgFill(vg);
  145. nvgStrokeWidth(vg, 1.5);
  146. nvgStrokeColor(vg, borderColor);
  147. nvgStroke(vg);
  148. // text
  149. nvgFontSize(vg, 18);
  150. nvgFontFaceId(vg, font->handle);
  151. nvgTextLetterSpacing(vg, 2.5);
  152. std::stringstream to_display;
  153. to_display << std::right << std::setw(2) << *value;
  154. Vec textPos = Vec(4.0f, 17.0f);
  155. NVGcolor textColor = nvgRGB(0xdf, 0xd2, 0x2c);
  156. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  157. nvgText(vg, textPos.x, textPos.y, "~~", NULL);
  158. textColor = nvgRGB(0xda, 0xe9, 0x29);
  159. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  160. nvgText(vg, textPos.x, textPos.y, "\\\\", NULL);
  161. textColor = nvgRGB(0xf0, 0x00, 0x00);
  162. nvgFillColor(vg, textColor);
  163. nvgText(vg, textPos.x, textPos.y, to_display.str().c_str(), NULL);
  164. }
  165. };
  166. ////////////////////////////////////
  167. struct KillGateWidget : ModuleWidget
  168. {
  169. KillGateWidget(KillGate *module);
  170. };
  171. KillGateWidget::KillGateWidget(KillGate *module) : ModuleWidget(module) {
  172. setPanel(SVG::load(assetPlugin(plugin, "res/KillGate.svg")));
  173. //SCREWS
  174. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, 0)));
  175. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  176. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  177. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  178. // counter 1
  179. //COUNT DISPLAY
  180. NumberDisplayWidget *display1 = new NumberDisplayWidget();
  181. display1->box.pos = Vec(10,50);
  182. display1->box.size = Vec(30, 20);
  183. display1->value = &module->count1;
  184. addChild(display1);
  185. //KillGate DISPLAY
  186. NumberDisplayWidget *display2 = new NumberDisplayWidget();
  187. display2->box.pos = Vec(50,50);
  188. display2->box.size = Vec(30, 20);
  189. display2->value = &module->count_limit1;
  190. addChild(display2);
  191. int group_offset = 160;
  192. addParam(ParamWidget::create<LEDBezel>(Vec(11, 82), module, KillGate::RST_BUTTON1 , 0.0f, 1.0f, 0.0f));
  193. addChild(ModuleLightWidget::create<LedLight<RedLight>>(Vec(11+2.2, 82+2.3), module, KillGate::RESET_LIGHT1));
  194. addParam(ParamWidget::create<as_KnobBlack>(Vec(43, 73), module, KillGate::COUNT_NUM_PARAM_1, 1.0f, 64.0f, 1.0f));
  195. addInput(Port::create<as_PJ301MPort>(Vec(10, 125), Port::INPUT, module, KillGate::RESET_IN_1));
  196. addInput(Port::create<as_PJ301MPort>(Vec(55, 125), Port::INPUT, module, KillGate::CLK_IN_1));
  197. addInput(Port::create<as_PJ301MPort>(Vec(10, 170), Port::INPUT, module, KillGate::INPUT_1));
  198. addOutput(Port::create<as_PJ301MPort>(Vec(55, 170), Port::OUTPUT, module, KillGate::OUTPUT_1));
  199. // counter 2
  200. //COUNT DISPLAY
  201. NumberDisplayWidget *display3 = new NumberDisplayWidget();
  202. display3->box.pos = Vec(10,50 + group_offset);
  203. display3->box.size = Vec(30, 20);
  204. display3->value = &module->count_2;
  205. addChild(display3);
  206. //KillGate DISPLAY
  207. NumberDisplayWidget *display4 = new NumberDisplayWidget();
  208. display4->box.pos = Vec(50,50 + group_offset);
  209. display4->box.size = Vec(30, 20);
  210. display4->value = &module->count_limit_2;
  211. addChild(display4);
  212. addParam(ParamWidget::create<LEDBezel>(Vec(11, 82+ group_offset), module, KillGate::RST_BUTTON2 , 0.0f, 1.0f, 0.0f));
  213. addChild(ModuleLightWidget::create<LedLight<RedLight>>(Vec(11+2.2, 82+2.3+ group_offset), module, KillGate::RESET_LIGHT2));
  214. addParam(ParamWidget::create<as_KnobBlack>(Vec(43, 73 + group_offset), module, KillGate::COUNT_NUM_PARAM_2, 1.0f, 64.0f, 1.0f));
  215. addInput(Port::create<as_PJ301MPort>(Vec(10, 125 + group_offset), Port::INPUT, module, KillGate::RESET_IN_2));
  216. addInput(Port::create<as_PJ301MPort>(Vec(55, 125 + group_offset), Port::INPUT, module, KillGate::CLK_IN_2));
  217. addInput(Port::create<as_PJ301MPort>(Vec(10, 170 + group_offset), Port::INPUT, module, KillGate::INPUT_2));
  218. addOutput(Port::create<as_PJ301MPort>(Vec(55, 170 + group_offset), Port::OUTPUT, module, KillGate::OUTPUT_2));
  219. }
  220. RACK_PLUGIN_MODEL_INIT(AS, KillGate) {
  221. Model *modelKillGate = Model::create<KillGate, KillGateWidget>("AS", "KillGate", "Kill Gate", SWITCH_TAG, SEQUENCER_TAG, UTILITY_TAG, DELAY_TAG);
  222. return modelKillGate;
  223. }