Audacity 3.2.0
SpectrumAnalyst.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 SpectrumAnalyst.cpp
6
7 Dominic Mazzoni
8 Paul Licameli split from FreqWindow.cpp
9
10*******************************************************************//*******************************************************************/
19
20/*
21 Salvo Ventura - November 2006
22 Extended range check for additional FFT windows
23*/
24
25#include "SpectrumAnalyst.h"
26#include "FFT.h"
27#include "MemoryX.h"
28
30: mAlg(Spectrum)
31, mRate(0.0)
32, mWindowSize(0)
33{
34}
35
37{
38}
39
40bool SpectrumAnalyst::Calculate(Algorithm alg, int windowFunc,
41 size_t windowSize, double rate,
42 const float *data, size_t dataLen,
43 float *pYMin, float *pYMax,
44 ProgressFn progress)
45{
46 // Wipe old data
47 mProcessed.resize(0);
48 mRate = 0.0;
49 mWindowSize = 0;
50
51 // Validate inputs
52 int f = NumWindowFuncs();
53
54 if (!(windowSize >= 32 && windowSize <= 131072 &&
57 windowFunc >= 0 && windowFunc < f)) {
58 return false;
59 }
60
61 if (dataLen < windowSize) {
62 return false;
63 }
64
65 // Now repopulate
66 mRate = rate;
67 mWindowSize = windowSize;
68 mAlg = alg;
69
70 auto half = mWindowSize / 2;
71 mProcessed.resize(mWindowSize);
72
77
78 for (size_t i = 0; i < mWindowSize; i++) {
79 mProcessed[i] = 0.0f;
80 win[i] = 1.0f;
81 }
82
83 WindowFunc(windowFunc, mWindowSize, win.get());
84
85 // Scale window such that an amplitude of 1.0 in the time domain
86 // shows an amplitude of 0dB in the frequency domain
87 double wss = 0;
88 for (size_t i = 0; i<mWindowSize; i++)
89 wss += win[i];
90 if(wss > 0)
91 wss = 4.0 / (wss*wss);
92 else
93 wss = 1.0;
94
95 size_t start = 0;
96 int windows = 0;
97 while (start + mWindowSize <= dataLen) {
98 for (size_t i = 0; i < mWindowSize; i++)
99 in[i] = win[i] * data[start + i];
100
101 switch (alg) {
102 case Spectrum:
103 PowerSpectrum(mWindowSize, in.get(), out.get());
104
105 for (size_t i = 0; i < half; i++)
106 mProcessed[i] += out[i];
107 break;
108
109 case Autocorrelation:
112
113 // Take FFT
114 RealFFT(mWindowSize, in.get(), out.get(), out2.get());
115 // Compute power
116 for (size_t i = 0; i < mWindowSize; i++)
117 in[i] = (out[i] * out[i]) + (out2[i] * out2[i]);
118
119 if (alg == Autocorrelation) {
120 for (size_t i = 0; i < mWindowSize; i++)
121 in[i] = sqrt(in[i]);
122 }
123 if (alg == CubeRootAutocorrelation ||
125 // Tolonen and Karjalainen recommend taking the cube root
126 // of the power, instead of the square root
127
128 for (size_t i = 0; i < mWindowSize; i++)
129 in[i] = pow(in[i], 1.0f / 3.0f);
130 }
131 // Take FFT
132 RealFFT(mWindowSize, in.get(), out.get(), out2.get());
133
134 // Take real part of result
135 for (size_t i = 0; i < half; i++)
136 mProcessed[i] += out[i];
137 break;
138
139 case Cepstrum:
140 RealFFT(mWindowSize, in.get(), out.get(), out2.get());
141
142 // Compute log power
143 // Set a sane lower limit assuming maximum time amplitude of 1.0
144 {
145 float power;
146 float minpower = 1e-20*mWindowSize*mWindowSize;
147 for (size_t i = 0; i < mWindowSize; i++)
148 {
149 power = (out[i] * out[i]) + (out2[i] * out2[i]);
150 if(power < minpower)
151 in[i] = log(minpower);
152 else
153 in[i] = log(power);
154 }
155 // Take IFFT
156 InverseRealFFT(mWindowSize, in.get(), NULL, out.get());
157
158 // Take real part of result
159 for (size_t i = 0; i < half; i++)
160 mProcessed[i] += out[i];
161 }
162
163 break;
164
165 default:
166 wxASSERT(false);
167 break;
168 } //switch
169
170 if (progress) {
171 progress(start, dataLen);
172 }
173
174 start += half;
175 windows++;
176 }
177
178 float mYMin = 1000000, mYMax = -1000000;
179 double scale;
180 switch (alg) {
181 case Spectrum:
182 // Convert to decibels
183 mYMin = 1000000.;
184 mYMax = -1000000.;
185 scale = wss / (double)windows;
186 for (size_t i = 0; i < half; i++)
187 {
188 mProcessed[i] = 10 * log10(mProcessed[i] * scale);
189 if(mProcessed[i] > mYMax)
190 mYMax = mProcessed[i];
191 else if(mProcessed[i] < mYMin)
192 mYMin = mProcessed[i];
193 }
194 break;
195
196 case Autocorrelation:
198 for (size_t i = 0; i < half; i++)
199 mProcessed[i] = mProcessed[i] / windows;
200
201 // Find min/max
202 mYMin = mProcessed[0];
203 mYMax = mProcessed[0];
204 for (size_t i = 1; i < half; i++)
205 if (mProcessed[i] > mYMax)
206 mYMax = mProcessed[i];
207 else if (mProcessed[i] < mYMin)
208 mYMin = mProcessed[i];
209 break;
210
212 for (size_t i = 0; i < half; i++)
213 mProcessed[i] = mProcessed[i] / windows;
214
215 // Peak Pruning as described by Tolonen and Karjalainen, 2000
216
217 // Clip at zero, copy to temp array
218 for (size_t i = 0; i < half; i++) {
219 if (mProcessed[i] < 0.0)
220 mProcessed[i] = float(0.0);
221 out[i] = mProcessed[i];
222 }
223
224 // Subtract a time-doubled signal (linearly interp.) from the original
225 // (clipped) signal
226 for (size_t i = 0; i < half; i++)
227 if ((i % 2) == 0)
228 mProcessed[i] -= out[i / 2];
229 else
230 mProcessed[i] -= ((out[i / 2] + out[i / 2 + 1]) / 2);
231
232 // Clip at zero again
233 for (size_t i = 0; i < half; i++)
234 if (mProcessed[i] < 0.0)
235 mProcessed[i] = float(0.0);
236
237 // Find NEW min/max
238 mYMin = mProcessed[0];
239 mYMax = mProcessed[0];
240 for (size_t i = 1; i < half; i++)
241 if (mProcessed[i] > mYMax)
242 mYMax = mProcessed[i];
243 else if (mProcessed[i] < mYMin)
244 mYMin = mProcessed[i];
245 break;
246
247 case Cepstrum:
248 for (size_t i = 0; i < half; i++)
249 mProcessed[i] = mProcessed[i] / windows;
250
251 // Find min/max, ignoring first and last few values
252 {
253 size_t ignore = 4;
254 mYMin = mProcessed[ignore];
255 mYMax = mProcessed[ignore];
256 for (size_t i = ignore + 1; i + ignore < half; i++)
257 if (mProcessed[i] > mYMax)
258 mYMax = mProcessed[i];
259 else if (mProcessed[i] < mYMin)
260 mYMin = mProcessed[i];
261 }
262 break;
263
264 default:
265 wxASSERT(false);
266 break;
267 }
268
269 if (pYMin)
270 *pYMin = mYMin;
271 if (pYMax)
272 *pYMax = mYMax;
273
274 return true;
275}
276
278{
279 return &mProcessed[0];
280}
281
283{
284 return mProcessed.size() / 2;
285}
286
287float SpectrumAnalyst::GetProcessedValue(float freq0, float freq1) const
288{
289 float bin0, bin1, binwidth;
290
291 if (mAlg == Spectrum) {
292 bin0 = freq0 * mWindowSize / mRate;
293 bin1 = freq1 * mWindowSize / mRate;
294 } else {
295 bin0 = freq0 * mRate;
296 bin1 = freq1 * mRate;
297 }
298 binwidth = bin1 - bin0;
299
300 float value = float(0.0);
301
302 if (binwidth < 1.0) {
303 float binmid = (bin0 + bin1) / 2.0;
304 int ibin = (int)(binmid) - 1;
305 if (ibin < 1)
306 ibin = 1;
307 if (ibin >= GetProcessedSize() - 3)
308 ibin = std::max(0, GetProcessedSize() - 4);
309
310 value = CubicInterpolate(mProcessed[ibin],
311 mProcessed[ibin + 1],
312 mProcessed[ibin + 2],
313 mProcessed[ibin + 3], binmid - ibin);
314
315 } else {
316 if (bin0 < 0)
317 bin0 = 0;
318 if (bin1 >= GetProcessedSize())
319 bin1 = GetProcessedSize() - 1;
320
321 if ((int)(bin1) > (int)(bin0))
322 value += mProcessed[(int)(bin0)] * ((int)(bin0) + 1 - bin0);
323 bin0 = 1 + (int)(bin0);
324 while (bin0 < (int)(bin1)) {
325 value += mProcessed[(int)(bin0)];
326 bin0 += 1.0;
327 }
328 value += mProcessed[(int)(bin1)] * (bin1 - (int)(bin1));
329
330 value /= binwidth;
331 }
332
333 return value;
334}
335
336float SpectrumAnalyst::FindPeak(float xPos, float *pY) const
337{
338 float bestpeak = 0.0f;
339 float bestValue = 0.0;
340 if (GetProcessedSize() > 1) {
341 bool up = (mProcessed[1] > mProcessed[0]);
342 float bestdist = 1000000;
343 for (int bin = 3; bin < GetProcessedSize() - 1; bin++) {
344 bool nowUp = mProcessed[bin] > mProcessed[bin - 1];
345 if (!nowUp && up) {
346 // Local maximum. Find actual value by cubic interpolation
347 int leftbin = bin - 2;
348 /*
349 if (leftbin < 1)
350 leftbin = 1;
351 */
352 float valueAtMax = 0.0;
353 float max = leftbin + CubicMaximize(mProcessed[leftbin],
354 mProcessed[leftbin + 1],
355 mProcessed[leftbin + 2],
356 mProcessed[leftbin + 3],
357 &valueAtMax);
358
359 float thispeak;
360 if (mAlg == Spectrum)
361 thispeak = max * mRate / mWindowSize;
362 else
363 thispeak = max / mRate;
364
365 if (fabs(thispeak - xPos) < bestdist) {
366 bestpeak = thispeak;
367 bestdist = fabs(thispeak - xPos);
368 bestValue = valueAtMax;
369 // Should this test come after the enclosing if?
370 if (thispeak > xPos)
371 break;
372 }
373 }
374 up = nowUp;
375 }
376 }
377
378 if (pY)
379 *pY = bestValue;
380 return bestpeak;
381}
382
383// If f(0)=y0, f(1)=y1, f(2)=y2, and f(3)=y3, this function finds
384// the degree-three polynomial which best fits these points and
385// returns the value of this polynomial at a value x. Usually
386// 0 < x < 3
387
388float SpectrumAnalyst::CubicInterpolate(float y0, float y1, float y2, float y3, float x) const
389{
390 float a, b, c, d;
391
392 a = y0 / -6.0 + y1 / 2.0 - y2 / 2.0 + y3 / 6.0;
393 b = y0 - 5.0 * y1 / 2.0 + 2.0 * y2 - y3 / 2.0;
394 c = -11.0 * y0 / 6.0 + 3.0 * y1 - 3.0 * y2 / 2.0 + y3 / 3.0;
395 d = y0;
396
397 float xx = x * x;
398 float xxx = xx * x;
399
400 return (a * xxx + b * xx + c * x + d);
401}
402
403float SpectrumAnalyst::CubicMaximize(float y0, float y1, float y2, float y3, float * max) const
404{
405 // Find coefficients of cubic
406
407 float a, b, c, d;
408
409 a = y0 / -6.0 + y1 / 2.0 - y2 / 2.0 + y3 / 6.0;
410 b = y0 - 5.0 * y1 / 2.0 + 2.0 * y2 - y3 / 2.0;
411 c = -11.0 * y0 / 6.0 + 3.0 * y1 - 3.0 * y2 / 2.0 + y3 / 3.0;
412 d = y0;
413
414 // Take derivative
415
416 float da, db, dc;
417
418 da = 3 * a;
419 db = 2 * b;
420 dc = c;
421
422 // Find zeroes of derivative using quadratic equation
423
424 float discriminant = db * db - 4 * da * dc;
425 if (discriminant < 0.0)
426 return float(-1.0); // error
427
428 float x1 = (-db + sqrt(discriminant)) / (2 * da);
429 float x2 = (-db - sqrt(discriminant)) / (2 * da);
430
431 // The one which corresponds to a local _maximum_ in the
432 // cubic is the one we want - the one with a negative
433 // second derivative
434
435 float dda = 2 * da;
436 float ddb = db;
437
438 if (dda * x1 + ddb < 0)
439 {
440 *max = a*x1*x1*x1+b*x1*x1+c*x1+d;
441 return x1;
442 }
443 else
444 {
445 *max = a*x2*x2*x2+b*x2*x2+c*x2+d;
446 return x2;
447 }
448}
void WindowFunc(int whichFunction, size_t NumSamples, float *in)
Definition: FFT.cpp:513
int NumWindowFuncs()
Definition: FFT.cpp:327
void PowerSpectrum(size_t NumSamples, const float *In, float *Out)
Definition: FFT.cpp:302
void InverseRealFFT(size_t NumSamples, const float *RealIn, const float *ImagIn, float *RealOut)
Definition: FFT.cpp:266
void RealFFT(size_t NumSamples, const float *RealIn, float *RealOut, float *ImagOut)
Definition: FFT.cpp:228
const float * GetProcessed() const
std::vector< float > mProcessed
float CubicMaximize(float y0, float y1, float y2, float y3, float *max) const
float CubicInterpolate(float y0, float y1, float y2, float y3, float x) const
float FindPeak(float xPos, float *pY) const
float GetProcessedValue(float freq0, float freq1) const
std::function< void(long long num, long long den)> ProgressFn
int GetProcessedSize() const
bool Calculate(Algorithm alg, int windowFunc, size_t windowSize, double rate, const float *data, size_t dataLen, float *pYMin=NULL, float *pYMax=NULL, ProgressFn progress=NULL)
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept
Definition: fast_float.h:1454
__finl float_x4 __vecc sqrt(const float_x4 &a)