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.

248 lines
6.1KB

  1. #pragma once
  2. #include "ButterworthFilterDesigner.h"
  3. #include "Decimator.h"
  4. #include "GraphicEq.h"
  5. #include "LowpassFilter.h"
  6. #include "BiquadParams.h"
  7. #include "BiquadState.h"
  8. #include "BiquadFilter.h"
  9. #include "ObjectCache.h"
  10. #include <random>
  11. /**
  12. * Noise generator feeding a graphic equalizer.
  13. * Calculated at very low sample rate, then re-sampled
  14. * up to audio rate.
  15. *
  16. * Below assumes 44k SR. TODO: other rates.
  17. *
  18. * We first design the EQ around bands of 100, 200, 400, 800,
  19. * 1600. EQ gets noise.
  20. *
  21. * Then output of EQ is re-sampled up by a factor of 100
  22. * to bring the first band down to 1hz.
  23. * or : decimation factor = 100 * (fs) / 44100.
  24. *
  25. * A butterworth lowpass then removes the re-sampling artifacts.
  26. * Otherwise these images bring in high frequencies that we
  27. * don't want.
  28. *
  29. * Cutoff for the filter can be as low as the top of the eq,
  30. * which is 3.2khz. 44k/3.2k is about 10,
  31. * so fc/fs can be 1/1000.
  32. *
  33. * or : fc = (fs / 44100) / 1000;
  34. *
  35. * (had been using fc/fs = float(1.0 / (44 * 100.0)));)
  36. *
  37. * Design for R = root freq (was 1 Hz, above)
  38. * EQ first band at E (was 100 Hz, above)
  39. *
  40. * Decimation divider = E / R
  41. *
  42. * Imaging filter fc = 3.2khz / decimation-divider
  43. * fc/fs = 3200 * (reciprocal sr) / decimation-divider.
  44. *
  45. * Experiment: let's use those values and compare to what we had been using.
  46. * result: not too far off.
  47. *
  48. * make a range/base control. map -5 to +5 into 1/10 Hz to 2 Hz rate. Can use regular
  49. * functions, since we won't calc that often.
  50. */
  51. template <class TBase>
  52. class LFN : public TBase
  53. {
  54. public:
  55. LFN(struct Module * module) : TBase(module)
  56. {
  57. }
  58. LFN() : TBase()
  59. {
  60. }
  61. void setSampleTime(float time)
  62. {
  63. reciprocalSampleRate = time;
  64. updateLPF();
  65. }
  66. /**
  67. * re-calc everything that changes with sample
  68. * rate. Also everything that depends on baseFrequency.
  69. *
  70. * Only needs to be called once.
  71. */
  72. void init();
  73. enum ParamIds
  74. {
  75. EQ0_PARAM,
  76. EQ1_PARAM,
  77. EQ2_PARAM,
  78. EQ3_PARAM,
  79. EQ4_PARAM,
  80. FREQ_RANGE_PARAM,
  81. NUM_PARAMS
  82. };
  83. enum InputIds
  84. {
  85. EQ0_INPUT,
  86. EQ1_INPUT,
  87. EQ2_INPUT,
  88. EQ3_INPUT,
  89. EQ4_INPUT,
  90. NUM_INPUTS
  91. };
  92. enum OutputIds
  93. {
  94. OUTPUT,
  95. NUM_OUTPUTS
  96. };
  97. enum LightIds
  98. {
  99. NUM_LIGHTS
  100. };
  101. /**
  102. * Main processing entry point. Called every sample
  103. */
  104. void step() override;
  105. float getBaseFrequency() const
  106. {
  107. return baseFrequency;
  108. }
  109. /**
  110. * This lets the butterworth get re-calculated on the UI thread.
  111. * We can't do it on the audio thread, because it calls malloc.
  112. */
  113. void pollForChangeOnUIThread();
  114. private:
  115. float reciprocalSampleRate = 0;
  116. ::Decimator decimator;
  117. GraphicEq2<5> geq;
  118. /**
  119. * Template type for butterworth reconstruction filter
  120. * Tried double for best low frequency performance. It's
  121. * probably overkill, but calculates plenty fast.
  122. */
  123. using TButter = double;
  124. BiquadParams<TButter, 2> lpfParams;
  125. BiquadState<TButter, 2> lpfState;
  126. /**
  127. * Frequency, in Hz, of the lowest band in the graphic EQ
  128. */
  129. float baseFrequency = 1;
  130. /**
  131. * The last value baked by the LPF filter calculation
  132. * done on the UI thread.
  133. */
  134. float lastBaseFrequencyParamValue = -100;
  135. std::default_random_engine generator{57};
  136. std::normal_distribution<double> distribution{-1.0, 1.0};
  137. float noise()
  138. {
  139. return (float) distribution(generator);
  140. }
  141. /**
  142. * Must be called after baseFrequency is updated.
  143. * re-calculates the butterworth lowpass.
  144. */
  145. void updateLPF();
  146. /**
  147. * scaling function for the range / base frequency knob
  148. * map knob range from .1 Hz to 2.0 Hz
  149. */
  150. std::function<double(double)> rangeFunc =
  151. {AudioMath::makeFunc_Exp(-5, 5, .1, 2)};
  152. /**
  153. * Audio taper for the EQ gains. Arbitrary max value selected
  154. * to give "good" output level.
  155. */
  156. AudioMath::SimpleScaleFun<float> gainScale =
  157. {AudioMath::makeSimpleScalerAudioTaper(0, 35)};
  158. };
  159. template <class TBase>
  160. inline void LFN<TBase>::pollForChangeOnUIThread()
  161. {
  162. if (lastBaseFrequencyParamValue != TBase::params[FREQ_RANGE_PARAM].value) {
  163. lastBaseFrequencyParamValue = TBase::params[FREQ_RANGE_PARAM].value;
  164. baseFrequency = float(rangeFunc(lastBaseFrequencyParamValue));
  165. updateLPF(); // now get the filters updated
  166. }
  167. }
  168. template <class TBase>
  169. inline void LFN<TBase>::init()
  170. {
  171. updateLPF();
  172. }
  173. template <class TBase>
  174. inline void LFN<TBase>::updateLPF()
  175. {
  176. assert(reciprocalSampleRate > 0);
  177. // decimation must be 100hz (what our EQ is designed at)
  178. // divided by base.
  179. const float decimationDivider = float(100.0 / baseFrequency);
  180. decimator.setDecimationRate(decimationDivider);
  181. // calculate lpFc ( Fc / sr)
  182. // Imaging filter fc = 3.2khz / decimation-divider
  183. // fc/fs = 3200 * (reciprocal sr) / decimation-divider.
  184. const float lpFc = 3200 * reciprocalSampleRate / decimationDivider;
  185. ButterworthFilterDesigner<TButter>::designThreePoleLowpass(
  186. lpfParams, lpFc);
  187. }
  188. template <class TBase>
  189. inline void LFN<TBase>::step()
  190. {
  191. // Let's only check the inputs every 4 samples. Still plenty fast, but
  192. // get the CPU usage down really far.
  193. static int count = 0;
  194. if (count++ > 4) {
  195. count = 0;
  196. const int numEqStages = geq.getNumStages();
  197. for (int i = 0; i < numEqStages; ++i) {
  198. auto paramNum = i + EQ0_PARAM;
  199. auto cvNum = i + EQ0_INPUT;
  200. const float gainParamKnob = TBase::params[paramNum].value;
  201. const float gainParamCV = TBase::inputs[cvNum].value;
  202. const float gain = gainScale(gainParamKnob, gainParamCV);
  203. geq.setGain(i, gain);
  204. }
  205. }
  206. bool needsData;
  207. TButter x = decimator.clock(needsData);
  208. x = BiquadFilter<TButter>::run(x, lpfState, lpfParams);
  209. if (needsData) {
  210. const float z = geq.run(noise());
  211. decimator.acceptData(z);
  212. }
  213. TBase::outputs[OUTPUT].value = (float) x;
  214. }