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.

223 lines
7.3KB

  1. #include "Squinky.hpp"
  2. #ifdef _GROWLER
  3. #include "WidgetComposite.h"
  4. #include "VocalAnimator.h"
  5. #include "ctrl/SqMenuItem.h"
  6. /**
  7. * Implementation class for VocalWidget
  8. */
  9. struct VocalModule : Module
  10. {
  11. VocalModule();
  12. /**
  13. * Overrides of Module functions
  14. */
  15. void step() override;
  16. void onSampleRateChange() override;
  17. using Animator = VocalAnimator<WidgetComposite>;
  18. Animator animator;
  19. private:
  20. typedef float T;
  21. };
  22. VocalModule::VocalModule() : Module(animator.NUM_PARAMS, animator.NUM_INPUTS, animator.NUM_OUTPUTS, animator.NUM_LIGHTS),
  23. animator(this)
  24. {
  25. onSampleRateChange();
  26. animator.init();
  27. }
  28. void VocalModule::onSampleRateChange()
  29. {
  30. T rate = engineGetSampleRate();
  31. animator.setSampleRate(rate);
  32. }
  33. void VocalModule::step()
  34. {
  35. animator.step();
  36. }
  37. ////////////////////
  38. // module widget
  39. ////////////////////
  40. struct VocalWidget : ModuleWidget
  41. {
  42. VocalWidget(VocalModule *);
  43. Menu* createContextMenu() override;
  44. };
  45. inline Menu* VocalWidget::createContextMenu()
  46. {
  47. Menu* theMenu = ModuleWidget::createContextMenu();
  48. ManualMenuItem* manual = new ManualMenuItem(
  49. "https://github.com/squinkylabs/SquinkyVCV/blob/master/docs/growler.md");
  50. theMenu->addChild(manual);
  51. return theMenu;
  52. }
  53. template <typename BASE>
  54. struct MuteLight : BASE
  55. {
  56. MuteLight()
  57. {
  58. this->box.size = mm2px(Vec(6.0f, 6.0f));
  59. }
  60. };
  61. struct NKK2 : SVGSwitch, ToggleSwitch
  62. {
  63. NKK2()
  64. {
  65. addFrame(SVG::load(assetGlobal("res/ComponentLibrary/NKK_0.svg")));
  66. addFrame(SVG::load(assetGlobal("res/ComponentLibrary/NKK_2.svg")));
  67. }
  68. };
  69. /**
  70. * Widget constructor will describe my implementation structure and
  71. * provide meta-data.
  72. * This is not shared by all modules in the DLL, just one
  73. */
  74. #ifdef __V1
  75. VocalWidget::VocalWidget(VocalModule *module)
  76. {
  77. setModule(module);
  78. #else
  79. VocalWidget::VocalWidget(VocalModule *module) : ModuleWidget(module)
  80. {
  81. #endif
  82. const float width = 14 * RACK_GRID_WIDTH;
  83. box.size = Vec(width, RACK_GRID_HEIGHT);
  84. {
  85. SVGPanel *panel = new SVGPanel();
  86. panel->box.size = box.size;
  87. panel->setBackground(SVG::load(assetPlugin(plugin, "res/vocal_animator_panel.svg")));
  88. addChild(panel);
  89. }
  90. /**
  91. * LEDs and LFO outputs
  92. */
  93. const float lfoBlockY = 38; // was 22. move down to make space
  94. const float ledX = width - 46;
  95. const float ledY = lfoBlockY + 7.5;
  96. const float ledSpacingY = 30;
  97. const float lfoOutY = lfoBlockY;
  98. const float lfoOutX = width - 30;
  99. const float lfoInputX = 24;
  100. const float lfoInputY = lfoBlockY + 0;
  101. const float lfoTrimX = 68;
  102. const float lfoTrimY = lfoInputY + 3;
  103. const float lfoRateKnobX = 100;
  104. const float lfoRateKnobY = lfoBlockY + 24;
  105. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(
  106. Vec(ledX, ledY), module, module->animator.LFO0_LIGHT));
  107. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(
  108. Vec(ledX, ledY + ledSpacingY), module, module->animator.LFO1_LIGHT));
  109. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(
  110. Vec(ledX, ledY + 2 * ledSpacingY), module, module->animator.LFO2_LIGHT));
  111. addOutput(Port::create<PJ301MPort>(
  112. Vec(lfoOutX, lfoOutY), Port::OUTPUT, module, VocalModule::Animator::LFO0_OUTPUT));
  113. addOutput(Port::create<PJ301MPort>(
  114. Vec(lfoOutX, lfoOutY + 1 * ledSpacingY), Port::OUTPUT, module, VocalModule::Animator::LFO1_OUTPUT));
  115. addOutput(Port::create<PJ301MPort>(
  116. Vec(lfoOutX, lfoOutY + 2 * ledSpacingY), Port::OUTPUT, module, VocalModule::Animator::LFO2_OUTPUT));
  117. addParam(ParamWidget::create<Rogan1PSBlue>(
  118. Vec(lfoRateKnobX, lfoRateKnobY), module, module->animator.LFO_RATE_PARAM, -5.0, 5.0, 0.0));
  119. addInput(Port::create<PJ301MPort>(
  120. Vec(lfoInputX, lfoInputY), Port::INPUT, module, VocalModule::Animator::LFO_RATE_CV_INPUT));
  121. addParam(ParamWidget::create<Trimpot>(
  122. Vec(lfoTrimX, lfoTrimY), module, module->animator.LFO_RATE_TRIM_PARAM, -1.0, 1.0, 1.0));
  123. // the matrix switch
  124. addParam(ParamWidget::create<NKK>(
  125. Vec(42, 65), module, module->animator.LFO_MIX_PARAM, 0.0f, 2.0f, 0.0f));
  126. /**
  127. * Parameters and CV
  128. */
  129. const float mainBlockY = 140;
  130. const float mainBlockX = 20;
  131. const float colSpacingX = 64;
  132. const float knobX = mainBlockX + 0;
  133. const float knobY = mainBlockY + 24;
  134. const float trimX = mainBlockX + 11;
  135. const float trimY = mainBlockY + 78;
  136. const float inputX = mainBlockX + 8;
  137. const float inputY = mainBlockY + 108;
  138. addParam(ParamWidget::create<Rogan1PSBlue>(
  139. Vec(knobX, knobY), module, module->animator.FILTER_FC_PARAM, -5.0, 5.0, 0.0));
  140. addInput(Port::create<PJ301MPort>(
  141. Vec(inputX, inputY), Port::INPUT, module, VocalModule::Animator::FILTER_FC_CV_INPUT));
  142. addParam(ParamWidget::create<Trimpot>(
  143. Vec(trimX, trimY), module, module->animator.FILTER_FC_TRIM_PARAM, -1.0, 1.0, 1.0));
  144. addParam(ParamWidget::create<Rogan1PSBlue>(
  145. Vec(knobX + colSpacingX, knobY), module, module->animator.FILTER_Q_PARAM, -5.0, 5.0, 0.0));
  146. addInput(Port::create<PJ301MPort>(
  147. Vec(inputX + colSpacingX, inputY), Port::INPUT, module, VocalModule::Animator::FILTER_Q_CV_INPUT));
  148. addParam(ParamWidget::create<Trimpot>(
  149. Vec(trimX + colSpacingX, trimY), module, module->animator.FILTER_Q_TRIM_PARAM, -1.0, 1.0, 1.0));
  150. addParam(ParamWidget::create<Rogan1PSBlue>(
  151. Vec(knobX + 2 * colSpacingX, knobY), module, module->animator.FILTER_MOD_DEPTH_PARAM, -5.0, 5.0, 0.0));
  152. addInput(Port::create<PJ301MPort>(
  153. Vec(inputX + 2 * colSpacingX, inputY), Port::INPUT, module, VocalModule::Animator::FILTER_MOD_DEPTH_CV_INPUT));
  154. addParam(ParamWidget::create<Trimpot>(
  155. Vec(trimX + 2 * colSpacingX, trimY), module, module->animator.FILTER_MOD_DEPTH_TRIM_PARAM, -1.0, 1.0, 1.0));
  156. const float row3 = 310;
  157. // I.O on row 3
  158. const float AudioInputX = inputX;
  159. const float outputX = inputX + 2 * colSpacingX;
  160. addInput(Port::create<PJ301MPort>(
  161. Vec(AudioInputX, row3), Port::INPUT, module, VocalModule::Animator::AUDIO_INPUT));
  162. addOutput(Port::create<PJ301MPort>(
  163. Vec(outputX, row3), Port::OUTPUT, module, VocalModule::Animator::AUDIO_OUTPUT));
  164. const float bassX = inputX + colSpacingX - 4;
  165. const float bassY = row3 - 8;
  166. // the bass boost switch
  167. addParam(ParamWidget::create<NKK2>(
  168. Vec(bassX, bassY), module, module->animator.BASS_EXP_PARAM, 0.0f, 1.0f, 0.0f));
  169. /*************************************************
  170. * screws
  171. */
  172. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  173. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  174. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  175. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  176. }
  177. RACK_PLUGIN_MODEL_INIT(squinkylabs_plug1, Vocal) {
  178. Model *modelVocalModule = Model::create<VocalModule, VocalWidget>("Squinky Labs",
  179. "squinkylabs-vocalanimator",
  180. "Growler: Vocal Animator", EFFECT_TAG, FILTER_TAG, LFO_TAG, RANDOM_TAG);
  181. return modelVocalModule;
  182. }
  183. #endif