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