Audacity 3.2.0
Public Member Functions | Public Attributes | List of all members
SpecCache Class Reference

#include <SpectrumCache.h>

Collaboration diagram for SpecCache:
[legend]

Public Member Functions

 SpecCache ()
 
 ~SpecCache ()
 
bool Matches (int dirty_, double pixelsPerSecond, const SpectrogramSettings &settings, double rate) const
 
bool CalculateOneSpectrum (const SpectrogramSettings &settings, SampleTrackCache &waveTrackCache, const int xx, sampleCount numSamples, double offset, double rate, double pixelsPerSecond, int lowerBoundX, int upperBoundX, const std::vector< float > &gainFactors, float *__restrict scratch, float *__restrict out) const
 
void Grow (size_t len_, SpectrogramSettings &settings, double pixelsPerSecond, double start_)
 
void Populate (const SpectrogramSettings &settings, SampleTrackCache &waveTrackCache, int copyBegin, int copyEnd, size_t numPixels, sampleCount numSamples, double offset, double rate, double pixelsPerSecond)
 

Public Attributes

size_t len { 0 }
 
int algorithm
 
double pps
 
double leftTrim { .0 }
 
double rightTrim { .0 }
 
double start
 
int windowType
 
size_t windowSize { 0 }
 
unsigned zeroPaddingFactor { 0 }
 
int frequencyGain
 
std::vector< float > freq
 
std::vector< sampleCountwhere
 
int dirty
 

Detailed Description

Definition at line 24 of file SpectrumCache.h.

Constructor & Destructor Documentation

◆ SpecCache()

SpecCache::SpecCache ( )
inline

Definition at line 28 of file SpectrumCache.h.

29 : algorithm(-1)
30 , pps(-1.0)
31 , start(-1.0)
32 , windowType(-1)
33 , frequencyGain(-1)
34 , dirty(-1)
35 {
36 }
int windowType
Definition: SpectrumCache.h:73
double start
Definition: SpectrumCache.h:72
int frequencyGain
Definition: SpectrumCache.h:76
double pps
Definition: SpectrumCache.h:69

◆ ~SpecCache()

SpecCache::~SpecCache ( )
inline

Definition at line 38 of file SpectrumCache.h.

39 {
40 }

Member Function Documentation

◆ CalculateOneSpectrum()

bool SpecCache::CalculateOneSpectrum ( const SpectrogramSettings settings,
SampleTrackCache waveTrackCache,
const int  xx,
sampleCount  numSamples,
double  offset,
double  rate,
double  pixelsPerSecond,
int  lowerBoundX,
int  upperBoundX,
const std::vector< float > &  gainFactors,
float *__restrict  scratch,
float *__restrict  out 
) const

Definition at line 97 of file SpectrumCache.cpp.

