diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 0bdc51778..7074da3cf 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -35,6 +35,7 @@ #include "Project.h" #include "WaveTrack.h" #include "FFT.h" +#include "Profiler.h" #include "prefs/SpectrogramSettings.h" @@ -42,6 +43,17 @@ #include "Experimental.h" +//#undef _OPENMP +#ifdef _OPENMP +#include <omp.h> +#else +// Comment this out if you want to profile non OpenMP builds too. +#undef BEGIN_TASK_PROFILING +#undef END_TASK_PROFILING +#define BEGIN_TASK_PROFILING(TASK_DESCRIPTION) +#define END_TASK_PROFILING(TASK_DESCRIPTION) +#endif + class WaveCache { public: WaveCache() @@ -261,7 +273,7 @@ protected: }; static void ComputeSpectrumUsingRealFFTf - (float *buffer, HFFT hFFT, const float *window, int len, float *out) + (float * __restrict buffer, HFFT hFFT, const float * __restrict window, int len, float * __restrict out) { int i; if(len > hFFT->Points*2) @@ -276,7 +288,7 @@ static void ComputeSpectrumUsingRealFFTf if(power <= 0) out[0] = -160.0; else - out[0] = 10.0*log10(power); + out[0] = 10.0*log10f(power); for(i=1;i<hFFT->Points;i++) { const int index = hFFT->BitReversed[i]; const float re = buffer[index], im = buffer[index + 1]; @@ -765,7 +777,7 @@ bool SpecCache::CalculateOneSpectrum double offset, double rate, double pixelsPerSecond, int lowerBoundX, int upperBoundX, const std::vector<float> &gainFactors, - float *scratch) + float* __restrict scratch, float* __restrict out) const { bool result = false; const bool reassignment = @@ -790,11 +802,13 @@ bool SpecCache::CalculateOneSpectrum if (start <= 0 || start >= numSamples) { if (xx >= 0 && xx < len) { // Pixel column is out of bounds of the clip! Should not happen. - float *const results = &freq[half * xx]; + float *const results = &out[half * xx]; std::fill(results, results + half, 0.0f); } } else { + + // We can avoid copying memory when ComputeSpectrum is used below bool copy = !autocorrelation || (padding > 0) || reassignment; float *useBuffer = 0; @@ -826,6 +840,7 @@ bool SpecCache::CalculateOneSpectrum if (myLen > 0) { useBuffer = (float*)(waveTrackCache.Get(floatSample, floor(0.5 + start + offset * rate), myLen)); + if (copy) memcpy(adj, useBuffer, myLen * sizeof(float)); } @@ -835,7 +850,7 @@ bool SpecCache::CalculateOneSpectrum useBuffer = scratch; if (autocorrelation) { - float *const results = &freq[half * xx]; + float *const results = &out[half * xx]; // This function does not mutate useBuffer ComputeSpectrum(useBuffer, windowSize, windowSize, rate, results, @@ -916,12 +931,12 @@ bool SpecCache::CalculateOneSpectrum int correctedX = (floor(0.5 + xx + timeCorrection * pixelsPerSecond / rate)); if (correctedX >= lowerBoundX && correctedX < upperBoundX) result = true, - freq[half * correctedX + bin] += power; + out[half * correctedX + bin] += power; } } } else { - float *const results = &freq[half * xx]; + float *const results = &out[half * xx]; // Do the FFT. Note that useBuffer is multiplied by the window, // and the window is initialized with leading and trailing zeroes @@ -938,6 +953,7 @@ bool SpecCache::CalculateOneSpectrum } } } + return result; } @@ -967,24 +983,65 @@ void SpecCache::Populate const int half = fftLen / 2; const size_t bufferSize = fftLen; - - std::vector<float> buffer(reassignment ? 3 * bufferSize : bufferSize); + const size_t scratchSize = reassignment ? 3 * bufferSize : bufferSize; + std::vector<float> scratch(scratchSize); std::vector<float> gainFactors; if (!autocorrelation) ComputeSpectrogramGainFactors(fftLen, rate, frequencyGain, gainFactors); +#ifdef _OPENMP + // todo: query # of threads or make it a setting + const int numThreads = 8; + omp_set_num_threads(numThreads); + + // We need certain per-thread data for thread safety + // Assumes WaveTrackCache is reentrant since it takes a const* to WaveTrack + struct { + WaveTrackCache* cache; + float* scratch; + } threadLocalStorage[numThreads]; + + // May as well use existing data for one of the threads + assert(numThreads > 0); + threadLocalStorage[0].cache = &waveTrackCache; + threadLocalStorage[0].scratch = &scratch[0]; + + for (int i = 1; i < numThreads; i++) { + threadLocalStorage[i].cache = new WaveTrackCache( waveTrackCache.GetTrack() ); + threadLocalStorage[i].scratch = new float[scratchSize]; + } +#endif + // Loop over the ranges before and after the copied portion and compute anew. // One of the ranges may be empty. for (int jj = 0; jj < 2; ++jj) { const int lowerBoundX = jj == 0 ? 0 : copyEnd; const int upperBoundX = jj == 0 ? copyBegin : numPixels; + +#ifdef _OPENMP + #pragma omp parallel for +#endif for (auto xx = lowerBoundX; xx < upperBoundX; ++xx) + { +#ifdef _OPENMP + int threadNum = omp_get_thread_num(); + + assert(threadNum >=0 && threadNum < numThreads); + + WaveTrackCache* cache = threadLocalStorage[threadNum].cache; + float* buffer = threadLocalStorage[threadNum].scratch; +#else + WaveTrackCache* cache = &waveTrackCache; + float* buffer = &scratch[0]; +#endif + CalculateOneSpectrum( - settings, waveTrackCache, xx, numSamples, + settings, *cache, xx, numSamples, offset, rate, pixelsPerSecond, lowerBoundX, upperBoundX, - gainFactors, &buffer[0]); + gainFactors, buffer, &freq[0]); + } if (reassignment) { // Need to look beyond the edges of the range to accumulate more @@ -1000,7 +1057,7 @@ void SpecCache::Populate settings, waveTrackCache, --xx, numSamples, offset, rate, pixelsPerSecond, lowerBoundX, upperBoundX, - gainFactors, &buffer[0]); + gainFactors, &scratch[0], &freq[0]); if (!result) break; } @@ -1013,13 +1070,16 @@ void SpecCache::Populate settings, waveTrackCache, xx++, numSamples, offset, rate, pixelsPerSecond, lowerBoundX, upperBoundX, - gainFactors, &buffer[0]); + gainFactors, &scratch[0], &freq[0]); if (!result) break; } // Now Convert to dB terms. Do this only after accumulating // power values, which may cross columns with the time correction. +#ifdef _OPENMP + #pragma omp parallel for +#endif for (auto xx = lowerBoundX; xx < upperBoundX; ++xx) { float *const results = &freq[half * xx]; const HFFT hFFT = settings.hFFT; @@ -1038,6 +1098,14 @@ void SpecCache::Populate } } } + +#ifdef _OPENMP + for (int i = 1; i < numThreads; i++) + { + delete[] threadLocalStorage[i].scratch; + delete threadLocalStorage[i].cache; + } +#endif } bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, @@ -1045,6 +1113,8 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, int numPixels, double t0, double pixelsPerSecond) const { + BEGIN_TASK_PROFILING("GetSpectrogram"); + const WaveTrack *const track = waveTrackCache.GetTrack(); const SpectrogramSettings &settings = track->GetSpectrogramSettings(); const bool autocorrelation = @@ -1074,6 +1144,9 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, mSpecCache->len >= numPixels) { spectrogram = &mSpecCache->freq[0]; where = &mSpecCache->where[0]; + + END_TASK_PROFILING("GetSpectrogram"); + return false; //hit cache completely } @@ -1125,14 +1198,21 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, half * (copyEnd - copyBegin) * sizeof(float)); } + BEGIN_TASK_PROFILING("Populate"); + mSpecCache->Populate (settings, waveTrackCache, copyBegin, copyEnd, numPixels, mSequence->GetNumSamples(), mOffset, mRate, pixelsPerSecond); + END_TASK_PROFILING("Populate"); + mSpecCache->dirty = mDirty; spectrogram = &mSpecCache->freq[0]; where = &mSpecCache->where[0]; + + END_TASK_PROFILING("GetSpectrogram"); + return true; } diff --git a/src/WaveClip.h b/src/WaveClip.h index 23f9ad8f2..c12f2f989 100644 --- a/src/WaveClip.h +++ b/src/WaveClip.h @@ -97,7 +97,8 @@ public: double offset, double rate, double pixelsPerSecond, int lowerBoundX, int upperBoundX, const std::vector<float> &gainFactors, - float *scratch); + float* __restrict scratch, + float* __restrict out) const; void Populate (const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache, diff --git a/src/widgets/NumericTextCtrl.cpp b/src/widgets/NumericTextCtrl.cpp index d7eacfc2c..5b6369e06 100644 --- a/src/widgets/NumericTextCtrl.cpp +++ b/src/widgets/NumericTextCtrl.cpp @@ -1667,6 +1667,7 @@ void NumericTextCtrl::OnKeyDown(wxKeyEvent &event) } mValueString[digitPosition] = wxChar(keyCode); ControlsToValue(); + Refresh();// Force an update of the control. [Bug 1497] ValueToControls(); mFocusedDigit = (mFocusedDigit + 1) % (mDigits.GetCount()); Updated(); @@ -1686,6 +1687,7 @@ void NumericTextCtrl::OnKeyDown(wxKeyEvent &event) if (theDigit != wxChar('-')) theDigit = '0'; ControlsToValue(); + Refresh();// Force an update of the control. [Bug 1497] ValueToControls(); Updated(); }