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.

231 lines
6.3KB

  1. #include "plugin.hpp"
  2. /** Based on "The Voss algorithm"
  3. http://www.firstpr.com.au/dsp/pink-noise/
  4. */
  5. template <int QUALITY = 8>
  6. struct PinkNoiseGenerator {
  7. int frame = -1;
  8. float values[QUALITY] = {};
  9. float process() {
  10. int lastFrame = frame;
  11. frame++;
  12. if (frame >= (1 << QUALITY))
  13. frame = 0;
  14. int diff = lastFrame ^ frame;
  15. float sum = 0.f;
  16. for (int i = 0; i < QUALITY; i++) {
  17. if (diff & (1 << i)) {
  18. values[i] = random::uniform() - 0.5f;
  19. }
  20. sum += values[i];
  21. }
  22. return sum;
  23. }
  24. };
  25. /**
  26. a_0 is normalized to 1 and omitted from the `a` array, so its indices are shifted down by 1.
  27. */
  28. template <int B_ORDER, int A_ORDER>
  29. struct IIRFilter {
  30. float b[B_ORDER] = {};
  31. float a[A_ORDER - 1] = {};
  32. float xState[B_ORDER] = {};
  33. float yState[A_ORDER - 1] = {};
  34. void setCoefficients(const float* b, const float* a) {
  35. for (int i = 0; i < B_ORDER; i++) {
  36. this->b[i] = b[i];
  37. }
  38. for (int i = 1; i < A_ORDER; i++) {
  39. this->a[i - 1] = a[i - 1];
  40. }
  41. }
  42. float process(float x) {
  43. // Shift x state
  44. for (int i = B_ORDER - 1; i >= 1; i--) {
  45. xState[i] = xState[i - 1];
  46. }
  47. xState[0] = x;
  48. float y = 0.f;
  49. // Add x state
  50. for (int i = 0; i < B_ORDER; i++) {
  51. y += b[i] * xState[i];
  52. }
  53. // Subtract y state
  54. for (int i = 1; i < A_ORDER; i++) {
  55. y -= a[i - 1] * yState[i - 1];
  56. }
  57. // Shift y state
  58. for (int i = A_ORDER - 1; i >= 2; i--) {
  59. yState[i - 1] = yState[i - 2];
  60. }
  61. yState[0] = y;
  62. return y;
  63. }
  64. };
  65. struct InverseAWeightingFFTFilter {
  66. static constexpr int BUFFER_LEN = 1024;
  67. alignas(16) float inputBuffer[BUFFER_LEN] = {};
  68. alignas(16) float outputBuffer[BUFFER_LEN] = {};
  69. int frame = 0;
  70. dsp::RealFFT fft;
  71. InverseAWeightingFFTFilter() : fft(BUFFER_LEN) {}
  72. float process(float deltaTime, float x) {
  73. inputBuffer[frame] = x;
  74. if (++frame >= BUFFER_LEN) {
  75. frame = 0;
  76. alignas(16) float freqBuffer[BUFFER_LEN * 2];
  77. fft.rfft(inputBuffer, freqBuffer);
  78. for (int i = 0; i < BUFFER_LEN; i++) {
  79. float f = 1 / deltaTime / 2 / BUFFER_LEN * i;
  80. float amp = 0.f;
  81. if (80.f <= f && f <= 20000.f) {
  82. float f2 = f * f;
  83. // Inverse A-weighted curve
  84. amp = ((424.36f + f2) * std::sqrt((11599.3f + f2) * (544496.f + f2)) * (148693636.f + f2)) / (148693636.f * f2 * f2);
  85. }
  86. freqBuffer[2 * i + 0] *= amp / BUFFER_LEN;
  87. freqBuffer[2 * i + 1] *= amp / BUFFER_LEN;
  88. }
  89. fft.irfft(freqBuffer, outputBuffer);
  90. }
  91. return outputBuffer[frame];
  92. }
  93. };
  94. struct Noise : Module {
  95. enum ParamIds {
  96. NUM_PARAMS
  97. };
  98. enum InputIds {
  99. NUM_INPUTS
  100. };
  101. enum OutputIds {
  102. WHITE_OUTPUT,
  103. PINK_OUTPUT,
  104. RED_OUTPUT,
  105. VIOLET_OUTPUT,
  106. BLUE_OUTPUT,
  107. GRAY_OUTPUT,
  108. BLACK_OUTPUT,
  109. NUM_OUTPUTS
  110. };
  111. enum LightIds {
  112. NUM_LIGHTS
  113. };
  114. dsp::ClockDivider blackDivider;
  115. PinkNoiseGenerator<8> pinkNoiseGenerator;
  116. IIRFilter<2, 2> redFilter;
  117. float lastWhite = 0.f;
  118. float lastPink = 0.f;
  119. InverseAWeightingFFTFilter grayFilter;
  120. // For calibrating levels
  121. // float meanSqr = 0.f;
  122. Noise() {
  123. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  124. // Hard-code coefficients for Butterworth lowpass with cutoff 20 Hz @ 44.1kHz.
  125. const float b[] = {0.00425611, 0.00425611};
  126. const float a[] = {-0.99148778};
  127. redFilter.setCoefficients(b, a);
  128. }
  129. void process(const ProcessArgs& args) override {
  130. // All noise is calibrated to 1 RMS.
  131. // Then they should be scaled to match the RMS of a sine wave with 5V amplitude.
  132. const float gain = 5.f / std::sqrt(2.f);
  133. if (outputs[WHITE_OUTPUT].isConnected() || outputs[RED_OUTPUT].isConnected() || outputs[VIOLET_OUTPUT].isConnected() || outputs[GRAY_OUTPUT].isConnected()) {
  134. // White noise: equal power density
  135. float white = random::normal();
  136. outputs[WHITE_OUTPUT].setVoltage(white * gain);
  137. // Red/Brownian noise: -6dB/oct
  138. if (outputs[RED_OUTPUT].isConnected()) {
  139. float red = redFilter.process(white) / 0.0645f;
  140. outputs[RED_OUTPUT].setVoltage(red * gain);
  141. }
  142. // Violet/purple noise: 6dB/oct
  143. if (outputs[VIOLET_OUTPUT].isConnected()) {
  144. float violet = (white - lastWhite) / 1.41f;
  145. lastWhite = white;
  146. outputs[VIOLET_OUTPUT].setVoltage(violet * gain);
  147. }
  148. // Gray noise: psychoacoustic equal loudness curve, specifically inverted A-weighted
  149. if (outputs[GRAY_OUTPUT].isConnected()) {
  150. float gray = grayFilter.process(args.sampleTime, white) / 1.67f;
  151. outputs[GRAY_OUTPUT].setVoltage(gray * gain);
  152. }
  153. }
  154. if (outputs[PINK_OUTPUT].isConnected() || outputs[BLUE_OUTPUT].isConnected()) {
  155. // Pink noise: -3dB/oct
  156. float pink = pinkNoiseGenerator.process() / 0.816f;
  157. outputs[PINK_OUTPUT].setVoltage(pink * gain);
  158. // Blue noise: 3dB/oct
  159. if (outputs[BLUE_OUTPUT].isConnected()) {
  160. float blue = (pink - lastPink) / 0.705f;
  161. lastPink = pink;
  162. outputs[BLUE_OUTPUT].setVoltage(blue * gain);
  163. }
  164. }
  165. // Black noise: uniform noise
  166. // Note: I made this definition up.
  167. if (outputs[BLACK_OUTPUT].isConnected()) {
  168. float u = random::uniform();
  169. outputs[BLACK_OUTPUT].setVoltage(u * 10.f - 5.f);
  170. }
  171. // meanSqr += (std::pow(gray, 2.f) - meanSqr) * args.sampleTime / 10.f;
  172. // DEBUG("%f", std::sqrt(meanSqr));
  173. }
  174. };
  175. struct NoiseWidget : ModuleWidget {
  176. NoiseWidget(Noise* module) {
  177. setModule(module);
  178. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Noise.svg")));
  179. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  180. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  181. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  182. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  183. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.621, 21.727)), module, Noise::WHITE_OUTPUT));
  184. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.621, 36.726)), module, Noise::PINK_OUTPUT));
  185. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.621, 51.727)), module, Noise::RED_OUTPUT));
  186. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.621, 66.727)), module, Noise::VIOLET_OUTPUT));
  187. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.621, 81.727)), module, Noise::BLUE_OUTPUT));
  188. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.621, 96.727)), module, Noise::GRAY_OUTPUT));
  189. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.621, 111.727)), module, Noise::BLACK_OUTPUT));
  190. }
  191. };
  192. Model* modelNoise = createModel<Noise, NoiseWidget>("Noise");