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.

182 lines
6.1KB

  1. #include <string.h>
  2. #include "Befaco.hpp"
  3. #include "dsp/functions.hpp"
  4. #include "dsp/samplerate.hpp"
  5. #include "dsp/ringbuffer.hpp"
  6. #include "dsp/filter.hpp"
  7. #include "dsp/fir.hpp"
  8. #include "pffft.h"
  9. BINARY(src_SpringReverbIR_pcm);
  10. static const size_t BLOCK_SIZE = 1024;
  11. struct SpringReverb : Module {
  12. enum ParamIds {
  13. WET_PARAM,
  14. LEVEL1_PARAM,
  15. LEVEL2_PARAM,
  16. HPF_PARAM,
  17. NUM_PARAMS
  18. };
  19. enum InputIds {
  20. CV1_INPUT,
  21. CV2_INPUT,
  22. IN1_INPUT,
  23. IN2_INPUT,
  24. MIX_CV_INPUT,
  25. NUM_INPUTS
  26. };
  27. enum OutputIds {
  28. MIX_OUTPUT,
  29. WET_OUTPUT,
  30. NUM_OUTPUTS
  31. };
  32. enum LightIds {
  33. PEAK_LIGHT,
  34. VU1_LIGHT,
  35. NUM_LIGHTS = VU1_LIGHT + 7
  36. };
  37. RealTimeConvolver *convolver = NULL;
  38. SampleRateConverter<1> inputSrc;
  39. SampleRateConverter<1> outputSrc;
  40. DoubleRingBuffer<Frame<1>, 16*BLOCK_SIZE> inputBuffer;
  41. DoubleRingBuffer<Frame<1>, 16*BLOCK_SIZE> outputBuffer;
  42. RCFilter dryFilter;
  43. PeakFilter vuFilter;
  44. PeakFilter lightFilter;
  45. SpringReverb();
  46. ~SpringReverb();
  47. void step() override;
  48. };
  49. SpringReverb::SpringReverb() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  50. convolver = new RealTimeConvolver(BLOCK_SIZE);
  51. const float *kernel = (const float*) BINARY_START(src_SpringReverbIR_pcm);
  52. size_t kernelLen = BINARY_SIZE(src_SpringReverbIR_pcm) / sizeof(float);
  53. convolver->setKernel(kernel, kernelLen);
  54. }
  55. SpringReverb::~SpringReverb() {
  56. delete convolver;
  57. }
  58. void SpringReverb::step() {
  59. float in1 = inputs[IN1_INPUT].value;
  60. float in2 = inputs[IN2_INPUT].value;
  61. const float levelScale = 0.030;
  62. const float levelBase = 25.0;
  63. float level1 = levelScale * exponentialBipolar(levelBase, params[LEVEL1_PARAM].value) * inputs[CV1_INPUT].normalize(10.0) / 10.0;
  64. float level2 = levelScale * exponentialBipolar(levelBase, params[LEVEL2_PARAM].value) * inputs[CV2_INPUT].normalize(10.0) / 10.0;
  65. float dry = in1 * level1 + in2 * level2;
  66. // HPF on dry
  67. float dryCutoff = 200.0 * powf(20.0, params[HPF_PARAM].value) * engineGetSampleTime();
  68. dryFilter.setCutoff(dryCutoff);
  69. dryFilter.process(dry);
  70. // Add dry to input buffer
  71. if (!inputBuffer.full()) {
  72. Frame<1> inputFrame;
  73. inputFrame.samples[0] = dryFilter.highpass();
  74. inputBuffer.push(inputFrame);
  75. }
  76. if (outputBuffer.empty()) {
  77. float input[BLOCK_SIZE] = {};
  78. float output[BLOCK_SIZE];
  79. // Convert input buffer
  80. {
  81. inputSrc.setRates(engineGetSampleRate(), 48000);
  82. int inLen = inputBuffer.size();
  83. int outLen = BLOCK_SIZE;
  84. inputSrc.process(inputBuffer.startData(), &inLen, (Frame<1>*) input, &outLen);
  85. inputBuffer.startIncr(inLen);
  86. }
  87. // Convolve block
  88. convolver->processBlock(input, output);
  89. // Convert output buffer
  90. {
  91. outputSrc.setRates(48000, engineGetSampleRate());
  92. int inLen = BLOCK_SIZE;
  93. int outLen = outputBuffer.capacity();
  94. outputSrc.process((Frame<1>*) output, &inLen, outputBuffer.endData(), &outLen);
  95. outputBuffer.endIncr(outLen);
  96. }
  97. }
  98. // Set output
  99. if (outputBuffer.empty())
  100. return;
  101. float wet = outputBuffer.shift().samples[0];
  102. float balance = clamp(params[WET_PARAM].value + inputs[MIX_CV_INPUT].value / 10.0f, 0.0f, 1.0f);
  103. float mix = crossfade(in1, wet, balance);
  104. outputs[WET_OUTPUT].value = clamp(wet, -10.0f, 10.0f);
  105. outputs[MIX_OUTPUT].value = clamp(mix, -10.0f, 10.0f);
  106. // Set lights
  107. float lightRate = 5.0 * engineGetSampleTime();
  108. vuFilter.setRate(lightRate);
  109. vuFilter.process(fabsf(wet));
  110. lightFilter.setRate(lightRate);
  111. lightFilter.process(fabsf(dry*50.0));
  112. float vuValue = vuFilter.peak();
  113. for (int i = 0; i < 7; i++) {
  114. float light = powf(1.413, i) * vuValue / 10.0 - 1.0;
  115. lights[VU1_LIGHT + i].value = clamp(light, 0.0f, 1.0f);
  116. }
  117. lights[PEAK_LIGHT].value = lightFilter.peak();
  118. }
  119. struct SpringReverbWidget : ModuleWidget {
  120. SpringReverbWidget(SpringReverb *module) : ModuleWidget(module) {
  121. setPanel(SVG::load(assetPlugin(plugin, "res/SpringReverb.svg")));
  122. addChild(Widget::create<Knurlie>(Vec(15, 0)));
  123. addChild(Widget::create<Knurlie>(Vec(15, 365)));
  124. addChild(Widget::create<Knurlie>(Vec(15*6, 0)));
  125. addChild(Widget::create<Knurlie>(Vec(15*6, 365)));
  126. addParam(ParamWidget::create<BefacoBigKnob>(Vec(22, 29), module, SpringReverb::WET_PARAM, 0.0, 1.0, 0.5));
  127. addParam(ParamWidget::create<BefacoSlidePot>(Vec(12, 116), module, SpringReverb::LEVEL1_PARAM, 0.0, 1.0, 0.0));
  128. addParam(ParamWidget::create<BefacoSlidePot>(Vec(93, 116), module, SpringReverb::LEVEL2_PARAM, 0.0, 1.0, 0.0));
  129. addParam(ParamWidget::create<Davies1900hWhiteKnob>(Vec(42, 210), module, SpringReverb::HPF_PARAM, 0.0, 1.0, 0.5));
  130. addInput(Port::create<PJ301MPort>(Vec(7, 243), Port::INPUT, module, SpringReverb::CV1_INPUT));
  131. addInput(Port::create<PJ301MPort>(Vec(88, 243), Port::INPUT, module, SpringReverb::CV2_INPUT));
  132. addInput(Port::create<PJ301MPort>(Vec(27, 281), Port::INPUT, module, SpringReverb::IN1_INPUT));
  133. addInput(Port::create<PJ301MPort>(Vec(67, 281), Port::INPUT, module, SpringReverb::IN2_INPUT));
  134. addOutput(Port::create<PJ301MPort>(Vec(7, 317), Port::OUTPUT, module, SpringReverb::MIX_OUTPUT));
  135. addInput(Port::create<PJ301MPort>(Vec(47, 324), Port::INPUT, module, SpringReverb::MIX_CV_INPUT));
  136. addOutput(Port::create<PJ301MPort>(Vec(88, 317), Port::OUTPUT, module, SpringReverb::WET_OUTPUT));
  137. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(55, 269), module, SpringReverb::PEAK_LIGHT));
  138. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(55, 113), module, SpringReverb::VU1_LIGHT + 0));
  139. addChild(ModuleLightWidget::create<MediumLight<YellowLight>>(Vec(55, 126), module, SpringReverb::VU1_LIGHT + 1));
  140. addChild(ModuleLightWidget::create<MediumLight<YellowLight>>(Vec(55, 138), module, SpringReverb::VU1_LIGHT + 2));
  141. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(55, 150), module, SpringReverb::VU1_LIGHT + 3));
  142. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(55, 163), module, SpringReverb::VU1_LIGHT + 4));
  143. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(55, 175), module, SpringReverb::VU1_LIGHT + 5));
  144. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(55, 188), module, SpringReverb::VU1_LIGHT + 6));
  145. }
  146. };
  147. Model *modelSpringReverb = Model::create<SpringReverb, SpringReverbWidget>("Befaco", "SpringReverb", "Spring Reverb", REVERB_TAG, DUAL_TAG);