CSL 5.2

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

- File List
- File Members

Filters.cpp
Go to the documentation of this file.
1 //
2 // Filters.cpp -- implementation of the base Filter class and its standard subclasses
3 //
4 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
5 //
6
7 #include <stdlib.h>
8 #include <string.h>
9 #include <math.h>
10
11 #include "Filters.h"
12
13 using namespace csl;
14
15 // FrequencyAmount -- Constructors
16
17 FrequencyAmount::FrequencyAmount() {
18 #ifdef CSL_DEBUG
19 logMsg("FrequencyAmount::add null inputs");
20 #endif
21 }
22
23 FrequencyAmount::~FrequencyAmount() { /* no-op for now */ }
24
25 // FrequencyAmount -- Accessors
26
27 void FrequencyAmount::setFrequency(UnitGenerator & frequency) {
28 this->addInput(CSL_FILTER_FREQUENCY, frequency);
29 #ifdef CSL_DEBUG
30 logMsg("FrequencyAmount::set scale input UG");
31 #endif
32 }
33
34 void FrequencyAmount::setFrequency(float frequency) {
35 this->addInput(CSL_FILTER_FREQUENCY, frequency);
36 #ifdef CSL_DEBUG
37 logMsg("FrequencyAmount::set scale input value");
38 #endif
39 }
40
41 float FrequencyAmount::getFrequency() {
42 return(getPort(CSL_FILTER_FREQUENCY)->nextValue());
43 }
44
45 void FrequencyAmount::setAmount(UnitGenerator & amount) {
46 this->addInput(CSL_FILTER_AMOUNT, amount);
47 #ifdef CSL_DEBUG
48 logMsg("FrequencyAmount::set offset input UG");
49 #endif
50 }
51
52 void FrequencyAmount::setAmount(float amount) {
53 this->addInput(CSL_FILTER_AMOUNT, amount);
54 #ifdef CSL_DEBUG
55 logMsg("FrequencyAmount::set offset input value");
56 #endif
57 }
58
59 /// Generic Filter class with scalable order and generic next_buffer method
60 /// that implememnts the canonical filter diference equation.
61 /// Subclasses must supply filter order and override the setupCoeffs() method.
62
63 /// Default constructor generates a zeroth order "do-nothing" filter
64 Filter::Filter () : Effect(), Scalable(1.f,0.f) {
65 init(1,1);
66 };
67
68
69 Filter::Filter(unsigned num_b, unsigned num_a) : Effect(), Scalable(1.f,0.f) {
70 init(num_a, num_b);
71 }
72
73 Filter::Filter(UnitGenerator & in, unsigned num_b, unsigned num_a) : Effect(in), Scalable(1.f,0.f) {
74 init(num_a, num_b);
75 };
76
77 /// This constructor takes arrays of coefficients and constructs the filter accordingly.
78 Filter::Filter(UnitGenerator & in, SampleBuffer bCoeffs, SampleBuffer aCoeffs, unsigned num_b, unsigned num_a)
79 : Effect(in), Scalable(1.f,0.f) {
80 init(num_a, num_b);
81 setupCoeffs(bCoeffs, aCoeffs, num_b, num_a);
82 };
83
84 void Filter::init(unsigned a, unsigned b) {
85 mBNum = b;
86 mANum = a;
87 mBCoeff[0] = 1.f;
88 mACoeff[0] = 1.f;
89 mPrevOutputs = new Buffer(1, mANum);
90 mPrevInputs = new Buffer(1, mBNum);
91 mPrevOutputs->allocateBuffers();
92 mPrevInputs->allocateBuffers();
93 mFrameRate = CGestalt::frameRate();
94 }
95
96 /// Filter destructor frees temp memory
97
98 Filter::~Filter (void) {
99 if (mPrevOutputs) delete mPrevOutputs;
100 if (mPrevInputs) delete mPrevInputs;
101 };
102
103 // This version does in-place filtering
104
105 void Filter::nextBuffer(Buffer & outputBuffer, unsigned outBufNum) throw (CException) {
106 #ifdef CSL_DEBUG
107 logMsg("Filter nextBuffer");
108 #endif
109 SampleBuffer out = outputBuffer.monoBuffer(outBufNum); // get ptr to output channel
110 unsigned numFrames = outputBuffer.mNumFrames; // get buffer length
111 SampleBuffer prevOuts = mPrevOutputs->monoBuffer(0);
112 SampleBuffer prevIns = mPrevInputs->monoBuffer(0);
113 SampleBuffer inputPtr;
114
115 DECLARE_SCALABLE_CONTROLS; // declare the scale/offset buffers and values
116 DECLARE_FILTER_CONTROLS; // declare the freq/bw buffers and values
117 LOAD_SCALABLE_CONTROLS;
118 LOAD_FILTER_CONTROLS;
119
120 bool isDynamic = false;
121 if ((freqPort && ( ! freqPort->isFixed())) || (bwPort && ( ! bwPort->isFixed())))
122 isDynamic = true;
123
124 if (! isInline) {
125 Effect::pullInput(numFrames); // get some input
126 inputPtr = mInputPtr; // get a pointer to the input samples
127 } else
128 inputPtr = out;
129
130 SampleBuffer prevOPtr = prevOuts;
131 SampleBuffer prevIPtr = prevIns;
132
133 for (unsigned i = 0; i < numFrames; i++) { // here's the canonical N-quad filter loop
134 if (isDynamic)
135 this->setupCoeffs(); // calculate new coefficients for next sample
136 *prevOPtr = 0.f;
137 *prevIPtr = scaleValue * *inputPtr++ + offsetValue; // get next input sample, scale & offset
138 for (unsigned j = mBNum - 1; j > 0; j--) {
139 *prevOPtr += mBCoeff[j] * prevIns[j]; // prevIns or prevOuts?
140 prevIns[j] = prevIns[j-1];
141 }
142 *prevOPtr += mBCoeff[0] * prevIns[0];
143 for (unsigned j = mANum - 1; j > 0; j--) {
144 *prevOPtr += -mACoeff[j] * prevOuts[j];
145 prevOuts[j] = prevOuts[j-1];
146 }
147 // put current output in the buffer and increment
148 *out++ = (*prevOPtr * scaleValue) + offsetValue;
149
150 UPDATE_SCALABLE_CONTROLS; // update the dynamic scale/offset
151 }
152 }
153
154 /// this version is to be inherited by the subclasses. provides a way to directly supply the filter info
155
156 void Filter::setupCoeffs(SampleBuffer bCoeffs, SampleBuffer aCoeffs, unsigned num_b, unsigned num_a ) {
157 for (unsigned i = 0; i < num_b; i++)
158 mBCoeff[i] = bCoeffs[i];
159 for (unsigned i = 0; i < num_a; i++)
160 mACoeff[i] = aCoeffs[i];
161 }
162
163 void Filter::clear(void) {
164 mPrevInputs->zeroBuffers();
165 mPrevOutputs->zeroBuffers();
166 }
167
168 /// log information about myself
169 void Filter::dump() {
170 logMsg("a Filter");
171 Scalable::dump();
172 UnitGenerator::dump();
173
174 fprintf(stderr, "A coefficients ");
175 for (unsigned i=0;i<mANum;i++) {
176 fprintf(stderr, "%.2f, ",mACoeff[i]);
177 }
178 fprintf(stderr, "\n");
179 fprintf(stderr, "B coefficients ");
180 for (unsigned i=0;i<mBNum;i++) {
181 fprintf(stderr, "%.2f, ",mBCoeff[i]);
182 }
183 fprintf(stderr, "\n");
184 }
185
186 /// Butterworth IIR (2nd order recursive) filter.
187 /// This operates upon a buffer of frames of amplitude samples by applying the following equation
188 /// y(n) = a0*x(n) + a1*x(n-1) + a2*x(n-2) - b1*y(n-1) - b2*y(n-2) where x is an amplitude sample.
189 /// It has constructors that can calculate the coefficients from a given cutoff frequency.
190
191 Butter::Butter () { }
192
193 Butter::Butter (ButterworthType type, float cutoff) : Filter(3,3) {
194 mFilterType = type;
195 setFrequency(cutoff);
196 setAmount(cutoff / 10.0);
197 setupCoeffs();
198 clear();
199 }
200
201 Butter::Butter (UnitGenerator & in, ButterworthType type, float cutoff) : Filter(in,3,3) {
202 mFilterType = type;
203 setFrequency(cutoff);
204 setAmount(cutoff / 10.0);
205 setupCoeffs();
206 clear();
207 }
208
209 Butter::Butter (UnitGenerator & in, ButterworthType type, UnitGenerator & cutoff) : Filter(in,3,3) {
210 mFilterType = type;
211 setFrequency(cutoff);
212 setAmount(100);
213 setupCoeffs();
214 clear();
215 }
216
217 // constructor for dual filter parameters (i.e. band pass and reject)
218
219 Butter::Butter (ButterworthType type, float center, float bandwidth) : Filter(3,3) {
220 mFilterType = type;
221 setFrequency(center);
222 setAmount(bandwidth);
223 setupCoeffs();
224 clear();
225 }
226
227 Butter::Butter (UnitGenerator & in, ButterworthType type, float center, float bandwidth)
228 : Filter(in,3,3) {
229 mFilterType = type;
230 setFrequency(center);
231 setAmount(bandwidth);
232 setupCoeffs();
233 clear();
234 }
235
236 Butter::Butter (UnitGenerator & in, ButterworthType type, UnitGenerator & center, UnitGenerator & bandwidth)
237 : Filter(in,3,3) {
238 mFilterType = type;
239 setFrequency(center);
240 setAmount(bandwidth);
241 setupCoeffs();
242 clear();
243 }
244
245 // Calculate the filter coefficients based on the frequency characteristics
246
247 void Butter::setupCoeffs () {
248 float C, D; // handy intermediate variables
249 float centreFreq = mInputs[CSL_FILTER_FREQUENCY]->nextValue();
250 float bandwidth = mInputs[CSL_FILTER_AMOUNT]->nextValue();
251
252 mACoeff[0] = 0.f;
253 switch (mFilterType) { // These are the Butterworth equations
254 case BW_LOW_PASS :
255 C = 1 / (tanf (CSL_PI * (centreFreq/mFrameRate)) );
256 mBCoeff[0] = 1 / (1 + (CSL_SQRT_TWO * C) + (C * C) );
257 mBCoeff[1] = 2 * mBCoeff[0];
258 mBCoeff[2] = mBCoeff[0];
259 mACoeff[1] = 2 * mBCoeff[0] * (1 - (C * C));
260 mACoeff[2] = mBCoeff[0] * (1 - (CSL_SQRT_TWO * C) + (C * C) );
261 break;
262 case BW_HIGH_PASS :
263 C = tanf (CSL_PI * centreFreq / mFrameRate);
264 mBCoeff[0] = 1 / (1 + (CSL_SQRT_TWO * C) + (C * C) );
265 mBCoeff[1] = -2 * mBCoeff[0];
266 mBCoeff[2] = mBCoeff[0];
267 mACoeff[1] = 2 * mBCoeff[0] * ((C * C) - 1);
268 mACoeff[2] = mBCoeff[0] * (1 - (CSL_SQRT_TWO * C) + (C * C) );
269 break;
270 case BW_BAND_PASS :
271 C = 1 / (tanf (CSL_PI * bandwidth / mFrameRate) );
272 D = 2 * cos (2 * CSL_PI * centreFreq / mFrameRate);
273 mBCoeff[0] = 1 / (1 + C);
274 mBCoeff[1] = 0;
275 mBCoeff[2] = -1 * mBCoeff[0];
276 mACoeff[1] = -1 * mBCoeff[0] * C * D;
277 mACoeff[2] = mBCoeff[0] * (C - 1);
278 break;
279 case BW_BAND_STOP :
280 C = tanf (CSL_PI * bandwidth / mFrameRate);
281 D = 2 * cos (2 * CSL_PI * centreFreq / mFrameRate);
282 mBCoeff[0] = 1 / (1 + C);
283 mBCoeff[1] = -1 * mBCoeff[0] * D;
284 mBCoeff[2] = mBCoeff[0];
285 mACoeff[1] = -1 * mBCoeff[0] * D;
286 mACoeff[2] = mBCoeff[0] * (1 - C);
287 break;
288 } // switch
289 }
290
291
292 Formant::Formant (UnitGenerator & in, float cutoff, float radius) : Filter (in, 3,3) {
293 mNormalize = true;
294 setFrequency(cutoff);
295 setAmount(radius);
296 setupCoeffs();
297 clear();
298 }
299
300 Formant::Formant (UnitGenerator & in, UnitGenerator & cutoff, float radius) : Filter (in, 3,3) {
301 mNormalize = true;
302 setFrequency(cutoff);
303 setAmount(radius);
304 setupCoeffs();
305 clear();
306 }
307
308 void Formant::setNormalize(bool normalize) {
309 mNormalize = normalize;
310 setupCoeffs();
311 };
312
313 // Calculate the filter coefficients based on the frequency characteristics
314 // to be done every sample for dynamic controls
315
316 void Formant::setupCoeffs () {
317 float centreFreq = mInputs[CSL_FILTER_FREQUENCY]->nextValue();
318 float radius = mInputs[CSL_FILTER_AMOUNT]->nextValue();
319
320 mACoeff[0] = 1.0F;
321 mACoeff[1] = cos(CSL_TWOPI * centreFreq * 1.f / mFrameRate ) * (-2.0F) * radius;
322 mACoeff[2] = radius * radius;
323 if (mNormalize ) { // Use zeros at +- 1 and normalize the filter peak gain.
324 mBCoeff[0] = 0.5 - 0.5 * mACoeff[2];
325 mBCoeff[1] = 0.0;
326 mBCoeff[2] = -mBCoeff[0];
327 } else {
328 mBCoeff[0] = 1.0F;
329 mBCoeff[1] = 0.0F;
330 mBCoeff[2] = -1.0F;
331 }
332 }
333
334 Notch::Notch (UnitGenerator & in, float cutoff, float radius) : Filter (in,3,3) {
335 setFrequency(cutoff);
336 setAmount(radius);
337 clear();
338 }
339
340 Notch::Notch (UnitGenerator & in, UnitGenerator & cutoff, float radius) : Filter(in,3,3) {
341 setFrequency(cutoff);
342 setAmount(radius);
343 clear();
344 }
345
346 // Calculate the filter coefficients based on the frequency characteristics
347 void Notch::setupCoeffs () {
348 float centreFreq = mInputs[CSL_FILTER_FREQUENCY]->nextValue();
349 float radius = mInputs[CSL_FILTER_AMOUNT]->nextValue();
350
351 //coeff's similar to formant but opposite
352 mBCoeff[0] = 1.0F;
353 mBCoeff[1] = cos(CSL_TWOPI * centreFreq * 1.0f / mFrameRate ) * (-2.0F) * radius;
354 mBCoeff[2] = radius * radius;
355 mACoeff[0] = 1.0F;
356 mACoeff[1] = 0.0F;
357 mACoeff[2] = 0.0F;
358 }
359
360 // the Frequency input of FrequencyAmount is used as the Allpass coefficient
361 Allpass::Allpass (UnitGenerator & in, float coeff) : Filter(in,2,2) { // 1st order 1-pole 1-zero filter
362 setFrequency(coeff);
363 setAmount(1);
364 clear();
365 }
366
367 Allpass::Allpass (UnitGenerator & in, UnitGenerator & coeff) : Filter(in,2,2) {
368 setFrequency(coeff);
369 setAmount(1);
370 clear();
371 }
372
373
374 // Calculate the filter coefficients based on supplied coeffs
375 void Allpass::setupCoeffs () {
376 float coefficient = mInputs[CSL_FILTER_FREQUENCY]->nextValue();
377
378 mACoeff[0] = mBCoeff[1] = 1.0;
379 mBCoeff[0] = mACoeff[1] = coefficient;
380 }
381
382 Moog::Moog (UnitGenerator & in) : Filter(in) {
383 setFrequency(500.0);
384 setAmount(0.99);
385 y1 = y2 = y3 = y4 = oldy1 = oldy2 = oldy3 = x = oldx = 0.f;
386 }
387
388 Moog::Moog (UnitGenerator & in, UnitGenerator & cutoff) : Filter(in) {
389 setFrequency(cutoff);
390 setAmount(0.99);
391 y1 = y2 = y3 = y4 = oldy1 = oldy2 = oldy3 = x = oldx = 0.f;
392 }
393
394 Moog::Moog (UnitGenerator & in, UnitGenerator & cutoff, UnitGenerator & resonance) : Filter(in) {
395 setFrequency(cutoff);
396 setAmount(resonance);
397 y1 = y2 = y3 = y4 = oldy1 = oldy2 = oldy3 = x = oldx = 0.f;
398 }
399
400 Moog::Moog (UnitGenerator & in, float cutoff) : Filter(in) {
401 setFrequency(cutoff);
402 setAmount(0.5);
403 y1 = y2 = y3 = y4 = oldy1 = oldy2 = oldy3 = x = oldx = 0.f;
404 }
405
406 Moog::Moog (UnitGenerator & in, float cutoff, float resonance) : Filter(in) {
407 setFrequency(cutoff);
408 setAmount(resonance);
409 y1 = y2 = y3 = y4 = oldy1 = oldy2 = oldy3 = x = oldx = 0.f;
410 }
411
412 // Filter the next buffer of the input -- adapted from moog filter at music.dsp source code archive
413
414 void Moog::nextBuffer(Buffer & outputBuffer, unsigned outBufNum) throw (CException) {
415 #ifdef CSL_DEBUG
416 logMsg("Moog Filter nextBuffer");
417 #endif
418 sample* out = outputBuffer.monoBuffer(outBufNum); // get ptr to output channel
419 unsigned numFrames = outputBuffer.mNumFrames; // get buffer length
420 SampleBuffer inputPtr = mInputs[CSL_INPUT]->mBuffer->buffer(outBufNum);
421 DECLARE_SCALABLE_CONTROLS; // declare the scale/offset buffers and values
422 DECLARE_FILTER_CONTROLS; // declare the freq/bw buffers and values
423 LOAD_SCALABLE_CONTROLS;
424 LOAD_FILTER_CONTROLS;
425 this->setupCoeffs();
426
427 for (unsigned i = 0; i < numFrames; i++) {
428 // this->setupCoeffs();
429 // --Inverted feed back for corner peaking
430 x = *inputPtr++ - r * y4;
431 // Four cascaded onepole filters (bilinear transform)
432 y1 = x * p + oldx * p - k * y1;
433 y2 = y1 * p + oldy1 * p - k * y2;
434 y3 = y2 * p + oldy2 * p - k * y3;
435 y4 = y3 * p + oldy3 * p - k * y4;
436 // Clipper band limited sigmoid
437 y4 = y4 - (y4*y4*y4) / 6;
438 oldx = x;
439 oldy1 = y1;
440 oldy2 = y2;
441 oldy3 = y3;
442 *out++ = y4;
443 UPDATE_SCALABLE_CONTROLS; // update the dynamic scale/offset
444 }
445 }
446
447 // Calculate the filter coefficients based on the frequency characteristics
448
449 void Moog::setupCoeffs () {
450 float centreFreq = mInputs[CSL_FILTER_FREQUENCY]->nextValue();
451 float resonance = mInputs[CSL_FILTER_AMOUNT]->nextValue();
452
453 float f, scale;
454 f = 2 * centreFreq / mFrameRate; //[0 - 1]
455 k = 3.6 * f - 1.6 * f * f - 1; //(Empirical tunning)
456 p = (k + 1 ) * 0.5;
457 scale = exp((1 - p ) * 1.386249 );
458 r = resonance * scale;
459 }