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.

178 lines
5.0KB

  1. #include "Fundamental.hpp"
  2. #include "dsp/functions.hpp"
  3. #include "dsp/resampler.hpp"
  4. #include "dsp/ode.hpp"
  5. inline float clip(float x) {
  6. return tanhf(x);
  7. }
  8. struct LadderFilter {
  9. float omega0;
  10. float resonance = 1.0f;
  11. float state[4];
  12. float input;
  13. float lowpass;
  14. float highpass;
  15. LadderFilter() {
  16. reset();
  17. setCutoff(0.f);
  18. }
  19. void reset() {
  20. for (int i = 0; i < 4; i++) {
  21. state[i] = 0.f;
  22. }
  23. }
  24. void setCutoff(float cutoff) {
  25. omega0 = 2.f*M_PI * cutoff;
  26. }
  27. void process(float input, float dt) {
  28. ode::stepRK4(0.f, dt, state, 4, [&](float t, const float x[], float dxdt[]) {
  29. float inputc = clip(input - resonance * x[3]);
  30. float yc0 = clip(x[0]);
  31. float yc1 = clip(x[1]);
  32. float yc2 = clip(x[2]);
  33. float yc3 = clip(x[3]);
  34. dxdt[0] = omega0 * (inputc - yc0);
  35. dxdt[1] = omega0 * (yc0 - yc1);
  36. dxdt[2] = omega0 * (yc1 - yc2);
  37. dxdt[3] = omega0 * (yc2 - yc3);
  38. });
  39. lowpass = state[3];
  40. // TODO This is incorrect when `resonance > 0`. Is the math wrong?
  41. highpass = clip((input - resonance*state[3]) - 4 * state[0] + 6*state[1] - 4*state[2] + state[3]);
  42. }
  43. };
  44. static const int UPSAMPLE = 2;
  45. struct VCF : Module {
  46. enum ParamIds {
  47. FREQ_PARAM,
  48. FINE_PARAM,
  49. RES_PARAM,
  50. FREQ_CV_PARAM,
  51. DRIVE_PARAM,
  52. NUM_PARAMS
  53. };
  54. enum InputIds {
  55. FREQ_INPUT,
  56. RES_INPUT,
  57. DRIVE_INPUT,
  58. IN_INPUT,
  59. NUM_INPUTS
  60. };
  61. enum OutputIds {
  62. LPF_OUTPUT,
  63. HPF_OUTPUT,
  64. NUM_OUTPUTS
  65. };
  66. LadderFilter filter;
  67. // Upsampler<UPSAMPLE, 8> inputUpsampler;
  68. // Decimator<UPSAMPLE, 8> lowpassDecimator;
  69. // Decimator<UPSAMPLE, 8> highpassDecimator;
  70. VCF() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
  71. void onReset() override {
  72. filter.reset();
  73. }
  74. void step() override {
  75. if (!outputs[LPF_OUTPUT].active && !outputs[HPF_OUTPUT].active) {
  76. outputs[LPF_OUTPUT].value = 0.f;
  77. outputs[HPF_OUTPUT].value = 0.f;
  78. return;
  79. }
  80. float input = inputs[IN_INPUT].value / 5.f;
  81. float drive = clamp(params[DRIVE_PARAM].value + inputs[DRIVE_INPUT].value / 10.f, 0.f, 1.f);
  82. float gain = powf(1.f + drive, 5);
  83. input *= gain;
  84. // Add -60dB noise to bootstrap self-oscillation
  85. input += 1e-6f * (2.f * randomUniform() - 1.f);
  86. // Set resonance
  87. float res = clamp(params[RES_PARAM].value + inputs[RES_INPUT].value / 10.f, 0.f, 1.f);
  88. filter.resonance = powf(res, 2) * 10.f;
  89. // Set cutoff frequency
  90. float pitch = 0.f;
  91. if (inputs[FREQ_INPUT].active)
  92. pitch += inputs[FREQ_INPUT].value * quadraticBipolar(params[FREQ_CV_PARAM].value);
  93. pitch += params[FREQ_PARAM].value * 10.f - 5.f;
  94. pitch += quadraticBipolar(params[FINE_PARAM].value * 2.f - 1.f) * 7.f / 12.f;
  95. float cutoff = 261.626f * powf(2.f, pitch);
  96. cutoff = clamp(cutoff, 1.f, 8000.f);
  97. filter.setCutoff(cutoff);
  98. /*
  99. // Process sample
  100. float dt = engineGetSampleTime() / UPSAMPLE;
  101. float inputBuf[UPSAMPLE];
  102. float lowpassBuf[UPSAMPLE];
  103. float highpassBuf[UPSAMPLE];
  104. inputUpsampler.process(input, inputBuf);
  105. for (int i = 0; i < UPSAMPLE; i++) {
  106. // Step the filter
  107. filter.process(inputBuf[i], dt);
  108. lowpassBuf[i] = filter.lowpass;
  109. highpassBuf[i] = filter.highpass;
  110. }
  111. // Set outputs
  112. if (outputs[LPF_OUTPUT].active) {
  113. outputs[LPF_OUTPUT].value = 5.f * lowpassDecimator.process(lowpassBuf);
  114. }
  115. if (outputs[HPF_OUTPUT].active) {
  116. outputs[HPF_OUTPUT].value = 5.f * highpassDecimator.process(highpassBuf);
  117. }
  118. */
  119. filter.process(input, engineGetSampleTime());
  120. outputs[LPF_OUTPUT].value = 5.f * filter.lowpass;
  121. outputs[HPF_OUTPUT].value = 5.f * filter.highpass;
  122. }
  123. };
  124. struct VCFWidget : ModuleWidget {
  125. VCFWidget(VCF *module) : ModuleWidget(module) {
  126. setPanel(SVG::load(assetPlugin(plugin, "res/VCF.svg")));
  127. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  128. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  129. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  130. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  131. addParam(ParamWidget::create<RoundHugeBlackKnob>(Vec(33, 61), module, VCF::FREQ_PARAM, 0.f, 1.f, 0.5f));
  132. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(12, 143), module, VCF::FINE_PARAM, 0.f, 1.f, 0.5f));
  133. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(71, 143), module, VCF::RES_PARAM, 0.f, 1.f, 0.f));
  134. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(12, 208), module, VCF::FREQ_CV_PARAM, -1.f, 1.f, 0.f));
  135. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(71, 208), module, VCF::DRIVE_PARAM, 0.f, 1.f, 0.f));
  136. addInput(Port::create<PJ301MPort>(Vec(10, 276), Port::INPUT, module, VCF::FREQ_INPUT));
  137. addInput(Port::create<PJ301MPort>(Vec(48, 276), Port::INPUT, module, VCF::RES_INPUT));
  138. addInput(Port::create<PJ301MPort>(Vec(85, 276), Port::INPUT, module, VCF::DRIVE_INPUT));
  139. addInput(Port::create<PJ301MPort>(Vec(10, 320), Port::INPUT, module, VCF::IN_INPUT));
  140. addOutput(Port::create<PJ301MPort>(Vec(48, 320), Port::OUTPUT, module, VCF::LPF_OUTPUT));
  141. addOutput(Port::create<PJ301MPort>(Vec(85, 320), Port::OUTPUT, module, VCF::HPF_OUTPUT));
  142. }
  143. };
  144. RACK_PLUGIN_MODEL_INIT(Fundamental, VCF) {
  145. Model *modelVCF = Model::create<VCF, VCFWidget>("Fundamental", "VCF", "VCF", FILTER_TAG);
  146. return modelVCF;
  147. }