CSL  5.2
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 
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 
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 
42  return(getPort(CSL_FILTER_FREQUENCY)->nextValue());
43 }
44 
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);
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
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) {
166 }
167 
168 /// log information about myself
169 void Filter::dump() {
170  logMsg("a Filter");
171  Scalable::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 
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 
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 
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 
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 
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
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 
368  setFrequency(coeff);
369  setAmount(1);
370  clear();
371 }
372 
373 
374 // Calculate the filter coefficients based on supplied coeffs
376  float coefficient = mInputs[CSL_FILTER_FREQUENCY]->nextValue();
377 
378  mACoeff[0] = mBCoeff[1] = 1.0;
379  mBCoeff[0] = mACoeff[1] = coefficient;
380 }
381 
383  setFrequency(500.0);
384  setAmount(0.99);
385  y1 = y2 = y3 = y4 = oldy1 = oldy2 = oldy3 = x = oldx = 0.f;
386 }
387 
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
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 
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 }