diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index fa080fed8..6031181c6 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -1803,6 +1803,7 @@ static float sumFreqValues( // Helper function to decide on which color set to use. // dashCount counts both dashes and the spaces between them. +inline AColor::ColorGradientChoice ChooseColorSet( float bin0, float bin1, float selBinLo, float selBinCenter, float selBinHi, int dashCount, bool isSpectral ) { @@ -1959,8 +1960,8 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, if (!image)return; unsigned char *data = image->GetData(); - int windowSize = GetSpectrumWindowSize(); - int half = windowSize/2; + int windowSize = GetSpectrumWindowSize(!autocorrelation); + const int half = windowSize / 2; float *freq = new float[mid.width * half]; sampleCount *where = new sampleCount[mid.width+1]; @@ -2106,7 +2107,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, AColor::ColorGradientChoice selected = AColor::ColorGradientUnselected; - // If we are in the time selected range, then we may use a differnt color set. + // If we are in the time selected range, then we may use a different color set. if (ssel0 <= w0 && w1 < ssel1) { bool isSpectral = ((track->GetDisplay() == WaveTrack::SpectralSelectionDisplay) || @@ -2220,7 +2221,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, AColor::ColorGradientChoice selected = AColor::ColorGradientUnselected; - // If we are in the time selected range, then we may use a differnt color set. + // If we are in the time selected range, then we may use a different color set. if (ssel0 <= w0 && w1 < ssel1) { bool isSpectral = ((track->GetDisplay() == WaveTrack::SpectralSelectionDisplay) || @@ -3029,6 +3030,9 @@ void TrackArtist::UpdatePrefs() mLogMinFreq = 1; mWindowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256); +#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS + mZeroPaddingFactor = gPrefs->Read(wxT("/Spectrum/ZeroPaddingFactor"), 1); +#endif mIsGrayscale = (gPrefs->Read(wxT("/Spectrum/Grayscale"), 0L) != 0); #ifdef EXPERIMENTAL_FFT_Y_GRID @@ -3070,9 +3074,14 @@ int TrackArtist::GetSpectrumLogMaxFreq(int deffreq) return mLogMaxFreq < 0 ? deffreq : mLogMaxFreq; } -int TrackArtist::GetSpectrumWindowSize() +int TrackArtist::GetSpectrumWindowSize(bool includeZeroPadding) { - return mWindowSize; +#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS + if (includeZeroPadding) + return mWindowSize * mZeroPaddingFactor; + else +#endif + return mWindowSize; } #ifdef EXPERIMENTAL_FFT_SKIP_POINTS diff --git a/src/TrackArtist.h b/src/TrackArtist.h index fcee3033e..467c775cb 100644 --- a/src/TrackArtist.h +++ b/src/TrackArtist.h @@ -73,7 +73,7 @@ class AUDACITY_DLL_API TrackArtist { int GetSpectrumMaxFreq(int deffreq); int GetSpectrumLogMinFreq(int deffreq); int GetSpectrumLogMaxFreq(int deffreq); - int GetSpectrumWindowSize(); + int GetSpectrumWindowSize(bool includeZeroPadding); #ifdef EXPERIMENTAL_FFT_SKIP_POINTS int GetSpectrumFftSkipPoints(); @@ -184,6 +184,9 @@ class AUDACITY_DLL_API TrackArtist { int mMaxFreq; // "/Spectrum/MaxFreq" int mMinFreq; // "/Spectrum/MinFreq" int mWindowSize; // "/Spectrum/FFTSize" +#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS + int mZeroPaddingFactor; // "/Spectrum/ZeroPaddingFactor" +#endif bool mIsGrayscale; // "/Spectrum/Grayscale" bool mbShowTrackNameInWaveform; // "/GUI/ShowTrackNameInWaveform" diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 55b4d677a..c37c206e6 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -2977,7 +2977,8 @@ inline double findMaxRatio(double center, double rate) void TrackPanel::SnapCenterOnce(WaveTrack *pTrack, bool up) { - const int windowSize = mTrackArtist->GetSpectrumWindowSize(); + // Always spectrogram, never pitch view, pass true + const int windowSize = mTrackArtist->GetSpectrumWindowSize(true); const double rate = pTrack->GetRate(); const double nyq = rate / 2.0; const double binFrequency = rate / windowSize; @@ -3038,7 +3039,10 @@ void TrackPanel::StartSnappingFreqSelection (WaveTrack *pTrack) // Use same settings as are now used for spectrogram display, // except, shrink the window as needed so we get some answers - int windowSize = mTrackArtist->GetSpectrumWindowSize(); + + // Always spectrogram, never pitch view, pass true + int windowSize = mTrackArtist->GetSpectrumWindowSize(true); + while(windowSize > effectiveLength) windowSize >>= 1; int windowType; @@ -4714,7 +4718,9 @@ void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event ) max = mTrackArtist->GetSpectrumMaxFreq(8000); if(max > rate/2.) max = rate/2.; - windowSize = mTrackArtist->GetSpectrumWindowSize(); + + // Always spectrogram, never pitch view, pass true + windowSize = mTrackArtist->GetSpectrumWindowSize(true); #ifdef EXPERIMENTAL_FFT_SKIP_POINTS fftSkipPoints = mTrackArtist->GetSpectrumFftSkipPoints(); #endif //EXPERIMENTAL_FFT_SKIP_POINTS @@ -4729,7 +4735,9 @@ void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event ) max = mTrackArtist->GetSpectrumLogMaxFreq(lrint(rate/2.)); if(max > rate/2.) max = rate/2.; - windowSize = mTrackArtist->GetSpectrumWindowSize(); + + // Always spectrogram, never pitch view, pass true + windowSize = mTrackArtist->GetSpectrumWindowSize(true); #ifdef EXPERIMENTAL_FFT_SKIP_POINTS fftSkipPoints = mTrackArtist->GetSpectrumFftSkipPoints(); #endif //EXPERIMENTAL_FFT_SKIP_POINTS diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 61a6373a3..48f2f5e16 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -226,6 +226,7 @@ public: rangeOld = -1; windowTypeOld = -1; windowSizeOld = -1; + zeroPaddingFactorOld = 1; frequencyGainOld = false; #ifdef EXPERIMENTAL_FFT_SKIP_POINTS fftSkipPointsOld = -1; @@ -252,6 +253,7 @@ public: int rangeOld; int windowTypeOld; int windowSizeOld; + int zeroPaddingFactorOld; int frequencyGainOld; #ifdef EXPERIMENTAL_FFT_SKIP_POINTS int fftSkipPointsOld; @@ -284,8 +286,9 @@ static void ComputeSpectrumUsingRealFFTf(float *buffer, HFFT hFFT, float *window else out[0] = 10.0*log10(power); for(i=1;iPoints;i++) { - power = (buffer[hFFT->BitReversed[i] ]*buffer[hFFT->BitReversed[i] ]) - + (buffer[hFFT->BitReversed[i]+1]*buffer[hFFT->BitReversed[i]+1]); + const int index = hFFT->BitReversed[i]; + const float re = buffer[index], im = buffer[index + 1]; + power = re * re + im * im; if(power <= 0) out[i] = -160.0; else @@ -307,6 +310,7 @@ WaveClip::WaveClip(DirManager *projDirManager, sampleFormat format, int rate) hFFT = NULL; mWindow = NULL; #endif + mZeroPaddingFactor = 1; mSpecCache = new SpecCache(0, 1, false); mSpecPxCache = new SpecPxCache(1); mAppendBuffer = NULL; @@ -335,6 +339,7 @@ WaveClip::WaveClip(const WaveClip& orig, DirManager *projDirManager) hFFT = NULL; mWindow = NULL; #endif + mZeroPaddingFactor = 1; mSpecCache = new SpecCache(0, 1, false); mSpecPxCache = new SpecPxCache(1); @@ -754,6 +759,67 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, return true; } +namespace +{ +enum { WINDOW, TWINDOW, DWINDOW }; +void RecreateWindow( + float *&window, int which, int fftLen, + int padding, int windowType, int windowSize, double &scale) +{ + if (window != NULL) + delete[] window; + // Create the requested window function + window = new float[fftLen]; + int ii; + + wxASSERT(windowSize % 2 == 0); + const int endOfWindow = padding + windowSize; + // Left and right padding + for (ii = 0; ii < padding; ++ii) { + window[ii] = 0.0; + window[fftLen - ii - 1] = 0.0; + } + // Default rectangular window in the middle + for (; ii < endOfWindow; ++ii) + window[ii] = 1.0; + // Overwrite middle as needed + switch (which) { + case WINDOW: + WindowFunc(windowType, windowSize, window + padding); + // NewWindowFunc(windowType, windowSize, extra, window + padding); + break; + case TWINDOW: + wxASSERT(false); +#if 0 + // Future, reassignment + NewWindowFunc(windowType, windowSize, extra, window + padding); + for (int ii = padding, multiplier = -windowSize / 2; ii < endOfWindow; ++ii, ++multiplier) + window[ii] *= multiplier; + break; +#endif + case DWINDOW: + wxASSERT(false); +#if 0 + // Future, reassignment + DerivativeOfWindowFunc(windowType, windowSize, extra, window + padding); + break; +#endif + default: + wxASSERT(false); + } + // Scale the window function to give 0dB spectrum for 0dB sine tone + if (which == WINDOW) { + scale = 0.0; + for (ii = padding; ii < endOfWindow; ++ii) + scale += window[ii]; + if (scale > 0) + scale = 2.0 / scale; + } + for (ii = padding; ii < endOfWindow; ++ii) + window[ii] *= scale; +} +} + bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, float *freq, sampleCount *where, int numPixels, @@ -771,37 +837,34 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, int fftSkipPoints = gPrefs->Read(wxT("/Spectrum/FFTSkipPoints"), 0L); int fftSkipPoints1 = fftSkipPoints+1; #endif //EXPERIMENTAL_FFT_SKIP_POINTS - int half = windowSize/2; + const int zeroPaddingFactor = + autocorrelation ? 1 : gPrefs->Read(wxT("/Spectrum/ZeroPaddingFactor"), 1); gPrefs->Read(wxT("/Spectrum/WindowType"), &windowType, 3); + // FFT length may be longer than the window of samples that affect results + // because of zero padding done for increased frequency resolution + const int fftLen = windowSize * zeroPaddingFactor; + const int half = fftLen / 2; + const int padding = (windowSize * (zeroPaddingFactor - 1)) / 2; + #ifdef EXPERIMENTAL_USE_REALFFTF // Update the FFT and window if necessary if((mWindowType != windowType) || (mWindowSize != windowSize) - || (hFFT == NULL) || (mWindow == NULL) || (mWindowSize != hFFT->Points*2) ) { + || (hFFT == NULL) || (mWindow == NULL) || (fftLen != hFFT->Points * 2) + || (mZeroPaddingFactor != zeroPaddingFactor)) { mWindowType = windowType; mWindowSize = windowSize; if(hFFT != NULL) EndFFT(hFFT); - hFFT = InitializeFFT(mWindowSize); - if(mWindow != NULL) delete[] mWindow; - // Create the requested window function - mWindow = new float[mWindowSize]; - int i; - for(i=0; i 0) { - ws = 2.0/ws; - for(i=0; idirty == mDirty && @@ -811,6 +874,7 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, mSpecCache->gainOld == gain && mSpecCache->windowTypeOld == windowType && mSpecCache->windowSizeOld == windowSize && + mSpecCache->zeroPaddingFactorOld == zeroPaddingFactor && mSpecCache->frequencyGainOld == frequencygain && #ifdef EXPERIMENTAL_FFT_SKIP_POINTS mSpecCache->fftSkipPointsOld == fftSkipPoints && @@ -914,19 +978,26 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, } } - float *useBuffer; + float *useBuffer = 0; #ifdef EXPERIMENTAL_FFT_SKIP_POINTS - float *buffer = new float[windowSize*fftSkipPoints1]; + float *buffer = new float[fftLen*fftSkipPoints1]; mSpecCache->fftSkipPointsOld = fftSkipPoints; #else //!EXPERIMENTAL_FFT_SKIP_POINTS - float *buffer = new float[windowSize]; + float *buffer = new float[fftLen]; #endif //EXPERIMENTAL_FFT_SKIP_POINTS + // Initialize zero padding in the buffer + for (int ii = 0; ii < padding; ++ii) { + buffer[ii] = 0.0; + buffer[fftLen - ii - 1] = 0.0; + } + mSpecCache->minFreqOld = minFreq; mSpecCache->maxFreqOld = maxFreq; mSpecCache->gainOld = gain; mSpecCache->rangeOld = range; mSpecCache->windowTypeOld = windowType; mSpecCache->windowSizeOld = windowSize; + mSpecCache->zeroPaddingFactorOld = zeroPaddingFactor; mSpecCache->frequencyGainOld = frequencygain; float *gainfactor = NULL; @@ -953,10 +1024,9 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, mSpecCache->freq[half * x + i] = 0; } - else - { - bool copy = !autocorrelation; - float *adj = buffer; + else { + bool copy = !autocorrelation || (padding > 0); + float *adj = buffer + padding; start -= windowSize >> 1; if (start < 0) { @@ -1015,7 +1085,7 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, mRate, &mSpecCache->freq[half * x], autocorrelation, windowType); } else { - ComputeSpectrumUsingRealFFTf(useBuffer, hFFT, mWindow, mWindowSize, &mSpecCache->freq[half * x]); + ComputeSpectrumUsingRealFFTf(useBuffer, hFFT, mWindow, fftLen, &mSpecCache->freq[half * x]); } #else // EXPERIMENTAL_USE_REALFFTF ComputeSpectrum(buffer, windowSize, windowSize, diff --git a/src/WaveClip.h b/src/WaveClip.h index f303a32f9..1d2d581ab 100644 --- a/src/WaveClip.h +++ b/src/WaveClip.h @@ -257,6 +257,7 @@ protected: int mWindowType; int mWindowSize; #endif + int mZeroPaddingFactor; samplePtr mAppendBuffer; sampleCount mAppendBufferLen;