105{
106 bool result = false;
107 const bool reassignment =
109 const size_t windowSizeSetting = settings.WindowSize();
110
111 sampleCount from;
112
113 // xx may be for a column that is out of the visible bounds, but only
114 // when we are calculating reassignment contributions that may cross into
115 // the visible area.
116
117 if (xx < 0)
118 from = sampleCount(
119 where[0].as_double() + xx * (rate / pixelsPerSecond)
120 );
121 else if (xx > (int)len)
122 from = sampleCount(
123 where[len].as_double() + (xx - len) * (rate / pixelsPerSecond)
124 );
125 else
126 from = where[xx];
127
128 const bool autocorrelation =
130 const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
131 const size_t padding = (windowSizeSetting * (zeroPaddingFactorSetting - 1)) / 2;
132 const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
133 auto nBins = settings.NBins();
134
135 if (from < 0 || from >= numSamples) {
136 if (xx >= 0 && xx < (int)len) {
137 // Pixel column is out of bounds of the clip! Should not happen.
138 float *const results = &out[nBins * xx];
139 std::fill(results, results + nBins, 0.0f);
140 }
141 }
142 else {
143
144
145 // We can avoid copying memory when ComputeSpectrum is used below
146 bool copy = !autocorrelation || (padding > 0) || reassignment;
147 float *useBuffer = 0;
148 float *adj = scratch + padding;
149
150 {
151 auto myLen = windowSizeSetting;
152 // Take a window of the track centered at this sample.
153 from -= windowSizeSetting >> 1;
154 if (from < 0) {
155 // Near the start of the clip, pad left with zeroes as needed.
156 // from is at least -windowSize / 2
157 for (auto ii = from; ii < 0; ++ii)
158 *adj++ = 0;
159 myLen += from.as_long_long(); // add a negative
160 from = 0;
161 copy = true;
162 }
163
164 if (from + myLen >= numSamples) {
165 // Near the end of the clip, pad right with zeroes as needed.
166 // newlen is bounded by myLen:
167 auto newlen = ( numSamples - from ).as_size_t();
168 for (decltype(myLen) ii = newlen; ii < myLen; ++ii)
169 adj[ii] = 0;
170 myLen = newlen;
171 copy = true;
172 }
173
174 if (myLen > 0) {
175 useBuffer = (float*)(waveTrackCache.GetFloats(
177 floor(0.5 + from.as_double() + offset * rate)
178 ),
179 myLen,
180 // Don't throw in this drawing operation
181 false)
182 );
183
184 if (copy) {
185 if (useBuffer)
186 memcpy(adj, useBuffer, myLen * sizeof(float));
187 else
188 memset(adj, 0, myLen * sizeof(float));
189 }
190 }
191 }
192
193 if (copy || !useBuffer)
194 useBuffer = scratch;
195
196 if (autocorrelation) {
197 // not reassignment, xx is surely within bounds.
198 wxASSERT(xx >= 0);
199 float *const results = &out[nBins * xx];
200 // This function does not mutate useBuffer
201 ComputeSpectrum(useBuffer, windowSizeSetting, windowSizeSetting,
202 rate, results,
203 autocorrelation, settings.windowType);
204 }
205 else if (reassignment) {
206 static const double epsilon = 1e-16;
207 const auto hFFT = settings.hFFT.get();
208
209 float *const scratch2 = scratch + fftLen;
210 std::copy(scratch, scratch2, scratch2);
211
212 float *const scratch3 = scratch + 2 * fftLen;
213 std::copy(scratch, scratch2, scratch3);
214
215 {
216 const float *const window = settings.window.get();
217 for (size_t ii = 0; ii < fftLen; ++ii)
218 scratch[ii] *= window[ii];
219 RealFFTf(scratch, hFFT);
220 }
221
222 {
223 const float *const dWindow = settings.dWindow.get();
224 for (size_t ii = 0; ii < fftLen; ++ii)
225 scratch2[ii] *= dWindow[ii];
226 RealFFTf(scratch2, hFFT);
227 }
228
229 {
230 const float *const tWindow = settings.tWindow.get();
231 for (size_t ii = 0; ii < fftLen; ++ii)
232 scratch3[ii] *= tWindow[ii];
233 RealFFTf(scratch3, hFFT);
234 }
235
236 for (size_t ii = 0; ii < hFFT->Points; ++ii) {
237 const int index = hFFT->BitReversed[ii];
238 const float
239 denomRe = scratch[index],
240 denomIm = ii == 0 ? 0 : scratch[index + 1];
241 const double power = denomRe * denomRe + denomIm * denomIm;
242 if (power < epsilon)
243 // Avoid dividing by near-zero below
244 continue;
245
246 double freqCorrection;
247 {
248 const double multiplier = -(fftLen / (2.0f * M_PI));
249 const float
250 numRe = scratch2[index],
251 numIm = ii == 0 ? 0 : scratch2[index + 1];
252 // Find complex quotient --
253 // Which means, multiply numerator by conjugate of denominator,
254 // then divide by norm squared of denominator --
255 // Then just take its imaginary part.
256 const double
257 quotIm = (-numRe * denomIm + numIm * denomRe) / power;
258 // With appropriate multiplier, that becomes the correction of
259 // the frequency bin.
260 freqCorrection = multiplier * quotIm;
261 }
262
263 const int bin = (int)((int)ii + freqCorrection + 0.5f);
264 // Must check if correction takes bin out of bounds, above or below!
265 // bin is signed!
266 if (bin >= 0 && bin < (int)hFFT->Points) {
267 double timeCorrection;
268 {
269 const float
270 numRe = scratch3[index],
271 numIm = ii == 0 ? 0 : scratch3[index + 1];
272 // Find another complex quotient --
273 // Then just take its real part.
274 // The result has sample interval as unit.
275 timeCorrection =
276 (numRe * denomRe + numIm * denomIm) / power;
277 }
278
279 int correctedX = (floor(0.5 + xx + timeCorrection * pixelsPerSecond / rate));
280 if (correctedX >= lowerBoundX && correctedX < upperBoundX)
281 {
282 result = true;
283
284 // This is non-negative, because bin and correctedX are
285 auto ind = (int)nBins * correctedX + bin;
286#ifdef _OPENMP
287 // This assignment can race if index reaches into another thread's bins.
288 // The probability of a race very low, so this carries little overhead,
289 // about 5% slower vs allowing it to race.
290 #pragma omp atomic update
291#endif
292 out[ind] += power;
293 }
294 }
295 }
296 }
297 else {
298 // not reassignment, xx is surely within bounds.
299 wxASSERT(xx >= 0);
300 float *const results = &out[nBins * xx];
301
302 // Do the FFT. Note that useBuffer is multiplied by the window,
303 // and the window is initialized with leading and trailing zeroes
304 // when there is padding. Therefore we did not need to reinitialize
305 // the part of useBuffer in the padding zones.
306
307 // This function mutates useBuffer
309 (useBuffer, settings.hFFT.get(), settings.window.get(), fftLen, results);
310 if (!gainFactors.empty()) {
311 // Apply a frequency-dependent gain factor
312 for (size_t ii = 0; ii < nBins; ++ii)
313 results[ii] += gainFactors[ii];
314 }
315 }
316 }
317
318 return result;
319}
#define M_PI
Definition: Distortion.cpp:30
void RealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:161
bool ComputeSpectrum(const float *data, size_t width, size_t windowSize, double WXUNUSED(rate), float *output, bool autocorrelation, int windowFunc)
Definition: Spectrum.cpp:22
static Settings & settings()
Definition: TrackInfo.cpp:87
const float * GetFloats(sampleCount start, size_t len, bool mayThrow)
Retrieve samples as floats from the track or from the memory cache.
size_t len
Definition: SpectrumCache.h:67
std::vector< sampleCount > where
Definition: SpectrumCache.h:78
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
long long as_long_long() const
Definition: SampleCount.h:48
double as_double() const
Definition: SampleCount.h:46
static void ComputeSpectrumUsingRealFFTf(float *__restrict buffer, const FFTParam *hFFT, const float *__restrict window, size_t len, float *__restrict out)
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept
Definition: fast_float.h:1454

