25 (
float * __restrict buffer,
const FFTParam *hFFT,
26 const float * __restrict window,
size_t len,
float * __restrict out)
31 for(i = 0; i < len; i++)
32 buffer[i] *= window[i];
33 for( ; i < (hFFT->
Points * 2); i++)
37 float power = buffer[0] * buffer[0];
41 out[0] = 10.0 * log10f(
power);
42 for(i = 1; i < hFFT->
Points; i++) {
44 const float re = buffer[index], im = buffer[index + 1];
45 power = re * re + im * im;
49 out[i] = 10.0*log10f(
power);
54 (
size_t fftLen,
double rate,
int frequencyGain, std::vector<float> &gainFactors)
56 if (frequencyGain > 0) {
61 const double factor = ((double)rate / (
double)fftLen) / 1000.0;
63 auto half = fftLen / 2;
64 gainFactors.reserve(half);
66 gainFactors.push_back(frequencyGain*log10(factor));
67 for (
decltype(half) x = 1; x < half; x++) {
68 gainFactors.push_back(frequencyGain*log10(factor * x));
76 int dirty_,
double samplesPerPixel,
82 const bool sppMatch = (fabs(samplesPerPixel -
spp) *
len < 1.0);
96 const int xx,
double pixelsPerSecond,
int lowerBoundX,
int upperBoundX,
97 const std::vector<float>& gainFactors,
float* __restrict scratch,
98 float* __restrict out)
const
101 const bool reassignment =
103 const size_t windowSizeSetting =
settings.WindowSize();
110 const auto samplesPerPixel =
sampleRate / pixelsPerSecond / stretchRatio;
117 else if (xx > (
int)
len)
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;
129 if (from < 0 || from >= numSamples) {
130 if (xx >= 0 && xx < (
int)
len) {
132 float *
const results = &out[nBins * xx];
133 std::fill(results, results + nBins, 0.0f);
140 bool copy = !autocorrelation || (padding > 0) || reassignment;
141 std::vector<float> floats;
142 float* useBuffer = 0;
143 float *adj = scratch + padding;
146 auto myLen = windowSizeSetting;
148 from -= windowSizeSetting >> 1;
152 for (
auto ii = from; ii < 0; ++ii)
159 if (from + myLen >= numSamples) {
162 auto newlen = ( numSamples - from ).as_size_t();
163 for (
decltype(myLen) ii = newlen; ii < myLen; ++ii)
171 constexpr auto mayThrow =
false;
174 floats.resize(myLen);
176 useBuffer = floats.data();
179 memcpy(adj, useBuffer, myLen *
sizeof(
float));
181 memset(adj, 0, myLen *
sizeof(
float));
186 if (
copy || !useBuffer)
189 if (autocorrelation) {
192 float *
const results = &out[nBins * xx];
195 useBuffer, windowSizeSetting, windowSizeSetting, results,
196 autocorrelation,
settings.windowType);
198 else if (reassignment) {
199 static const double epsilon = 1e-16;
200 const auto hFFT =
settings.hFFT.get();
202 float *
const scratch2 = scratch + fftLen;
205 float *
const scratch3 = scratch + 2 * fftLen;
209 const float *
const window =
settings.window.get();
210 for (
size_t ii = 0; ii < fftLen; ++ii)
211 scratch[ii] *= window[ii];
216 const float *
const dWindow =
settings.dWindow.get();
217 for (
size_t ii = 0; ii < fftLen; ++ii)
218 scratch2[ii] *= dWindow[ii];
223 const float *
const tWindow =
settings.tWindow.get();
224 for (
size_t ii = 0; ii < fftLen; ++ii)
225 scratch3[ii] *= tWindow[ii];
229 for (
size_t ii = 0; ii < hFFT->Points; ++ii) {
230 const int index = hFFT->BitReversed[ii];
232 denomRe = scratch[index],
233 denomIm = ii == 0 ? 0 : scratch[index + 1];
234 const double power = denomRe * denomRe + denomIm * denomIm;
239 double freqCorrection;
241 const double multiplier = -(fftLen / (2.0f *
M_PI));
243 numRe = scratch2[index],
244 numIm = ii == 0 ? 0 : scratch2[index + 1];
250 quotIm = (-numRe * denomIm + numIm * denomRe) /
power;
253 freqCorrection = multiplier * quotIm;
256 const int bin = (int)((
int)ii + freqCorrection + 0.5f);
259 if (bin >= 0 && bin < (
int)hFFT->Points) {
260 double timeCorrection;
263 numRe = scratch3[index],
264 numIm = ii == 0 ? 0 : scratch3[index + 1];
269 (numRe * denomRe + numIm * denomIm) /
power;
275 int correctedX = (floor(
276 0.5 + xx + timeCorrection * pixelsPerSecond /
sampleRate));
277 if (correctedX >= lowerBoundX && correctedX < upperBoundX)
282 auto ind = (int)nBins * correctedX + bin;
287 #pragma omp atomic update
297 float *
const results = &out[nBins * xx];
307 if (!gainFactors.empty()) {
309 for (
size_t ii = 0; ii < nBins; ++ii)
310 results[ii] += gainFactors[ii];
330 where.resize(len_ + 1);
334 spp = samplesPerPixel;
344 int copyBegin,
int copyEnd,
size_t numPixels,
double pixelsPerSecond)
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();
357 const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
358 const auto nBins =
settings.NBins();
360 const size_t bufferSize = fftLen;
361 const size_t scratchSize = reassignment ? 3 * bufferSize : bufferSize;
362 std::vector<float> scratch(scratchSize);
364 std::vector<float> gainFactors;
365 if (!autocorrelation)
367 fftLen,
sampleRate, frequencyGainSetting, gainFactors);
371 for (
int jj = 0; jj < 2; ++jj) {
372 const int lowerBoundX = jj == 0 ? 0 : copyEnd;
373 const int upperBoundX = jj == 0 ? copyBegin : numPixels;
380 struct ThreadLocalStorage {
381 ThreadLocalStorage() { }
382 ~ThreadLocalStorage() { }
384 void init(SampleTrackCache &waveTrackCache,
size_t scratchSize) {
386 cache = std::make_unique<SampleTrackCache>(waveTrackCache.GetTrack());
387 scratch.resize(scratchSize);
390 std::unique_ptr<SampleTrackCache> cache;
391 std::vector<float> scratch;
394 #pragma omp parallel for private(tls)
396 for (
auto xx = lowerBoundX; xx < upperBoundX; ++xx)
399 tls.init(waveTrackCache, scratchSize);
400 SampleTrackCache& cache = *tls.cache;
401 float* buffer = &tls.scratch[0];
403 float* buffer = &scratch[0];
406 settings, clip, xx, pixelsPerSecond, lowerBoundX, upperBoundX,
407 gainFactors, buffer, &
freq[0]);
414 auto xx = lowerBoundX;
415 const double pixelsPerSample =
417 const int limit =
std::min((
int)(0.5 + fftLen * pixelsPerSample), 100);
418 for (
int ii = 0; ii < limit; ++ii)
421 settings, clip, --xx, pixelsPerSecond, lowerBoundX, upperBoundX,
422 gainFactors, &scratch[0], &
freq[0]);
428 for (
int ii = 0; ii < limit; ++ii)
431 settings, clip, xx++, pixelsPerSecond, lowerBoundX, upperBoundX,
432 gainFactors, &scratch[0], &
freq[0]);
440 #pragma omp parallel for
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];
451 if (!gainFactors.empty()) {
453 for (
size_t ii = 0; ii < nBins; ++ii)
454 results[ii] += gainFactors[ii];
464 const sampleCount*& where,
size_t numPixels,
double t0,
465 double pixelsPerSecond)
472 const auto samplesPerPixel =
sampleRate / pixelsPerSecond / stretchRatio;
476 bool match = mSpecCache && mSpecCache->leftTrim == clip.
GetTrimLeft() &&
478 mSpecCache->len > 0 &&
481 if (match && mSpecCache->start == t0 && mSpecCache->len >= numPixels)
483 spectrogram = &mSpecCache->freq[0];
484 where = &mSpecCache->where[0];
498 if (mSpecCache->freq.capacity() > 2.1 * mSpecCache->freq.size() ||
499 mSpecCache->windowSize*mSpecCache->zeroPaddingFactor <
503 mSpecCache = std::make_unique<SpecCache>();
507 double correction = 0.0;
509 int copyBegin = 0, copyEnd = 0;
512 mSpecCache->where, mSpecCache->len, numPixels, t0,
sampleRate,
513 stretchRatio, samplesPerPixel, oldX0, correction);
517 copyBegin =
std::min((
int)numPixels, std::max(0, -oldX0));
518 copyEnd =
std::min((
int)numPixels, std::max(0,
519 (
int)mSpecCache->len - oldX0
524 mSpecCache->Grow(numPixels,
settings, samplesPerPixel, t0);
532 if (copyEnd > copyBegin)
535 memmove(&mSpecCache->freq[nBins * copyBegin],
536 &mSpecCache->freq[nBins * (copyBegin + oldX0)],
537 nBins * (copyEnd - copyBegin) *
sizeof(
float));
548 (copyBegin >= 0 && copyEnd == (
int)numPixels) ||
549 (copyBegin == 0 && copyEnd <= (
int)numPixels)
552 int zeroBegin = copyBegin > 0 ? 0 : copyEnd-copyBegin;
553 int zeroEnd = copyBegin > 0 ? copyBegin : numPixels;
555 memset(&mSpecCache->freq[nBins*zeroBegin], 0, nBins*(zeroEnd-zeroBegin)*
sizeof(
float));
560 constexpr auto addBias =
true;
562 mSpecCache->where, numPixels, addBias, correction, t0,
sampleRate,
563 stretchRatio, samplesPerPixel);
565 mSpecCache->Populate(
566 settings, clip, copyBegin, copyEnd, numPixels, pixelsPerSecond);
568 mSpecCache->dirty =
mDirty;
569 spectrogram = &mSpecCache->freq[0];
570 where = &mSpecCache->where[0];
576 : mSpecCaches(nChannels)
577 , mSpecPxCaches(nChannels)
580 pCache = std::make_unique<SpecCache>();
590 return std::make_unique<WaveClipSpectrumCache>(
mSpecCaches.size());
594 return std::make_unique<WaveClipSpectrumCache>(clip.NChannels());
601 .Attachments::Get< WaveClipSpectrumCache >(
sKeyS );
613 pCache = std::make_unique<SpecCache>();
620 mSpecCaches.push_back(move(pOther->mSpecCaches[0]));
void RealFFTf(fft_type *buffer, const FFTParam *h)
bool ComputeSpectrum(const float *data, size_t width, size_t windowSize, float *output, bool autocorrelation, int windowFunc)
static WaveClip::Attachments::RegisteredFactory sKeyS
static Settings & settings()
sampleCount GetNumSamples() const
unsigned zeroPaddingFactor
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)
bool Matches(int dirty_, double samplesPerPixel, const SpectrogramSettings &settings) const
std::vector< float > freq
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
std::vector< sampleCount > where
std::optional< AudioSegmentSampleView > mSampleCacheHolder
Spectrogram settings, either for one track or as defaults.
size_t GetChannelIndex() const
double GetTrimRight() const
int GetRate() const override
double GetTrimLeft() const
const Sequence & GetSequence() const
double GetStretchRatio() const override
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....
This allows multiple clips to be a part of one WaveTrack.
Positions or offsets within audio files need a wide type.
long long as_long_long() const
AUDACITY_DLL_API void fillWhere(std::vector< sampleCount > &where, size_t len, bool addBias, double correction, double t0, double sampleRate, double stretchRatio, double samplesPerPixel)
AUDACITY_DLL_API void findCorrection(const std::vector< sampleCount > &oldWhere, size_t oldLen, size_t newLen, double t0, double sampleRate, double stretchRatio, double samplesPerPixel, int &oldX0, double &correction)
constexpr auto sampleRate
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
void ComputeSpectrogramGainFactors(size_t fftLen, double rate, int frequencyGain, std::vector< float > &gainFactors)
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
void copy(const T *src, T *dst, int32_t n)
ArrayOf< int > BitReversed
void MakeStereo(WaveClipListener &&other, bool aligned) override
std::vector< std::unique_ptr< SpecPxCache > > mSpecPxCaches
void SwapChannels() override
Default implementation does nothing.
bool GetSpectrogram(const WaveChannelInterval &clip, const float *&spectrogram, SpectrogramSettings &spectrogramSettings, const sampleCount *&where, size_t numPixels, double t0, double pixelsPerSecond)
static WaveClipSpectrumCache & Get(const WaveChannelInterval &clip)
~WaveClipSpectrumCache() override
std::unique_ptr< WaveClipListener > Clone() const override
void MarkChanged() noexcept override
void Erase(size_t index) override
WaveClipSpectrumCache(size_t nChannels)
void Invalidate() override
std::vector< std::unique_ptr< SpecCache > > mSpecCaches