CSL 5.2

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

Test_Effects.cpp
Go to the documentation of this file.
1 //
2 // Test_Effects.cpp -- C main functions for the basic CSL effect and filter tests.
3 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
4 //
5 // This program simply reads the run_tests() function (at the bottom of this file)
6 // and executes a list of basic CSL tests
7 //
8 // Examples to add
9 // chords
10 // Microphone input
11 // JUCE MIDI
12
13 #ifndef USE_JUCE
14 #define USE_TEST_MAIN // use the main() function in test_support.h
15 #include "Test_Support.cpp" // include all of CSL core and the test support functions
16 #else
17 #include "Test_Support.h"
18 #endif
19
20 #include "RingBuffer.h" /// Utility circular buffer
21 #include "Clipper.h"
22 #include "FIR.h"
23 #include "InOut.h"
24
25 using namespace csl; // this is the namespace, dummy!
26
27 /////////////////////// Here are the actual unit tests ////////////////////
28
29 /// Clip some sound
30
31 void testClipper() {
32 Osc vox(110); // We need some sound, so . . . let's get a sine.
33 // ADSR vol(3.0, 0.3, 2.0, 0.2, 0.5); // The amplitude envelope.
34 // vox.setScale(vol); // We set the envelope to affect the sine.
35 Clipper clipIt(vox, -0.3, 0.3); // To use clipper, just specify a min and max value.
36 logMsg("playing clipped sin...");
37 runTest(clipIt);
38 logMsg("done.\n");
39 }
40
41 /// Test the FIR filter BP 200-300 Hz on pink noise
42
43 void testFIR() {
44 PinkNoise noise; // the sound source
45 double resp[2] = { 0, 1 }; // amplitudes in the 2 freq bands (i.e., hi-pass)
46 double freq[4] = { 0, 2000, 3000, 22050 }; // corner freqs of the pass, transition, and stop bands
47 double weight[2] = { 10, 20 }; // weights for error (ripple) in the 2 bands
48 FilterSpecification fs(64, 2, freq, resp, weight); // 64 taps (64-step IR), 2 bands
49 FIR vox(noise, fs); // create the filter
50 MulOp mul(vox, 10); // scale it back up
51 logMsg("playing FIR filtered noise...");
52 runTest(mul);
53 logMsg("FIR done.");
54 }
55
56 /// Filter tests
57
58 void testFilters() {
59 float dur = 2.f; // seconds to play each test for
60 // Butterworth high pass filter
61 WhiteNoise white1(0.1);
62 Butter butter(white1, BW_HIGH_PASS, 4000.0, 333.3); // corresponds to Q of 3
63 logMsg("playing Butterworth high-passed white noise...");
64 // butter.dump();
65 runTest(butter, dur);
66 logMsg("done.");
67
68 // Butterworth band pass filter
69 WhiteNoise white2;
70 Butter butter1(white2, BW_BAND_PASS, 1000.f, 100.f);
71 // butter1.dump();
72 logMsg("playing Butterworth band-passed white noise...");
73 // butter1.dump();
74 runTest(butter1, dur);
75 logMsg("done.");
76 //
77 // Butterworth low pass filter
78 WhiteNoise white3;
79 Butter butter2(white3, BW_LOW_PASS, 250.f, 100.f);
80 logMsg("playing Butterworth low-passed white noise...");
81 // butter2.dump();
82 runTest(butter2, dur);
83 logMsg("done.");
84 //
85 // Butterworth band-stop filter
86 WhiteNoise white4(0.1);
87 Butter butter3(white4, BW_BAND_STOP, 3000.f, 2500.f);
88 logMsg("playing Butterworth band-rejected white noise...");
89 // butter3.dump();
90 runTest(butter3, dur);
91 logMsg("done.");
92
93 // if (true) return;
94 // Notch filter
95 WhiteNoise white5(0.3);
96 Notch notch(white5, 500.f, 0.99995f);
97 logMsg("playing Notch filtered white noise...");
98 // notch.dump();
99 runTest(notch, dur);
100 logMsg("done.");
101
102 // Formant filter
103 WhiteNoise white6(0.3);
104 Formant formant(white6, 100.f, 0.5f);
105 logMsg("playing Formant filtered white noise...");
106 // formant.dump();
107 runTest(formant, dur);
108 logMsg("done.");
109
110 // Allpass filter
111 Square osc(400,0.2);
112 Allpass allpass(osc, 0.9995f);
113 logMsg("playing All-passed square wave...");
114 runTest(allpass, dur);
115 logMsg("done.");
116
117 // a hand-built band pass filter
118 float bcoeffs[3] = {0.05f, 0.f, -0.0496f};
119 float acoeffs[2] = {-1.843f, 0.9f};
120 WhiteNoise white0;
121 Filter filter(white0, bcoeffs ,acoeffs, 3, 2);
122 logMsg("playing BP filtered white noise...");
123 runTest(filter, dur);
124 logMsg("done.");
125
126 // Moog filter
127 // WhiteNoise white8;
128 // Moog moog(white8, 1000.f, 0.9f);
129 // logMsg("playing Moog filtered white noise...");
130 // runTest(moog, dur);
131 // logMsg("done.");
132
133 }
134
135 /// Test dynamic BP filter
136
137 void testDynamicFilters() {
138 float dur = 6.0f; // seconds to play each test for
139 WhiteNoise white(1.0); // noise
140 RandEnvelope center(3, 1000, 2000, 600); // center/bw freq random walk
141 RandEnvelope bw(3, 100, 100, 40); // (frq, amp, offset)
142 Butter butter(white, BW_BAND_PASS, center, bw); // Butterworth BP filter
143 logMsg("playing dynamic Butterworth band-passed white noise...");
144 // butter.dump();
145 // center.trigger();
146 // bw.trigger();
147 runTest(butter, dur);
148 logMsg("done.");
149 }
150
151 /// Test dynamic BP filter on a sound file
152
153 void testDynamicVoice() {
154 float dur = 6.0f; // seconds to play each test for
155 RandEnvelope center(4, 1400, 1800, 600); // center/bw freq random walk
156 RandEnvelope bw(5, 60, 160, 40); // (frq, amp, offset, step)
157 SoundFile sfile(CGestalt::dataFolder() + "sns.aiff"); // open a speak'n'spell file
158 Butter butter(sfile, BW_BAND_PASS, center, bw); // Butterworth BP filter
159 logMsg("playing filtered snd file...");
160 sfile.trigger();
161 runTest(butter, dur);
162 logMsg("done.");
163 }
164
165 /// Pan and mix many sines
166
167 void testNDynamicFilters() {
168 int num = 20; // # of layers
169 float scale = 3.0f / (float) num; // ampl scale
170 Mixer mix(2); // stereo mixer
171 for (int i = 0; i < num; i++) { // loop to add a panning, LFO-controlled osc to the mix
172 // (frq, amp, offset, step)
173 RandEnvelope * center = new RandEnvelope(3, 1800, fRandM(2000, 4000), 200);
174 RandEnvelope * bw = new RandEnvelope(3, 200, 400, 40);
175 WhiteNoise * white = new WhiteNoise(1.0); // noise
176 Butter * butter = new Butter(*white, BW_BAND_PASS, *center, *bw);
177 MulOp * mul = new MulOp(*butter, scale);
178 Osc * lfo = new Osc(fRandM(0.3, 0.6), 1, 0, fRandM(0, CSL_PI)); // panning LFO with rand phase
179 Panner * pan = new Panner(*mul, *lfo);
180 mix.addInput(*pan);
181 }
182 logMsg("playing mix of %d Butterworth band-passed white noise layers...", num);
183 // srand(getpid()); // seed the rand generator -- UNIX SPECIFIC CODE HERE
184 runTest(mix, 8.0f);
185 logMsg("done.\n");
186 mix.deleteInputs(); // clean up
187 }
188
189 /// Play noise bursts into reverb
190
191 void testReverb() {
192 ADSR mChiffEnv(1, 0.01, 0.01, 0.0, 1.5); // attack-chiff envelope
193 WhiteNoise mChiff; // attack-chiff noise source
194 Butter mChFilter(mChiff, BW_BAND_PASS, 2000.f, 500.f); // and filter
195 mChiffEnv.setScale(10); // scale chiff envelope
196 mChFilter.setScale(mChiffEnv); // apply chiff envelope
197 Freeverb mReverb(mChFilter); // stereo reverb
198 mReverb.setRoomSize(0.95); // longer reverb
199
200 mChiffEnv.trigger();
201 logMsg("playing Reverb test\n");
202 theIO->setRoot(mReverb); // make some sound
203 for (unsigned i = 0; i < 4; i++) {
204 mChiffEnv.trigger();
205 sleepSec(4);
206 }
207 sleepSec(6);
208 theIO->clearRoot();
209 logMsg("done.\n");
210 }
211
212 /// Play noise bursts into reverb
213
214 void testStereoverb() {
215 ADSR mEnv(1, 0.005, 0.01, 0.0, 1.5); // attack-chiff envelope
216 WhiteNoise mChiff; // attack-chiff noise source
217 float ctrFrq = fRandB(3000, 1000); // filter center freq
218 Butter mFilter(mChiff, BW_BAND_PASS, ctrFrq, 500.0f); // BP filter
219 mEnv.setScale(10); // scale chiff envelope
220 mFilter.setScale(mEnv); // apply chiff envelope
221
222 Panner mPanner(mFilter, 0.0); // stereo panner
223 Stereoverb mReverb(mPanner); // stereo reverb
224 mReverb.setRoomSize(0.988); // long reverb time
225
226 theIO->setRoot(mReverb); // start sound output
227 #ifndef CSL_WINDOWS
228 srand(getpid()); // seed the rand generator -- UNIX SPECIFIC CODE HERE
229 #endif
230 float nap = 1.0f; // sleep time between noise bursts
231 logMsg("playing Stereoverb test\n");
232 for (unsigned i = 0; i < 10; i++) { // play a loop of notes
233 mPanner.setPosition(fRand1()); // select a random stereo position
234 mEnv.trigger(); // trigger the burst envelope
235 sleepSec(nap); // sleep a few seconds
236 nap *= 1.25f; // slow down
237 mFilter.setFrequency(fRandB(2000, 1000)); // pick a new frequency, 2k +- 1k
238 }
239 sleepSec(2); // sleep at the end to let it die out
240 theIO->clearRoot();
241 logMsg("done.\n");
242 }
243
244 /// Play noise bursts into multi-tap delay line
245
246 void testMultiTap() {
247 ADSR mChiffEnv(1, 0.01, 0.01, 0.0, 1.5); // attack-chiff envelope
248 WhiteNoise mChiff; // attack-chiff noise source
249 Butter mChFilter(mChiff, BW_BAND_PASS, 2000.f, 500.f); // and filter
250 mChiffEnv.setScale(10); // scale chiff envelope
251 mChFilter.setScale(mChiffEnv); // apply chiff envelope
252
253 RingBuffer rbuf(mChFilter, 1, 22050); // mono, 1.2 sec.
254 rbuf.mTap.setOffset(2000);
255 RingBufferTap tap1(& rbuf, 4000);
256 RingBufferTap tap2(& rbuf, 8000);
257 RingBufferTap tap3(& rbuf, 12000);
258 RingBufferTap tap4(& rbuf, 16000);
259 RingBufferTap tap5(& rbuf, 20000);
260
261 Mixer mix(2); // create a stereo mixer
262 mix.addInput(rbuf); // add the taps to the mixer
263 mix.addInput(tap1);
264 mix.addInput(tap2);
265 mix.addInput(tap3);
266 mix.addInput(tap4);
267 mix.addInput(tap5);
268
269 mChiffEnv.trigger();
270 logMsg("");
271 logMsg("playing multi-tap delay test\n");
272 theIO->setRoot(mix); // make some sound
273 for (unsigned i = 0; i < 4; i++) {
274 mChiffEnv.trigger();
275 sleepSec(1);
276 }
277 // sleepSec(3);
278 theIO->clearRoot();
279 logMsg("done.\n");
280 }
281
282 /// Test a block resizer by running a random gliss with a small block size
283
284 void testBlockUpsizer() {
285 float dur = 6.0f; // seconds to play each test for
286 Osc vox; // declare an oscillator
287 AR a_env(6, 1, 1); // dur, att, rel
288 RandEnvelope f_env(3, 80, 200, 40); // freq env = random walk
289 vox.setFrequency(f_env); // set the carrier's frequency
290 vox.setScale(a_env); // multiply index envelope by mod freq
291 a_env.trigger();
292 BlockResizer blocker(vox, 300); // small buffer, not a divisor of CSL's block size
293 logMsg("playing random gliss in a block up-sizer...");
294 runTest(blocker, dur); // run test
295 logMsg("done.");
296 }
297
298 /// Test a block resizer by running a random gliss with a huge block size
299
300 void testBlockDownsizer() {
301 float dur = 6.0f; // seconds to play each test for
302 Osc vox; // declare an oscillator
303 AR a_env(6, 1, 1); // dur, att, rel
304 RandEnvelope f_env(3, 80, 200, 40); // freq env = random walk
305 vox.setFrequency(f_env); // set the carrier's frequency
306 vox.setScale(a_env); // multiply index envelope by mod freq
307 a_env.trigger();
308 BlockResizer blocker(vox, 1100); // large buffer, not a multiple of CSL's block size
309 logMsg("playing random gliss in a block down-sizer...");
310 runTest(blocker, dur); // run test
311 logMsg("done.");
312 }
313
314 /// Test a Split/Join with a cross-over filter pair LPF to left, HPF to the right
315
316 void testSplitJoin1() {
317 float dur = 6.0f; // seconds to play each test for
318 SumOfSines sos1(16, 0.5); // create a cmoplex sum-of-sines
319 sos1.createCache(); // make the cached wavetable
320 AR a_env(6, 1, 1); // dur, att, rel
321 RandEnvelope f_env(3, 180, 300, 40); // freq env = random walk
322 sos1.setFrequency(f_env); // set the carrier's frequency
323 sos1.setScale(a_env); // multiply index envelope by mod freq
324 a_env.trigger(); // start the envelope
325 Panner pan(sos1, 0); // stereo panner, center
326
327 Splitter split(pan); // stereo-to-mono splitter
328 Butter lpf(split, BW_LOW_PASS, 500.0, 333.3); // lo-pass with Q of 3
329 Butter hpf(split, BW_HIGH_PASS, 500.0, 333.3); // hi-pass with Q of 3
330 Joiner join(lpf, hpf); // mono-to-stereo joiner
331
332 logMsg("playing splitter/joiner/mixer-based crossover filter...");
333 runTest(join, dur); // run test
334 logMsg("done.");
335 }
336
337 /// Test a Split/Join with a cross-over filter pair LPF to left, HPF to the right
338 /// mixer added in just for show
339
340 void testSplitJoin2() {
341 float dur = 6.0f; // seconds to play each test for
342 SumOfSines sos1(16, 0.5); // create a cmoplex sum-of-sines
343 sos1.createCache(); // make the cached wavetable
344 AR a_env(6, 1, 1); // dur, att, rel
345 RandEnvelope f_env(3, 180, 300, 40); // freq env = random walk
346 sos1.setFrequency(f_env); // set the carrier's frequency
347 sos1.setScale(a_env); // multiply index envelope by mod freq
348 a_env.trigger(); // start the envelope
349 Panner pan(sos1, 0); // stereo panner, center
350 Mixer mix1(2); // stereo mixer, fixed
351 mix1.addInput(pan);
352
353 Splitter split(mix1); // stereo-to-mono splitter
354 Butter lpf(split, BW_LOW_PASS, 500.0, 333.3); // lo-pass with Q of 3
355 Butter hpf(split, BW_HIGH_PASS, 500.0, 333.3); // hi-pass with Q of 3
356 Joiner join(lpf, hpf); // mono-to-stereo joiner
357 Mixer mix2(2); // stereo mixer, fixed
358 mix2.addInput(join);
359
360 logMsg("playing splitter/joiner/mixer-based crossover filter...");
361 runTest(mix2, dur); // run test
362 logMsg("done.");
363 }
364
365 /// Test a fan-out + mixer using UnitGenerator's built-in fan-out
366
367 void testFanMix1() {
368 float dur = 6.0f; // seconds to play each test for
369 Osc vox; // declare an oscillator
370 AR a_env(6, 1, 1); // dur, att, rel
371 RandEnvelope f_env(3, 80, 200, 40); // freq env = random walk
372 vox.setFrequency(f_env); // set the carrier's frequency
373 a_env.setScale(0.5); // multiply index envelope by mod freq
374 vox.setScale(a_env); // multiply index envelope by mod freq
375 a_env.trigger(); // start the envelope
376 Panner pan(vox, fRand1()); // stereo panner
377
378 Mixer mix(2); // stereo mixer, fixed
379 mix.addInput(pan); // add panner twice
380 mix.addInput(pan);
381
382 logMsg("playing fan-out + mixer 1...");
383 runTest(mix, dur); // run test
384 logMsg("done.");
385 }
386
387 /// Test a real fan-out + mixer
388
389 void testFanMix2() {
390 float dur = 6.0f; // seconds to play each test for
391 Osc vox; // declare an oscillator
392 AR a_env(6, 1, 1); // dur, att, rel
393 RandEnvelope f_env(3, 80, 200, 40); // freq env = random walk
394 vox.setFrequency(f_env); // set the carrier's frequency
395 a_env.setScale(0.5); // multiply index envelope by mod freq
396 vox.setScale(a_env); // multiply index envelope by mod freq
397 a_env.trigger(); // start the envelope
398 Panner pan(vox, fRand1()); // stereo panner
399
400 FanOut fan(pan, 2); // send the panner to 2 mixer ins
401 Mixer mix(2); // stereo mixer, fixed
402 mix.addInput(fan); // add fan-out twice
403 mix.addInput(fan);
404
405 logMsg("playing fan-out + mixer 2...");
406 runTest(mix, dur); // run test
407 logMsg("done.");
408 }
409
410 /// Function to create and answer a RandFreqEnv UGen patch
411 /// this is in test envelopes
412
413 UnitGenerator * createRandFreqEnvPatch(float dur);
414
415 /// Mix a few sources, adding/dropping graphs
416
417 void testDynamicMixer() {
418 float duration = 4.0f; // seg dur
419 float pauset = 0.5f; // pause dur
420 float scale = 8.0f; // ampl scale
421 Mixer mix(2); // stereo mixer
422
423 UGenVector insts;
424
425 for (unsigned i = 0; i < 4; i++) { // loop to add 4 srcs to the mix
426 UnitGenerator * pan = createRandFreqEnvPatch(duration);
427 mix.addInput(*pan);
428 mix.scaleInput(*pan, scale);
429 insts.push_back(pan);
430 }
431 logMsg("playing mix of 4 sweep layers...");
432 // srand(getpid()); // seed the rand generator -- UNIX SPECIFIC CODE HERE
433 runTest(mix, duration);
434 sleepSec(pauset); // wait some
435
436 UGenVector * ins = mix.getInputs();
437 for (unsigned i = 0; i < 2; i++) // remove 2 inputs
438 mix.removeInput(insts[i]);
439 logMsg("removed 2");
440 runTest(mix, duration);
441 sleepSec(pauset); // wait some
442
443 for (unsigned i = 0; i < 4; i++) { // add 4 more
444 UnitGenerator * pan = createRandFreqEnvPatch(duration);
445 mix.addInput(*pan);
446 mix.scaleInput(*pan, scale);
447 }
448 logMsg("added 4");
449 runTest(mix, duration);
450 sleepSec(pauset); // wait some
451
452 ins = mix.getInputs();
453 for (unsigned i = 2; i < 5; i++) // remove 3
454 mix.removeInput((*ins)[i]);
455 logMsg("removed 3");
456 runTest(mix, duration);
457 sleepSec(pauset); // wait some
458
459 for (unsigned i = 0; i < 2; i++) { // add 2 more
460 mix.addInput(insts[i]);
461 mix.scaleInput(*insts[i], scale);
462 }
463 logMsg("added 2");
464 runTest(mix, duration);
465 sleepSec(pauset); // wait some
466
467 ins = mix.getInputs();
468 for (unsigned i = 0; i < 6; i++) // remove all but one
469 mix.removeInput((*ins)[i]);
470 logMsg("removed all but one");
471 runTest(mix, duration);
472
473 logMsg("done.\n");
474 mix.deleteInputs(); // clean up
475 }
476
477 // Simple sample-average filter class - an example of a custom UnitGenerator definition
478 // This implements nextBuffer() with its own DSP routine: a scaled past-sample averager (lo-pass filter)
479
480 class SAFliter : public Effect { // It's an Effect/UnitGenerator
481 public: // created with an input UGen &scale coeff
482 SAFliter(UnitGenerator & in, float coeff = 0.5f) : Effect(in), mCoeff(coeff), mStore(0) { };
483 ~SAFliter() { };
484 // nextBuffer() gets input and operates on it
485 void nextBuffer(Buffer & outputBuffer, unsigned outBufNum) throw (CException) {
486 unsigned numFrames = outputBuffer.mNumFrames; // get buffer length
487 csl::SampleBuffer out = outputBuffer.monoBuffer(outBufNum); // get ptr to output channel
488
489 Effect::pullInput(numFrames); // get my input buffer
490 csl::SampleBuffer inPtr = mInputPtr; // get a pointer to the input samples
491
492 float val;
493 for (unsigned i = 0; i < numFrames; i++) { // here's the filter loop
494 val = *inPtr++ * mCoeff; // get next input sample, scale
495 *out++ = val + mStore; // put current output in the buffer and increment
496 mStore = val; // store val
497 }
498 }
499 protected: // class' data members
500 float mCoeff, mStore; // scale coeff & past value
501 }; // end of class SAFliter
502
503 // test it
504
505 void testSAFilter() {
506 SoundFile sfile(CGestalt::dataFolder() + "sns.aiff"); // open a speak'n'spell file
507
508 SAFliter averager(sfile); // s-a filter
509 logMsg("playing filtered snd file...");
510 runTest(averager, 5.0);
511 logMsg("done.");
512 }
513
514 //////// RUN_TESTS Function ////////
515
516 #ifndef USE_JUCE
517
518 void runTests() {
519 // testClipper(); // Simple clipper tests
520 // testFIR(); // Noise filtering test
521 // testFilters();
522 // testDynamicFilters();
523 // testReverb();
524 testStereoverb();
525 // testMultiTap();
526 }
527
528 #else
529
530 // test list for Juce GUI
531
532 testStruct effTestList[] = {
533 "Clipper", testClipper, "Demonstrate the signal clipper",
534 "FIR filter", testFIR, "Play an FIR band-pass filter",
535 "All filters", testFilters, "Test different filter types",
536 "Filtered snd file", testDynamicVoice, "Dynamic BPF on a voice track",
537 "Dynamic filter", testDynamicFilters, "Play a dynamic BP filter on noise",
538 "Many dynamic filters", testNDynamicFilters, "Many dynamic filtered-noise instruments",
539 "Reverb", testReverb, "Show mono reverb on impulses",
540 "Stereo-verb", testStereoverb, "Listen to the stereo reverb",
541 "Multi-tap delay", testMultiTap, "Play a multi-tap delay line",
542 "Split/Join filter", testSplitJoin1, "Play a splitter/joiner cross-over filter",
543 "Split/Join/Mix filter", testSplitJoin2, "Play a splitter/joiner/mixer cross-over filter",
544 "FanOut + Mixer 1", testFanMix1, "Play a sound through fan-out + mixer",
545 "FanOut + Mixer 2", testFanMix2, "Play a sound through fan-out + mixer",
546 "Dynamic Mixer", testDynamicMixer, "Mix adding/dropping sources",
547 "Block up-sizer", testBlockUpsizer, "Test the block resizer on up-sizing",
548 "Block down-sizer", testBlockDownsizer, "Test the block resizer on down-sizing",
549 "Sample-avg filter", testSAFilter, "Demo in-line sample-average-filter class",
550 NULL, NULL, NULL
551 };
552
553 #endif