References SpectrogramSettings::algPitchEAC, SpectrogramSettings::algReassignment, sampleCount::as_double(), sampleCount::as_long_long(), ComputeSpectrum(), anonymous_namespace{SpectrumCache.cpp}::ComputeSpectrumUsingRealFFTf(), SampleTrackCache::GetFloats(), len, M_PI, fast_float::detail::power(), RealFFTf(), settings(), and where.

Referenced by Populate().

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

◆ Grow()

void SpecCache::Grow ( size_t  len_,
SpectrogramSettings settings,
double  pixelsPerSecond,
double  start_ 
)

Definition at line 321 of file SpectrumCache.cpp.

323{
324 settings.CacheWindows();
325
326 // len columns, and so many rows, column-major.
327 // Don't take column literally -- this isn't pixel data yet, it's the
328 // raw data to be mapped onto the display.
329 freq.resize(len_ * settings.NBins());
330
331 // Sample counts corresponding to the columns, and to one past the end.
332 where.resize(len_ + 1);
333
334 len = len_;
335 algorithm = settings.algorithm;
336 pps = pixelsPerSecond;
337 start = start_;
338 windowType = settings.windowType;
339 windowSize = settings.WindowSize();
340 zeroPaddingFactor = settings.ZeroPaddingFactor();
341 frequencyGain = settings.frequencyGain;
342}
unsigned zeroPaddingFactor
Definition: SpectrumCache.h:75
std::vector< float > freq
Definition: SpectrumCache.h:77
size_t windowSize
Definition: SpectrumCache.h:74

