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.

167 lines
5.1KB

  1. #include "Befaco.hpp"
  2. #include "dsp/minblep.hpp"
  3. #include "dsp/filter.hpp"
  4. struct EvenVCO : Module {
  5. enum ParamIds {
  6. OCTAVE_PARAM,
  7. TUNE_PARAM,
  8. PWM_PARAM,
  9. NUM_PARAMS
  10. };
  11. enum InputIds {
  12. PITCH1_INPUT,
  13. PITCH2_INPUT,
  14. FM_INPUT,
  15. SYNC_INPUT,
  16. PWM_INPUT,
  17. NUM_INPUTS
  18. };
  19. enum OutputIds {
  20. TRI_OUTPUT,
  21. SINE_OUTPUT,
  22. EVEN_OUTPUT,
  23. SAW_OUTPUT,
  24. SQUARE_OUTPUT,
  25. NUM_OUTPUTS
  26. };
  27. float phase = 0.0;
  28. /** The value of the last sync input */
  29. float sync = 0.0;
  30. /** The outputs */
  31. float tri = 0.0;
  32. /** Whether we are past the pulse width already */
  33. bool halfPhase = false;
  34. MinBLEP<16> triSquareMinBLEP;
  35. MinBLEP<16> triMinBLEP;
  36. MinBLEP<16> sineMinBLEP;
  37. MinBLEP<16> doubleSawMinBLEP;
  38. MinBLEP<16> sawMinBLEP;
  39. MinBLEP<16> squareMinBLEP;
  40. RCFilter triFilter;
  41. EvenVCO();
  42. void step() override;
  43. };
  44. EvenVCO::EvenVCO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
  45. triSquareMinBLEP.minblep = minblep_16_32;
  46. triSquareMinBLEP.oversample = 32;
  47. triMinBLEP.minblep = minblep_16_32;
  48. triMinBLEP.oversample = 32;
  49. sineMinBLEP.minblep = minblep_16_32;
  50. sineMinBLEP.oversample = 32;
  51. doubleSawMinBLEP.minblep = minblep_16_32;
  52. doubleSawMinBLEP.oversample = 32;
  53. sawMinBLEP.minblep = minblep_16_32;
  54. sawMinBLEP.oversample = 32;
  55. squareMinBLEP.minblep = minblep_16_32;
  56. squareMinBLEP.oversample = 32;
  57. }
  58. void EvenVCO::step() {
  59. // Compute frequency, pitch is 1V/oct
  60. float pitch = 1.0 + roundf(params[OCTAVE_PARAM].value) + params[TUNE_PARAM].value / 12.0;
  61. pitch += inputs[PITCH1_INPUT].value + inputs[PITCH2_INPUT].value;
  62. pitch += inputs[FM_INPUT].value / 4.0;
  63. float freq = 261.626 * powf(2.0, pitch);
  64. freq = clamp(freq, 0.0f, 20000.0f);
  65. // Pulse width
  66. float pw = params[PWM_PARAM].value + inputs[PWM_INPUT].value / 5.0;
  67. const float minPw = 0.05;
  68. pw = rescale(clamp(pw, -1.0f, 1.0f), -1.0f, 1.0f, minPw, 1.0f - minPw);
  69. // Advance phase
  70. float deltaPhase = clamp(freq * engineGetSampleTime(), 1e-6f, 0.5f);
  71. float oldPhase = phase;
  72. phase += deltaPhase;
  73. if (oldPhase < 0.5 && phase >= 0.5) {
  74. float crossing = -(phase - 0.5) / deltaPhase;
  75. triSquareMinBLEP.jump(crossing, 2.0);
  76. doubleSawMinBLEP.jump(crossing, -2.0);
  77. }
  78. if (!halfPhase && phase >= pw) {
  79. float crossing = -(phase - pw) / deltaPhase;
  80. squareMinBLEP.jump(crossing, 2.0);
  81. halfPhase = true;
  82. }
  83. // Reset phase if at end of cycle
  84. if (phase >= 1.0) {
  85. phase -= 1.0;
  86. float crossing = -phase / deltaPhase;
  87. triSquareMinBLEP.jump(crossing, -2.0);
  88. doubleSawMinBLEP.jump(crossing, -2.0);
  89. squareMinBLEP.jump(crossing, -2.0);
  90. sawMinBLEP.jump(crossing, -2.0);
  91. halfPhase = false;
  92. }
  93. // Outputs
  94. float triSquare = (phase < 0.5) ? -1.0 : 1.0;
  95. triSquare += triSquareMinBLEP.shift();
  96. // Integrate square for triangle
  97. tri += 4.0 * triSquare * freq * engineGetSampleTime();
  98. tri *= (1.0 - 40.0 * engineGetSampleTime());
  99. float sine = -cosf(2*M_PI * phase);
  100. float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5));
  101. doubleSaw += doubleSawMinBLEP.shift();
  102. float even = 0.55 * (doubleSaw + 1.27 * sine);
  103. float saw = -1.0 + 2.0*phase;
  104. saw += sawMinBLEP.shift();
  105. float square = (phase < pw) ? -1.0 : 1.0;
  106. square += squareMinBLEP.shift();
  107. // Set outputs
  108. outputs[TRI_OUTPUT].value = 5.0*tri;
  109. outputs[SINE_OUTPUT].value = 5.0*sine;
  110. outputs[EVEN_OUTPUT].value = 5.0*even;
  111. outputs[SAW_OUTPUT].value = 5.0*saw;
  112. outputs[SQUARE_OUTPUT].value = 5.0*square;
  113. }
  114. struct EvenVCOWidget : ModuleWidget {
  115. EvenVCOWidget(EvenVCO *module) : ModuleWidget(module) {
  116. setPanel(SVG::load(assetPlugin(plugin, "res/EvenVCO.svg")));
  117. addChild(Widget::create<Knurlie>(Vec(15, 0)));
  118. addChild(Widget::create<Knurlie>(Vec(15, 365)));
  119. addChild(Widget::create<Knurlie>(Vec(15*6, 0)));
  120. addChild(Widget::create<Knurlie>(Vec(15*6, 365)));
  121. addParam(ParamWidget::create<BefacoBigSnapKnob>(Vec(22, 32), module, EvenVCO::OCTAVE_PARAM, -5.0, 4.0, 0.0));
  122. addParam(ParamWidget::create<BefacoTinyKnob>(Vec(73, 131), module, EvenVCO::TUNE_PARAM, -7.0, 7.0, 0.0));
  123. addParam(ParamWidget::create<Davies1900hRedKnob>(Vec(16, 230), module, EvenVCO::PWM_PARAM, -1.0, 1.0, 0.0));
  124. addInput(Port::create<PJ301MPort>(Vec(8, 120), Port::INPUT, module, EvenVCO::PITCH1_INPUT));
  125. addInput(Port::create<PJ301MPort>(Vec(19, 157), Port::INPUT, module, EvenVCO::PITCH2_INPUT));
  126. addInput(Port::create<PJ301MPort>(Vec(48, 183), Port::INPUT, module, EvenVCO::FM_INPUT));
  127. addInput(Port::create<PJ301MPort>(Vec(86, 189), Port::INPUT, module, EvenVCO::SYNC_INPUT));
  128. addInput(Port::create<PJ301MPort>(Vec(72, 236), Port::INPUT, module, EvenVCO::PWM_INPUT));
  129. addOutput(Port::create<PJ301MPort>(Vec(10, 283), Port::OUTPUT, module, EvenVCO::TRI_OUTPUT));
  130. addOutput(Port::create<PJ301MPort>(Vec(87, 283), Port::OUTPUT, module, EvenVCO::SINE_OUTPUT));
  131. addOutput(Port::create<PJ301MPort>(Vec(48, 306), Port::OUTPUT, module, EvenVCO::EVEN_OUTPUT));
  132. addOutput(Port::create<PJ301MPort>(Vec(10, 327), Port::OUTPUT, module, EvenVCO::SAW_OUTPUT));
  133. addOutput(Port::create<PJ301MPort>(Vec(87, 327), Port::OUTPUT, module, EvenVCO::SQUARE_OUTPUT));
  134. }
  135. };
  136. RACK_PLUGIN_MODEL_INIT(Befaco, EvenVCO) {
  137. Model *modelEvenVCO = Model::create<EvenVCO, EvenVCOWidget>("Befaco", "EvenVCO", "EvenVCO", OSCILLATOR_TAG);
  138. return modelEvenVCO;
  139. }