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.

248 lines
8.4KB

  1. #include "plugin.hpp"
  2. using simd::float_4;
  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_4 phase[4] = {};
  27. float_4 tri[4] = {};
  28. /** The value of the last sync input */
  29. float sync = 0.0;
  30. /** The outputs */
  31. /** Whether we are past the pulse width already */
  32. bool halfPhase[PORT_MAX_CHANNELS] = {};
  33. dsp::MinBlepGenerator<16, 32> triSquareMinBlep[PORT_MAX_CHANNELS];
  34. dsp::MinBlepGenerator<16, 32> triMinBlep[PORT_MAX_CHANNELS];
  35. dsp::MinBlepGenerator<16, 32> sineMinBlep[PORT_MAX_CHANNELS];
  36. dsp::MinBlepGenerator<16, 32> doubleSawMinBlep[PORT_MAX_CHANNELS];
  37. dsp::MinBlepGenerator<16, 32> sawMinBlep[PORT_MAX_CHANNELS];
  38. dsp::MinBlepGenerator<16, 32> squareMinBlep[PORT_MAX_CHANNELS];
  39. EvenVCO() {
  40. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
  41. configParam(OCTAVE_PARAM, -5.0, 4.0, 0.0, "Octave", "'", 0.5);
  42. getParamQuantity(OCTAVE_PARAM)->snapEnabled = true;
  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. configInput(PITCH1_INPUT, "Pitch 1");
  46. configInput(PITCH2_INPUT, "Pitch 2");
  47. configInput(FM_INPUT, "FM");
  48. configInput(SYNC_INPUT, "Sync (not implemented)");
  49. configInput(PWM_INPUT, "Pulse Width Modulation");
  50. configOutput(TRI_OUTPUT, "Triangle");
  51. configOutput(SINE_OUTPUT, "Sine");
  52. configOutput(EVEN_OUTPUT, "Even");
  53. configOutput(SAW_OUTPUT, "Sawtooth");
  54. configOutput(SQUARE_OUTPUT, "Square");
  55. }
  56. void process(const ProcessArgs& args) override {
  57. int channels_pitch1 = inputs[PITCH1_INPUT].getChannels();
  58. int channels_pitch2 = inputs[PITCH2_INPUT].getChannels();
  59. int channels = 1;
  60. channels = std::max(channels, channels_pitch1);
  61. channels = std::max(channels, channels_pitch2);
  62. float pitch_0 = 1.f + std::round(params[OCTAVE_PARAM].getValue()) + params[TUNE_PARAM].getValue() / 12.f;
  63. // Compute frequency, pitch is 1V/oct
  64. float_4 pitch[4] = {};
  65. for (int c = 0; c < channels; c += 4)
  66. pitch[c / 4] = pitch_0;
  67. if (inputs[PITCH1_INPUT].isConnected()) {
  68. for (int c = 0; c < channels; c += 4)
  69. pitch[c / 4] += inputs[PITCH1_INPUT].getPolyVoltageSimd<float_4>(c);
  70. }
  71. if (inputs[PITCH2_INPUT].isConnected()) {
  72. for (int c = 0; c < channels; c += 4)
  73. pitch[c / 4] += inputs[PITCH2_INPUT].getPolyVoltageSimd<float_4>(c);
  74. }
  75. if (inputs[FM_INPUT].isConnected()) {
  76. for (int c = 0; c < channels; c += 4)
  77. pitch[c / 4] += inputs[FM_INPUT].getPolyVoltageSimd<float_4>(c) / 4.f;
  78. }
  79. float_4 freq[4] = {};
  80. for (int c = 0; c < channels; c += 4) {
  81. freq[c / 4] = dsp::FREQ_C4 * simd::pow(2.f, pitch[c / 4]);
  82. freq[c / 4] = clamp(freq[c / 4], 0.f, 20000.f);
  83. }
  84. // Pulse width
  85. float_4 pw[4] = {};
  86. for (int c = 0; c < channels; c += 4)
  87. pw[c / 4] = params[PWM_PARAM].getValue();
  88. if (inputs[PWM_INPUT].isConnected()) {
  89. for (int c = 0; c < channels; c += 4)
  90. pw[c / 4] += inputs[PWM_INPUT].getPolyVoltageSimd<float_4>(c) / 5.f;
  91. }
  92. float_4 deltaPhase[4] = {};
  93. float_4 oldPhase[4] = {};
  94. for (int c = 0; c < channels; c += 4) {
  95. pw[c / 4] = rescale(clamp(pw[c / 4], -1.0f, 1.0f), -1.0f, 1.0f, 0.05f, 1.0f - 0.05f);
  96. // Advance phase
  97. deltaPhase[c / 4] = clamp(freq[c / 4] * args.sampleTime, 1e-6f, 0.5f);
  98. oldPhase[c / 4] = phase[c / 4];
  99. phase[c / 4] += deltaPhase[c / 4];
  100. }
  101. // the next block can't be done with SIMD instructions, but should at least be completed with
  102. // blocks of 4 (otherwise popping artfifacts are generated from invalid phase/oldPhase/deltaPhase)
  103. const int channelsRoundedUpNearestFour = (1 + (channels - 1) / 4) * 4;
  104. for (int c = 0; c < channelsRoundedUpNearestFour; c++) {
  105. if (oldPhase[c / 4].s[c % 4] < 0.5 && phase[c / 4].s[c % 4] >= 0.5) {
  106. float crossing = -(phase[c / 4].s[c % 4] - 0.5) / deltaPhase[c / 4].s[c % 4];
  107. triSquareMinBlep[c].insertDiscontinuity(crossing, 2.f);
  108. doubleSawMinBlep[c].insertDiscontinuity(crossing, -2.f);
  109. }
  110. if (!halfPhase[c] && phase[c / 4].s[c % 4] >= pw[c / 4].s[c % 4]) {
  111. float crossing = -(phase[c / 4].s[c % 4] - pw[c / 4].s[c % 4]) / deltaPhase[c / 4].s[c % 4];
  112. squareMinBlep[c].insertDiscontinuity(crossing, 2.f);
  113. halfPhase[c] = true;
  114. }
  115. // Reset phase if at end of cycle
  116. if (phase[c / 4].s[c % 4] >= 1.f) {
  117. phase[c / 4].s[c % 4] -= 1.f;
  118. float crossing = -phase[c / 4].s[c % 4] / deltaPhase[c / 4].s[c % 4];
  119. triSquareMinBlep[c].insertDiscontinuity(crossing, -2.f);
  120. doubleSawMinBlep[c].insertDiscontinuity(crossing, -2.f);
  121. squareMinBlep[c].insertDiscontinuity(crossing, -2.f);
  122. sawMinBlep[c].insertDiscontinuity(crossing, -2.f);
  123. halfPhase[c] = false;
  124. }
  125. }
  126. float_4 triSquareMinBlepOut[4] = {};
  127. float_4 doubleSawMinBlepOut[4] = {};
  128. float_4 sawMinBlepOut[4] = {};
  129. float_4 squareMinBlepOut[4] = {};
  130. float_4 triSquare[4] = {};
  131. float_4 sine[4] = {};
  132. float_4 doubleSaw[4] = {};
  133. float_4 even[4] = {};
  134. float_4 saw[4] = {};
  135. float_4 square[4] = {};
  136. float_4 triOut[4] = {};
  137. for (int c = 0; c < channelsRoundedUpNearestFour; c++) {
  138. triSquareMinBlepOut[c / 4].s[c % 4] = triSquareMinBlep[c].process();
  139. doubleSawMinBlepOut[c / 4].s[c % 4] = doubleSawMinBlep[c].process();
  140. sawMinBlepOut[c / 4].s[c % 4] = sawMinBlep[c].process();
  141. squareMinBlepOut[c / 4].s[c % 4] = squareMinBlep[c].process();
  142. }
  143. for (int c = 0; c < channels; c += 4) {
  144. triSquare[c / 4] = simd::ifelse((phase[c / 4] < 0.5f), -1.f, +1.f);
  145. triSquare[c / 4] += triSquareMinBlepOut[c / 4];
  146. // Integrate square for triangle
  147. tri[c / 4] += (4.f * triSquare[c / 4]) * (freq[c / 4] * args.sampleTime);
  148. tri[c / 4] *= (1.f - 40.f * args.sampleTime);
  149. triOut[c / 4] = 5.f * tri[c / 4];
  150. sine[c / 4] = 5.f * simd::cos(2 * M_PI * phase[c / 4]);
  151. doubleSaw[c / 4] = simd::ifelse((phase[c / 4] < 0.5), (-1.f + 4.f * phase[c / 4]), (-1.f + 4.f * (phase[c / 4] - 0.5f)));
  152. doubleSaw[c / 4] += doubleSawMinBlepOut[c / 4];
  153. doubleSaw[c / 4] *= 5.f;
  154. even[c / 4] = 0.55 * (doubleSaw[c / 4] + 1.27 * sine[c / 4]);
  155. saw[c / 4] = -1.f + 2.f * phase[c / 4];
  156. saw[c / 4] += sawMinBlepOut[c / 4];
  157. saw[c / 4] *= 5.f;
  158. square[c / 4] = simd::ifelse((phase[c / 4] < pw[c / 4]), -1.f, +1.f);
  159. square[c / 4] += squareMinBlepOut[c / 4];
  160. square[c / 4] *= 5.f;
  161. // Set outputs
  162. outputs[TRI_OUTPUT].setVoltageSimd(triOut[c / 4], c);
  163. outputs[SINE_OUTPUT].setVoltageSimd(sine[c / 4], c);
  164. outputs[EVEN_OUTPUT].setVoltageSimd(even[c / 4], c);
  165. outputs[SAW_OUTPUT].setVoltageSimd(saw[c / 4], c);
  166. outputs[SQUARE_OUTPUT].setVoltageSimd(square[c / 4], c);
  167. }
  168. // Outputs
  169. outputs[TRI_OUTPUT].setChannels(channels);
  170. outputs[SINE_OUTPUT].setChannels(channels);
  171. outputs[EVEN_OUTPUT].setChannels(channels);
  172. outputs[SAW_OUTPUT].setChannels(channels);
  173. outputs[SQUARE_OUTPUT].setChannels(channels);
  174. }
  175. };
  176. struct EvenVCOWidget : ModuleWidget {
  177. EvenVCOWidget(EvenVCO* module) {
  178. setModule(module);
  179. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/panels/EvenVCO.svg")));
  180. addChild(createWidget<Knurlie>(Vec(15, 0)));
  181. addChild(createWidget<Knurlie>(Vec(15, 365)));
  182. addChild(createWidget<Knurlie>(Vec(15 * 6, 0)));
  183. addChild(createWidget<Knurlie>(Vec(15 * 6, 365)));
  184. addParam(createParam<BefacoBigKnob>(Vec(22, 32), module, EvenVCO::OCTAVE_PARAM));
  185. addParam(createParam<BefacoTinyKnob>(Vec(73, 131), module, EvenVCO::TUNE_PARAM));
  186. addParam(createParam<Davies1900hRedKnob>(Vec(16, 230), module, EvenVCO::PWM_PARAM));
  187. addInput(createInput<BefacoInputPort>(Vec(8, 120), module, EvenVCO::PITCH1_INPUT));
  188. addInput(createInput<BefacoInputPort>(Vec(19, 157), module, EvenVCO::PITCH2_INPUT));
  189. addInput(createInput<BefacoInputPort>(Vec(48, 183), module, EvenVCO::FM_INPUT));
  190. addInput(createInput<BefacoInputPort>(Vec(86, 189), module, EvenVCO::SYNC_INPUT));
  191. addInput(createInput<BefacoInputPort>(Vec(72, 236), module, EvenVCO::PWM_INPUT));
  192. addOutput(createOutput<BefacoOutputPort>(Vec(10, 283), module, EvenVCO::TRI_OUTPUT));
  193. addOutput(createOutput<BefacoOutputPort>(Vec(87, 283), module, EvenVCO::SINE_OUTPUT));
  194. addOutput(createOutput<BefacoOutputPort>(Vec(48, 306), module, EvenVCO::EVEN_OUTPUT));
  195. addOutput(createOutput<BefacoOutputPort>(Vec(10, 327), module, EvenVCO::SAW_OUTPUT));
  196. addOutput(createOutput<BefacoOutputPort>(Vec(87, 327), module, EvenVCO::SQUARE_OUTPUT));
  197. }
  198. };
  199. Model* modelEvenVCO = createModel<EvenVCO, EvenVCOWidget>("EvenVCO");