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.

219 lines
7.0KB

  1. #include "VCO.hpp"
  2. #include "dsp/pitch.hpp"
  3. void VCO::onReset() {
  4. _syncTrigger.reset();
  5. _modulationStep = modulationSteps;
  6. }
  7. void VCO::onSampleRateChange() {
  8. setSampleRate(engineGetSampleRate());
  9. _modulationStep = modulationSteps;
  10. }
  11. void VCO::step() {
  12. lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].value > 0.5f;
  13. _fmLinearMode = params[FM_TYPE_PARAM].value < 0.5f;
  14. if (!(
  15. outputs[SQUARE_OUTPUT].active ||
  16. outputs[SAW_OUTPUT].active ||
  17. outputs[TRIANGLE_OUTPUT].active ||
  18. outputs[SINE_OUTPUT].active
  19. )) {
  20. return;
  21. }
  22. ++_modulationStep;
  23. if (_modulationStep >= modulationSteps) {
  24. _modulationStep = 0;
  25. _baseVOct = params[FREQUENCY_PARAM].value;
  26. _baseVOct += params[FINE_PARAM].value / 12.0f;
  27. if (inputs[PITCH_INPUT].active) {
  28. _baseVOct += clamp(inputs[PITCH_INPUT].value, -5.0f, 5.0f);
  29. }
  30. if (_slowMode) {
  31. _baseVOct -= 7.0f;
  32. }
  33. _baseHz = cvToFrequency(_baseVOct);
  34. float pw = params[PW_PARAM].value;
  35. if (inputs[PW_INPUT].active) {
  36. pw *= clamp(inputs[PW_INPUT].value / 5.0f, -1.0f, 1.0f);
  37. }
  38. pw *= 1.0f - 2.0f * _square.minPulseWidth;
  39. pw *= 0.5f;
  40. pw += 0.5f;
  41. _square.setPulseWidth(_squarePulseWidthSL.next(pw));
  42. _fmDepth = params[FM_PARAM].value;
  43. }
  44. if (_syncTrigger.next(inputs[SYNC_INPUT].value)) {
  45. _phasor.resetPhase();
  46. }
  47. float frequency = _baseHz;
  48. Phasor::phase_delta_t phaseOffset = 0;
  49. if (inputs[FM_INPUT].active && _fmDepth > 0.01f) {
  50. float fm = inputs[FM_INPUT].value * _fmDepth;
  51. if (_fmLinearMode) {
  52. phaseOffset = Phasor::radiansToPhase(2.0f * fm);
  53. }
  54. else {
  55. frequency = cvToFrequency(_baseVOct + fm);
  56. }
  57. }
  58. setFrequency(frequency);
  59. const float oversampleWidth = 100.0f;
  60. float mix, oMix;
  61. if (frequency > _oversampleThreshold) {
  62. if (frequency > _oversampleThreshold + oversampleWidth) {
  63. mix = 0.0f;
  64. oMix = 1.0f;
  65. }
  66. else {
  67. oMix = (frequency - _oversampleThreshold) / oversampleWidth;
  68. mix = 1.0f - oMix;
  69. }
  70. }
  71. else {
  72. mix = 1.0f;
  73. oMix = 0.0f;
  74. }
  75. float squareOut = 0.0f;
  76. float sawOut = 0.0f;
  77. float triangleOut = 0.0f;
  78. if (oMix > 0.0f) {
  79. for (int i = 0; i < oversample; ++i) {
  80. _phasor.advancePhase();
  81. if (outputs[SQUARE_OUTPUT].active) {
  82. _squareBuffer[i] = _square.nextFromPhasor(_phasor, phaseOffset);
  83. }
  84. if (outputs[SAW_OUTPUT].active) {
  85. _sawBuffer[i] = _saw.nextFromPhasor(_phasor, phaseOffset);
  86. }
  87. if (outputs[TRIANGLE_OUTPUT].active) {
  88. _triangleBuffer[i] = _triangle.nextFromPhasor(_phasor, phaseOffset);
  89. }
  90. }
  91. if (outputs[SQUARE_OUTPUT].active) {
  92. squareOut += oMix * amplitude * _squareDecimator.next(_squareBuffer);
  93. }
  94. if (outputs[SAW_OUTPUT].active) {
  95. sawOut += oMix * amplitude * _sawDecimator.next(_sawBuffer);
  96. }
  97. if (outputs[TRIANGLE_OUTPUT].active) {
  98. triangleOut += oMix * amplitude * _triangleDecimator.next(_triangleBuffer);
  99. }
  100. }
  101. else {
  102. _phasor.advancePhase(oversample);
  103. }
  104. if (mix > 0.0f) {
  105. if (outputs[SQUARE_OUTPUT].active) {
  106. squareOut += mix * amplitude * _square.nextFromPhasor(_phasor, phaseOffset);
  107. }
  108. if (outputs[SAW_OUTPUT].active) {
  109. sawOut += mix * amplitude * _saw.nextFromPhasor(_phasor, phaseOffset);
  110. }
  111. if (outputs[TRIANGLE_OUTPUT].active) {
  112. triangleOut += mix * amplitude * _triangle.nextFromPhasor(_phasor, phaseOffset);
  113. }
  114. }
  115. outputs[SQUARE_OUTPUT].value = squareOut;
  116. outputs[SAW_OUTPUT].value = sawOut;
  117. outputs[TRIANGLE_OUTPUT].value = triangleOut;
  118. outputs[SINE_OUTPUT].value = outputs[SINE_OUTPUT].active ? (amplitude * _sine.nextFromPhasor(_phasor, phaseOffset)) : 0.0f;
  119. }
  120. void VCO::setSampleRate(float sampleRate) {
  121. _oversampleThreshold = 0.06f * sampleRate;
  122. _phasor.setSampleRate(sampleRate);
  123. _square.setSampleRate(sampleRate);
  124. _saw.setSampleRate(sampleRate);
  125. _squareDecimator.setParams(sampleRate, oversample);
  126. _sawDecimator.setParams(sampleRate, oversample);
  127. _triangleDecimator.setParams(sampleRate, oversample);
  128. _squarePulseWidthSL.setParams(sampleRate, 0.1f, 2.0f);
  129. }
  130. void VCO::setFrequency(float frequency) {
  131. if (_frequency != frequency && frequency < 0.475f * _phasor._sampleRate) {
  132. _frequency = frequency;
  133. _phasor.setFrequency(_frequency / (float)oversample);
  134. _square.setFrequency(_frequency);
  135. _saw.setFrequency(_frequency);
  136. }
  137. }
  138. struct VCOWidget : ModuleWidget {
  139. static constexpr int hp = 10;
  140. VCOWidget(VCO* module) : ModuleWidget(module) {
  141. box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
  142. {
  143. SVGPanel *panel = new SVGPanel();
  144. panel->box.size = box.size;
  145. panel->setBackground(SVG::load(assetPlugin(plugin, "res/VCO.svg")));
  146. addChild(panel);
  147. }
  148. addChild(Widget::create<ScrewSilver>(Vec(0, 0)));
  149. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 15, 0)));
  150. addChild(Widget::create<ScrewSilver>(Vec(0, 365)));
  151. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 15, 365)));
  152. // generated by svg_widgets.rb
  153. auto frequencyParamPosition = Vec(41.0, 45.0);
  154. auto fineParamPosition = Vec(48.0, 153.0);
  155. auto slowParamPosition = Vec(122.0, 157.2);
  156. auto pwParamPosition = Vec(62.0, 188.0);
  157. auto fmParamPosition = Vec(62.0, 230.0);
  158. auto fmTypeParamPosition = Vec(100.5, 231.5);
  159. auto pwInputPosition = Vec(15.0, 274.0);
  160. auto fmInputPosition = Vec(47.0, 274.0);
  161. auto pitchInputPosition = Vec(15.0, 318.0);
  162. auto syncInputPosition = Vec(47.0, 318.0);
  163. auto squareOutputPosition = Vec(79.0, 274.0);
  164. auto sawOutputPosition = Vec(111.0, 274.0);
  165. auto triangleOutputPosition = Vec(79.0, 318.0);
  166. auto sineOutputPosition = Vec(111.0, 318.0);
  167. auto slowLightPosition = Vec(82.0, 158.5);
  168. // end generated by svg_widgets.rb
  169. addParam(ParamWidget::create<Knob68>(frequencyParamPosition, module, VCO::FREQUENCY_PARAM, -3.0, 6.0, 0.0));
  170. addParam(ParamWidget::create<Knob16>(fineParamPosition, module, VCO::FINE_PARAM, -1.0, 1.0, 0.0));
  171. addParam(ParamWidget::create<StatefulButton9>(slowParamPosition, module, VCO::SLOW_PARAM, 0.0, 1.0, 0.0));
  172. addParam(ParamWidget::create<Knob26>(pwParamPosition, module, VCO::PW_PARAM, -1.0, 1.0, 0.0));
  173. addParam(ParamWidget::create<Knob26>(fmParamPosition, module, VCO::FM_PARAM, 0.0, 1.0, 0.0));
  174. addParam(ParamWidget::create<SliderSwitch2State14>(fmTypeParamPosition, module, VCO::FM_TYPE_PARAM, 0.0, 1.0, 1.0));
  175. addInput(Port::create<Port24>(pitchInputPosition, Port::INPUT, module, VCO::PITCH_INPUT));
  176. addInput(Port::create<Port24>(syncInputPosition, Port::INPUT, module, VCO::SYNC_INPUT));
  177. addInput(Port::create<Port24>(pwInputPosition, Port::INPUT, module, VCO::PW_INPUT));
  178. addInput(Port::create<Port24>(fmInputPosition, Port::INPUT, module, VCO::FM_INPUT));
  179. addOutput(Port::create<Port24>(squareOutputPosition, Port::OUTPUT, module, VCO::SQUARE_OUTPUT));
  180. addOutput(Port::create<Port24>(sawOutputPosition, Port::OUTPUT, module, VCO::SAW_OUTPUT));
  181. addOutput(Port::create<Port24>(triangleOutputPosition, Port::OUTPUT, module, VCO::TRIANGLE_OUTPUT));
  182. addOutput(Port::create<Port24>(sineOutputPosition, Port::OUTPUT, module, VCO::SINE_OUTPUT));
  183. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(slowLightPosition, module, VCO::SLOW_LIGHT));
  184. }
  185. };
  186. RACK_PLUGIN_MODEL_INIT(Bogaudio, VCO) {
  187. Model *modelVCO = createModel<VCO, VCOWidget>("Bogaudio-VCO", "VCO", "oscillator", OSCILLATOR_TAG);
  188. return modelVCO;
  189. }