Nsound  0.9.4
FilterLeastSquaresFIR.cc
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 //
3 // $Id: FilterLeastSquaresFIR.cc 874 2014-09-08 02:21:29Z weegreenblobbie $
4 //
5 // Copyright (c) 2010 to Present Nick Hilton
6 //
7 // weegreenblobbie_yahoo_com (replace '_' with '@' and '.')
8 //
9 //-----------------------------------------------------------------------------
10 
11 //-----------------------------------------------------------------------------
12 //
13 // This program is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU Library General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 //
27 //-----------------------------------------------------------------------------
28 
29 #include <Nsound/AudioStream.h>
30 #include <Nsound/Buffer.h>
32 #include <Nsound/Generator.h>
33 #include <Nsound/Plotter.h>
34 
35 #include <cmath>
36 #include <cstring>
37 #include <cstdio>
38 #include <iostream>
39 #include <sstream>
40 
41 using namespace Nsound;
42 
43 using std::cout;
44 using std::cerr;
45 using std::endl;
46 
47 #define CERR_HEADER __FILE__ << ":" << __LINE__ << ": ***ERROR: "
48 
49 //-----------------------------------------------------------------------------
52  const float64 & sample_rate,
53  uint32 kernel_size,
54  const Buffer & freq_axis,
55  const Buffer & amplitude_axis,
56  const float64 & beta)
57  :
58  Filter(sample_rate),
59  b_(NULL),
60  window_(NULL),
61  x_history_(NULL),
62  x_ptr_(NULL),
63  x_end_ptr_(NULL),
64  f_axis_(NULL),
65  a_axis_(NULL)
66 {
67  kernel_size_ = kernel_size;
68 
69  b_ = new float64[kernel_size_];
70 
71  x_history_ = new float64[kernel_size_ + 1];
74 
75  // Create the Kaiser window.
77 
78  Generator gen(1.0);
79 
80  Buffer kaiser = gen.drawWindowKaiser(kernel_size_, beta);
81 
82  memcpy(window_, kaiser.getPointer(), sizeof(float64) * kernel_size_);
83 
84  // Allocate f & a axis.
85  f_axis_ = new Buffer(16);
86  a_axis_ = new Buffer(16);
87 
88  FilterLeastSquaresFIR::makeKernel(freq_axis, amplitude_axis);
90 }
91 
92 //-----------------------------------------------------------------------------
93 // This is private and therefor disabled
96  :
97  Filter(copy.sample_rate_),
98  b_(NULL),
99  window_(NULL),
100  x_history_(NULL),
101  x_ptr_(NULL),
102  x_end_ptr_(NULL),
103  f_axis_(NULL),
104  a_axis_(NULL)
105 {
106  kernel_size_ = copy.kernel_size_;
107 
108  b_ = new float64[kernel_size_];
109 
110  x_history_ = new float64[kernel_size_ + 1];
111  x_ptr_ = x_history_;
113 
114  // Create the Kaiser window.
116 
117  // Allocate f & a axis.
118  f_axis_ = new Buffer(16);
119  a_axis_ = new Buffer(16);
120 
121  // do the rest in the oprator=()
122  *this = copy;
123 }
124 
125 //-----------------------------------------------------------------------------
128 {
129  delete [] b_;
130  delete [] window_;
131  delete [] x_history_;
132  delete f_axis_;
133  delete a_axis_;
134 }
135 
136 Buffer
138 getKernel() const
139 {
140  Buffer k(kernel_size_);
141  for(uint32 i = 0; i < kernel_size_; ++i)
142  {
143  k << b_[i];
144  }
145 
146  return k;
147 }
148 
149 void
151 setKernel(const Buffer & k)
152 {
153  if(k.getLength() != kernel_size_)
154  {
155  delete [] b_;
156  delete [] window_;
157  delete [] x_history_;
158 
159  kernel_size_ = k.getLength();
160 
161  b_ = new float64[kernel_size_];
162 
163  x_history_ = new float64[kernel_size_ + 1];
164  x_ptr_ = x_history_;
166 
167  // Create the Kaiser window.
169 
170  Generator gen(1.0);
171 
172  Buffer kaiser = gen.drawWindowKaiser(kernel_size_, 5.0);
173 
174  memcpy(window_, kaiser.getPointer(), sizeof(float64) * kernel_size_);
175 
177  }
178 
179  for(uint32 i = 0; i < kernel_size_; ++i)
180  {
181  b_[i] = k[i];
182  }
183 
184  return;
185 }
186 
187 Buffer
190 {
191  return *f_axis_;
192 }
193 
194 Buffer
197 {
198  return *a_axis_;
199 }
200 
203 filter(const AudioStream & x)
204 {
205  return Filter::filter(x);
206 }
207 
208 Buffer
210 filter(const Buffer & x)
211 {
212  return Filter::filter(x);
213 }
214 
215 float64
217 filter(const float64 & x)
218 {
219  // Write x to history
220  *x_ptr_ = x;
221 
222  // Increment history pointer
223  ++x_ptr_;
224 
225  // Bounds check.
226  if(x_ptr_ >= x_end_ptr_)
227  {
228  x_ptr_ = x_history_;
229  }
230 
231  // Perform the convolution.
232 
233  // y[n] = kernel_[0] * x[n]
234  // + kernel_[1] * x[n - 1]
235  // + kernel_[2] * x[n - 2]
236  // ...
237  // + kernel_[N] * x[n - N]
238 
239  float64 y = 0.0;
240  float64 * history = x_ptr_;
241  for(float64 * b = b_; b != b_ + kernel_size_; ++b)
242  {
243  // When we enter this loop, history is pointing at x[n + 1].
244  --history;
245 
246  // Bounds check
247  if(history < x_history_)
248  {
249  history = x_end_ptr_ - 1;
250  }
251 
252  y += *b * *history;
253  }
254 
255  return y;
256 }
257 
258 void
261  const Buffer & freq_axis,
262  const Buffer & amplitude_axis)
263 {
264  // Reset the buffers.
265  *f_axis_ = Buffer(16);
266  *a_axis_ = Buffer(16);
267 
268  float64 max_freq = sample_rate_ / 2.0;
269 
270  // Validate freq_axis and amplitude_axis.
271  if(freq_axis.getLength() != amplitude_axis.getLength() ||
272  freq_axis.getLength() % 2 != 0)
273  {
274  M_THROW("FilterLeastSquaresFIR::makeKernel(): "
275  << "freq_axis and amplitude_axis must be same length and even!");
276 
277  *f_axis_ << 0.0 << max_freq;
278  *a_axis_ << 1.0 << 1.0;
279 
281  return;
282  }
283 
284  // Veirfy all samples inf freq_axis are in assending order.
285  if(freq_axis[0] != 0.0)
286  {
287  M_THROW("FilterLeastSquaresFIR::makeKernel(): "
288  << "freq_axis[0] must be 0.0!");
289 
290  *f_axis_ << 0.0 << max_freq;
291  *a_axis_ << 1.0 << 1.0;
292 
294  return;
295  }
296 
297  if(freq_axis[freq_axis.getLength() -1 ] != max_freq)
298  {
299  M_THROW("FilterLeastSquaresFIR::makeKernel(): "
300  << "freq_axis[end] (" << freq_axis[freq_axis.getLength() -1 ]
301  << ") must be " << max_freq << "!");
302 
303  *f_axis_ << 0.0 << max_freq;
304  *a_axis_ << 1.0 << 1.0;
305 
307  return;
308  }
309 
310  // Test all samples are in assending order.
311  Buffer df = freq_axis.getDerivative(1);
312  if(df.getMin() < 0.0)
313  {
314  M_THROW("FilterLeastSquaresFIR::makeKernel(): "
315  << "Frequencies in freq_axis must be increasing!");
316 
317  *f_axis_ << 0.0 << max_freq;
318  *a_axis_ << 1.0 << 1.0;
319 
321  return;
322  }
323 
324  // All is okay.
325 
326  *f_axis_ << (freq_axis / max_freq);
327  *a_axis_ << amplitude_axis;
328 
329 //~ // Debug
330 //~ std::cout << "faxis:" << *f_axis_ << std::endl;
331 
332  Generator gen(1.0);
333 
334  Buffer f(*f_axis_/2.0);
335  Buffer a(*a_axis_);
336 
337  Buffer w = gen.drawLine(freq_axis.getLength() / 2, 1.0, 1.0);
338 
339 //~ cout << "N = " << kernel_size_ << endl
340 //~ << "F = " << endl;
341 //~
342 //~ for(uint32 i = 0; i < f.getLength(); ++i)
343 //~ {
344 //~ cout << " " << f[i] << endl;
345 //~ }
346 
347 //~ cout << "M = " << endl;
348 //~
349 //~ for(uint32 i = 0; i < a.getLength(); ++i)
350 //~ {
351 //~ cout << " " << a[i] << endl;
352 //~ }
353 
354 //~ cout << "W = " << endl;
355 //~
356 //~ for(uint32 i = 0; i < w.getLength(); ++i)
357 //~ {
358 //~ cout << " " << w[i] << endl;
359 //~ }
360 
361  df = f.getDerivative(1);
362 
363  // throw out last value
364  df = df.subbuffer(0, df.getLength() -1);
365 
366 //~ cout << "dF = " << endl;
367 
368 //~ for(uint32 i = 0; i < df.getLength(); ++i)
369 //~ {
370 //~ cout << " " << df[i] << endl;
371 //~ }
372 
373  // Check for full band
374 
375  boolean fullband = true;
376 
377 //~ cerr << CERR_HEADER << endl;
378 
379  if(df.getLength() > 1)
380  {
381  for(uint32 i = 1; i < df.getLength() - 1; i += 2)
382  {
383 
384 //~ cerr << "df[" << i << "] = " << df[i] << endl;
385 
386  if(df[i] != 0.0)
387  {
388  fullband = false;
389  break;
390  }
391  }
392  }
393 
394 //~ cout << "fullband = " << fullband << endl;
395 
396  int32 L = (kernel_size_ - 1) / 2;
397 
398 //~ cout << "L = " << L << endl;
399 
400  boolean is_odd = true;
401 
402  if(kernel_size_ % 2 == 0)
403  {
404  is_odd = false;
405  }
406 
407 //~ cout << "Nodd = " << is_odd << endl;
408 
409  Buffer m;
410 
411  if(L > 0)
412  {
413  m = gen.drawLine(L, 0, L);
414  }
415 
416  m << L;
417 
418  if(!is_odd)
419  {
420  m += 0.5;
421  }
422 
423 //~ cout << "length(m) = " << m.getLength() << endl;
424 //~ cout << "m[0] = " << m[0] << endl;
425 //~ cout << "m[end] = " << m[m.getLength()-1] << endl;
426 
427  boolean need_matrix = ! fullband;
428 
429 //~ cout << "need_matrix = " << need_matrix << endl;
430 
431  if(need_matrix)
432  {
433  M_THROW("FilterLeastSquaresFIR::makeKernel(): "
434  << "FIXME: need_matix is true!");
435  }
436 
437  Buffer k(m);
438 
439  float64 b0 = 0.0;
440 
441  if(is_odd)
442  {
443  k = m.subbuffer(1, m.getLength()-1);
444  }
445 
446 //~ cout << "length(k) = " << k.getLength() << endl;
447 //~ cout << "k[0] = " << k[0] << endl;
448 //~ cout << "k[end] = " << k[k.getLength()-1] << endl;
449 
450  float64 PI_x_2 = 2.0 * M_PI;
451 //~ float64 PI_x_4 = 4.0 * M_PI;
452  float64 PI_x_PI_x_4 = 4.0 * M_PI * M_PI;
453 
454  Buffer b = 0.0 * k;
455  Buffer kk = k * k;
456 
457  for(uint32 i = 0; i < f.getLength(); i += 2)
458  {
459  float64 t_df = f[i+1] - f[i];
460 
461  float64 mm = 0.0;
462  float64 b1 = 0.0;
463 
464  mm = (a[i+1] - a[i]) / t_df; // slope
465  b1 = a[i] - mm * f[i]; // y-intercept
466 
467 //~ cout << endl;
468 //~ cout << "s = " << i+1 << endl;
469 //~ cout << "m = " << mm << endl;
470 //~ cout << "b1 = " << b1 << endl;
471 
472  if(is_odd)
473  {
474  b0 += (b1 * t_df + mm / 2.0 * (f[i+1] * f[i+1] - f[i]*f[i]))
475  * w[(i+1)/2] * w[(i+1)/2];
476  }
477 
478 //~ cout << "b0 = " << b0 << endl;
479 
480  Buffer f1 = PI_x_2 * k * f[i+1];
481  Buffer f2 = PI_x_2 * k * f[i];
482 
483  std::stringstream ss;
484 
485  float64 abs_w2 = std::fabs(w[(i+1)/2] * w[(i+1)/2]);
486 
487 //~ cout << "W[" << i << "] = " << w[(i+1)/2] << endl;
488 //~ cout << "abs(W^2) = " << abs_w2 << endl;
489 
490 
491  for(uint32 j = 0; j < b.getLength(); ++j)
492  {
493  b[j] += (mm / PI_x_PI_x_4
494  * (std::cos(f1[j]) - std::cos(f2[j]))
495  / (k[j] * k[j])
496  ) * abs_w2;
497  }
498 
499  float64 fi = f[i];
500  float64 fi1 = f[i+1];
501 
502 //~ ss.str("");
503 //~ ss << "s = " << (i+1) << ", b1";
504 //~ b.plot(ss.str());
505 
506 //~ cout << "F[" << (i) << "] = " << fi << endl;
507 //~ cout << "F[" << (i+1) << "] = " << fi1 << endl;
508 
509 //~ // DEBUG
510 //~ Buffer SINC;
511 //~ for(uint32 j = 0; j < b.getLength(); ++j)
512 //~ {
513 //~ float64 x = PI_x_2 * k[j] * fi1;
514 //~
515 //~ if(x == 0.0) x = M_PI;
516 //~
517 //~ SINC << std::sin(x) / x;
518 //~ }
519 //~
520 //~ ss.str("");
521 //~ ss << "s = " << (i+1) << ", sinc";
522 //~ SINC.plot(ss.str());
523 
524  for(uint32 j = 0; j < b.getLength(); ++j)
525  {
526  float64 t = PI_x_2 * k[j];
527  float64 x = 0.0;
528  float64 sinc = 0.0;
529  float64 sinc1 = 0.0;
530 
531  x = t*fi;
532 
533  if(x == 0.0) x = M_PI;
534 
535  sinc = std::sin(x) / x;
536 
537  x = t*fi1;
538 
539  if(x == 0.0) x = M_PI;
540 
541  sinc1 = std::sin(x) / x;
542 
543  b[j] += ( fi1 * (mm * fi1 + b1) * sinc1
544  - fi * (mm * fi + b1) * sinc )
545  * abs_w2;
546  }
547 
548 //~ ss << "s = " << (i+1) << ", f1";
549 //~ f1.plot(ss.str());
550 //~ ss.str("");
551 //~ ss << "s = " << (i+1) << ", f2";
552 //~ f2.plot(ss.str());
553 //~
554 //~ ss.str("");
555 //~ ss << "s = " << (i+1) << ", b2";
556 //~ b.plot(ss.str());
557 
558  }
559 
560 //~ cout << "below loop" << endl
561 //~ << "b0 = " << b0 << endl;
562 
563 //~ b.plot("below loop: b");
564 
565  if(is_odd)
566  {
567  Buffer t;
568  t << b0 << b;
569  b = t;
570  }
571 
572 //~ b.plot("below loop: b2");
573 
574 //~ a = b;
575  Buffer h;
576 
577  if(! need_matrix)
578  {
579  a = w[0] * w[0] * 4.0 * b;
580 
581  if(is_odd)
582  {
583  a[0] *= 0.5;
584  }
585  }
586 
587  if(is_odd)
588  {
589  float64 a0 = a[0];
590  Buffer a2 = 0.5 * a.subbuffer(1, a.getLength());
591  h = a2.getReverse() << a0 << a2;
592  }
593  else
594  {
595  h = a.getReverse() << a;
596 
597  h *= 0.5;
598  }
599 
600 //~ h.plot("h");
601 //~ Plotter::show();
602 
603  if(kernel_size_ != h.getLength())
604  {
605  M_THROW("FilterLeastSquaresFIR::makeKernel(): "
606  << "kernel_size_ != h.getLength()!");
607 
608  for(uint32 i = 0; i < kernel_size_; ++i)
609  {
610  b_[i] = 0.0;
611  }
612  }
613  else
614  {
615  memcpy(b_, h.getPointer(), sizeof(float64)*kernel_size_);
616  }
617 
618  // Apply window
619  for(uint32 i = 0; i < kernel_size_; ++i)
620  {
621  b_[i] *= window_[i];
622  }
623 }
624 
625 //-----------------------------------------------------------------------------
629 {
630  if(this == &rhs)
631  {
632  return *this;
633  }
634 
635  if(kernel_size_ != rhs.kernel_size_)
636  {
637  delete [] b_;
638  delete [] window_;
639  delete [] x_history_;
640 
642 
643  b_ = new float64[kernel_size_];
644 
645  x_history_ = new float64[kernel_size_ + 1];
646  x_ptr_ = x_history_;
648 
649  // Create the Kaiser window.
651  }
652 
654 
655  *f_axis_ = *rhs.f_axis_;
656  *a_axis_ = *rhs.a_axis_;
657 
658  memcpy(b_, rhs.b_, sizeof(float64) * kernel_size_);
659  memcpy(window_, rhs.window_, sizeof(float64) * kernel_size_);
660 
662 
663  return *this;
664 }
665 
666 void
668 plot(boolean show_fc, boolean show_phase)
669 {
670  char title[256];
671  sprintf(title,
672  "Least Square FIR Frequency Response\n"
673  "order = %d, sr = %0.1f Hz",
675 
676  Filter::plot(show_phase);
677 
678  Plotter pylab;
679 
680  uint32 n_rows = 1;
681 
682  if(show_phase)
683  {
684  n_rows = 2;
685  }
686 
687  if(show_fc)
688  {
689  pylab.subplot(n_rows, 1, 1);
690 
691  pylab.title(title);
692  }
693 }
694 
695 void
698 {
699  memset(x_history_, 0, sizeof(float64) * (kernel_size_ + 1));
700  x_ptr_ = x_history_;
701 }
702 
703 void
706 {
707  Generator gen(1.0);
708 
709  Buffer window = gen.drawWindow(kernel_size_, type);
710 
711  memcpy(window_, window.getPointer(), sizeof(float64)*kernel_size_);
712 
713  // Make new kernel with window.
714  Buffer f(*f_axis_);
715  Buffer a(*a_axis_);
716 
717  f *= 0.5 * sample_rate_;
718 
719  makeKernel(f, a);
720 }
Buffer subbuffer(uint32 start_index, uint32 n_samples=0) const
Slice the Buffer.
Definition: Buffer.cc:2073
unsigned int uint32
Definition: Nsound.h:153
void makeKernel(const Buffer &freq_axis, const Buffer &amplitude_axis)
Buffer drawWindowKaiser(const float64 &duration, const float64 &beta=5.0) const
Draws a Kaiser window.
Definition: Generator.cc:879
AudioStream filter(const AudioStream &x)
#define M_PI
Definition: Nsound.h:121
void plot(boolean show_fc=true, boolean show_phase=false)
void title(const std::string &title, const std::string &kwargs="")
Add a title to the plot at the top and centered.
Definition: Plotter.cc:1127
Buffer drawWindow(const float64 &duration, WindowType type) const
Draws a window of the specified type.
Definition: Generator.cc:702
double float64
Definition: Nsound.h:146
Buffer getDerivative(uint32 n) const
Returns the nth derivative of the Buffer.
Definition: Buffer.h:518
uint32 getLength() const
Returns the number of samples in the Buffer.
Definition: Buffer.h:587
Base class for IIR Filters, defines the interface.
Definition: Filter.h:49
Axes subplot(const uint32 n_rows, const uint32 n_cols, const uint32 n, const std::string &kwargs="", Axes *sharex=NULL, Axes *sharey=NULL)
Creates a figure in a subplot, subplot(A, B, C, **kwargs)
Definition: Plotter.cc:1031
uint32 kernel_size_
Definition: Filter.h:116
FilterLeastSquaresFIR(const float64 &sample_rate, uint32 kernel_size, const Buffer &freq_axis, const Buffer &amplitude_axis, const float64 &beta=5.0)
void plot(boolean show_phase=false)
Definition: Filter.cc:262
AudioStream filter(const AudioStream &x)
Definition: Filter.cc:53
FilterLeastSquaresFIR & operator=(const FilterLeastSquaresFIR &rhs)
#define M_THROW(message)
Definition: Macros.h:108
const float64 * getPointer() const
Returns the raw point to the first sample in the Buffer.
Definition: Buffer.h:1382
A Buffer for storing audio samples.
Definition: Buffer.h:60
signed int int32
Definition: Nsound.h:142
float64 getMin() const
Returns the minimum sample value in the Buffer.
Definition: Buffer.cc:1004
A FIR filter that is defined as the least square error to the desired requency response.
Buffer drawLine(const float64 &duration, const float64 &amplitude_start, const float64 &amplitude_finish) const
This method draws a linear line beteween 2 points.
Definition: Generator.cc:464
Buffer getReverse() const
Reverses the samples in a copy of this Buffer.
Definition: Buffer.h:1558
WindowType
Definition: WindowType.h:39
A class the provides draw utilities and a wavetable oscillator.
Definition: Generator.h:50
float64 sample_rate_
Definition: Filter.h:113