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.

200 lines
6.9KB

  1. #include "Bidoo.hpp"
  2. #include "BidooComponents.hpp"
  3. #include "dsp/digital.hpp"
  4. using namespace std;
  5. namespace rack_plugin_Bidoo {
  6. struct TOCANTE : Module {
  7. enum ParamIds {
  8. BPM_PARAM,
  9. BPMFINE_PARAM,
  10. BEATS_PARAM,
  11. REF_PARAM,
  12. RUN_PARAM,
  13. RESET_PARAM,
  14. NUM_PARAMS
  15. };
  16. enum InputIds {
  17. BPM_INPUT,
  18. BPMFINE_INPUT,
  19. BEATS_INPUT,
  20. REF_INPUT,
  21. RUN_INPUT,
  22. RESET_INPUT,
  23. NUM_INPUTS
  24. };
  25. enum OutputIds {
  26. OUT_MEASURE,
  27. OUT_BEAT,
  28. OUT_TRIPLET,
  29. OUT_QUARTER,
  30. OUT_EIGHTH,
  31. OUT_SIXTEENTH,
  32. NUM_OUTPUTS
  33. };
  34. enum LightIds {
  35. RUNNING_LIGHT,
  36. NUM_LIGHTS
  37. };
  38. int ref = 2;
  39. int beats = 1;
  40. int currentStep = 0;
  41. int stepsPerMeasure = 1;
  42. int stepsPerBeat = 1;
  43. int stepsPerSixteenth = 1;
  44. int stepsPerEighth = 1;
  45. int stepsPerQuarter = 1;
  46. int stepsPerTriplet = 1;
  47. PulseGenerator gatePulse;
  48. PulseGenerator gatePulse_triplets;
  49. SchmittTrigger runningTrigger;
  50. SchmittTrigger resetTrigger;
  51. bool running = true;
  52. bool reset = false;
  53. float runningLight = 0.0f;
  54. bool pulseEven = false, pulseTriplets = false;
  55. float bpm = 0.0f;
  56. TOCANTE() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { }
  57. void step() override;
  58. json_t *toJson() override {
  59. json_t *rootJ = json_object();
  60. json_object_set_new(rootJ, "running", json_boolean(running));
  61. return rootJ;
  62. }
  63. void fromJson(json_t *rootJ) override {
  64. json_t *runningJ = json_object_get(rootJ, "running");
  65. if (runningJ){
  66. running = json_is_true(runningJ);
  67. }
  68. }
  69. };
  70. void TOCANTE::step() {
  71. if (runningTrigger.process(params[RUN_PARAM].value)) {
  72. running = !running;
  73. if(running){
  74. currentStep = 0;
  75. }
  76. }
  77. if (resetTrigger.process(params[RESET_PARAM].value)){
  78. currentStep = 0;
  79. }
  80. float invESR = 1 / engineGetSampleRate();
  81. ref = clamp(powf(2.0f,params[REF_PARAM].value+(int)rescale(clamp(inputs[REF_INPUT].value,0.0f,10.0f),0.0f,10.0f,0.0f,3.0f)),2.0f,16.0f);
  82. beats = clamp(params[BEATS_PARAM].value+rescale(clamp(inputs[BEATS_INPUT].value,0.0f,10.0f),0.0f,10.0f,0.0f,32.0f),1.0f,32.0f);
  83. bpm = clamp(round(params[BPM_PARAM].value+rescale(clamp(inputs[BPM_INPUT].value,0.0f,10.0f),0.0f,10.0f,0.0f,350.0f)) + round(100*(params[BPMFINE_PARAM].value+rescale(clamp(inputs[BPMFINE_INPUT].value,0.0f,10.0f),0.0f,10.0f,0.0f,0.99f))) * 0.01f, 1.0f, 350.0f);
  84. stepsPerSixteenth = floor(engineGetSampleRate() / bpm * 60 * ref / 32) * 2;
  85. stepsPerEighth = stepsPerSixteenth * 2;
  86. stepsPerQuarter = stepsPerEighth * 2;
  87. stepsPerTriplet = floor(stepsPerQuarter / 3);
  88. stepsPerBeat = stepsPerSixteenth * 16 / ref;
  89. stepsPerMeasure = beats*stepsPerBeat;
  90. if ((stepsPerSixteenth>0) && ((currentStep % stepsPerSixteenth) == 0)) {
  91. gatePulse.trigger(1e-3f);
  92. }
  93. if ((stepsPerTriplet>0) && ((currentStep % stepsPerTriplet) == 0) && (currentStep <= (stepsPerMeasure-100))) {
  94. gatePulse_triplets.trigger(1e-3f);
  95. }
  96. pulseEven = gatePulse.process(invESR);
  97. pulseTriplets = gatePulse_triplets.process(invESR);
  98. outputs[OUT_MEASURE].value = (currentStep == 0) ? 10.0f : 0.0f;
  99. outputs[OUT_BEAT].value = (pulseEven && (currentStep % stepsPerBeat == 0)) ? 10.0f : 0.0f;
  100. outputs[OUT_TRIPLET].value = (pulseTriplets && (currentStep % stepsPerTriplet == 0)) ? 10.0f : 0.0f;
  101. outputs[OUT_QUARTER].value = (pulseEven && (currentStep % stepsPerQuarter == 0)) ? 10.0f : 0.0f;
  102. outputs[OUT_EIGHTH].value = (pulseEven && (currentStep % stepsPerEighth == 0)) ? 10.0f : 0.0f;
  103. outputs[OUT_SIXTEENTH].value = (pulseEven && (currentStep % stepsPerSixteenth == 0)) ? 10.0f : 0.0f;
  104. if (running) {
  105. currentStep = floor((currentStep + 1) % stepsPerMeasure);
  106. }
  107. lights[RUNNING_LIGHT].value = running ? 1.0 : 0.0;
  108. }
  109. struct TOCANTEDisplay : TransparentWidget {
  110. TOCANTE *module;
  111. std::shared_ptr<Font> font;
  112. TOCANTEDisplay() {
  113. font = Font::load(assetPlugin(plugin, "res/DejaVuSansMono.ttf"));
  114. }
  115. void draw(NVGcontext *vg) override {
  116. char tBPM[128],tBeats[128];
  117. snprintf(tBPM, sizeof(tBPM), "%1.2f BPM", module->bpm);
  118. snprintf(tBeats, sizeof(tBeats), "%1i/%1i", module->beats, module->ref);
  119. nvgFontSize(vg, 16.0f);
  120. nvgFontFaceId(vg, font->handle);
  121. nvgTextLetterSpacing(vg, -2.0f);
  122. nvgFillColor(vg, YELLOW_BIDOO);
  123. nvgText(vg, 0.0f, 0.0f, tBPM, NULL);
  124. nvgText(vg, 0.0f, 15.0f, tBeats, NULL);
  125. }
  126. };
  127. struct BPMBlueKnob : BidooBlueKnob {
  128. void onChange (EventChange &e) override {
  129. BidooBlueKnob::onChange(e);
  130. }
  131. };
  132. struct TOCANTEWidget : ModuleWidget {
  133. TOCANTEWidget(TOCANTE *module) : ModuleWidget(module) {
  134. setPanel(SVG::load(assetPlugin(plugin, "res/TOCANTE.svg")));
  135. TOCANTEDisplay *display = new TOCANTEDisplay();
  136. display->module = module;
  137. display->box.pos = Vec(16.0f, 55.0f);
  138. display->box.size = Vec(50.0f, 85.0f);
  139. addChild(display);
  140. addParam(ParamWidget::create<LEDButton>(Vec(78, 185), module, TOCANTE::RUN_PARAM, 0.0, 1.0, 0.0));
  141. addChild(ModuleLightWidget::create<SmallLight<BlueLight>>(Vec(84, 190), module, TOCANTE::RUNNING_LIGHT));
  142. addParam(ParamWidget::create<BlueCKD6>(Vec(39, 180), module, TOCANTE::RESET_PARAM, 0.0, 1.0, 0.0));
  143. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  144. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  145. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  146. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  147. addParam(ParamWidget::create<BPMBlueKnob>(Vec(3.0f,90.0f), module, TOCANTE::BPM_PARAM, 1.0f, 350.0f, 60.0f));
  148. addParam(ParamWidget::create<BPMBlueKnob>(Vec(3.0f,155.0f), module, TOCANTE::BPMFINE_PARAM, 0.0f, 0.99f, 0.0f));
  149. addParam(ParamWidget::create<BidooBlueSnapKnob>(Vec(37.0f,90.0f), module, TOCANTE::BEATS_PARAM, 1.0f, 32.0f, 4.0f));
  150. addParam(ParamWidget::create<BidooBlueSnapKnob>(Vec(72.0f,90.0f), module, TOCANTE::REF_PARAM, 1.0f, 4.0f, 2.0f));
  151. addInput(Port::create<TinyPJ301MPort>(Vec(10, 125), Port::INPUT, module, TOCANTE::BPM_INPUT));
  152. addInput(Port::create<TinyPJ301MPort>(Vec(10, 190), Port::INPUT, module, TOCANTE::BPMFINE_INPUT));
  153. addInput(Port::create<TinyPJ301MPort>(Vec(45, 125), Port::INPUT, module, TOCANTE::BEATS_INPUT));
  154. addInput(Port::create<TinyPJ301MPort>(Vec(80, 125), Port::INPUT, module, TOCANTE::REF_INPUT));
  155. addOutput(Port::create<PJ301MPort>(Vec(18.0f, 230.0f), Port::OUTPUT, module, TOCANTE::OUT_MEASURE));
  156. addOutput(Port::create<PJ301MPort>(Vec(62.0f, 230.0f), Port::OUTPUT, module, TOCANTE::OUT_BEAT));
  157. addOutput(Port::create<PJ301MPort>(Vec(18.0f, 280.0f), Port::OUTPUT, module, TOCANTE::OUT_QUARTER));
  158. addOutput(Port::create<PJ301MPort>(Vec(62.0f, 280.0f), Port::OUTPUT, module, TOCANTE::OUT_TRIPLET));
  159. addOutput(Port::create<PJ301MPort>(Vec(18.0f, 331.0f), Port::OUTPUT, module, TOCANTE::OUT_EIGHTH));
  160. addOutput(Port::create<PJ301MPort>(Vec(62.0f, 331.0f), Port::OUTPUT, module, TOCANTE::OUT_SIXTEENTH));
  161. }
  162. };
  163. } // namespace rack_plugin_Bidoo
  164. using namespace rack_plugin_Bidoo;
  165. RACK_PLUGIN_MODEL_INIT(Bidoo, TOCANTE) {
  166. Model *modelTOCANTE = Model::create<TOCANTE, TOCANTEWidget>("Bidoo", "tOCAnTe", "tOCAnTe clock", CLOCK_TAG);
  167. return modelTOCANTE;
  168. }