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.

312 lines
9.9KB

  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 "pffft.h"
  8. float *springReverbIR = NULL;
  9. int springReverbIRLen;
  10. void springReverbInit() {
  11. if(NULL == springReverbIR)
  12. {
  13. std::string irFilename = assetPlugin(plugin, "res/SpringReverbIR.pcm");
  14. FILE *f = fopen(irFilename.c_str(), "rb");
  15. assert(f);
  16. fseek(f, 0, SEEK_END);
  17. int size = ftell(f);
  18. fseek(f, 0, SEEK_SET);
  19. springReverbIRLen = size / sizeof(float);
  20. springReverbIR = new float[springReverbIRLen];
  21. fread(springReverbIR, sizeof(float), springReverbIRLen, f);
  22. fclose(f);
  23. // TODO Add springReverbDestroy() function once plugins have destroy() callbacks
  24. }
  25. }
  26. namespace rack_plugin_Befaco {
  27. // clashes with rack/include/dsp/fir.h
  28. struct RealTimeConvolver {
  29. // `kernelBlocks` number of contiguous FFT blocks of size `blockSize`
  30. // indexed by [i * blockSize*2 + j]
  31. float *kernelFfts = NULL;
  32. float *inputFfts = NULL;
  33. float *outputTail = NULL;
  34. float *tmpBlock = NULL;
  35. size_t blockSize;
  36. size_t kernelBlocks = 0;
  37. size_t inputPos = 0;
  38. // kiss_fftr_cfg fft_cfg;
  39. // kiss_fftr_cfg ifft_cfg;
  40. PFFFT_Setup *pffft;
  41. /** blocksize should be >=32 and a power of 2 */
  42. RealTimeConvolver(size_t blockSize) {
  43. this->blockSize = blockSize;
  44. pffft = pffft_new_setup(blockSize*2, PFFFT_REAL);
  45. outputTail = new float[blockSize]();
  46. tmpBlock = new float[blockSize*2]();
  47. }
  48. ~RealTimeConvolver() {
  49. clear();
  50. delete[] outputTail;
  51. delete[] tmpBlock;
  52. pffft_destroy_setup(pffft);
  53. }
  54. void clear() {
  55. if (kernelFfts) {
  56. pffft_aligned_free(kernelFfts);
  57. kernelFfts = NULL;
  58. }
  59. if (inputFfts) {
  60. pffft_aligned_free(inputFfts);
  61. inputFfts = NULL;
  62. }
  63. kernelBlocks = 0;
  64. inputPos = 0;
  65. }
  66. void setKernel(const float *kernel, size_t length) {
  67. clear();
  68. if (!kernel || length == 0)
  69. return;
  70. // Round up to the nearest factor blockSize
  71. kernelBlocks = (length - 1) / blockSize + 1;
  72. // Allocate blocks
  73. kernelFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize*2 * kernelBlocks);
  74. inputFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize*2 * kernelBlocks);
  75. memset(inputFfts, 0, sizeof(float) * blockSize*2 * kernelBlocks);
  76. for (size_t i = 0; i < kernelBlocks; i++) {
  77. // Pad each block with zeros
  78. memset(tmpBlock, 0, sizeof(float) * blockSize*2);
  79. size_t len = min((int) blockSize, (int) (length - i*blockSize));
  80. memcpy(tmpBlock, &kernel[i*blockSize], sizeof(float)*len);
  81. // Compute fft
  82. pffft_transform(pffft, tmpBlock, &kernelFfts[blockSize*2 * i], NULL, PFFFT_FORWARD);
  83. }
  84. }
  85. /** Applies reverb to input
  86. input and output must be size blockSize
  87. */
  88. void processBlock(const float *input, float *output) {
  89. if (kernelBlocks == 0) {
  90. memset(output, 0, sizeof(float) * blockSize);
  91. return;
  92. }
  93. // Step input position
  94. inputPos = (inputPos + 1) % kernelBlocks;
  95. // Pad block with zeros
  96. memset(tmpBlock, 0, sizeof(float) * blockSize*2);
  97. memcpy(tmpBlock, input, sizeof(float) * blockSize);
  98. // Compute input fft
  99. pffft_transform(pffft, tmpBlock, &inputFfts[blockSize*2 * inputPos], NULL, PFFFT_FORWARD);
  100. // Create output fft
  101. memset(tmpBlock, 0, sizeof(float) * blockSize*2);
  102. // convolve input fft by kernel fft
  103. // Note: This is the CPU bottleneck loop
  104. for (size_t i = 0; i < kernelBlocks; i++) {
  105. size_t pos = (inputPos - i + kernelBlocks) % kernelBlocks;
  106. pffft_zconvolve_accumulate(pffft, &kernelFfts[blockSize*2 * i], &inputFfts[blockSize*2 * pos], tmpBlock, 1.0);
  107. }
  108. // Compute output
  109. pffft_transform(pffft, tmpBlock, tmpBlock, NULL, PFFFT_BACKWARD);
  110. // Add block tail from last output block
  111. for (size_t i = 0; i < blockSize; i++) {
  112. tmpBlock[i] += outputTail[i];
  113. }
  114. // Copy output block to output
  115. for (size_t i = 0; i < blockSize; i++) {
  116. // Scale based on FFT
  117. output[i] = tmpBlock[i] / blockSize;
  118. }
  119. // Set tail
  120. for (size_t i = 0; i < blockSize; i++) {
  121. outputTail[i] = tmpBlock[i + blockSize];
  122. }
  123. }
  124. };
  125. } // namespace rack_plugin_Befaco
  126. #define BLOCKSIZE 1024
  127. struct SpringReverb : Module {
  128. enum ParamIds {
  129. WET_PARAM,
  130. LEVEL1_PARAM,
  131. LEVEL2_PARAM,
  132. HPF_PARAM,
  133. NUM_PARAMS
  134. };
  135. enum InputIds {
  136. CV1_INPUT,
  137. CV2_INPUT,
  138. IN1_INPUT,
  139. IN2_INPUT,
  140. MIX_CV_INPUT,
  141. NUM_INPUTS
  142. };
  143. enum OutputIds {
  144. MIX_OUTPUT,
  145. WET_OUTPUT,
  146. NUM_OUTPUTS
  147. };
  148. enum LightIds {
  149. PEAK_LIGHT,
  150. VU1_LIGHT,
  151. NUM_LIGHTS = VU1_LIGHT + 7
  152. };
  153. rack_plugin_Befaco::RealTimeConvolver *convolver = NULL;
  154. SampleRateConverter<1> inputSrc;
  155. SampleRateConverter<1> outputSrc;
  156. DoubleRingBuffer<Frame<1>, 16*BLOCKSIZE> inputBuffer;
  157. DoubleRingBuffer<Frame<1>, 16*BLOCKSIZE> outputBuffer;
  158. RCFilter dryFilter;
  159. PeakFilter vuFilter;
  160. PeakFilter lightFilter;
  161. SpringReverb();
  162. ~SpringReverb();
  163. void step() override;
  164. };
  165. SpringReverb::SpringReverb() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  166. convolver = new rack_plugin_Befaco::RealTimeConvolver(BLOCKSIZE);
  167. convolver->setKernel(springReverbIR, springReverbIRLen);
  168. }
  169. SpringReverb::~SpringReverb() {
  170. delete convolver;
  171. }
  172. void SpringReverb::step() {
  173. float in1 = inputs[IN1_INPUT].value;
  174. float in2 = inputs[IN2_INPUT].value;
  175. const float levelScale = 0.030;
  176. const float levelBase = 25.0;
  177. float level1 = levelScale * exponentialBipolar(levelBase, params[LEVEL1_PARAM].value) * inputs[CV1_INPUT].normalize(10.0) / 10.0;
  178. float level2 = levelScale * exponentialBipolar(levelBase, params[LEVEL2_PARAM].value) * inputs[CV2_INPUT].normalize(10.0) / 10.0;
  179. float dry = in1 * level1 + in2 * level2;
  180. // HPF on dry
  181. float dryCutoff = 200.0 * powf(20.0, params[HPF_PARAM].value) * engineGetSampleTime();
  182. dryFilter.setCutoff(dryCutoff);
  183. dryFilter.process(dry);
  184. // Add dry to input buffer
  185. if (!inputBuffer.full()) {
  186. Frame<1> inputFrame;
  187. inputFrame.samples[0] = dryFilter.highpass();
  188. inputBuffer.push(inputFrame);
  189. }
  190. if (outputBuffer.empty()) {
  191. float input[BLOCKSIZE] = {};
  192. float output[BLOCKSIZE];
  193. // Convert input buffer
  194. {
  195. inputSrc.setRates(engineGetSampleRate(), 48000);
  196. int inLen = inputBuffer.size();
  197. int outLen = BLOCKSIZE;
  198. inputSrc.process(inputBuffer.startData(), &inLen, (Frame<1>*) input, &outLen);
  199. inputBuffer.startIncr(inLen);
  200. }
  201. // Convolve block
  202. convolver->processBlock(input, output);
  203. // Convert output buffer
  204. {
  205. outputSrc.setRates(48000, engineGetSampleRate());
  206. int inLen = BLOCKSIZE;
  207. int outLen = outputBuffer.capacity();
  208. outputSrc.process((Frame<1>*) output, &inLen, outputBuffer.endData(), &outLen);
  209. outputBuffer.endIncr(outLen);
  210. }
  211. }
  212. // Set output
  213. if (outputBuffer.empty())
  214. return;
  215. float wet = outputBuffer.shift().samples[0];
  216. float balance = clamp(params[WET_PARAM].value + inputs[MIX_CV_INPUT].value / 10.0f, 0.0f, 1.0f);
  217. float mix = crossfade(in1, wet, balance);
  218. outputs[WET_OUTPUT].value = clamp(wet, -10.0f, 10.0f);
  219. outputs[MIX_OUTPUT].value = clamp(mix, -10.0f, 10.0f);
  220. // Set lights
  221. float lightRate = 5.0 * engineGetSampleTime();
  222. vuFilter.setRate(lightRate);
  223. vuFilter.process(fabsf(wet));
  224. lightFilter.setRate(lightRate);
  225. lightFilter.process(fabsf(dry*50.0));
  226. float vuValue = vuFilter.peak();
  227. for (int i = 0; i < 7; i++) {
  228. float light = powf(1.413, i) * vuValue / 10.0 - 1.0;
  229. lights[VU1_LIGHT + i].value = clamp(light, 0.0f, 1.0f);
  230. }
  231. lights[PEAK_LIGHT].value = lightFilter.peak();
  232. }
  233. struct SpringReverbWidget : ModuleWidget {
  234. SpringReverbWidget(SpringReverb *module) : ModuleWidget(module) {
  235. setPanel(SVG::load(assetPlugin(plugin, "res/SpringReverb.svg")));
  236. addChild(Widget::create<Knurlie>(Vec(15, 0)));
  237. addChild(Widget::create<Knurlie>(Vec(15, 365)));
  238. addChild(Widget::create<Knurlie>(Vec(15*6, 0)));
  239. addChild(Widget::create<Knurlie>(Vec(15*6, 365)));
  240. addParam(ParamWidget::create<BefacoBigKnob>(Vec(22, 29), module, SpringReverb::WET_PARAM, 0.0, 1.0, 0.5));
  241. addParam(ParamWidget::create<BefacoSlidePot>(Vec(12, 116), module, SpringReverb::LEVEL1_PARAM, 0.0, 1.0, 0.0));
  242. addParam(ParamWidget::create<BefacoSlidePot>(Vec(93, 116), module, SpringReverb::LEVEL2_PARAM, 0.0, 1.0, 0.0));
  243. addParam(ParamWidget::create<Davies1900hWhiteKnob>(Vec(42, 210), module, SpringReverb::HPF_PARAM, 0.0, 1.0, 0.5));
  244. addInput(Port::create<PJ301MPort>(Vec(7, 243), Port::INPUT, module, SpringReverb::CV1_INPUT));
  245. addInput(Port::create<PJ301MPort>(Vec(88, 243), Port::INPUT, module, SpringReverb::CV2_INPUT));
  246. addInput(Port::create<PJ301MPort>(Vec(27, 281), Port::INPUT, module, SpringReverb::IN1_INPUT));
  247. addInput(Port::create<PJ301MPort>(Vec(67, 281), Port::INPUT, module, SpringReverb::IN2_INPUT));
  248. addOutput(Port::create<PJ301MPort>(Vec(7, 317), Port::OUTPUT, module, SpringReverb::MIX_OUTPUT));
  249. addInput(Port::create<PJ301MPort>(Vec(47, 324), Port::INPUT, module, SpringReverb::MIX_CV_INPUT));
  250. addOutput(Port::create<PJ301MPort>(Vec(88, 317), Port::OUTPUT, module, SpringReverb::WET_OUTPUT));
  251. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(55, 269), module, SpringReverb::PEAK_LIGHT));
  252. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(55, 113), module, SpringReverb::VU1_LIGHT + 0));
  253. addChild(ModuleLightWidget::create<MediumLight<YellowLight>>(Vec(55, 126), module, SpringReverb::VU1_LIGHT + 1));
  254. addChild(ModuleLightWidget::create<MediumLight<YellowLight>>(Vec(55, 138), module, SpringReverb::VU1_LIGHT + 2));
  255. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(55, 150), module, SpringReverb::VU1_LIGHT + 3));
  256. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(55, 163), module, SpringReverb::VU1_LIGHT + 4));
  257. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(55, 175), module, SpringReverb::VU1_LIGHT + 5));
  258. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(55, 188), module, SpringReverb::VU1_LIGHT + 6));
  259. }
  260. };
  261. RACK_PLUGIN_MODEL_INIT(Befaco, SpringReverb) {
  262. Model *modelSpringReverb = Model::create<SpringReverb, SpringReverbWidget>("Befaco", "SpringReverb", "Spring Reverb", REVERB_TAG, DUAL_TAG);
  263. return modelSpringReverb;
  264. }