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.

201 lines
5.8KB

  1. #include "RJModules.hpp"
  2. #include "dsp/digital.hpp"
  3. #include <iostream>
  4. #include <cmath>
  5. #include <sstream>
  6. #include <iomanip>
  7. namespace rack_plugin_RJModules {
  8. /*
  9. Thank you to MSCHacks for the starting point on this one!
  10. Thanks to Strum for the display widget!
  11. */
  12. struct BPM: Module {
  13. enum ParamIds {
  14. BPM_PARAM,
  15. RESET_PARAM,
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. CH1_CV_INPUT,
  20. RESET_CV_INPUT,
  21. NUM_INPUTS
  22. };
  23. enum OutputIds {
  24. CH1_OUTPUT,
  25. CH2_OUTPUT,
  26. CH3_OUTPUT,
  27. CH4_OUTPUT,
  28. CH5_OUTPUT,
  29. CH6_OUTPUT,
  30. NUM_OUTPUTS
  31. };
  32. enum LightIds {
  33. RESET_LIGHT,
  34. PULSE_LIGHT,
  35. NUM_LIGHTS
  36. };
  37. float resetLight = 0.0;
  38. float pulseLight = 0.0;
  39. int m_fBPM = 133.0;
  40. float m_fBeatsPers;
  41. float m_fMainClockCount;
  42. BPM() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  43. void step() override;
  44. void BPMChange( float fbmp, bool bforce );
  45. };
  46. template <typename BASE>
  47. struct BigOlLight : BASE {
  48. BigOlLight() {
  49. this->box.size = mm2px(Vec(34, 34));
  50. }
  51. };
  52. struct NumberDisplayWidget : TransparentWidget {
  53. int *value;
  54. std::shared_ptr<Font> font;
  55. NumberDisplayWidget() {
  56. font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf"));
  57. };
  58. void draw(NVGcontext *vg) override
  59. {
  60. // Background
  61. NVGcolor backgroundColor = nvgRGB(0xC0, 0xC0, 0xC0);
  62. nvgBeginPath(vg);
  63. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0);
  64. nvgFillColor(vg, backgroundColor);
  65. nvgFill(vg);
  66. // text
  67. nvgFontSize(vg, 32);
  68. nvgFontFaceId(vg, font->handle);
  69. nvgTextLetterSpacing(vg, 2.5);
  70. std::stringstream to_display;
  71. to_display << std::setw(3) << (int) *value;
  72. Vec textPos = Vec(16.0f, 33.0f);
  73. NVGcolor textColor = nvgRGB(0x00, 0x00, 0x00);
  74. nvgFillColor(vg, textColor);
  75. nvgText(vg, textPos.x, textPos.y, to_display.str().c_str(), NULL);
  76. }
  77. };
  78. void BPM::BPMChange( float fbpm, bool bforce ){
  79. // // don't change if it is already the same
  80. if( !bforce && ( (int)(fbpm * 1000.0f ) == (int)(m_fBPM * 1000.0f ) ) )
  81. return;
  82. m_fBPM = fbpm;
  83. m_fBeatsPers = fbpm / 60.0;
  84. }
  85. void BPM::step() {
  86. const float lightLambda = 0.075;
  87. float output = 0.0;
  88. SchmittTrigger resetTrigger;
  89. bool bMainClockTrig = false;
  90. // new_value = ( (old_value - old_min) / (old_max - old_min) ) * (new_max - new_min) + new_min
  91. float bpm_val = params[BPM_PARAM].value * clamp(inputs[CH1_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f);
  92. float mapped_bpm = ((bpm_val - 0.0) / (1.0 - 0.0) ) * (600.0 - 40.0) + 40.0;
  93. m_fBPM = mapped_bpm;
  94. m_fMainClockCount += (mapped_bpm/60.0);
  95. if( ( m_fMainClockCount ) >= engineGetSampleRate() )
  96. {
  97. m_fMainClockCount = m_fMainClockCount - engineGetSampleRate();
  98. bMainClockTrig = true;
  99. }
  100. if( bMainClockTrig )
  101. {
  102. output = 12.0;
  103. resetLight = 1.0;
  104. pulseLight = 1.0;
  105. }
  106. // Reset
  107. if (params[RESET_PARAM].value > 0 || inputs[RESET_CV_INPUT].value > 0) {
  108. resetLight = 1.0;
  109. output = 12.0;
  110. m_fMainClockCount = 0;
  111. }
  112. pulseLight -= pulseLight / lightLambda / engineGetSampleRate();
  113. outputs[CH1_OUTPUT].value = output;
  114. outputs[CH2_OUTPUT].value = output;
  115. outputs[CH3_OUTPUT].value = output;
  116. outputs[CH4_OUTPUT].value = output;
  117. outputs[CH5_OUTPUT].value = output;
  118. outputs[CH6_OUTPUT].value = output;
  119. lights[PULSE_LIGHT].value = pulseLight;
  120. }
  121. struct BPMWidget: ModuleWidget {
  122. BPMWidget(BPM *module);
  123. };
  124. BPMWidget::BPMWidget(BPM *module) : ModuleWidget(module) {
  125. box.size = Vec(15*10, 380);
  126. {
  127. SVGPanel *panel = new SVGPanel();
  128. panel->box.size = box.size;
  129. panel->setBackground(SVG::load(assetPlugin(plugin, "res/BPM.svg")));
  130. addChild(panel);
  131. }
  132. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  133. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  134. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  135. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  136. addInput(Port::create<PJ301MPort>(Vec(24, 160), Port::INPUT, module, BPM::CH1_CV_INPUT));
  137. addInput(Port::create<PJ301MPort>(Vec(106, 165), Port::INPUT, module, BPM::RESET_CV_INPUT));
  138. addParam(ParamWidget::create<LEDButton>(Vec(109, 132), module, BPM::RESET_PARAM, 0.0, 1.0, 0.0));
  139. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(113.4, 136.4), module, BPM::RESET_LIGHT));
  140. addOutput(Port::create<PJ301MPort>(Vec(24, 223), Port::OUTPUT, module, BPM::CH1_OUTPUT));
  141. addOutput(Port::create<PJ301MPort>(Vec(65, 223), Port::OUTPUT, module, BPM::CH2_OUTPUT));
  142. addOutput(Port::create<PJ301MPort>(Vec(105, 223), Port::OUTPUT, module, BPM::CH3_OUTPUT));
  143. addOutput(Port::create<PJ301MPort>(Vec(24, 274), Port::OUTPUT, module, BPM::CH4_OUTPUT));
  144. addOutput(Port::create<PJ301MPort>(Vec(65, 274), Port::OUTPUT, module, BPM::CH5_OUTPUT));
  145. addOutput(Port::create<PJ301MPort>(Vec(106, 274), Port::OUTPUT, module, BPM::CH6_OUTPUT));
  146. addParam(ParamWidget::create<RoundBlackKnob>(Vec(58, 140), module, BPM::BPM_PARAM, 0.0, 1.0, 0.165));
  147. // addChild(ModuleLightWidget::create<LargeLight<GreenLight>>(Vec(28, 130), module, BPM::PULSE_LIGHT));
  148. //addChild(ModuleLightWidget::create<BigOlLight<GreenLight>>(Vec(25, 70), module, BPM::RESET_LIGHT));
  149. NumberDisplayWidget *display = new NumberDisplayWidget();
  150. display->box.pos = Vec(28, 70);
  151. display->box.size = Vec(100, 40);
  152. display->value = &module->m_fBPM;
  153. addChild(display);
  154. }
  155. } // namespace rack_plugin_RJModules
  156. using namespace rack_plugin_RJModules;
  157. RACK_PLUGIN_MODEL_INIT(RJModules, BPM) {
  158. Model *modelBPM = Model::create<BPM, BPMWidget>("RJModules", "BPM", "[LIVE] BPM", UTILITY_TAG);
  159. return modelBPM;
  160. }