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.

180 lines
6.3KB

  1. #include "BaconPlugs.hpp"
  2. #include <vector>
  3. #include <algorithm>
  4. #define NUM_CLOCKS 4
  5. namespace rack_plugin_BaconMusic {
  6. struct PolyGnome : virtual Module {
  7. enum ParamIds {
  8. CLOCK_PARAM,
  9. CLOCK_NUMERATOR_1,
  10. CLOCK_DENOMINATOR_1 = CLOCK_NUMERATOR_1 + NUM_CLOCKS,
  11. NUM_PARAMS = CLOCK_DENOMINATOR_1 + NUM_CLOCKS,
  12. };
  13. enum InputIds {
  14. CLOCK_INPUT,
  15. NUM_INPUTS,
  16. };
  17. enum OutputIds {
  18. CLOCK_GATE_0,
  19. NUM_OUTPUTS = CLOCK_GATE_0 + NUM_CLOCKS + 1 // the "1" is for the 1/4 note clock which isn't parameterized
  20. };
  21. enum LightIds {
  22. LIGHT_NUMERATOR_1,
  23. LIGHT_DENOMINATOR_1 = LIGHT_NUMERATOR_1 + NUM_CLOCKS,
  24. NUM_LIGHTS = LIGHT_DENOMINATOR_1 + NUM_CLOCKS
  25. };
  26. float phase;
  27. long phase_longpart;
  28. PolyGnome() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS )
  29. {
  30. phase = 0.0f;
  31. phase_longpart = 274;
  32. }
  33. inline int numi( int i ) { return (int)params[ CLOCK_NUMERATOR_1 + i ].value; }
  34. inline int deni( int i ) { return (int)params[ CLOCK_DENOMINATOR_1 + i ].value; }
  35. void step() override
  36. {
  37. float clockTime = powf(2.0f, params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value);
  38. phase += clockTime * engineGetSampleTime();
  39. while( phase > 1 )
  40. {
  41. phase = phase - 1;
  42. phase_longpart ++;
  43. }
  44. /* Alright we have to stop that longpart from getting too big otherwise it will swamp
  45. the fractional parts but we have to reset it when all the clocks are firing at once
  46. otherwise one of the clocks will stutter. So figure out the product of my fractions.
  47. Probably we should use the common prime factors so we can get an earlier step
  48. but lets leave it for now.
  49. */
  50. int sd = 1;
  51. int sn = 1;
  52. for( int i=0; i<NUM_CLOCKS; ++i )
  53. {
  54. if( outputs[ CLOCK_GATE_0 + i + 1 ].active )
  55. {
  56. sd *= deni( i );
  57. sn *= numi( i );
  58. }
  59. }
  60. int commonp = sd * sn; // so we know at least that the clocks will intersect at this tick.
  61. while( phase_longpart > commonp )
  62. {
  63. phase_longpart -= commonp;
  64. }
  65. for( int i=0; i<NUM_CLOCKS+1 ; ++i )
  66. {
  67. bool gateIn = false;
  68. float frac;
  69. if( i == 0 )
  70. frac = 1;
  71. else
  72. frac = deni( i - 1 ) / ( 1.0f * numi( i - 1 ) );
  73. // Note that we have two parts which comprise the phase number now, the float and the long.
  74. // The addition can overflow, though which is why I mod the phase_longpart with a larger number
  75. float lphase = phase * frac;
  76. double liphase = phase_longpart * frac;
  77. double ipart;
  78. // I still worry a bit this + may overflow if you let this run long enough and blow out the precision in the decimal
  79. float fractPhase = modf( lphase + liphase, &ipart );
  80. gateIn = (fractPhase < 0.5f);
  81. outputs[ CLOCK_GATE_0 + i ].value = gateIn ? 10.0f : 0.0f;
  82. }
  83. for( int i=0; i<NUM_CLOCKS; ++i )
  84. {
  85. lights[ LIGHT_NUMERATOR_1 + i ].value = (int)params[ CLOCK_NUMERATOR_1 + i ].value;
  86. lights[ LIGHT_DENOMINATOR_1 + i ].value = (int)params[ CLOCK_DENOMINATOR_1 + i ].value;
  87. }
  88. }
  89. };
  90. struct PolyGnomeWidget : ModuleWidget {
  91. PolyGnomeWidget( PolyGnome *module);
  92. };
  93. PolyGnomeWidget::PolyGnomeWidget( PolyGnome *module ) : ModuleWidget( module )
  94. {
  95. box.size = Vec( SCREW_WIDTH * 14, RACK_HEIGHT );
  96. BaconBackground *bg = new BaconBackground( box.size, "PolyGnome" );
  97. addChild( bg->wrappedInFramebuffer());
  98. bg->addLabel( Vec( 17, 45 ), "Clock", 13, NVG_ALIGN_LEFT | NVG_ALIGN_TOP );
  99. addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 55, 40 ),
  100. module,
  101. PolyGnome::CLOCK_PARAM,
  102. -2.0f, 6.0f, 2.0f ) );
  103. addInput( Port::create< PJ301MPort >( Vec( 85, 40 ),
  104. Port::INPUT,
  105. module,
  106. PolyGnome::CLOCK_INPUT ) );
  107. for( size_t i=0; i<= NUM_CLOCKS; ++i )
  108. {
  109. Vec outP = Vec( box.size.x - 45, 100 + 48 * i );
  110. if( i == 0 )
  111. {
  112. bg->addLabel( Vec( 17, outP.y + 21 ), "Unit (1/1) clock", 13, NVG_ALIGN_LEFT | NVG_ALIGN_BOTTOM );
  113. }
  114. else
  115. {
  116. int yoff = 2;
  117. // knob light knob light
  118. addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 17, outP.y + yoff ),
  119. module,
  120. PolyGnome::CLOCK_NUMERATOR_1 + (i-1),
  121. 1, 30, 1 ) );
  122. addChild( MultiDigitSevenSegmentLight< BlueLight, 2, 2 >::create( Vec( 48, outP.y + yoff ),
  123. module,
  124. PolyGnome::LIGHT_NUMERATOR_1 + (i-1) ) );
  125. int mv = 47 + 20 + 14 - 16;
  126. addParam( ParamWidget::create< RoundSmallBlackKnob >( Vec( 16 + mv, outP.y + yoff ),
  127. module,
  128. PolyGnome::CLOCK_DENOMINATOR_1 + (i-1),
  129. 1, 16, 1 ) );
  130. addChild( MultiDigitSevenSegmentLight< BlueLight, 2, 2 >::create( Vec( 47 + mv, outP.y + yoff ),
  131. module,
  132. PolyGnome::LIGHT_DENOMINATOR_1 + (i-1) ) );
  133. }
  134. addOutput( Port::create< PJ301MPort >( outP,
  135. Port::OUTPUT,
  136. module,
  137. PolyGnome::CLOCK_GATE_0 + i ) );
  138. bg->addRoundedBorder( Vec( 12, outP.y - 4 ), Vec( box.size.x - 24, 36 ) );
  139. }
  140. }
  141. } // namespace rack_plugin_BaconMusic
  142. using namespace rack_plugin_BaconMusic;
  143. RACK_PLUGIN_MODEL_INIT(BaconMusic, PolyGnome) {
  144. Model *modelPolyGnome = Model::create<PolyGnome, PolyGnomeWidget>("Bacon Music", "PolyGnome", "PolyGnome", CLOCK_TAG );
  145. return modelPolyGnome;
  146. }