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.

166 lines
5.2KB

  1. #include "plugin.hpp"
  2. #define MAX(a,b) (a>b)?a:b
  3. struct EvenVCO : Module {
  4. enum ParamIds {
  5. OCTAVE_PARAM,
  6. TUNE_PARAM,
  7. PWM_PARAM,
  8. NUM_PARAMS
  9. };
  10. enum InputIds {
  11. PITCH1_INPUT,
  12. PITCH2_INPUT,
  13. FM_INPUT,
  14. SYNC_INPUT,
  15. PWM_INPUT,
  16. NUM_INPUTS
  17. };
  18. enum OutputIds {
  19. TRI_OUTPUT,
  20. SINE_OUTPUT,
  21. EVEN_OUTPUT,
  22. SAW_OUTPUT,
  23. SQUARE_OUTPUT,
  24. NUM_OUTPUTS
  25. };
  26. float phase = 0.0;
  27. /** The value of the last sync input */
  28. float sync = 0.0;
  29. /** The outputs */
  30. float tri = 0.0;
  31. /** Whether we are past the pulse width already */
  32. bool halfPhase = false;
  33. dsp::MinBlepGenerator<16, 32> triSquareMinBlep;
  34. dsp::MinBlepGenerator<16, 32> triMinBlep;
  35. dsp::MinBlepGenerator<16, 32> sineMinBlep;
  36. dsp::MinBlepGenerator<16, 32> doubleSawMinBlep;
  37. dsp::MinBlepGenerator<16, 32> sawMinBlep;
  38. dsp::MinBlepGenerator<16, 32> squareMinBlep;
  39. dsp::RCFilter triFilter;
  40. EvenVCO() {
  41. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
  42. configParam(OCTAVE_PARAM, -5.0, 4.0, 0.0, "Octave", "'", 0.5);
  43. configParam(TUNE_PARAM, -7.0, 7.0, 0.0, "Tune", " semitones");
  44. configParam(PWM_PARAM, -1.0, 1.0, 0.0, "Pulse width");
  45. }
  46. void process(const ProcessArgs &args) override {
  47. // Compute frequency, pitch is 1V/oct
  48. int channels_pitch1 = inputs[PITCH1_INPUT].getChannels();
  49. int channels_pitch2 = inputs[PITCH2_INPUT].getChannels();
  50. int channels_fm = inputs[FM_INPUT].getChannels();
  51. int channels = 1;
  52. channels = MAX(channels, channels_pitch1);
  53. channels = MAX(channels, channels_pitch2);
  54. // channels = MAX(channels, channels_fm);
  55. float pitch_0 = 1.f + std::round(params[OCTAVE_PARAM].getValue()) + params[TUNE_PARAM].getValue() / 12.f;
  56. pitch += inputs[PITCH1_INPUT].getVoltage() + inputs[PITCH2_INPUT].getVoltage();
  57. pitch += inputs[FM_INPUT].getVoltage() / 4.f;
  58. float freq = dsp::FREQ_C4 * std::pow(2.f, pitch);
  59. freq = clamp(freq, 0.f, 20000.f);
  60. // Pulse width
  61. float pw = params[PWM_PARAM].getValue() + inputs[PWM_INPUT].getVoltage() / 5.f;
  62. const float minPw = 0.05;
  63. pw = rescale(clamp(pw, -1.f, 1.f), -1.f, 1.f, minPw, 1.f - minPw);
  64. // Advance phase
  65. float deltaPhase = clamp(freq * args.sampleTime, 1e-6f, 0.5f);
  66. float oldPhase = phase;
  67. phase += deltaPhase;
  68. if (oldPhase < 0.5 && phase >= 0.5) {
  69. float crossing = -(phase - 0.5) / deltaPhase;
  70. triSquareMinBlep.insertDiscontinuity(crossing, 2.f);
  71. doubleSawMinBlep.insertDiscontinuity(crossing, -2.f);
  72. }
  73. if (!halfPhase && phase >= pw) {
  74. float crossing = -(phase - pw) / deltaPhase;
  75. squareMinBlep.insertDiscontinuity(crossing, 2.f);
  76. halfPhase = true;
  77. }
  78. // Reset phase if at end of cycle
  79. if (phase >= 1.f) {
  80. phase -= 1.f;
  81. float crossing = -phase / deltaPhase;
  82. triSquareMinBlep.insertDiscontinuity(crossing, -2.f);
  83. doubleSawMinBlep.insertDiscontinuity(crossing, -2.f);
  84. squareMinBlep.insertDiscontinuity(crossing, -2.f);
  85. sawMinBlep.insertDiscontinuity(crossing, -2.f);
  86. halfPhase = false;
  87. }
  88. // Outputs
  89. float triSquare = (phase < 0.5) ? -1.f : 1.f;
  90. triSquare += triSquareMinBlep.process();
  91. // Integrate square for triangle
  92. tri += 4.f * triSquare * freq * args.sampleTime;
  93. tri *= (1.f - 40.f * args.sampleTime);
  94. float sine = -std::cos(2*M_PI * phase);
  95. float doubleSaw = (phase < 0.5) ? (-1.f + 4.f*phase) : (-1.f + 4.f*(phase - 0.5));
  96. doubleSaw += doubleSawMinBlep.process();
  97. float even = 0.55 * (doubleSaw + 1.27 * sine);
  98. float saw = -1.f + 2.f*phase;
  99. saw += sawMinBlep.process();
  100. float square = (phase < pw) ? -1.f : 1.f;
  101. square += squareMinBlep.process();
  102. // Set outputs
  103. outputs[TRI_OUTPUT].setVoltage(5.f*tri);
  104. outputs[SINE_OUTPUT].setVoltage(5.f*sine);
  105. outputs[EVEN_OUTPUT].setVoltage(5.f*even);
  106. outputs[SAW_OUTPUT].setVoltage(5.f*saw);
  107. outputs[SQUARE_OUTPUT].setVoltage(5.f*square);
  108. }
  109. };
  110. struct EvenVCOWidget : ModuleWidget {
  111. EvenVCOWidget(EvenVCO *module) {
  112. setModule(module);
  113. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/EvenVCO.svg")));
  114. addChild(createWidget<Knurlie>(Vec(15, 0)));
  115. addChild(createWidget<Knurlie>(Vec(15, 365)));
  116. addChild(createWidget<Knurlie>(Vec(15*6, 0)));
  117. addChild(createWidget<Knurlie>(Vec(15*6, 365)));
  118. addParam(createParam<BefacoBigSnapKnob>(Vec(22, 32), module, EvenVCO::OCTAVE_PARAM));
  119. addParam(createParam<BefacoTinyKnob>(Vec(73, 131), module, EvenVCO::TUNE_PARAM));
  120. addParam(createParam<Davies1900hRedKnob>(Vec(16, 230), module, EvenVCO::PWM_PARAM));
  121. addInput(createInput<PJ301MPort>(Vec(8, 120), module, EvenVCO::PITCH1_INPUT));
  122. addInput(createInput<PJ301MPort>(Vec(19, 157), module, EvenVCO::PITCH2_INPUT));
  123. addInput(createInput<PJ301MPort>(Vec(48, 183), module, EvenVCO::FM_INPUT));
  124. addInput(createInput<PJ301MPort>(Vec(86, 189), module, EvenVCO::SYNC_INPUT));
  125. addInput(createInput<PJ301MPort>(Vec(72, 236), module, EvenVCO::PWM_INPUT));
  126. addOutput(createOutput<PJ301MPort>(Vec(10, 283), module, EvenVCO::TRI_OUTPUT));
  127. addOutput(createOutput<PJ301MPort>(Vec(87, 283), module, EvenVCO::SINE_OUTPUT));
  128. addOutput(createOutput<PJ301MPort>(Vec(48, 306), module, EvenVCO::EVEN_OUTPUT));
  129. addOutput(createOutput<PJ301MPort>(Vec(10, 327), module, EvenVCO::SAW_OUTPUT));
  130. addOutput(createOutput<PJ301MPort>(Vec(87, 327), module, EvenVCO::SQUARE_OUTPUT));
  131. }
  132. };
  133. Model *modelEvenVCO = createModel<EvenVCO, EvenVCOWidget>("EvenVCO");