References algorithm, freq, frequencyGain, len, pps, settings(), start, where, windowSize, windowType, and zeroPaddingFactor.

Referenced by anonymous_namespace{SpectrumView.cpp}::DrawClipSpectrum().

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

◆ Matches()

bool SpecCache::Matches ( int  dirty_,
double  pixelsPerSecond,
const SpectrogramSettings settings,
double  rate 
) const

Definition at line 76 of file SpectrumCache.cpp.

79{
80 // Make a tolerant comparison of the pps values in this wise:
81 // accumulated difference of times over the number of pixels is less than
82 // a sample period.
83 const double tstep = 1.0 / pixelsPerSecond;
84 const bool ppsMatch =
85 (fabs(tstep - 1.0 / pps) * len < (1.0 / rate));
86
87 return
88 ppsMatch &&
89 dirty == dirty_ &&
90 windowType == settings.windowType &&
91 windowSize == settings.WindowSize() &&
92 zeroPaddingFactor == settings.ZeroPaddingFactor() &&
93 frequencyGain == settings.frequencyGain &&
94 algorithm == settings.algorithm;
95}

References algorithm, dirty, frequencyGain, len, pps, settings(), windowSize, windowType, and zeroPaddingFactor.

Here is the call graph for this function:

◆ Populate()

void SpecCache::Populate ( const SpectrogramSettings settings,
SampleTrackCache waveTrackCache,
int  copyBegin,
int  copyEnd,
size_t  numPixels,
sampleCount  numSamples,
double  offset,
double  rate,
double  pixelsPerSecond 
)

Definition at line 344 of file SpectrumCache.cpp.

349{
350 const int &frequencyGainSetting = settings.frequencyGain;
351 const size_t windowSizeSetting = settings.WindowSize();
352 const bool autocorrelation =
354 const bool reassignment =
356 const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
357
358 // FFT length may be longer than the window of samples that affect results
359 // because of zero padding done for increased frequency resolution
360 const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
361 const auto nBins = settings.NBins();
362
363 const size_t bufferSize = fftLen;
364 const size_t scratchSize = reassignment ? 3 * bufferSize : bufferSize;
365 std::vector<float> scratch(scratchSize);
366
367 std::vector<float> gainFactors;
368 if (!autocorrelation)
369 ComputeSpectrogramGainFactors(fftLen, rate, frequencyGainSetting, gainFactors);
370
371 // Loop over the ranges before and after the copied portion and compute anew.
372 // One of the ranges may be empty.
373 for (int jj = 0; jj < 2; ++jj) {
374 const int lowerBoundX = jj == 0 ? 0 : copyEnd;
375 const int upperBoundX = jj == 0 ? copyBegin : numPixels;
376
377#ifdef _OPENMP
378 // Storage for mutable per-thread data.
379 // private clause ensures one copy per thread
380 struct ThreadLocalStorage {
381 ThreadLocalStorage() { }
382 ~ThreadLocalStorage() { }
383
384 void init(SampleTrackCache &waveTrackCache, size_t scratchSize) {
385 if (!cache) {
386 cache = std::make_unique<SampleTrackCache>(waveTrackCache.GetTrack());
387 scratch.resize(scratchSize);
388 }
389 }
390 std::unique_ptr<SampleTrackCache> cache;
391 std::vector<float> scratch;
392 } tls;
393
394 #pragma omp parallel for private(tls)
395#endif
396 for (auto xx = lowerBoundX; xx < upperBoundX; ++xx)
397 {
398#ifdef _OPENMP
399 tls.init(waveTrackCache, scratchSize);
400 SampleTrackCache& cache = *tls.cache;
401 float* buffer = &tls.scratch[0];
402#else
403 SampleTrackCache& cache = waveTrackCache;
404 float* buffer = &scratch[0];
405#endif
407 settings, cache, xx, numSamples,
408 offset, rate, pixelsPerSecond,
409 lowerBoundX, upperBoundX,
410 gainFactors, buffer, &freq[0]);
411 }
412
413 if (reassignment) {
414 // Need to look beyond the edges of the range to accumulate more
415 // time reassignments.
416 // I'm not sure what's a good stopping criterion?
417 auto xx = lowerBoundX;
418 const double pixelsPerSample = pixelsPerSecond / rate;
419 const int limit = std::min((int)(0.5 + fftLen * pixelsPerSample), 100);
420 for (int ii = 0; ii < limit; ++ii)
421 {
422 const bool result =
424 settings, waveTrackCache, --xx, numSamples,
425 offset, rate, pixelsPerSecond,
426 lowerBoundX, upperBoundX,
427 gainFactors, &scratch[0], &freq[0]);
428 if (!result)
429 break;
430 }
431
432 xx = upperBoundX;
433 for (int ii = 0; ii < limit; ++ii)
434 {
435 const bool result =
437 settings, waveTrackCache, xx++, numSamples,
438 offset, rate, pixelsPerSecond,
439 lowerBoundX, upperBoundX,
440 gainFactors, &scratch[0], &freq[0]);
441 if (!result)
442 break;
443 }
444
445 // Now Convert to dB terms. Do this only after accumulating
446 // power values, which may cross columns with the time correction.
447#ifdef _OPENMP
448 #pragma omp parallel for
449#endif
450 for (xx = lowerBoundX; xx < upperBoundX; ++xx) {
451 float *const results = &freq[nBins * xx];
452 for (size_t ii = 0; ii < nBins; ++ii) {
453 float &power = results[ii];
454 if (power <= 0)
455 power = -160.0;
456 else
457 power = 10.0*log10f(power);
458 }
459 if (!gainFactors.empty()) {
460 // Apply a frequency-dependent gain factor
461 for (size_t ii = 0; ii < nBins; ++ii)
462 results[ii] += gainFactors[ii];
463 }
464 }
465 }
466 }
467}
int min(int a, int b)
A short-lived object, during whose lifetime, the contents of the WaveTrack are assumed not to change.
const std::shared_ptr< const SampleTrack > & GetTrack() const
bool CalculateOneSpectrum(const SpectrogramSettings &settings, SampleTrackCache &waveTrackCache, const int xx, sampleCount numSamples, double offset, double rate, double pixelsPerSecond, int lowerBoundX, int upperBoundX, const std::vector< float > &gainFactors, float *__restrict scratch, float *__restrict out) const
void ComputeSpectrogramGainFactors(size_t fftLen, double rate, int frequencyGain, std::vector< float > &gainFactors)

