Audacity 3.2.0
Public Types | Public Member Functions | Private Member Functions | Private Attributes | List of all members
SpectrumAnalyst Class Reference

Used for finding the peaks, for snapping to peaks. More...

#include <SpectrumAnalyst.h>

Collaboration diagram for SpectrumAnalyst:
[legend]

Public Types

enum  Algorithm {
  Spectrum , Autocorrelation , CubeRootAutocorrelation , EnhancedAutocorrelation ,
  Cepstrum , NumAlgorithms
}
 
using ProgressFn = std::function< void(long long num, long long den)>
 

Public Member Functions

 SpectrumAnalyst ()
 
 ~SpectrumAnalyst ()
 
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)
 
const float * GetProcessed () const
 
int GetProcessedSize () const
 
float GetProcessedValue (float freq0, float freq1) const
 
float FindPeak (float xPos, float *pY) const
 

Private Member Functions

float CubicInterpolate (float y0, float y1, float y2, float y3, float x) const
 
float CubicMaximize (float y0, float y1, float y2, float y3, float *max) const
 

Private Attributes

Algorithm mAlg
 
double mRate
 
size_t mWindowSize
 
std::vector< float > mProcessed
 

Detailed Description

Used for finding the peaks, for snapping to peaks.

This class is used to do the 'find peaks' snapping both in FreqPlot and in the spectrogram spectral selection.

Definition at line 17 of file SpectrumAnalyst.h.

Member Typedef Documentation

◆ ProgressFn

using SpectrumAnalyst::ProgressFn = std::function<void(long long num, long long den)>

Definition at line 31 of file SpectrumAnalyst.h.

Member Enumeration Documentation

◆ Algorithm

Enumerator
Spectrum 
Autocorrelation 
CubeRootAutocorrelation 
EnhancedAutocorrelation 
Cepstrum 
NumAlgorithms 

Definition at line 21 of file SpectrumAnalyst.h.

Constructor & Destructor Documentation

◆ SpectrumAnalyst()

SpectrumAnalyst::SpectrumAnalyst ( )

Definition at line 29 of file SpectrumAnalyst.cpp.

31, mRate(0.0)
32, mWindowSize(0)
33{
34}

◆ ~SpectrumAnalyst()

SpectrumAnalyst::~SpectrumAnalyst ( )

Definition at line 36 of file SpectrumAnalyst.cpp.

37{
38}

Member Function Documentation

◆ Calculate()

bool SpectrumAnalyst::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 
)

Definition at line 40 of file SpectrumAnalyst.cpp.

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}
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
std::vector< float > mProcessed
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)

References Autocorrelation, Cepstrum, CubeRootAutocorrelation, EnhancedAutocorrelation, ignore, InverseRealFFT(), mAlg, mProcessed, mRate, mWindowSize, NumAlgorithms, NumWindowFuncs(), fast_float::detail::power(), PowerSpectrum(), RealFFT(), Spectrum, staffpad::audio::simd::sqrt(), and WindowFunc().

Referenced by SelectHandle::StartSnappingFreqSelection().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CubicInterpolate()

float SpectrumAnalyst::CubicInterpolate ( float  y0,
float  y1,
float  y2,
float  y3,
float  x 
) const
private

Definition at line 388 of file SpectrumAnalyst.cpp.

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}

Referenced by GetProcessedValue().

Here is the caller graph for this function:

◆ CubicMaximize()

float SpectrumAnalyst::CubicMaximize ( float  y0,
float  y1,
float  y2,
float  y3,
float *  max 
) const
private

Definition at line 403 of file SpectrumAnalyst.cpp.

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}

References staffpad::audio::simd::sqrt().

Referenced by FindPeak().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ FindPeak()

float SpectrumAnalyst::FindPeak ( float  xPos,
float *  pY 
) const

Definition at line 336 of file SpectrumAnalyst.cpp.

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}
float CubicMaximize(float y0, float y1, float y2, float y3, float *max) const
int GetProcessedSize() const

References CubicMaximize(), GetProcessedSize(), mAlg, mProcessed, mRate, mWindowSize, and Spectrum.

Referenced by SelectHandle::SnapCenterOnce().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetProcessed()

const float * SpectrumAnalyst::GetProcessed ( ) const

Definition at line 277 of file SpectrumAnalyst.cpp.

278{
279 return &mProcessed[0];
280}

References mProcessed.

◆ GetProcessedSize()

int SpectrumAnalyst::GetProcessedSize ( ) const

Definition at line 282 of file SpectrumAnalyst.cpp.

283{
284 return mProcessed.size() / 2;
285}

References mProcessed.

Referenced by FindPeak(), and GetProcessedValue().

Here is the caller graph for this function:

◆ GetProcessedValue()

float SpectrumAnalyst::GetProcessedValue ( float  freq0,
float  freq1 
) const

Definition at line 287 of file SpectrumAnalyst.cpp.

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}
float CubicInterpolate(float y0, float y1, float y2, float y3, float x) const

References CubicInterpolate(), GetProcessedSize(), mAlg, mProcessed, mRate, mWindowSize, and Spectrum.

Here is the call graph for this function:

Member Data Documentation

◆ mAlg

Algorithm SpectrumAnalyst::mAlg
private

Definition at line 55 of file SpectrumAnalyst.h.

Referenced by Calculate(), FindPeak(), and GetProcessedValue().

◆ mProcessed

std::vector<float> SpectrumAnalyst::mProcessed
private

◆ mRate

double SpectrumAnalyst::mRate
private

Definition at line 56 of file SpectrumAnalyst.h.

Referenced by Calculate(), FindPeak(), and GetProcessedValue().

◆ mWindowSize

size_t SpectrumAnalyst::mWindowSize
private

Definition at line 57 of file SpectrumAnalyst.h.

Referenced by Calculate(), FindPeak(), and GetProcessedValue().


The documentation for this class was generated from the following files: