SoDaRadio-5.0.3-master:8901fb5
Spectrogram.cxx
Go to the documentation of this file.
1 /*
2 Copyright (c) 2012, Matthew H. Reilly (kb1vc)
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 
9  Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in
13  the documentation and/or other materials provided with the
14  distribution.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include "Spectrogram.hxx"
29 #include <math.h>
30 #include <iostream>
31 #include <boost/format.hpp>
32 #include <stdexcept>
33 
34 SoDa::Spectrogram::Spectrogram(unsigned int fftlen)
35 {
36  // remember how long the output will be
37  fft_len = fftlen;
38 
39  // allocate the internal buffers
40  win_samp = (std::complex<float>*) fftwf_alloc_complex(fft_len);
41  fft_out = (std::complex<float>*) fftwf_alloc_complex(fft_len);
42  result = new float[fft_len];
43 
44  // create the FFT plan
45  fftplan = fftwf_plan_dft_1d(fft_len,
46  (fftwf_complex *) win_samp,
47  (fftwf_complex *) fft_out,
48  FFTW_FORWARD, FFTW_ESTIMATE);
49 
50  // setup the blackman harris window.
52 }
53 
54 
56 {
57  unsigned int i;
58  float a0 = 0.35875;
59  float a1 = 0.48829;
60  float a2 = 0.14128;
61  float a3 = 0.01168;
62 
63  float * w = new float[fft_len];
64  float anginc = 2.0 * M_PI / ((float) fft_len - 1);
65  float ang = 0.0;
66  for(i = 0; i < fft_len; i++) {
67  ang = anginc * ((float) i);
68  w[i] = a0 - a1 * cos(ang) + a2 * cos(2.0 * ang) -a3 * cos(3.0 * ang);
69  }
70 
71  return w;
72 }
73 
74 void SoDa::Spectrogram::apply_common(std::complex<float> * invec,
75  unsigned int inveclen)
76 {
77  unsigned int i, j;
78  float repl_count;
79 
80  repl_count = floor(((float) inveclen) / ((float) fft_len));
81  float gain_adj = 1.0 / repl_count;
82 
83  // zero the result accumulate buffer.
84  for(j = 0; j < fft_len; j++) result[j] = 0.0;
85  if(fft_len > inveclen) {
86  throw std::runtime_error((boost::format("inveclen %d less than fftlen %d\n") % inveclen % fft_len).str());
87  }
88  // accumulate FFT results over the length of the buffer.
89  for(i = 0; i < (inveclen + 1 - fft_len); i += (fft_len / 2)) {
90  std::complex<float> *v = &(invec[i]);
91 
92  // window the input
93  for(j = 0; j < fft_len; j++) {
94  win_samp[j] = v[j] * window[j];
95  }
96 
97  // do the fft
98  fftwf_execute(fftplan);
99 
100  // accumulate the magnitude squared result
101  for(j = 0; j < fft_len; j++) {
102  float re, im;
103  re = fft_out[j].real();
104  im = fft_out[j].imag();
105  result[j] += gain_adj * (re * re + im * im);
106  }
107  }
108 }
109 
110 
111 void SoDa::Spectrogram::apply_acc(std::complex<float> * invec,
112  unsigned int inveclen,
113  float * outvec,
114  float accumulation_gain)
115 {
116 
117  if(inveclen < fft_len) {
118  std::cerr << "Input vector is shorter than FFT buffer " <<
119  inveclen << " less than " << fft_len << std::endl;
120  return;
121  }
122 
123  // do the front end FFT
124  apply_common(invec, inveclen);
125 
126  // now copy to the output buffer
127  // this is mag^2 divided by the square of of the
128  // number of segments we FFTd.
129  unsigned int j, k;
130 
131  for(j = 0, k = (fft_len / 2); j < (fft_len / 2); j++, k++) {
132  outvec[j] = result[k] * (1.0 - accumulation_gain) +
133  outvec[j] * accumulation_gain;
134  }
135 
136  for(j = (fft_len / 2), k = 0; j < fft_len; j++, k++) {
137  outvec[j] = result[k] * (1.0 - accumulation_gain) +
138  outvec[j] * accumulation_gain;
139  }
140 }
141 
142 void SoDa::Spectrogram::apply_max(std::complex<float> * invec,
143  unsigned int inveclen,
144  float * outvec,
145  bool first)
146 {
147  // do the front end FFT
148  apply_common(invec, inveclen);
149 
150  // now copy to the output buffer
151  // this is mag^2 divided by the square of of the
152  // number of segments we FFTd.
153  unsigned int j, k;
154 
155  for(j = 0, k = (fft_len / 2); j < (fft_len / 2); j++, k++) {
156  if(first) outvec[j] = result[k];
157  else {
158  outvec[j] = (result[k] > outvec[j]) ? result[k] : outvec[j];
159  }
160  }
161 
162  for(j = (fft_len / 2), k = 0; j < fft_len; j++, k++) {
163  if(first) outvec[j] = result[k];
164  else {
165  outvec[j] = (result[k] > outvec[j]) ? result[k] : outvec[j];
166  }
167  }
168 }
float * initBlackmanHarris()
all spectrograms are under a blackman harris window
Definition: Spectrogram.cxx:55
void apply_acc(std::complex< float > *invec, unsigned int inveclen, float *outvec, float accumulation_gain=0.0)
Calculate the spectrogram from an input vector – add it to an accumulation buffer.
fftwf_plan fftplan
Definition: Spectrogram.hxx:89
Spectrogram(unsigned int fftlen)
Constructor.
Definition: Spectrogram.cxx:34
std::complex< float > * win_samp
Definition: Spectrogram.hxx:91
unsigned int fft_len
Definition: Spectrogram.hxx:94
std::complex< float > * fft_out
Definition: Spectrogram.hxx:92
void apply_max(std::complex< float > *invec, unsigned int inveclen, float *outvec, bool first=true)
Calculate the spectrogram from an input vector – add it to an accumulation buffer.
void apply_common(std::complex< float > *invec, unsigned int inveclen)
this is the common spectrogram calculation (window + fft + mag)
Definition: Spectrogram.cxx:74