References SpectrogramSettings::algPitchEAC, SpectrogramSettings::algReassignment, CalculateOneSpectrum(), anonymous_namespace{SpectrumCache.cpp}::ComputeSpectrogramGainFactors(), freq, SampleTrackCache::GetTrack(), min(), fast_float::detail::power(), and settings().

Referenced by anonymous_namespace{SpectrumView.cpp}::DrawClipSpectrum().

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

Member Data Documentation

◆ algorithm

int SpecCache::algorithm

Definition at line 68 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ dirty

int SpecCache::dirty

Definition at line 80 of file SpectrumCache.h.

Referenced by Matches().

◆ freq

std::vector<float> SpecCache::freq

◆ frequencyGain

int SpecCache::frequencyGain

Definition at line 76 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ leftTrim

double SpecCache::leftTrim { .0 }

Definition at line 70 of file SpectrumCache.h.

◆ len

size_t SpecCache::len { 0 }

Definition at line 67 of file SpectrumCache.h.

Referenced by CalculateOneSpectrum(), Grow(), and Matches().

◆ pps

double SpecCache::pps

Definition at line 69 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ rightTrim

double SpecCache::rightTrim { .0 }

Definition at line 71 of file SpectrumCache.h.

◆ start

double SpecCache::start

Definition at line 72 of file SpectrumCache.h.

Referenced by Grow().

◆ where

std::vector<sampleCount> SpecCache::where

◆ windowSize

size_t SpecCache::windowSize { 0 }

Definition at line 74 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ windowType

int SpecCache::windowType

Definition at line 73 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ zeroPaddingFactor

unsigned SpecCache::zeroPaddingFactor { 0 }

Definition at line 75 of file SpectrumCache.h.

Referenced by Grow(), and Matches().


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