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 }