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.

349 lines
9.0KB

  1. #include "ML_modules.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "util/math.hpp"
  4. #include <iostream>
  5. #include <sstream>
  6. #include <iomanip>
  7. namespace rack_plugin_ML_modules {
  8. struct BPMdetect : Module {
  9. enum ParamIds {
  10. SMOOTH_PARAM,
  11. MULT2_PARAM,
  12. MULT3_PARAM,
  13. SWING2_PARAM,
  14. SWING3_PARAM,
  15. DELAY1_PARAM,
  16. DELAY2_PARAM,
  17. NUM_PARAMS
  18. };
  19. enum InputIds {
  20. GATE_INPUT,
  21. NUM_INPUTS
  22. };
  23. enum OutputIds {
  24. LFO_OUTPUT,
  25. SEQ_OUTPUT,
  26. DELAY_OUTPUT,
  27. TRIG1_OUTPUT,
  28. TRIG2_OUTPUT,
  29. TRIG3_OUTPUT,
  30. NUM_OUTPUTS
  31. };
  32. enum LighIds {
  33. NUM_LIGHTS
  34. };
  35. BPMdetect() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { misses = 0; onSampleRateChange();};
  36. void step() override;
  37. int misses = 0;
  38. int count2 = 0;
  39. int count3 = 0;
  40. float timer = 0.0;
  41. float timer1 = 0.0;
  42. float timer2 = 0.0;
  43. float timer3 = 0.0;
  44. float seconds = 0.0;
  45. float deltaT;
  46. float BPM=0.0;
  47. float lfo_volts=0.0;
  48. float delay_volts=0.0;
  49. bool fine = false;
  50. inline bool checkBeat(float timer, int mult) {
  51. return ( ((timer - mult*seconds) * (timer - mult*seconds) / (seconds*seconds) < 0.2 ) && misses < 4);
  52. }
  53. float gSampleRate;
  54. void reset() override {onSampleRateChange();};
  55. void onSampleRateChange() override {gSampleRate = engineGetSampleRate(); deltaT = 1.0/gSampleRate;}
  56. SchmittTrigger gateTrigger;
  57. PulseGenerator outPulse1, outPulse2, outPulse3;
  58. };
  59. void BPMdetect::step() {
  60. float mult2 = roundf(params[MULT2_PARAM].value);
  61. float mult3 = roundf(params[MULT3_PARAM].value);
  62. float factor2 = ( fine ? 1.0f + 0.25f * (params[SWING2_PARAM].value - 1.0f): params[SWING2_PARAM].value ) / mult2;
  63. float factor3 = ( fine ? 1.0f + 0.25f * (params[SWING3_PARAM].value - 1.0f): params[SWING3_PARAM].value ) / mult3;
  64. if( inputs[GATE_INPUT].active) {
  65. if( timer1 > seconds ) {
  66. outPulse1.trigger(0.01);
  67. timer1 = 0.0;
  68. }
  69. if( (timer2 > seconds*factor2) /* && (count2 < mult2) */ ) {
  70. // if(nearf(factor2,1.0)) std::cerr << timer2 << "\n";
  71. outPulse2.trigger(0.01);
  72. timer2 = 0.0;
  73. // count2++;
  74. }
  75. if( (timer3 > seconds*factor3) /* && (count3<mult3) */ ) {
  76. outPulse3.trigger(0.01);
  77. timer3 = 0.0 ;
  78. // count3++;
  79. }
  80. if( gateTrigger.process(inputs[GATE_INPUT].value) ) {
  81. if(timer>0) {
  82. float new_seconds;
  83. bool found=false;
  84. for(int mult=1; !found && mult < 20; mult++ ) {
  85. if(checkBeat(timer, mult)) {
  86. new_seconds = timer/mult;
  87. if(mult==1) misses=0;
  88. else misses++;
  89. found = true;
  90. };
  91. };
  92. if( !found ) {
  93. // std::cerr << "default. misses = " << misses << "\n";
  94. new_seconds = timer;
  95. misses=0;
  96. }
  97. float a = params[SMOOTH_PARAM].value;
  98. seconds = ( (1.0-a)*seconds + a*new_seconds);
  99. BPM=60.0/seconds;
  100. lfo_volts = 1.0 - log2(seconds) ;
  101. float num = roundf(params[DELAY1_PARAM].value);
  102. float denom = roundf(params[DELAY2_PARAM].value);
  103. delay_volts = 10.0*(3.0+log10(seconds * num/denom))/4.0;
  104. timer -= seconds;
  105. timer1 = 0.0;
  106. timer2 = 0.0;
  107. timer3 = 0.0;
  108. count2 = 1;
  109. count3 = 1;
  110. outPulse1.trigger(0.01);
  111. outPulse2.trigger(0.01);
  112. outPulse3.trigger(0.01);
  113. }
  114. };
  115. };
  116. timer += deltaT;
  117. timer1 += deltaT;
  118. timer2 += deltaT;
  119. timer3 += deltaT;
  120. outputs[TRIG1_OUTPUT].value = outPulse1.process(deltaT) ? 10.0 : 0.0;
  121. outputs[TRIG2_OUTPUT].value = outPulse2.process(deltaT) ? 10.0 : 0.0;
  122. outputs[TRIG3_OUTPUT].value = outPulse3.process(deltaT) ? 10.0 : 0.0;
  123. outputs[LFO_OUTPUT].value = lfo_volts;
  124. outputs[SEQ_OUTPUT].value = lfo_volts-3.0;
  125. outputs[DELAY_OUTPUT].value = delay_volts;
  126. };
  127. struct NumberDisplayWidget2 : TransparentWidget {
  128. float *value;
  129. std::shared_ptr<Font> font;
  130. NumberDisplayWidget2() {
  131. font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf"));
  132. };
  133. void draw(NVGcontext *vg) {
  134. // Background
  135. NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  136. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  137. nvgBeginPath(vg);
  138. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0);
  139. nvgFillColor(vg, backgroundColor);
  140. nvgFill(vg);
  141. nvgStrokeWidth(vg, 1.0);
  142. nvgStrokeColor(vg, borderColor);
  143. nvgStroke(vg);
  144. nvgFontSize(vg, 18);
  145. nvgFontFaceId(vg, font->handle);
  146. nvgTextLetterSpacing(vg, 2.5);
  147. char display_string[10];
  148. sprintf(display_string,"%6.1f",*value);
  149. Vec textPos = Vec(6.0f, 17.0f);
  150. NVGcolor textColor = nvgRGB(0xdf, 0xd2, 0x2c);
  151. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  152. nvgText(vg, textPos.x, textPos.y, "~~~~~", NULL);
  153. textColor = nvgRGB(0xda, 0xe9, 0x29);
  154. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  155. nvgText(vg, textPos.x, textPos.y, "\\\\\\\\\\", NULL);
  156. textColor = nvgRGB(0xf0, 0x00, 0x00);
  157. nvgFillColor(vg, textColor);
  158. nvgText(vg, textPos.x, textPos.y, display_string, NULL);
  159. }
  160. };
  161. struct FineMenuItem : MenuItem {
  162. BPMdetect *module;
  163. bool mfine;
  164. void onAction(EventAction &e) override {
  165. module->fine = mfine;
  166. };
  167. void step() override {
  168. rightText = (module->fine == mfine)? "✔" : "";
  169. };
  170. };
  171. struct NormalMenuItem : MenuItem {
  172. BPMdetect *module;
  173. bool mfine;
  174. void onAction(EventAction &e) override {
  175. module->fine = mfine;
  176. };
  177. void step() override {
  178. rightText = (module->fine != mfine)? "✔" : "";
  179. };
  180. };
  181. struct BPMdetectWidget : ModuleWidget {
  182. BPMdetectWidget(BPMdetect *module);
  183. json_t *toJsonData() ;
  184. void fromJsonData(json_t *root) ;
  185. Menu *createContextMenu() override;
  186. };
  187. Menu *BPMdetectWidget::createContextMenu() {
  188. Menu *menu = ModuleWidget::createContextMenu();
  189. MenuLabel *spacerLabel = new MenuLabel();
  190. menu->addChild(spacerLabel);
  191. BPMdetect *myModule = dynamic_cast<BPMdetect*>(module);
  192. assert(myModule);
  193. MenuLabel *modeLabel2 = new MenuLabel();
  194. modeLabel2->text = "Swing Range";
  195. menu->addChild(modeLabel2);
  196. FineMenuItem *fineMenuItem = new FineMenuItem();
  197. fineMenuItem->text = "Fine";
  198. fineMenuItem->module = myModule;
  199. fineMenuItem->mfine = true;
  200. menu->addChild(fineMenuItem);
  201. NormalMenuItem *normalMenuItem = new NormalMenuItem();
  202. normalMenuItem->text = "Legacy";
  203. normalMenuItem->module = myModule;
  204. normalMenuItem->mfine = false;
  205. menu->addChild(normalMenuItem);
  206. return menu;
  207. };
  208. BPMdetectWidget::BPMdetectWidget(BPMdetect *module) : ModuleWidget(module) {
  209. box.size = Vec(15*10, 380);
  210. {
  211. SVGPanel *panel = new SVGPanel();
  212. panel->box.size = box.size;
  213. panel->setBackground(SVG::load(assetPlugin(plugin,"res/BPMdetect.svg")));
  214. addChild(panel);
  215. }
  216. const float column1 = 15;
  217. const float column2 = 61;
  218. const float column3 = 110;
  219. const float row1 = 84;
  220. const float row2 = 140;
  221. const float row3 = row2 + 60;
  222. const float row4 = row3 + 58;
  223. const float row5 = 316;
  224. addChild(Widget::create<MLScrew>(Vec(15, 0)));
  225. addChild(Widget::create<MLScrew>(Vec(box.size.x-30, 0)));
  226. addChild(Widget::create<MLScrew>(Vec(15, 365)));
  227. addChild(Widget::create<MLScrew>(Vec(box.size.x-30, 365)));
  228. addInput(Port::create<MLPort>(Vec(column1+5, row1+2), Port::INPUT, module, BPMdetect::GATE_INPUT));
  229. addParam(ParamWidget::create<SmallBlueMLKnob>(Vec(column2, row1), module, BPMdetect::SMOOTH_PARAM, 0.0, 1.0, 0.5));
  230. addOutput(Port::create<MLPort>(Vec(column3-5,row1+2), Port::OUTPUT, module, BPMdetect::TRIG1_OUTPUT));
  231. addParam(ParamWidget::create<SmallBlueSnapMLKnob>(Vec(column1, row2), module, BPMdetect::MULT2_PARAM, 1.0, 8.0, 2.0));
  232. addParam(ParamWidget::create<SmallBlueMLKnob>(Vec(column2, row2), module, BPMdetect::SWING2_PARAM, 0.0, 2.0, 1.0));
  233. addOutput(Port::create<MLPort>(Vec(column3, row2+2), Port::OUTPUT, module, BPMdetect::TRIG2_OUTPUT));
  234. addParam(ParamWidget::create<SmallBlueSnapMLKnob>(Vec(column1, row3), module, BPMdetect::MULT3_PARAM, 1.0, 8.0, 3.0));
  235. addParam(ParamWidget::create<SmallBlueMLKnob>(Vec(column2, row3), module, BPMdetect::SWING3_PARAM, 0.0, 2.0, 1.0));
  236. addOutput(Port::create<MLPort>(Vec(column3, row3+2), Port::OUTPUT, module, BPMdetect::TRIG3_OUTPUT));
  237. addOutput(Port::create<MLPort>(Vec(column1, row4), Port::OUTPUT, module, BPMdetect::LFO_OUTPUT));
  238. addOutput(Port::create<MLPort>(Vec(column3, row4), Port::OUTPUT, module, BPMdetect::SEQ_OUTPUT));
  239. addParam(ParamWidget::create<SmallBlueSnapMLKnob>(Vec(column1, row5), module, BPMdetect::DELAY1_PARAM, 1.0, 8.0, 1.0));
  240. addParam(ParamWidget::create<SmallBlueSnapMLKnob>(Vec(column2, row5), module, BPMdetect::DELAY2_PARAM, 1.0, 8.0, 1.0));
  241. addOutput(Port::create<MLPort>(Vec(column3, row5), Port::OUTPUT, module, BPMdetect::DELAY_OUTPUT));
  242. NumberDisplayWidget2 *display = new NumberDisplayWidget2();
  243. display->box.pos = Vec(25,40);
  244. display->box.size = Vec(100, 20);
  245. display->value = &module->BPM;
  246. addChild(display);
  247. }
  248. } // namespace rack_plugin_ML_modules
  249. using namespace rack_plugin_ML_modules;
  250. RACK_PLUGIN_MODEL_INIT(ML_modules, BPMdetect) {
  251. Model *modelBPMdetect = Model::create<BPMdetect, BPMdetectWidget>("ML modules", "BPMdetect", "BPM Tools", UTILITY_TAG, CLOCK_TAG);
  252. return modelBPMdetect;
  253. }