1 // Copyright 2014 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 // See http://creativecommons.org/licenses/MIT/ for more information.
24 //
25 // -----------------------------------------------------------------------------
26 
27 
28 #include <cassert>
29 #include <cmath>
30 #include <cstdlib>
31 #include <cstring>
32 #include <vector>
33 #include <xmmintrin.h>
34 
35 #include "stmlib/test/wav_writer.h"
36 #include "stmlib/utils/random.h"
37 
38 #include "warps/dsp/modulator.h"
39 #include "warps/dsp/sample_rate_converter.h"
40 #include "warps/resources.h"
41 
42 using namespace warps;
43 using namespace std;
44 using namespace stmlib;
45 
46 const size_t kSampleRate = 96000;
47 const size_t kBlockSize = 96;
48 
49 template<typename T>
TestSRCUp(const char * name)50 void TestSRCUp(const char* name) {
51   size_t ratio = 6;
52   WavWriter wav_writer(1, kSampleRate * ratio, 10);
53   wav_writer.Open(name);
54 
55   T src_up;
56   src_up.Init();
57 
58   float phase = 0.0f;
59   while (!wav_writer.done()) {
60     float samples_in[kBlockSize];
61     float samples_out[kBlockSize * ratio];
62     float instantenous_frequency = fmod(
63         4.0f * wav_writer.progress(),
64         0.5f);
65     for (size_t i = 0; i < kBlockSize; ++i) {
66       samples_in[i] = sinf(phase * 2 * M_PI);
67       phase += instantenous_frequency;
68       if (phase >= 1.0f) {
69         phase -= 1.0f;
70       }
71     }
72 
73     src_up.Process(samples_in, samples_out, kBlockSize);
74     wav_writer.Write(samples_out, kBlockSize * ratio, 16067.0f);
75   }
76 }
77 
TestSRC96To576To96()78 void TestSRC96To576To96() {
79   size_t ratio = 6;
80   WavWriter wav_writer(1, kSampleRate, 10);
81   wav_writer.Open("warps_src_96_576_96.wav");
82 
83   SampleRateConverter<SRC_UP, 6, 48> src_up;
84   SampleRateConverter<SRC_DOWN, 6, 48> src_down;
85   src_up.Init();
86   src_down.Init();
87 
88   float phase = 0.0f;
89   while (!wav_writer.done()) {
90     float samples_in[kBlockSize];
91     float samples_out[kBlockSize * ratio];
92     float instantenous_frequency = fmod(
93         2.0f * wav_writer.progress(),
94         0.5f);
95     for (size_t i = 0; i < kBlockSize; ++i) {
96       samples_in[i] = sinf(phase * 2 * M_PI);
97       phase += instantenous_frequency;
98       if (phase >= 1.0f) {
99         phase -= 1.0f;
100       }
101     }
102     src_up.Process(samples_in, samples_out, kBlockSize);
103     src_down.Process(samples_out, samples_in, kBlockSize * ratio);
104     wav_writer.Write(samples_in, kBlockSize, 32200.0f);
105   }
106 }
107 
TestModulator()108 void TestModulator() {
109   FILE* fp_in = fopen("audio_samples/modulation_96k.wav", "rb");
110   WavWriter wav_writer(2, kSampleRate, 15);
111   wav_writer.Open("warps_modulator.wav");
112 
113   fseek(fp_in, 48, SEEK_SET);
114 
115   Modulator modulator;
116   modulator.Init(kSampleRate);
117 
118   Parameters* p = modulator.mutable_parameters();
119 
120   float phi = 0.0f;
121   float filtered_noise = 0.0f;
122 
123   while (!wav_writer.done()) {
124     float triangle = wav_writer.triangle();
125     float square = 0.5f * SoftClip(32.0f * (triangle - 0.5f)) + 0.5f;
126     float noise = (Random::GetFloat() - 0.5f) / 128.0f;
127     filtered_noise += (noise - filtered_noise) * 0.1f;
128 
129     p->carrier_shape = 1;
130     p->channel_drive[0] = 0.625f;
131     p->channel_drive[1] = 0.0f;
132     p->modulation_algorithm = 0.125f + 0.0f * square + 1.0f * filtered_noise;
133     p->modulation_parameter = 0.0f + 1.0f * triangle;
134     p->note = 48.0f + phi;
135 
136     ShortFrame input[kBlockSize];
137     ShortFrame output[kBlockSize];
138 
139     if (fread(
140             input,
141             sizeof(ShortFrame),
142             kBlockSize,
143             fp_in) != kBlockSize) {
144       break;
145     }
146 
147     for (size_t i = 0; i < kBlockSize; ++i) {
148       input[i].r = input[i].l;
149       input[i].l = 0;
150     }
151     modulator.Process(input, output, kBlockSize);
152     wav_writer.WriteFrames((short*)(output), kBlockSize);
153   }
154   fclose(fp_in);
155 }
156 
TestEasterEgg()157 void TestEasterEgg() {
158   FILE* fp_in = fopen("audio_samples/modulation_96k.wav", "rb");
159 
160   WavWriter wav_writer(2, kSampleRate, 15);
161   wav_writer.Open("warps_easter_egg.wav");
162 
163   fseek(fp_in, 48, SEEK_SET);
164 
165   Modulator modulator;
166   modulator.Init(kSampleRate);
167   modulator.set_easter_egg(true);
168 
169   Parameters* p = modulator.mutable_parameters();
170 
171   float phi = 0.0f;
172   float filtered_noise = 0.0f;
173   while (!wav_writer.done()) {
174     uint16_t tri = (wav_writer.remaining_frames() / 8);
175     tri = tri > 32767 ? 65535 - tri : tri;
176     float triangle = 0.5f * SoftClip(8.0f * (tri / 32768.0f - 0.5f)) + 0.5f;
177     float square = 0.5f * SoftClip(32.0f * (tri / 32768.0f - 0.5f)) + 0.5f;
178     float noise = (Random::GetFloat() - 0.5f) / 128.0f;
179     filtered_noise += (noise - filtered_noise) * 0.1f;
180 
181     p->frequency_shift_pot = 0.85f + 0.0f * triangle + 0.0f * filtered_noise;
182     p->frequency_shift_cv = 0.0f;
183     p->carrier_shape = 1;
184     p->channel_drive[0] = 0.0f;
185     p->channel_drive[1] = 1.0f;
186     p->modulation_parameter = 0.0f + 0.0f * square;
187     p->note = 48.0f + phi;
188 
189     ShortFrame input[kBlockSize];
190     ShortFrame output[kBlockSize];
191 
192     if (fread(
193             input,
194             sizeof(ShortFrame),
195             kBlockSize,
196             fp_in) != kBlockSize) {
197       break;
198     }
199 
200     for (size_t i = 0; i < kBlockSize; ++i) {
201       input[i].r = input[i].l;
202       input[i].l = 0;
203     }
204     modulator.Process(input, output, kBlockSize);
205     wav_writer.WriteFrames((short*)(output), kBlockSize);
206   }
207   fclose(fp_in);
208 }
209 
TestOscillators()210 void TestOscillators() {
211   WavWriter wav_writer(2, kSampleRate, 15);
212   wav_writer.Open("warps_oscillator.wav");
213 
214   Modulator modulator;
215   modulator.Init(kSampleRate);
216 
217   Parameters* p = modulator.mutable_parameters();
218 
219   p->carrier_shape = 3;
220   p->channel_drive[0] = 0.0f;
221   p->channel_drive[1] = 0.0f;
222   p->modulation_algorithm = 0.0f;
223   p->modulation_parameter = 0.0f;
224 
225   ShortFrame input[kBlockSize];
226   ShortFrame zero;
227   zero.l = zero.r = 0.0f;
228   fill(&input[0], &input[kBlockSize], zero);
229 
230   while (!wav_writer.done()) {
231     float triangle = wav_writer.triangle();
232     p->note = 20 + triangle * 0;
233 
234     ShortFrame output[kBlockSize];
235     modulator.Process(input, output, kBlockSize);
236     wav_writer.WriteFrames((short*)(output), kBlockSize);
237   }
238 }
239 
TestFilterBankReconstruction()240 void TestFilterBankReconstruction() {
241   FilterBank fb;
242   fb.Init(96000.0);
243 
244   size_t num_blocks = 1000;
245   const size_t block_size = 96;
246 
247   FILE* fp_out = fopen("warps_filterbank_ir_float32.bin", "wb");
248 
249   float in[block_size];
250   float out[block_size];
251 
252   for (size_t i = 0; i < num_blocks; ++i) {
253     fill(&in[0], &in[block_size], 0.0f);
254     if (i == 0) {
255       in[0] = 1.0f;
256     }
257     fb.Analyze(in, block_size);
258 
259     for (int32_t j = 0; j < kNumBands; ++j) {
260       if (false) {
261         float* samples = fb.band(j).samples;
262         size_t size = block_size / fb.band(j).decimation_factor;
263         fill(&samples[0], &samples[size], 0.0f);
264       }
265     }
266 
267     fb.Synthesize(out, block_size);
268     fwrite(out, sizeof(float), block_size, fp_out);
269   }
270   fclose(fp_out);
271 
272   // The impulse response and frequency responses can be seen with the following
273   // python program:
274   //
275   // import numpy
276   // import pylab
277   //
278   // x = numpy.fromfile('warps_filterbank_ir_float32.bin', dtype=numpy.float32)
279   // pylab.subplot(211)
280   // pylab.plot(x[:500])
281   // pylab.subplot(212)
282   // xf = numpy.fft.rfft(x)
283   // N = len(xf)
284   // f = numpy.arange(0.0, N) / N * 48000
285   // pylab.semilogx(f, 20 * numpy.log10(numpy.abs(xf + 1e-8)))
286   // pylab.show()
287 }
288 
TestSineTransition()289 void TestSineTransition() {
290   WavWriter wav_writer(2, kSampleRate, 15);
291   wav_writer.Open("warps_sine_transition.wav");
292 
293   Modulator modulator;
294   modulator.Init(kSampleRate);
295 
296   Parameters* p = modulator.mutable_parameters();
297 
298   while (!wav_writer.done()) {
299     float triangle = wav_writer.triangle();
300     p->carrier_shape = 1;
301     p->channel_drive[0] = 0.0;
302     p->channel_drive[1] = 0.0f;
303     p->modulation_algorithm = 0.0f + 0.125f * triangle;
304     p->modulation_parameter = 0.0f;
305     p->note = 36.0f;
306 
307     ShortFrame input[kBlockSize];
308     ShortFrame output[kBlockSize];
309 
310     for (size_t i = 0; i < kBlockSize; ++i) {
311       input[i].r = 0;
312       input[i].l = 0;
313     }
314 
315     modulator.Process(input, output, kBlockSize);
316     wav_writer.WriteFrames((short*)output, kBlockSize);
317   }
318 }
319 
TestGain()320 void TestGain() {
321   WavWriter wav_writer(2, kSampleRate, 10);
322   wav_writer.Open("warps_gain.wav");
323 
324   Modulator modulator;
325   modulator.Init(kSampleRate);
326 
327   Parameters* p = modulator.mutable_parameters();
328 
329   float phase = 0.0f;
330   while (!wav_writer.done()) {
331     float triangle = wav_writer.triangle();
332 
333     p->carrier_shape = 0;
334     p->channel_drive[0] = triangle * 1.0f;
335     p->channel_drive[1] = 0.0f;
336     p->modulation_algorithm = 0.0f + 0.125f * 0.0f;
337     p->modulation_parameter = 0.0f;
338     p->note = 36.0f;
339 
340     ShortFrame input[kBlockSize];
341     ShortFrame output[kBlockSize];
342 
343     for (size_t i = 0; i < kBlockSize; ++i) {
344       input[i].l = 20480.0f * sinf(phase * 2 * M_PI);
345       input[i].r = 20480.0f * sinf(phase * 2 * M_PI);
346       phase += 220.0f / kSampleRate;
347       if (phase >= 1.0f) {
348         phase -= 1.0f;
349       }
350     }
351 
352     modulator.Process(input, output, kBlockSize);
353     wav_writer.WriteFrames((short*)output, kBlockSize);
354   }
355 }
356 
TestQuadratureOscillator()357 void TestQuadratureOscillator() {
358   WavWriter wav_writer(2, kSampleRate, 10);
359   wav_writer.Open("warps_quadrature.wav");
360   QuadratureOscillator q;
361   q.Init(kSampleRate);
362   while (!wav_writer.done()) {
363     float triangle = wav_writer.triangle();
364     float x[2];
365     q.Render(triangle, 100.8f, &x[0], &x[1], 1);
366     wav_writer.Write(x, 2, 32767.0f);
367   }
368 }
369 
main(void)370 int main(void) {
371   _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
372   TestSRCUp<SampleRateConverter<SRC_UP, 6, 48> >("warps_src_up_fir_48.wav");
373   TestSRC96To576To96();
374   // TestModulator();
375   // TestEasterEgg();
376   TestOscillators();
377   TestFilterBankReconstruction();
378   TestSineTransition();
379   TestGain();
380   TestQuadratureOscillator();
381 }
382