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.

229 lines
6.2KB

  1. #include "plugin.hpp"
  2. using simd::float_4;
  3. template <typename T>
  4. static T clip(T x) {
  5. // return std::tanh(x);
  6. // Pade approximant of tanh
  7. x = simd::clamp(x, -3.f, 3.f);
  8. return x * (27 + x * x) / (27 + 9 * x * x);
  9. }
  10. template <typename T>
  11. struct LadderFilter {
  12. T omega0;
  13. T resonance = 1;
  14. T state[4];
  15. T input;
  16. LadderFilter() {
  17. reset();
  18. setCutoff(0);
  19. }
  20. void reset() {
  21. for (int i = 0; i < 4; i++) {
  22. state[i] = 0;
  23. }
  24. }
  25. void setCutoff(T cutoff) {
  26. omega0 = 2*M_PI * cutoff;
  27. }
  28. void process(T input, T dt) {
  29. dsp::stepRK4(T(0), dt, state, 4, [&](T t, const T x[], T dxdt[]) {
  30. T inputc = clip(input - resonance * x[3]);
  31. T yc0 = clip(x[0]);
  32. T yc1 = clip(x[1]);
  33. T yc2 = clip(x[2]);
  34. T yc3 = clip(x[3]);
  35. dxdt[0] = omega0 * (inputc - yc0);
  36. dxdt[1] = omega0 * (yc0 - yc1);
  37. dxdt[2] = omega0 * (yc1 - yc2);
  38. dxdt[3] = omega0 * (yc2 - yc3);
  39. });
  40. this->input = input;
  41. }
  42. T lowpass() {
  43. return state[3];
  44. }
  45. T highpass() {
  46. // TODO This is incorrect when `resonance > 0`. Is the math wrong?
  47. return clip((input - resonance*state[3]) - 4 * state[0] + 6*state[1] - 4*state[2] + state[3]);
  48. }
  49. };
  50. static const int UPSAMPLE = 2;
  51. struct VCF : Module {
  52. enum ParamIds {
  53. FREQ_PARAM,
  54. FINE_PARAM,
  55. RES_PARAM,
  56. FREQ_CV_PARAM,
  57. DRIVE_PARAM,
  58. NUM_PARAMS
  59. };
  60. enum InputIds {
  61. FREQ_INPUT,
  62. RES_INPUT,
  63. DRIVE_INPUT,
  64. IN_INPUT,
  65. NUM_INPUTS
  66. };
  67. enum OutputIds {
  68. LPF_OUTPUT,
  69. HPF_OUTPUT,
  70. NUM_OUTPUTS
  71. };
  72. LadderFilter<float_4> filters[4];
  73. // Upsampler<UPSAMPLE, 8> inputUpsampler;
  74. // Decimator<UPSAMPLE, 8> lowpassDecimator;
  75. // Decimator<UPSAMPLE, 8> highpassDecimator;
  76. VCF() {
  77. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
  78. configParam(FREQ_PARAM, 0.f, 1.f, 0.5f, "Frequency");
  79. configParam(FINE_PARAM, 0.f, 1.f, 0.5f, "Fine frequency");
  80. configParam(RES_PARAM, 0.f, 1.f, 0.f, "Resonance");
  81. configParam(FREQ_CV_PARAM, -1.f, 1.f, 0.f, "Frequency modulation");
  82. configParam(DRIVE_PARAM, 0.f, 1.f, 0.f, "Drive");
  83. }
  84. void onReset() override {
  85. for (int i = 0; i < 4; i++)
  86. filters[i].reset();
  87. }
  88. void process(const ProcessArgs &args) override {
  89. if (!outputs[LPF_OUTPUT].isConnected() && !outputs[HPF_OUTPUT].isConnected()) {
  90. return;
  91. }
  92. float driveParam = params[DRIVE_PARAM].getValue();
  93. float resParam = params[RES_PARAM].getValue();
  94. float fineParam = params[FINE_PARAM].getValue();
  95. fineParam = dsp::quadraticBipolar(fineParam * 2.f - 1.f) * 7.f / 12.f;
  96. float freqCvParam = params[FREQ_CV_PARAM].getValue();
  97. freqCvParam = dsp::quadraticBipolar(freqCvParam);
  98. float freqParam = params[FREQ_PARAM].getValue();
  99. freqParam = freqParam * 10.f - 5.f;
  100. int channels = std::max(1, inputs[IN_INPUT].getChannels());
  101. for (int c = 0; c < channels; c += 4) {
  102. auto *filter = &filters[c / 4];
  103. float_4 input = float_4::load(inputs[IN_INPUT].getVoltages(c)) / 5.f;
  104. // Drive gain
  105. float_4 drive = driveParam;
  106. if (inputs[DRIVE_INPUT].isPolyphonic())
  107. drive += float_4::load(inputs[DRIVE_INPUT].getVoltages(c)) / 10.f;
  108. else
  109. drive += inputs[DRIVE_INPUT].getVoltage() / 10.f;
  110. drive = clamp(drive, 0.f, 1.f);
  111. float_4 gain = simd::pow(1.f + drive, 5);
  112. input *= gain;
  113. // Add -120dB noise to bootstrap self-oscillation
  114. input += 1e-6f * (2.f * random::uniform() - 1.f);
  115. // Set resonance
  116. float_4 resonance = resParam;
  117. if (inputs[RES_INPUT].isPolyphonic())
  118. resonance += float_4::load(inputs[RES_INPUT].getVoltages(c)) / 10.f;
  119. else
  120. resonance += inputs[RES_INPUT].getVoltage() / 10.f;
  121. resonance = clamp(resonance, 0.f, 1.f);
  122. filter->resonance = simd::pow(resonance, 2) * 10.f;
  123. // Get pitch
  124. float_4 pitch = 0.f;
  125. if (inputs[FREQ_INPUT].isPolyphonic())
  126. pitch += float_4::load(inputs[FREQ_INPUT].getVoltages(c)) * freqCvParam;
  127. else
  128. pitch += inputs[FREQ_INPUT].getVoltage() * freqCvParam;
  129. pitch += freqParam;
  130. pitch += fineParam;
  131. // Set cutoff
  132. float_4 cutoff = dsp::FREQ_C4 * simd::pow(2.f, pitch);
  133. cutoff = clamp(cutoff, 1.f, 8000.f);
  134. filter->setCutoff(cutoff);
  135. // Set outputs
  136. filter->process(input, args.sampleTime);
  137. float_4 lowpass = 5.f * filter->lowpass();
  138. lowpass.store(outputs[LPF_OUTPUT].getVoltages(c));
  139. float_4 highpass = 5.f * filter->highpass();
  140. highpass.store(outputs[HPF_OUTPUT].getVoltages(c));
  141. }
  142. outputs[LPF_OUTPUT].setChannels(channels);
  143. outputs[HPF_OUTPUT].setChannels(channels);
  144. /*
  145. // Process sample
  146. float dt = args.sampleTime / UPSAMPLE;
  147. float inputBuf[UPSAMPLE];
  148. float lowpassBuf[UPSAMPLE];
  149. float highpassBuf[UPSAMPLE];
  150. inputUpsampler.process(input, inputBuf);
  151. for (int i = 0; i < UPSAMPLE; i++) {
  152. // Step the filter
  153. filter.process(inputBuf[i], dt);
  154. lowpassBuf[i] = filter.lowpass;
  155. highpassBuf[i] = filter.highpass;
  156. }
  157. // Set outputs
  158. if (outputs[LPF_OUTPUT].isConnected()) {
  159. outputs[LPF_OUTPUT].setVoltage(5.f * lowpassDecimator.process(lowpassBuf));
  160. }
  161. if (outputs[HPF_OUTPUT].isConnected()) {
  162. outputs[HPF_OUTPUT].setVoltage(5.f * highpassDecimator.process(highpassBuf));
  163. }
  164. */
  165. }
  166. };
  167. struct VCFWidget : ModuleWidget {
  168. VCFWidget(VCF *module) {
  169. setModule(module);
  170. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/VCF.svg")));
  171. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  172. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
  173. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  174. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
  175. addParam(createParam<RoundHugeBlackKnob>(Vec(33, 61), module, VCF::FREQ_PARAM));
  176. addParam(createParam<RoundLargeBlackKnob>(Vec(12, 143), module, VCF::FINE_PARAM));
  177. addParam(createParam<RoundLargeBlackKnob>(Vec(71, 143), module, VCF::RES_PARAM));
  178. addParam(createParam<RoundLargeBlackKnob>(Vec(12, 208), module, VCF::FREQ_CV_PARAM));
  179. addParam(createParam<RoundLargeBlackKnob>(Vec(71, 208), module, VCF::DRIVE_PARAM));
  180. addInput(createInput<PJ301MPort>(Vec(10, 276), module, VCF::FREQ_INPUT));
  181. addInput(createInput<PJ301MPort>(Vec(48, 276), module, VCF::RES_INPUT));
  182. addInput(createInput<PJ301MPort>(Vec(85, 276), module, VCF::DRIVE_INPUT));
  183. addInput(createInput<PJ301MPort>(Vec(10, 320), module, VCF::IN_INPUT));
  184. addOutput(createOutput<PJ301MPort>(Vec(48, 320), module, VCF::LPF_OUTPUT));
  185. addOutput(createOutput<PJ301MPort>(Vec(85, 320), module, VCF::HPF_OUTPUT));
  186. }
  187. };
  188. Model *modelVCF = createModel<VCF, VCFWidget>("VCF");