Nsound  0.9.4
Spectrogram.cc
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 //
3 // $Id: Spectrogram.cc 878 2014-11-23 04:51:23Z weegreenblobbie $
4 //
5 // Copyright (c) 2008 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>
31 #include <Nsound/FFTransform.h>
32 #include <Nsound/Generator.h>
33 #include <Nsound/Spectrogram.h>
34 #include <Nsound/Plotter.h>
35 
36 #include <iostream>
37 
38 using namespace Nsound;
39 
40 using std::cerr;
41 using std::endl;
42 
45  const Buffer & x,
46  const float64 & sample_rate,
47  const float64 & time_window,
48  const float64 & time_step,
49  const WindowType & type)
50  :
51  sample_rate_(sample_rate),
52  frequency_axis_(NULL),
53  time_axis_(NULL),
54  real_(NULL),
55  imag_(NULL),
56  fft_window_(new Buffer()),
57  nfft_(0),
58  n_window_samples_(0),
59  fft_(new FFTransform(sample_rate))
60 {
62 
63  if(sample_rate <= 0.0)
64  {
65  cerr << "Nsound::Spectrogram(): "
66  << "sample_rate <= 0.0 ("
67  << sample_rate
68  << "<= 0.0)"
69  << endl;
70 
71  // Use some default.
72  sr = 44100.0;
73  }
74 
75  float64 time_w = time_window;
76 
77  if(time_window <= 0.0)
78  {
79  cerr << "Nsound::Spectrogram(): "
80  << "time_window <= 0.0 ("
81  << time_window
82  << "<= 0.0)"
83  << endl;
84 
85  // Use some default.
86  time_w = 0.020;
87  }
88 
89  float64 time_s = time_step;
90 
91  if(time_step <= 0.0)
92  {
93  cerr << "Nsound::Spectrogram(): "
94  << "time_step <= 0.0 ("
95  << time_step
96  << "<= 0.0)"
97  << endl;
98 
99  // Use some default.
100  time_s = 0.020;
101  }
102 
103  n_window_samples_ = static_cast<int32>(time_w * sr + 0.5);
104  uint32 window_step = static_cast<int32>(time_s * sr + 0.5);
105 
106  // Calculate the fft size.
108 
109  Generator gen(1);
110 
112 
113  int32 n_samples = x.getLength();
114  int32 h_window_samples = n_window_samples_ / 2;
115 
116  int32 i = - h_window_samples;
117 
118  // Count how many windows we'll need.
119  uint32 k = 0;
120 
121  while(i < n_samples)
122  {
123  ++k;
124  i += window_step;
125  }
126 
127  // Allocate matricies.
128 
129  frequency_axis_ = new Buffer(1); // gets assigned to later.
130  time_axis_ = new Buffer(k);
131 
132  // Using AudioStreams for a matrix-like container where channels are rows
133  // and Buffer length is columns. Here I'm setting samplerate to 1 (doesn't
134  // really matter) and pre allocating the buffers to length 1, since they
135  // get assigned to later.
136  real_ = new AudioStream(1, k, 1);
137  imag_ = new AudioStream(1, k, 1);
138 
139  if(frequency_axis_ == NULL)
140  {
141  cerr << "Nsound::Spectrogram::Spectrogram(): "
142  << "failed to allocate memory for frequency_axis_"
143  << endl;
144 
145  return;
146  }
147 
148  if(time_axis_ == NULL)
149  {
150  cerr << "Nsound::Spectrogram::Spectrogram(): "
151  << "failed to allocate memory for time_axis_"
152  << endl;
153 
154  return;
155  }
156 
157  if(real_ == NULL)
158  {
159  cerr << "Nsound::Spectrogram::Spectrogram(): "
160  << "failed to allocate memory for real_"
161  << endl;
162 
163  return;
164  }
165 
166  if(imag_ == NULL)
167  {
168  cerr << "Nsound::Spectrogram::Spectrogram(): "
169  << "failed to allocate memory for imag_"
170  << endl;
171 
172  return;
173  }
174 
175  float64 time = 0.0;
176  boolean once = true;
177  k = 0;
178  i = - h_window_samples;
179  while(i < n_samples)
180  {
181  *time_axis_ << time;
182 
183  // Extract a sub signal.
184  int32 i0 = i;
185 
187 
188  // Range check
189  if(i0 < 0)
190  {
191  int32 n_left = i + n_window_samples_;
192  int32 n_zeros = n_window_samples_ - n_left;
193 
194  sub = gen.silence(n_zeros)
195  << (x.subbuffer(0, n_left) * gen.drawWindow(n_left, type));
196  }
197  else
198  {
199  sub = x.subbuffer(i0, n_window_samples_) * *fft_window_;
200  }
201 
202  // Pad with zeros if at the end.
203  if(sub.getLength() < n_window_samples_)
204  {
205  int32 n_left = sub.getLength();
206  int32 n_zeros = n_window_samples_ - n_left ;
207 
208  sub *= gen.drawWindow(n_left, type);
209 
210  sub << gen.silence(n_zeros);
211  }
212 
213  // Forward FFT
214  FFTChunkVector vec = fft_->fft(sub, nfft_, 0);
215 
216  (*real_)[k] = vec[0].getReal();
217  (*imag_)[k] = vec[0].getImaginary();
218  ++k;
219 
220  if(once)
221  {
222  once = false;
223  *frequency_axis_ = vec[0].getFrequencyAxis().subbuffer(1);
224  }
225 
226  i += window_step;
227  time += time_step;
228  }
229 }
230 
233  :
234  frequency_axis_(new Buffer(*copy.frequency_axis_)),
235  time_axis_(new Buffer(*copy.time_axis_)),
236  real_(new AudioStream(*copy.real_)),
237  imag_(new AudioStream(*copy.imag_)),
238  fft_window_(new Buffer(*copy.fft_window_)),
239  nfft_(copy.nfft_),
240  n_window_samples_(copy.n_window_samples_),
241  fft_(new FFTransform(*copy.fft_))
242 {
243 }
244 
247 {
248  delete frequency_axis_;
249  delete time_axis_;
250  delete real_;
251  delete imag_;
252  delete fft_window_;
253  delete fft_;
254 };
255 
256 Buffer
259 {
260  return Buffer(*frequency_axis_);
261 }
262 
266 {
267  return ((*real_^2.0) + (*imag_^2.0))^0.5;
268 }
269 
270 Buffer
272 getTimeAxis() const
273 {
274  return Buffer(*time_axis_);
275 }
276 
277 Spectrogram &
279 operator=(const Spectrogram & rhs)
280 {
281  if(this == &rhs)
282  {
283  return *this;
284  }
285 
288  *time_axis_ = *rhs.time_axis_;
289  *real_ = *rhs.real_;
290  *imag_ = *rhs.imag_;
291  *fft_window_ = *rhs.fft_window_;
292  nfft_ = rhs.nfft_;
294  *fft_ = *rhs.fft_;
295 
296  return *this;
297 }
298 
299 void
302  const std::string & title,
303  const boolean & use_dB,
304  const float64 & squash) const
305 {
306  AudioStream mag = getMagnitude();
307 
308  // Transpose so x is the time axis.
309  mag.transpose();
310 
311  if(use_dB)
312  {
313  mag += 1.0;
314  mag.dB();
315  }
316  else if(squash > 0.0)
317  {
318  mag ^= squash;
319  }
320  else
321  {
322  cerr << "Nsound::Spectrogram::plot(): "
323  << "use_dB is false and squash <= 0.0 ("
324  << squash
325  << " <= 0.0)"
326  << endl;
327 
328  return;
329  }
330 
331  Plotter pylab;
332 
333  pylab.figure();
334  pylab.imagesc(*time_axis_, *frequency_axis_, mag);
335  pylab.xlabel("Time (sec)");
336  pylab.ylabel("Frequency (Hz)");
337  pylab.title(title);
338 }
339 
340 Buffer
343 {
344  Buffer signal(n_window_samples_);
345 
346  uint32 n_samples = x.getLength();
347 
348  // grab last n samples
349  if(n_samples >= n_window_samples_)
350  {
351  uint32 index = n_samples - n_window_samples_;
352  signal = x.subbuffer(index, n_window_samples_);
353  }
354 
355  // pad with zeros
356  else
357  {
358  uint32 n_zeros = n_window_samples_ - n_samples;
359 
360  signal << x << Buffer::zeros(n_zeros);
361  }
362 
363  // Apply window
364  signal *= *fft_window_;
365 
366  // Forward FFT
367  FFTChunkVector vec = fft_->fft(signal, nfft_, 0);
368 
369  return vec[0].getMagnitude();
370 }
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 xlabel(const std::string &label, const std::string &kwargs="")
Add a label x axis.
Definition: Plotter.cc:1154
Buffer computeMagnitude(const Buffer &x)
Definition: Spectrogram.cc:342
Buffer * frequency_axis_
Definition: Spectrogram.h:88
Buffer getTimeAxis() const
Definition: Spectrogram.cc:272
Spectrogram & operator=(const Spectrogram &rhs)
Definition: Spectrogram.cc:279
static Buffer zeros(const uint32 n_samples)
Returns a Buffer full of zeros of length n_samples.
Definition: Buffer.cc:2265
void figure(const std::string &kwargs="") const
Creates a new figure window to plot in.
Definition: Plotter.cc:455
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
static int32 roundUp2(int32 raw)
Returns nearest power of 2 >= raw.
Definition: FFTransform.cc:274
uint32 getLength() const
Returns the number of samples in the Buffer.
Definition: Buffer.h:587
Buffer silence(const float64 &duration) const
This method generates silence.
Definition: Generator.cc:1310
The result from an STFT.
Definition: Spectrogram.h:47
uint32 n_window_samples_
Definition: Spectrogram.h:96
void imagesc(const AudioStream &Z, const std::string &kwargs="")
Plots the AudioStream like a 2D matrix.
Definition: Plotter.cc:593
AudioStream * imag_
Definition: Spectrogram.h:92
Buffer getFrequencyAxis() const
Definition: Spectrogram.cc:258
AudioStream * real_
Definition: Spectrogram.h:91
void transpose()
Treating the AudioStream as a matrix, this peforms a matrix transpose.
Definition: AudioStream.cc:875
void ylabel(const std::string &label, const std::string &kwargs="")
Add a label y axis.
Definition: Plotter.cc:1180
A Class that performes the Fast Fouier Transfrom on a Buffer.
Definition: FFTransform.h:57
Buffer * fft_window_
Definition: Spectrogram.h:94
void dB()
Modifies the AudioStream so each sample is converted to dB, 20 * log10(sample).
Definition: AudioStream.cc:161
A Buffer for storing audio samples.
Definition: Buffer.h:60
FFTransform * fft_
Definition: Spectrogram.h:97
signed int int32
Definition: Nsound.h:142
Buffer fft(const Buffer &time_domain) const
Transforms the time_domain signal and calculates the FFT.
Definition: FFTransform.cc:50
void plot(const std::string &title="", const boolean &use_dB=true, const float64 &squash=0.5) const
Definition: Spectrogram.cc:301
Spectrogram(const Buffer &x, const float64 &sample_rate, const float64 &time_window, const float64 &time_step, const WindowType &type)
Definition: Spectrogram.cc:44
AudioStream getMagnitude() const
Definition: Spectrogram.cc:265
std::vector< FFTChunk > FFTChunkVector
Definition: FFTChunk.h:119
WindowType
Definition: WindowType.h:39
A class the provides draw utilities and a wavetable oscillator.
Definition: Generator.h:50
float64 sr
Definition: example3.cc:24