1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-22 15:20:15 +02:00

zero-padding factor for spectrograms, internals

This commit is contained in:
Paul Licameli 2015-05-31 13:23:21 -04:00
parent 4b9d81f228
commit 6a1227f039
5 changed files with 131 additions and 40 deletions

View File

@ -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,8 +3074,13 @@ int TrackArtist::GetSpectrumLogMaxFreq(int deffreq)
return mLogMaxFreq < 0 ? deffreq : mLogMaxFreq;
}
int TrackArtist::GetSpectrumWindowSize()
int TrackArtist::GetSpectrumWindowSize(bool includeZeroPadding)
{
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
if (includeZeroPadding)
return mWindowSize * mZeroPaddingFactor;
else
#endif
return mWindowSize;
}

View File

@ -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"

View File

@ -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

View File

@ -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;i<hFFT->Points;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<windowSize; i++)
mWindow[i]=1.0;
WindowFunc(mWindowType, mWindowSize, mWindow);
// Scale the window function to give 0dB spectrum for 0dB sine tone
double ws=0;
for(i=0; i<windowSize; i++)
ws += mWindow[i];
if(ws > 0) {
ws = 2.0/ws;
for(i=0; i<windowSize; i++)
mWindow[i] *= ws;
}
hFFT = InitializeFFT(fftLen);
double scale;
RecreateWindow(mWindow, WINDOW, fftLen, padding, mWindowType, mWindowSize, scale);
}
#endif // EXPERIMENTAL_USE_REALFFTF
mZeroPaddingFactor = zeroPaddingFactor;
const bool match =
mSpecCache &&
mSpecCache->dirty == 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,

View File

@ -257,6 +257,7 @@ protected:
int mWindowType;
int mWindowSize;
#endif
int mZeroPaddingFactor;
samplePtr mAppendBuffer;
sampleCount mAppendBufferLen;