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.

255 lines
5.7KB

  1. #include "Southpole.hpp"
  2. #include "dsp/digital.hpp"
  3. namespace rack_plugin_Southpole {
  4. struct Fuse : Module {
  5. enum ParamIds {
  6. SWITCH1_PARAM,
  7. SWITCH2_PARAM,
  8. SWITCH3_PARAM,
  9. SWITCH4_PARAM,
  10. NUM_PARAMS
  11. };
  12. enum InputIds {
  13. ARM1_INPUT,
  14. ARM2_INPUT,
  15. ARM3_INPUT,
  16. ARM4_INPUT,
  17. CLK_INPUT,
  18. RESET_INPUT,
  19. NUM_INPUTS
  20. };
  21. enum OutputIds {
  22. OUT1_OUTPUT,
  23. OUT2_OUTPUT,
  24. OUT3_OUTPUT,
  25. OUT4_OUTPUT,
  26. NUM_OUTPUTS
  27. };
  28. enum LightIds {
  29. ARM1_LIGHT,
  30. ARM2_LIGHT,
  31. ARM3_LIGHT,
  32. ARM4_LIGHT,
  33. NUM_LIGHTS
  34. };
  35. bool gateMode;
  36. Fuse() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  37. params.resize(NUM_PARAMS);
  38. inputs.resize(NUM_INPUTS);
  39. outputs.resize(NUM_OUTPUTS);
  40. lights.resize(NUM_LIGHTS);
  41. }
  42. void step() override;
  43. SchmittTrigger clockTrigger;
  44. SchmittTrigger resetTrigger;
  45. SchmittTrigger armTrigger[4];
  46. PulseGenerator pulse[4];
  47. bool armed[4];
  48. bool gateOn[4];
  49. const unsigned maxsteps = 16;
  50. unsigned curstep = 0;
  51. json_t *toJson() override {
  52. json_t *rootJ = json_object();
  53. json_object_set_new(rootJ, "gateMode", json_boolean( gateMode ));
  54. return rootJ;
  55. }
  56. void fromJson(json_t *rootJ) override {
  57. json_t *gateModeJ = json_object_get(rootJ, "gateMode");
  58. if (gateModeJ) {
  59. gateMode = json_boolean_value(gateModeJ);
  60. }
  61. }
  62. };
  63. void Fuse::step() {
  64. bool nextStep = false;
  65. if (inputs[RESET_INPUT].active) {
  66. if (resetTrigger.process(inputs[RESET_INPUT].value)) {
  67. curstep = maxsteps;
  68. for (unsigned int i=0; i<4; i++) {
  69. armTrigger[i].reset();
  70. armed[i] = false;
  71. gateOn[i] = false;
  72. }
  73. }
  74. }
  75. if (inputs[CLK_INPUT].active) {
  76. if (clockTrigger.process(inputs[CLK_INPUT].value)) {
  77. nextStep = true;
  78. }
  79. }
  80. if ( nextStep ) {
  81. curstep++;
  82. if ( curstep >= maxsteps ) curstep = 0;
  83. if ( curstep % 4 == 0 ) {
  84. unsigned int i = curstep/4;
  85. gateOn[(i-1)%4] = false;
  86. if ( armed[i] ) {
  87. pulse[i].trigger(1e-3);
  88. if ( gateMode ) gateOn[i] = true;
  89. armed[i] = false;
  90. }
  91. }
  92. //printf("%d %d\n",curstep,gateOn[curstep/4]);
  93. }
  94. for (unsigned int i=0; i<4; i++) {
  95. if ( params[SWITCH1_PARAM + i].value > 0. ) armed[i] = true;
  96. if ( armTrigger[i].process(inputs[ARM1_INPUT + i].normalize(0.))) armed[i] = true;
  97. lights[ARM1_LIGHT + i].setBrightness( armed[i] ? 1.0 : 0.0 );
  98. bool p = pulse[i].process(1.0 / engineGetSampleRate());
  99. if (gateOn[i]) p = true;
  100. outputs[OUT1_OUTPUT + i].value = p ? 10.0 : 0.0;
  101. }
  102. };
  103. struct FuseDisplay : TransparentWidget {
  104. Fuse *module;
  105. FuseDisplay() {}
  106. void draw(NVGcontext *vg) override {
  107. // Background
  108. NVGcolor backgroundColor = nvgRGB(0x30, 0x00, 0x10);
  109. NVGcolor borderColor = nvgRGB(0xd0, 0xd0, 0xd0);
  110. nvgBeginPath(vg);
  111. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  112. nvgFillColor(vg, backgroundColor);
  113. nvgFill(vg);
  114. nvgStrokeWidth(vg, 1.5);
  115. nvgStrokeColor(vg, borderColor);
  116. nvgStroke(vg);
  117. // Lights
  118. nvgStrokeColor(vg, nvgRGBA(0x7f, 0x00, 0x00, 0xff));
  119. nvgFillColor(vg, nvgRGBA(0xff, 0x00, 0x00, 0xff));
  120. for ( unsigned y_ = 0; y_ < 16; y_++ ) {
  121. unsigned y = 15 - y_;
  122. nvgBeginPath(vg);
  123. nvgStrokeWidth(vg, 1.);
  124. nvgRect(vg, 3., y*box.size.y/18.+7.*floor(y/4.)+9., box.size.x-6., box.size.y/18.-6.);
  125. if (y_ <= module->curstep) nvgFill(vg);
  126. nvgStroke(vg);
  127. }
  128. }
  129. };
  130. struct FuseWidget : ModuleWidget {
  131. Menu *createContextMenu() override;
  132. FuseWidget(Fuse *module) : ModuleWidget(module) {
  133. box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
  134. {
  135. SVGPanel *panel = new SVGPanel();
  136. panel->box.size = box.size;
  137. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Fuse.svg")));
  138. addChild(panel);
  139. }
  140. {
  141. FuseDisplay *display = new FuseDisplay();
  142. display->module = module;
  143. display->box.pos = Vec( 32, 25.);
  144. display->box.size = Vec( 24., box.size.y-85. );
  145. addChild(display);
  146. }
  147. float y1 = 76;
  148. float yh = 73;
  149. float x1 = 5;
  150. float x2 = 35;
  151. for(int i = 0; i < 4; i++)
  152. {
  153. addParam(ParamWidget::create<LEDButton>(Vec(x1+1, y1 + i*yh-22), module, Fuse::SWITCH1_PARAM + 3 - i, 0.0, 1.0, 0.0));
  154. addChild(ModuleLightWidget::create<MediumLight<YellowLight>>(Vec(x1+5, y1+ i*yh-18), module, Fuse::ARM1_LIGHT + 3 - i));
  155. addInput(Port::create<sp_Port>(Vec(x1, y1 + i*yh-45), Port::INPUT, module, Fuse::ARM1_INPUT + 3 - i));
  156. addOutput(Port::create<sp_Port>(Vec(x1, y1 + i*yh), Port::OUTPUT, module, Fuse::OUT1_OUTPUT + 3 - i));
  157. }
  158. addInput(Port::create<sp_Port>(Vec(x1, 330), Port::INPUT, module, Fuse::CLK_INPUT));
  159. addInput(Port::create<sp_Port>(Vec(x2, 330), Port::INPUT, module, Fuse::RESET_INPUT));
  160. }
  161. };
  162. struct FuseGateModeItem : MenuItem {
  163. Fuse *fuse;
  164. bool gateMode;
  165. void onAction(EventAction &e) override {
  166. fuse->gateMode = gateMode;
  167. }
  168. void step() override {
  169. rightText = (fuse->gateMode == gateMode) ? "✔" : "";
  170. }
  171. };
  172. Menu *FuseWidget::createContextMenu() {
  173. Menu *menu = ModuleWidget::createContextMenu();
  174. MenuLabel *spacerLabel = new MenuLabel();
  175. menu->addChild(spacerLabel);
  176. Fuse *fuse = dynamic_cast<Fuse*>(module);
  177. assert(fuse);
  178. MenuLabel *modeLabel = new MenuLabel();
  179. modeLabel->text = "Gate Mode";
  180. menu->addChild(modeLabel);
  181. FuseGateModeItem *triggerItem = new FuseGateModeItem();
  182. triggerItem->text = "Trigger";
  183. triggerItem->fuse = fuse;
  184. triggerItem->gateMode = false;
  185. menu->addChild(triggerItem);
  186. FuseGateModeItem *gateItem = new FuseGateModeItem();
  187. gateItem->text = "Gate";
  188. gateItem->fuse = fuse;
  189. gateItem->gateMode = true;
  190. menu->addChild(gateItem);
  191. return menu;
  192. }
  193. } // namespace rack_plugin_Southpole
  194. using namespace rack_plugin_Southpole;
  195. RACK_PLUGIN_MODEL_INIT(Southpole, Fuse) {
  196. Model *modelFuse = Model::create<Fuse,FuseWidget>( "Southpole", "Fuse", "Fuse - next pattern", SEQUENCER_TAG);
  197. return modelFuse;
  198. }