Audacity 3.2.0
Public Member Functions | Public Attributes | Private Member Functions | Private 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 samplesPerPixel, const SpectrogramSettings &settings) const
 
void Grow (size_t len_, SpectrogramSettings &settings, double samplesPerPixel, double start)
 
void Populate (const SpectrogramSettings &settings, const WaveChannelInterval &clip, int copyBegin, int copyEnd, size_t numPixels, double pixelsPerSecond)
 

Public Attributes

size_t len { 0 }
 
int algorithm
 
double spp
 
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
 

Private Member Functions

bool CalculateOneSpectrum (const SpectrogramSettings &settings, const WaveChannelInterval &clip, const int xx, double pixelsPerSecond, int lowerBoundX, int upperBoundX, const std::vector< float > &gainFactors, float *__restrict scratch, float *__restrict out) const
 

Private Attributes

std::optional< AudioSegmentSampleViewmSampleCacheHolder
 

Detailed Description

Definition at line 26 of file SpectrumCache.h.

Constructor & Destructor Documentation

◆ SpecCache()

SpecCache::SpecCache ( )
inline

Definition at line 30 of file SpectrumCache.h.

31 : algorithm(-1)
32 , spp(-1.0)
33 , start(-1.0)
34 , windowType(-1)
35 , frequencyGain(-1)
36 , dirty(-1)
37 {
38 }
int windowType
Definition: SpectrumCache.h:64
double start
Definition: SpectrumCache.h:63
double spp
Definition: SpectrumCache.h:60
int frequencyGain
Definition: SpectrumCache.h:67

◆ ~SpecCache()

SpecCache::~SpecCache ( )
inline

Definition at line 40 of file SpectrumCache.h.

41 {
42 }

Member Function Documentation

◆ CalculateOneSpectrum()

bool SpecCache::CalculateOneSpectrum ( const SpectrogramSettings settings,
const WaveChannelInterval clip,
const int  xx,
double  pixelsPerSecond,
int  lowerBoundX,
int  upperBoundX,
const std::vector< float > &  gainFactors,
float *__restrict  scratch,
float *__restrict  out 
) const
private

Definition at line 94 of file SpectrumCache.cpp.

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

References SpectrogramSettings::algPitchEAC, SpectrogramSettings::algReassignment, sampleCount::as_long_long(), ComputeSpectrum(), anonymous_namespace{SpectrumCache.cpp}::ComputeSpectrumUsingRealFFTf(), staffpad::vo::copy(), Sequence::GetNumSamples(), WaveClipChannel::GetRate(), WaveClipChannel::GetSampleView(), WaveClipChannel::GetSequence(), WaveClipChannel::GetStretchRatio(), anonymous_namespace{StretchingSequenceIntegrationTest.cpp}::iChannel, len, M_PI, mSampleCacheHolder, fast_float::detail::power(), RealFFTf(), anonymous_namespace{ClipSegmentTest.cpp}::sampleRate, 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  samplesPerPixel,
double  start 
)

Definition at line 318 of file SpectrumCache.cpp.

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

References algorithm, freq, frequencyGain, len, settings(), spp, 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  samplesPerPixel,
const SpectrogramSettings settings 
) const

Definition at line 75 of file SpectrumCache.cpp.

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

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

Here is the call graph for this function:

◆ Populate()

void SpecCache::Populate ( const SpectrogramSettings settings,
const WaveChannelInterval clip,
int  copyBegin,
int  copyEnd,
size_t  numPixels,
double  pixelsPerSecond 
)

Definition at line 342 of file SpectrumCache.cpp.

