1 /** 2 * Copyright: Copyright Auburn Sounds 2015 and later. 3 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 4 * Authors: Guillaume Piolat 5 */ 6 module dplug.dsp.noise; 7 8 import std.random, 9 std.math; 10 11 import dplug.core.nogc; 12 import dplug.core.random; 13 14 /// Generates white gaussian noise. 15 struct WhiteNoise(T) if (is(T == float) || is(T == double)) 16 { 17 public: 18 void initialize() nothrow @nogc 19 { 20 _rng.seed(nogc_unpredictableSeed()); 21 } 22 23 T nextSample() nothrow @nogc 24 { 25 return randNormal!Xorshift32(_rng, cast(T)0, cast(T)1); 26 } 27 28 void nextBuffer(T* output, int frames) nothrow @nogc 29 { 30 for (int i = 0; i < frames; ++i) 31 output[i] = nextSample(); 32 } 33 34 private: 35 Xorshift32 _rng; 36 } 37 38 unittest 39 { 40 WhiteNoise!float a; 41 WhiteNoise!double b; 42 } 43 44 45 /// Makes a periodic noise for plugins demos. 46 /// Simply multiply you signal to footprint by the next() sample. 47 struct DemoNoise(T) if (is(T == float) || is(T == double)) 48 { 49 public: 50 enum int PERIOD = 30; 51 enum int NOISE_DURATION = 2; 52 53 void initialize(float sampleRate) nothrow @nogc 54 { 55 _noise.initialize(); 56 _increment = 1.0 / sampleRate; 57 _counter = 0; 58 } 59 60 T nextSample() nothrow @nogc 61 { 62 _counter += _increment; 63 while (_counter >= PERIOD) 64 _counter = _counter - PERIOD; 65 66 if (_counter > PERIOD - NOISE_DURATION) 67 return 1 + _noise.nextSample() * 0.3 * sin(PI * (_counter - PERIOD + NOISE_DURATION) / NOISE_DURATION); 68 else 69 return 1; 70 } 71 72 void nextBuffer(T* output, int frames) nothrow @nogc 73 { 74 for (int i = 0; i < frames; ++i) 75 output[i] = nextSample(); 76 } 77 78 private: 79 float _counter; 80 float _increment; 81 WhiteNoise!T _noise; 82 } 83 84 unittest 85 { 86 DemoNoise!float a; 87 DemoNoise!double b; 88 } 89 90 /// 1D perlin noise octave. 91 /// Is useful to slightly move parameters over time. 92 struct Perlin1D(T) if (is(T == float) || is(T == double)) 93 { 94 public: 95 void initialize(double frequency, double samplerate) nothrow @nogc 96 { 97 _rng.seed(nogc_unpredictableSeed()); 98 _current = 0.0f; 99 newGoal(); 100 _phase = 0.0f; 101 _phaseInc = cast(float)(frequency / samplerate); 102 } 103 104 T nextSample() nothrow @nogc 105 { 106 _phase += _phaseInc; 107 if (_phase > 1) 108 { 109 _current = _goal; 110 newGoal(); 111 _phase -= 1; 112 } 113 float f = smootherstep!float(_phase); 114 return f * _goal + (1 - f) * _current; 115 } 116 117 void nextBuffer(T* output, int frames) nothrow @nogc 118 { 119 for (int i = 0; i < frames; ++i) 120 output[i] = nextSample(); 121 } 122 123 private: 124 static T smootherstep(T)(T x) nothrow @nogc 125 { 126 return x * x * x * (x * (x * 6 - 15) + 10); 127 } 128 129 void newGoal() nothrow @nogc 130 { 131 _goal = 2 * (nogc_uniform_float(0.0f, 1.0f, _rng) - 0.5f); 132 } 133 134 float _current; 135 float _phase; 136 float _phaseInc; 137 float _goal; 138 void _newGoal(); 139 140 Xorshift32 _rng; 141 } 142 143 unittest 144 { 145 Perlin1D!float a; 146 Perlin1D!double b; 147 } 148 149 /// Pink noise class using the autocorrelated generator method. 150 /// Method proposed and described by Larry Trammell "the RidgeRat" -- 151 /// see http://home.earthlink.net/~ltrammell/tech/newpink.htm 152 /// There are no restrictions. 153 /// See_also: http://musicdsp.org/showArchiveComment.php?ArchiveID=244 154 struct PinkNoise(T) if (is(T == float) || is(T == double)) 155 { 156 public: 157 void initialize() nothrow @nogc 158 { 159 _rng.seed(nogc_unpredictableSeed()); 160 _contrib[] = 0; 161 _accum = 0; 162 } 163 164 float nextSample() nothrow @nogc 165 { 166 int randu = nogc_uniform_int(0, 32768, _rng); 167 int randv = nogc_uniform_int(-32768, 32768, _rng); // [-32768,32767] 168 169 // Structured block, at most one update is performed 170 for (int n = 0; n < 5; ++n) 171 { 172 if (randu < pPSUM[n]) 173 { 174 _accum -= _contrib[n]; 175 _contrib[n] = randv * pA[n]; 176 _accum += _contrib[n]; 177 break; 178 } 179 } 180 return _accum / 32768.0f; 181 } 182 183 void nextBuffer(T* output, int frames) nothrow @nogc 184 { 185 for (int i = 0; i < frames; ++i) 186 output[i] = nextSample(); 187 } 188 189 private: 190 191 int[5] _contrib; // stage contributions 192 int _accum; // combined generators 193 Xorshift32 _rng; 194 195 static immutable int[5] pA = [ 14055, 12759, 10733, 12273, 15716 ]; 196 static immutable int[5] pPSUM = [ 22347, 27917, 29523, 29942, 30007 ]; 197 } 198 199 unittest 200 { 201 PinkNoise!float a; 202 PinkNoise!double b; 203 } 204 205 private 206 { 207 /// Returns: Normal (Gaussian) random sample. 208 /// See_also: Box-Muller algorithm. 209 float randNormal(RNG)(ref RNG rng, float mean = 0.0, float standardDeviation = 1.0) nothrow @nogc 210 { 211 assert(standardDeviation > 0); 212 double u1; 213 214 do 215 { 216 u1 = nogc_uniform_float(0.0f, 1.0f, rng); 217 } while (u1 == 0); // u1 must not be zero 218 float u2 = nogc_uniform_float(0.0f, 1.0f, rng); 219 float r = sqrt(-2.0 * log(u1)); 220 float theta = 2.0 * PI * u2; 221 return mean + standardDeviation * r * sin(theta); 222 } 223 }