345{
346 const auto sampleRate = clip.GetRate();
347 const int &frequencyGainSetting = settings.frequencyGain;
348 const size_t windowSizeSetting = settings.WindowSize();
349 const bool autocorrelation =
351 const bool reassignment =
353 const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
354
355 // FFT length may be longer than the window of samples that affect results
356 // because of zero padding done for increased frequency resolution
357 const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
358 const auto nBins = settings.NBins();
359
360 const size_t bufferSize = fftLen;
361 const size_t scratchSize = reassignment ? 3 * bufferSize : bufferSize;
362 std::vector<float> scratch(scratchSize);
363
364 std::vector<float> gainFactors;
365 if (!autocorrelation)
367 fftLen, sampleRate, frequencyGainSetting, gainFactors);
368
369 // Loop over the ranges before and after the copied portion and compute anew.
370 // One of the ranges may be empty.
371 for (int jj = 0; jj < 2; ++jj) {
372 const int lowerBoundX = jj == 0 ? 0 : copyEnd;
373 const int upperBoundX = jj == 0 ? copyBegin : numPixels;
374
375// todo(mhodgkinson): I don't find an option to define _OPENMP anywhere. Is this
376// still of interest?
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 float* buffer = &scratch[0];
404#endif
406 settings, clip, xx, pixelsPerSecond, lowerBoundX, upperBoundX,
407 gainFactors, buffer, &freq[0]);
408 }
409
410 if (reassignment) {
411 // Need to look beyond the edges of the range to accumulate more
412 // time reassignments.
413 // I'm not sure what's a good stopping criterion?
414 auto xx = lowerBoundX;
415 const double pixelsPerSample =
416 pixelsPerSecond * clip.GetStretchRatio() / sampleRate;
417 const int limit = std::min((int)(0.5 + fftLen * pixelsPerSample), 100);
418 for (int ii = 0; ii < limit; ++ii)
419 {
420 const bool result = CalculateOneSpectrum(
421 settings, clip, --xx, pixelsPerSecond, lowerBoundX, upperBoundX,
422 gainFactors, &scratch[0], &freq[0]);
423 if (!result)
424 break;
425 }
426
427 xx = upperBoundX;
428 for (int ii = 0; ii < limit; ++ii)
429 {
430 const bool result = CalculateOneSpectrum(
431 settings, clip, xx++, pixelsPerSecond, lowerBoundX, upperBoundX,
432 gainFactors, &scratch[0], &freq[0]);
433 if (!result)
434 break;
435 }
436
437 // Now Convert to dB terms. Do this only after accumulating
438 // power values, which may cross columns with the time correction.
439#ifdef _OPENMP
440 #pragma omp parallel for
441#endif
442 for (xx = lowerBoundX; xx < upperBoundX; ++xx) {
443 float *const results = &freq[nBins * xx];
444 for (size_t ii = 0; ii < nBins; ++ii) {
445 float &power = results[ii];
446 if (power <= 0)
447 power = -160.0;
448 else
449 power = 10.0*log10f(power);
450 }
451 if (!gainFactors.empty()) {
452 // Apply a frequency-dependent gain factor
453 for (size_t ii = 0; ii < nBins; ++ii)
454 results[ii] += gainFactors[ii];
455 }
456 }
457 }
458 }
459}
int min(int a, int b)
bool CalculateOneSpectrum(const SpectrogramSettings &settings, const WaveChannelInterval &clip, const int xx, 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, WaveClipChannel::GetRate(), WaveClipChannel::GetStretchRatio(), min(), fast_float::detail::power(), anonymous_namespace{ClipSegmentTest.cpp}::sampleRate, 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 59 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ dirty

int SpecCache::dirty

Definition at line 71 of file SpectrumCache.h.

Referenced by Matches().

◆ freq

std::vector<float> SpecCache::freq

◆ frequencyGain

int SpecCache::frequencyGain

Definition at line 67 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ leftTrim

double SpecCache::leftTrim { .0 }

Definition at line 61 of file SpectrumCache.h.

◆ len

size_t SpecCache::len { 0 }

Definition at line 58 of file SpectrumCache.h.

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

◆ mSampleCacheHolder

std::optional<AudioSegmentSampleView> SpecCache::mSampleCacheHolder
mutableprivate

Definition at line 81 of file SpectrumCache.h.

Referenced by CalculateOneSpectrum().

◆ rightTrim

double SpecCache::rightTrim { .0 }

Definition at line 62 of file SpectrumCache.h.

◆ spp

double SpecCache::spp

Definition at line 60 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ start

double SpecCache::start

Definition at line 63 of file SpectrumCache.h.

Referenced by Grow().

◆ where

std::vector<sampleCount> SpecCache::where

◆ windowSize

size_t SpecCache::windowSize { 0 }

Definition at line 65 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ windowType

int SpecCache::windowType

Definition at line 64 of file SpectrumCache.h.

Referenced by Grow(), and Matches().

◆ zeroPaddingFactor

unsigned SpecCache::zeroPaddingFactor { 0 }

Definition at line 66 of file SpectrumCache.h.

Referenced by Grow(), and Matches().


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