1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-17 16:40:07 +02:00

Merge: more drawing code reorganizations...

Fix compilation of some disabled experimental branches for spectrograms ...
  Keep FFT windows for Spectrograms in one place in SpectrogramSettings...
  Created a global structure to hold spectrogram preferences...
  Don't invalidate SpecCache for changes of min, max, gain or range...
This commit is contained in:
Paul Licameli 2015-06-04 11:32:01 -04:00
commit cf54b4c314
8 changed files with 464 additions and 336 deletions

View File

@ -2,15 +2,15 @@
#define __realfftf_h #define __realfftf_h
#define fft_type float #define fft_type float
typedef struct FFTParamType { struct FFTParam {
int *BitReversed; int *BitReversed;
fft_type *SinTable; fft_type *SinTable;
int Points; int Points;
#ifdef EXPERIMENTAL_EQ_SSE_THREADED #ifdef EXPERIMENTAL_EQ_SSE_THREADED
int pow2Bits; int pow2Bits;
#endif #endif
} FFTParam; };
#define HFFT FFTParam * typedef FFTParam * HFFT;
HFFT InitializeFFT(int); HFFT InitializeFFT(int);
void EndFFT(HFFT); void EndFFT(HFFT);

View File

@ -173,6 +173,7 @@ audio tracks.
#include "LabelTrack.h" #include "LabelTrack.h"
#include "TimeTrack.h" #include "TimeTrack.h"
#include "Prefs.h" #include "Prefs.h"
#include "prefs/SpectrumPrefs.h"
#include "Sequence.h" #include "Sequence.h"
#include "Spectrum.h" #include "Spectrum.h"
#include "ViewInfo.h" #include "ViewInfo.h"
@ -270,14 +271,6 @@ TrackArtist::TrackArtist()
SetColours(); SetColours();
vruler = new Ruler(); vruler = new Ruler();
#ifdef EXPERIMENTAL_FFT_Y_GRID
fftYGridOld=true;
#endif //EXPERIMENTAL_FFT_Y_GRID
#ifdef EXPERIMENTAL_FIND_NOTES
fftFindNotesOld=false;
#endif
} }
TrackArtist::~TrackArtist() TrackArtist::~TrackArtist()
@ -672,6 +665,10 @@ void TrackArtist::DrawVRuler(Track *t, wxDC * dc, wxRect & r)
void TrackArtist::UpdateVRuler(Track *t, wxRect & r) void TrackArtist::UpdateVRuler(Track *t, wxRect & r)
{ {
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
const int fftSkipPoints = SpectrogramSettings::defaults().fftSkipPoints;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
// Label tracks do not have a vruler // Label tracks do not have a vruler
if (t->GetKind() == Track::Label) { if (t->GetKind() == Track::Label) {
return; return;
@ -811,14 +808,14 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & r)
int maxFreq = GetSpectrumMaxFreq(freq); int maxFreq = GetSpectrumMaxFreq(freq);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
maxFreq/=(mFftSkipPoints+1); maxFreq/=(fftSkipPoints+1);
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
if(maxFreq > freq) if(maxFreq > freq)
maxFreq = freq; maxFreq = freq;
int minFreq = GetSpectrumMinFreq(0); int minFreq = GetSpectrumMinFreq(0);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
minFreq/=(mFftSkipPoints+1); minFreq/=(fftSkipPoints+1);
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
if(minFreq < 0) if(minFreq < 0)
minFreq = 0; minFreq = 0;
@ -857,14 +854,14 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & r)
int maxFreq = GetSpectrumLogMaxFreq(freq); int maxFreq = GetSpectrumLogMaxFreq(freq);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
maxFreq/=(mFftSkipPoints+1); maxFreq/=(fftSkipPoints+1);
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
if(maxFreq > freq) if(maxFreq > freq)
maxFreq = freq; maxFreq = freq;
int minFreq = GetSpectrumLogMinFreq(freq/1000.0); int minFreq = GetSpectrumLogMinFreq(freq/1000.0);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
minFreq/=(mFftSkipPoints+1); minFreq/=(fftSkipPoints+1);
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
if(minFreq < 1) if(minFreq < 1)
minFreq = 1; minFreq = 1;
@ -1936,9 +1933,19 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
} }
#endif #endif
int range = gPrefs->Read(wxT("/Spectrum/Range"), 80L); const SpectrogramSettings &settings = SpectrogramSettings::defaults();
int gain = gPrefs->Read(wxT("/Spectrum/Gain"), 20L); const bool &isGrayscale = settings.isGrayscale;
const int &range = settings.range;
const int &gain = settings.gain;
#ifdef EXPERIMENTAL_FIND_NOTES
const bool &fftFindNotes = settings.fftFindNotes;
const bool &findNotesMinA = settings.findNotesMinA;
const bool &numberOfMaxima = settings.numberOfMaxima;
const bool &findNotesQuantize = settings.findNotesQuantize;
#endif
#ifdef EXPERIMENTAL_FFT_Y_GRID
const bool &fftYGrid = settings.fftYGrid;
#endif
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetPen(*wxTRANSPARENT_PEN);
@ -1959,7 +1966,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
t0, pps, autocorrelation); t0, pps, autocorrelation);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int fftSkipPoints = gPrefs->Read(wxT("/Spectrum/FFTSkipPoints"), 0L); int fftSkipPoints = SpectrogramSettings::defaults().fftSkipPoints;
int fftSkipPoints1 = fftSkipPoints + 1; int fftSkipPoints1 = fftSkipPoints + 1;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
@ -2009,8 +2016,8 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
for (int y = 0; y < mid.height; y++) { for (int y = 0; y < mid.height; y++) {
float n = (float(y) / mid.height*scale2 - lmin2) * 12; float n = (float(y) / mid.height*scale2 - lmin2) * 12;
float n2 = (float(y + 1) / mid.height*scale2 - lmin2) * 12; float n2 = (float(y + 1) / mid.height*scale2 - lmin2) * 12;
float f = float(minFreq) / (mFftSkipPoints + 1)*powf(2.0f, n / 12.0f + lmin2); float f = float(minFreq) / (fftSkipPoints + 1)*powf(2.0f, n / 12.0f + lmin2);
float f2 = float(minFreq) / (mFftSkipPoints + 1)*powf(2.0f, n2 / 12.0f + lmin2); float f2 = float(minFreq) / (fftSkipPoints + 1)*powf(2.0f, n2 / 12.0f + lmin2);
n = logf(f / 440) / log2 * 12; n = logf(f / 440) / log2 * 12;
n2 = logf(f2 / 440) / log2 * 12; n2 = logf(f2 / 440) / log2 * 12;
if (floor(n) < floor(n2)) if (floor(n) < floor(n2))
@ -2021,14 +2028,16 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
#endif //EXPERIMENTAL_FFT_Y_GRID #endif //EXPERIMENTAL_FFT_Y_GRID
if (!updated && clip->mSpecPxCache->valid && (clip->mSpecPxCache->len == mid.height * mid.width) if (!updated && clip->mSpecPxCache->valid && (clip->mSpecPxCache->len == mid.height * mid.width)
&& gain == clip->mSpecPxCache->gain
&& range == clip->mSpecPxCache->range
#ifdef EXPERIMENTAL_FFT_Y_GRID #ifdef EXPERIMENTAL_FFT_Y_GRID
&& mFftYGrid==fftYGridOld && fftYGrid==fftYGridOld
#endif //EXPERIMENTAL_FFT_Y_GRID #endif //EXPERIMENTAL_FFT_Y_GRID
#ifdef EXPERIMENTAL_FIND_NOTES #ifdef EXPERIMENTAL_FIND_NOTES
&& mFftFindNotes==fftFindNotesOld && fftFindNotes==fftFindNotesOld
&& mFindNotesMinA==findNotesMinAOld && findNotesMinA==findNotesMinAOld
&& mNumberOfMaxima==findNotesNOld && numberOfMaxima==findNotesNOld
&& mFindNotesQuantize==findNotesQuantizeOld && findNotesQuantize==findNotesQuantizeOld
#endif #endif
) { ) {
// cache is up to date // cache is up to date
@ -2037,21 +2046,23 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
delete clip->mSpecPxCache; delete clip->mSpecPxCache;
clip->mSpecPxCache = new SpecPxCache(mid.width * mid.height); clip->mSpecPxCache = new SpecPxCache(mid.width * mid.height);
clip->mSpecPxCache->valid = true; clip->mSpecPxCache->valid = true;
clip->mSpecPxCache->gain = gain;
clip->mSpecPxCache->range = range;
#ifdef EXPERIMENTAL_FIND_NOTES #ifdef EXPERIMENTAL_FIND_NOTES
fftFindNotesOld=mFftFindNotes; fftFindNotesOld = fftFindNotes;
findNotesMinAOld=mFindNotesMinA; findNotesMinAOld = findNotesMinA;
findNotesNOld=mNumberOfMaxima; findNotesNOld = numberOfMaxima;
findNotesQuantizeOld=mFindNotesQuantize; findNotesQuantizeOld = findNotesQuantize;
#endif #endif
#ifdef EXPERIMENTAL_FIND_NOTES #ifdef EXPERIMENTAL_FIND_NOTES
const float
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
lmins = logf(float(minFreq) / (mFftSkipPoints + 1)), const float
lmaxs = logf(float(maxFreq) / (mFftSkipPoints + 1)), lmins = logf(float(minFreq) / (fftSkipPoints + 1)),
lmaxs = logf(float(maxFreq) / (fftSkipPoints + 1))
#else //!EXPERIMENTAL_FFT_SKIP_POINTS #else //!EXPERIMENTAL_FFT_SKIP_POINTS
lmins = lmin, lmins = lmin,
lmaxs = lmax, lmaxs = lmax
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
; ;
#endif //EXPERIMENTAL_FIND_NOTES #endif //EXPERIMENTAL_FIND_NOTES
@ -2061,7 +2072,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
float maxima0[128], maxima1[128]; float maxima0[128], maxima1[128];
const float const float
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
f2bin = half / (rate / 2.0f / (mFftSkipPoints + 1)), f2bin = half / (rate / 2.0f / (fftSkipPoints + 1)),
#else //!EXPERIMENTAL_FFT_SKIP_POINTS #else //!EXPERIMENTAL_FFT_SKIP_POINTS
f2bin = half / (rate / 2.0f), f2bin = half / (rate / 2.0f),
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
@ -2088,13 +2099,14 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
else { else {
#ifdef EXPERIMENTAL_FIND_NOTES #ifdef EXPERIMENTAL_FIND_NOTES
int maximas=0; int maximas=0;
if (!usePxCache && mFftFindNotes) { const int x0 = half * x;
if (fftFindNotes) {
for (int i = maxTableSize-1; i >= 0; i--) for (int i = maxTableSize-1; i >= 0; i--)
indexes[i]=-1; indexes[i]=-1;
// Build a table of (most) values, put the index in it. // Build a table of (most) values, put the index in it.
for (int i = int(i0); i < int(i1); i++) { for (int i = int(i0); i < int(i1); i++) {
float freqi=freq[x0+int(i)]; float freqi=freq[x0 + int(i)];
int value=int((freqi+gain+range)/range*(maxTableSize-1)); int value=int((freqi+gain+range)/range*(maxTableSize-1));
if (value < 0) if (value < 0)
value=0; value=0;
@ -2103,25 +2115,25 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
indexes[value]=i; indexes[value]=i;
} }
// Build from the indices an array of maxima. // Build from the indices an array of maxima.
for (int i = maxTableSize-1; i >= 0; i--) { for (int i = maxTableSize - 1; i >= 0; i--) {
int index=indexes[i]; int index = indexes[i];
if (index >= 0) { if (index >= 0) {
float freqi=freq[x0+index]; float freqi = freq[x0 + index];
if (freqi < mFindNotesMinA) if (freqi < findNotesMinA)
break; break;
bool ok=true; bool ok = true;
for (int m=0; m < maximas; m++) { for (int m = 0; m < maximas; m++) {
// Avoid to store very close maxima. // Avoid to store very close maxima.
float maxm = maxima[m]; float maxm = maxima[m];
if (maxm/index < minDistance && index/maxm < minDistance) { if (maxm / index < minDistance && index / maxm < minDistance) {
ok=false; ok = false;
break; break;
} }
} }
if (ok) { if (ok) {
maxima[maximas++] = index; maxima[maximas++] = index;
if (maximas >= mNumberOfMaxima) if (maximas >= numberOfMaxima)
break; break;
} }
} }
@ -2134,7 +2146,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
for (int i=0; i < maximas; i++) { for (int i=0; i < maximas; i++) {
int index=maxima[i]; int index=maxima[i];
float f = float(index)*bin2f; float f = float(index)*bin2f;
if (mFindNotesQuantize) if (findNotesQuantize)
{ f = expf(int(log(f/440)/log2*12-0.5)/12.0f*log2)*440; { f = expf(int(log(f/440)/log2*12-0.5)/12.0f*log2)*440;
maxima[i] = f*f2bin; maxima[i] = f*f2bin;
} }
@ -2168,7 +2180,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
float value; float value;
#ifdef EXPERIMENTAL_FIND_NOTES #ifdef EXPERIMENTAL_FIND_NOTES
if (mFftFindNotes) { if (fftFindNotes) {
if (it < maximas) { if (it < maximas) {
float i0=maxima0[it]; float i0=maxima0[it];
if (yy >= i0) if (yy >= i0)
@ -2177,11 +2189,9 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
if (inMaximum) { if (inMaximum) {
float i1=maxima1[it]; float i1=maxima1[it];
if (yy+1 <= i1) { if (yy+1 <= i1) {
value=sumFreqValues(freq, x0, bin0, bin1); value=findValue(freq + x0, bin0, bin1, half, autocorrelation, gain, range);
if (value < mFindNotesMinA) if (value < findNotesMinA)
value = minColor; value = minColor;
else
value = (value + gain + range) / (double)range;
} else { } else {
it++; it++;
inMaximum = false; inMaximum = false;
@ -2249,7 +2259,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
unsigned char rv, gv, bv; unsigned char rv, gv, bv;
const float value = const float value =
clip->mSpecPxCache->values[x * mid.height + yy]; clip->mSpecPxCache->values[x * mid.height + yy];
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv); GetColorGradient(value, selected, isGrayscale, &rv, &gv, &bv);
int px = ((mid.height - 1 - yy) * mid.width + x) * 3; int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
data[px++] = rv; data[px++] = rv;
data[px++] = gv; data[px++] = gv;
@ -2289,10 +2299,10 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
yy2 = yy2_base; yy2 = yy2_base;
unsigned char rv, gv, bv; unsigned char rv, gv, bv;
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv); GetColorGradient(value, selected, isGrayscale, &rv, &gv, &bv);
#ifdef EXPERIMENTAL_FFT_Y_GRID #ifdef EXPERIMENTAL_FFT_Y_GRID
if (mFftYGrid && yGrid[yy]) { if (fftYGrid && yGrid[yy]) {
rv /= 1.1f; rv /= 1.1f;
gv /= 1.1f; gv /= 1.1f;
bv /= 1.1f; bv /= 1.1f;
@ -2325,9 +2335,6 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
#ifdef EXPERIMENTAL_FFT_Y_GRID #ifdef EXPERIMENTAL_FFT_Y_GRID
delete[] yGrid; delete[] yGrid;
#endif //EXPERIMENTAL_FFT_Y_GRID #endif //EXPERIMENTAL_FFT_Y_GRID
#ifdef EXPERIMENTAL_FIND_NOTES
delete[] indexes;
#endif //EXPERIMENTAL_FIND_NOTES
} }
void TrackArtist::InvalidateSpectrumCache(TrackList *tracks) void TrackArtist::InvalidateSpectrumCache(TrackList *tracks)
@ -3039,99 +3046,74 @@ void TrackArtist::UpdatePrefs()
mdBrange = gPrefs->Read(wxT("/GUI/EnvdBRange"), mdBrange); mdBrange = gPrefs->Read(wxT("/GUI/EnvdBRange"), mdBrange);
mShowClipping = gPrefs->Read(wxT("/GUI/ShowClipping"), mShowClipping); mShowClipping = gPrefs->Read(wxT("/GUI/ShowClipping"), mShowClipping);
// mMaxFreq should have the same default as in SpectrumPrefs.
mMaxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);
mMinFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), -1);
mLogMaxFreq = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), -1);
if( mLogMaxFreq < 0 )
mLogMaxFreq = mMaxFreq;
mLogMinFreq = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), -1);
if( mLogMinFreq < 0 )
mLogMinFreq = mMinFreq;
if (mLogMinFreq < 1)
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
mFftYGrid = (gPrefs->Read(wxT("/Spectrum/FFTYGrid"), 0L) != 0);
#endif //EXPERIMENTAL_FFT_Y_GRID
#ifdef EXPERIMENTAL_FIND_NOTES
mFftFindNotes = (gPrefs->Read(wxT("/Spectrum/FFTFindNotes"), 0L) != 0);
mFindNotesMinA = gPrefs->Read(wxT("/Spectrum/FindNotesMinA"), -30.0);
mNumberOfMaxima = gPrefs->Read(wxT("/Spectrum/FindNotesN"), 5L);
mFindNotesQuantize = (gPrefs->Read(wxT("/Spectrum/FindNotesQuantize"), 0L) != 0);
#endif //EXPERIMENTAL_FIND_NOTES
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
mFftSkipPoints = gPrefs->Read(wxT("/Spectrum/FFTSkipPoints"), 0L);
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
gPrefs->Flush(); gPrefs->Flush();
} }
// Get various preference values // Get various preference values
int TrackArtist::GetSpectrumMinFreq(int deffreq) int TrackArtist::GetSpectrumMinFreq(int deffreq)
{ {
return mMinFreq < 0 ? deffreq : mMinFreq; const int &minFreq = SpectrogramSettings::defaults().minFreq;
return minFreq < 0 ? deffreq : minFreq;
} }
int TrackArtist::GetSpectrumMaxFreq(int deffreq) int TrackArtist::GetSpectrumMaxFreq(int deffreq)
{ {
return mMaxFreq < 0 ? deffreq : mMaxFreq; const int &maxFreq = SpectrogramSettings::defaults().maxFreq;
return maxFreq < 0 ? deffreq : maxFreq;
} }
int TrackArtist::GetSpectrumLogMinFreq(int deffreq) int TrackArtist::GetSpectrumLogMinFreq(int deffreq)
{ {
return mLogMinFreq < 0 ? deffreq : mLogMinFreq; const int &logMinFreq = SpectrogramSettings::defaults().logMinFreq;
return logMinFreq < 0 ? deffreq : logMinFreq;
} }
int TrackArtist::GetSpectrumLogMaxFreq(int deffreq) int TrackArtist::GetSpectrumLogMaxFreq(int deffreq)
{ {
return mLogMaxFreq < 0 ? deffreq : mLogMaxFreq; const int &logMaxFreq = SpectrogramSettings::defaults().logMaxFreq;
return logMaxFreq < 0 ? deffreq : logMaxFreq;
} }
int TrackArtist::GetSpectrumWindowSize(bool includeZeroPadding) int TrackArtist::GetSpectrumWindowSize(bool includeZeroPadding)
{ {
includeZeroPadding;
const int &windowSize = SpectrogramSettings::defaults().windowSize;
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS #ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
if (includeZeroPadding) if (includeZeroPadding) {
return mWindowSize * mZeroPaddingFactor; const int &zeroPaddingFactor = SpectrogramSettings::defaults().zeroPaddingFactor;
return windowSize * zeroPaddingFactor;
}
else else
#endif #endif
return mWindowSize; return windowSize;
} }
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int TrackArtist::GetSpectrumFftSkipPoints() int TrackArtist::GetSpectrumFftSkipPoints()
{ {
return mFftSkipPoints; return SpectrogramSettings::defaults().fftSkipPoints;
} }
#endif #endif
// Set various preference values // Set various preference values
void TrackArtist::SetSpectrumMinFreq(int freq) void TrackArtist::SetSpectrumMinFreq(int freq)
{ {
mMinFreq = freq; SpectrogramSettings::defaults().minFreq = freq;
} }
void TrackArtist::SetSpectrumMaxFreq(int freq) void TrackArtist::SetSpectrumMaxFreq(int freq)
{ {
mMaxFreq = freq; SpectrogramSettings::defaults().maxFreq = freq;
} }
void TrackArtist::SetSpectrumLogMinFreq(int freq) void TrackArtist::SetSpectrumLogMinFreq(int freq)
{ {
mLogMinFreq = freq; SpectrogramSettings::defaults().logMinFreq = freq;
} }
void TrackArtist::SetSpectrumLogMaxFreq(int freq) void TrackArtist::SetSpectrumLogMaxFreq(int freq)
{ {
mLogMaxFreq = freq; SpectrogramSettings::defaults().logMaxFreq = freq;
} }
// Draws the sync-lock bitmap, tiled; always draws stationary relative to the DC // Draws the sync-lock bitmap, tiled; always draws stationary relative to the DC

View File

@ -173,32 +173,8 @@ class AUDACITY_DLL_API TrackArtist {
// Preference values // Preference values
float mdBrange; // "/GUI/EnvdBRange" float mdBrange; // "/GUI/EnvdBRange"
long mShowClipping; // "/GUI/ShowClipping" long mShowClipping; // "/GUI/ShowClipping"
int mLogMaxFreq; // "/SpectrumLog/MaxFreq"
int mLogMinFreq; // "/SpectrumLog/MinFreq"
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" bool mbShowTrackNameInWaveform; // "/GUI/ShowTrackNameInWaveform"
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int mFftSkipPoints; // "/Spectrum/FFTSkipPoints"
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
#ifdef EXPERIMENTAL_FFT_Y_GRID
bool mFftYGrid; // "/Spectrum/FFTYGrid"
#endif //EXPERIMENTAL_FFT_Y_GRID
#ifdef EXPERIMENTAL_FIND_NOTES
bool mFftFindNotes; // "/Spectrum/FFTFindNotes"
float mFindNotesMinA; // "/Spectrum/FindNotesMinA"
int mNumberOfMaxima; // "/Spectrum/FindNotesN"
bool mFindNotesQuantize; // "/Spectrum/FindNotesQuantize")
#endif //EXPERIMENTAL_FIND_NOTES
int mInsetLeft; int mInsetLeft;
int mInsetTop; int mInsetTop;
int mInsetRight; int mInsetRight;

View File

@ -214,6 +214,8 @@ is time to refresh some aspect of the screen.
#include "ondemand/ODManager.h" #include "ondemand/ODManager.h"
#include "prefs/SpectrumPrefs.h"
#include "toolbars/ControlToolBar.h" #include "toolbars/ControlToolBar.h"
#include "toolbars/ToolManager.h" #include "toolbars/ToolManager.h"
#include "toolbars/ToolsToolBar.h" #include "toolbars/ToolsToolBar.h"
@ -3045,8 +3047,7 @@ void TrackPanel::StartSnappingFreqSelection (WaveTrack *pTrack)
while(windowSize > effectiveLength) while(windowSize > effectiveLength)
windowSize >>= 1; windowSize >>= 1;
int windowType; const int windowType = SpectrogramSettings::defaults().windowType;
gPrefs->Read(wxT("/Spectrum/WindowType"), &windowType, 3);
mFrequencySnapper->Calculate( mFrequencySnapper->Calculate(
SpectrumAnalyst::Spectrum, windowType, windowSize, rate, SpectrumAnalyst::Spectrum, windowType, windowSize, rate,
&frequencySnappingData[0], length); &frequencySnappingData[0], length);
@ -4722,7 +4723,7 @@ void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event )
// Always spectrogram, never pitch view, pass true // Always spectrogram, never pitch view, pass true
windowSize = mTrackArtist->GetSpectrumWindowSize(true); windowSize = mTrackArtist->GetSpectrumWindowSize(true);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
fftSkipPoints = mTrackArtist->GetSpectrumFftSkipPoints(); fftSkipPoints = SpectrogramSettings::defaults().fftSkipPoints;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
binSize = rate / windowSize; binSize = rate / windowSize;
minBins = wxMin(10, windowSize/2); //minimum 10 freq bins, unless there are less minBins = wxMin(10, windowSize/2); //minimum 10 freq bins, unless there are less
@ -4739,7 +4740,7 @@ void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event )
// Always spectrogram, never pitch view, pass true // Always spectrogram, never pitch view, pass true
windowSize = mTrackArtist->GetSpectrumWindowSize(true); windowSize = mTrackArtist->GetSpectrumWindowSize(true);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
fftSkipPoints = mTrackArtist->GetSpectrumFftSkipPoints(); fftSkipPoints = SpectrogramSettings::defaults().fftSkipPoints;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
binSize = rate / windowSize; binSize = rate / windowSize;
minBins = wxMin(10, windowSize/2); //minimum 10 freq bins, unless there are less minBins = wxMin(10, windowSize/2); //minimum 10 freq bins, unless there are less

View File

@ -39,6 +39,8 @@ drawing). Cache's the Spectrogram frequency samples.
#include "Resample.h" #include "Resample.h"
#include "Project.h" #include "Project.h"
#include "prefs/SpectrumPrefs.h"
#include <wx/listimpl.cpp> #include <wx/listimpl.cpp>
WX_DEFINE_LIST(WaveClipList); WX_DEFINE_LIST(WaveClipList);
@ -254,26 +256,58 @@ protected:
class SpecCache { class SpecCache {
public: public:
SpecCache(int cacheLen, int half, bool autocorrelation)
{ // Make invalid cache
minFreqOld = -1; SpecCache()
maxFreqOld = -1; : len(-1)
gainOld = -1; , ac(false)
rangeOld = -1; , pps(-1.0)
windowTypeOld = -1; , start(-1.0)
windowSizeOld = -1; , windowType(-1)
zeroPaddingFactorOld = 1; , windowSize(-1)
frequencyGainOld = false; , zeroPaddingFactor(-1)
, frequencyGain(-1)
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
fftSkipPointsOld = -1; , fftSkipPoints(-1)
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
dirty = -1;
start = -1.0; , freq(NULL)
pps = 0.0; , where(NULL)
len = cacheLen;
ac = autocorrelation; , dirty(-1)
freq = len ? new float[len*half] : 0; {
where = new sampleCount[len+1]; }
// Make valid cache, to be filled in
SpecCache(int cacheLen, bool autocorrelation,
double pps_, double start_, int windowType_, int windowSize_,
int zeroPaddingFactor_, int frequencyGain_
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
, int fftSkipPoints_
#endif
)
: len(cacheLen)
, ac(autocorrelation)
, pps(pps_)
, start(start_)
, windowType(windowType_)
, windowSize(windowSize_)
, zeroPaddingFactor(zeroPaddingFactor_)
, frequencyGain(frequencyGain_)
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
, fftSkipPoints(fftSkipPoints_)
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
// len columns, and so many rows, column-major.
// Don't take column literally -- this isn't pixel data yet, it's the
// raw data to be mapped onto the display.
, freq(len ? new float[len * ((windowSize * zeroPaddingFactor) / 2)] : 0)
// Sample counts corresponding to the columns, and to one past the end.
, where(new sampleCount[len + 1])
, dirty(-1)
{
where[0] = 0; where[0] = 0;
} }
@ -283,29 +317,26 @@ public:
delete[] where; delete[] where;
} }
int minFreqOld; const sampleCount len;
int maxFreqOld; const bool ac;
int gainOld; const double pps;
int rangeOld; const double start;
int windowTypeOld; const int windowType;
int windowSizeOld; const int windowSize;
int zeroPaddingFactorOld; const int zeroPaddingFactor;
int frequencyGainOld; const int frequencyGain;
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int fftSkipPointsOld; const int fftSkipPoints;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
float *const freq;
sampleCount *const where;
int dirty; int dirty;
bool ac;
sampleCount len;
double start;
double pps;
sampleCount *where;
float *freq;
}; };
#ifdef EXPERIMENTAL_USE_REALFFTF #ifdef EXPERIMENTAL_USE_REALFFTF
#include "FFT.h" #include "FFT.h"
static void ComputeSpectrumUsingRealFFTf(float *buffer, HFFT hFFT, float *window, int len, float *out) static void ComputeSpectrumUsingRealFFTf(float *buffer, HFFT hFFT, const float *window, int len, float *out)
{ {
int i; int i;
if(len > hFFT->Points*2) if(len > hFFT->Points*2)
@ -340,14 +371,7 @@ WaveClip::WaveClip(DirManager *projDirManager, sampleFormat format, int rate)
mSequence = new Sequence(projDirManager, format); mSequence = new Sequence(projDirManager, format);
mEnvelope = new Envelope(); mEnvelope = new Envelope();
mWaveCache = new WaveCache(0); mWaveCache = new WaveCache(0);
#ifdef EXPERIMENTAL_USE_REALFFTF mSpecCache = new SpecCache();
mWindowType = -1;
mWindowSize = -1;
hFFT = NULL;
mWindow = NULL;
#endif
mZeroPaddingFactor = 1;
mSpecCache = new SpecCache(0, 1, false);
mSpecPxCache = new SpecPxCache(1); mSpecPxCache = new SpecPxCache(1);
mAppendBuffer = NULL; mAppendBuffer = NULL;
mAppendBufferLen = 0; mAppendBufferLen = 0;
@ -369,14 +393,7 @@ WaveClip::WaveClip(const WaveClip& orig, DirManager *projDirManager)
mEnvelope->SetOffset(orig.GetOffset()); mEnvelope->SetOffset(orig.GetOffset());
mEnvelope->SetTrackLen(((double)orig.mSequence->GetNumSamples()) / orig.mRate); mEnvelope->SetTrackLen(((double)orig.mSequence->GetNumSamples()) / orig.mRate);
mWaveCache = new WaveCache(0); mWaveCache = new WaveCache(0);
#ifdef EXPERIMENTAL_USE_REALFFTF mSpecCache = new SpecCache();
mWindowType = -1;
mWindowSize = -1;
hFFT = NULL;
mWindow = NULL;
#endif
mZeroPaddingFactor = 1;
mSpecCache = new SpecCache(0, 1, false);
mSpecPxCache = new SpecPxCache(1); mSpecPxCache = new SpecPxCache(1);
for (WaveClipList::compatibility_iterator it=orig.mCutLines.GetFirst(); it; it=it->GetNext()) for (WaveClipList::compatibility_iterator it=orig.mCutLines.GetFirst(); it; it=it->GetNext())
@ -398,12 +415,6 @@ WaveClip::~WaveClip()
delete mWaveCache; delete mWaveCache;
delete mSpecCache; delete mSpecCache;
delete mSpecPxCache; delete mSpecPxCache;
#ifdef EXPERIMENTAL_USE_REALFFTF
if(hFFT != NULL)
EndFFT(hFFT);
if(mWindow != NULL)
delete[] mWindow;
#endif
if (mAppendBuffer) if (mAppendBuffer)
DeleteSamples(mAppendBuffer); DeleteSamples(mAppendBuffer);
@ -499,7 +510,8 @@ namespace {
inline inline
void findCorrection(const sampleCount oldWhere[], int oldLen, int newLen, void findCorrection(const sampleCount oldWhere[], int oldLen, int newLen,
double t0, double rate, double samplesPerPixel, double t0, double rate, double samplesPerPixel,
double &oldWhere0, double &denom, long &oldX0, long &oldXLast, double &correction) double &oldWhere0, double &denom, long &oldX0, long &oldXLast, double &correction,
bool &overlap)
{ {
// Mitigate the accumulation of location errors // Mitigate the accumulation of location errors
// in copies of copies of ... of caches. // in copies of copies of ... of caches.
@ -533,6 +545,7 @@ void findCorrection(const sampleCount oldWhere[], int oldLen, int newLen,
denom < 0.5) denom < 0.5)
{ {
correction = 0.0; correction = 0.0;
overlap = false;
} }
else else
{ {
@ -540,6 +553,7 @@ void findCorrection(const sampleCount oldWhere[], int oldLen, int newLen,
const double correction0 = where0 - guessWhere0; const double correction0 = where0 - guessWhere0;
correction = std::max(-samplesPerPixel, std::min(samplesPerPixel, correction0)); correction = std::max(-samplesPerPixel, std::min(samplesPerPixel, correction0));
wxASSERT(correction == correction0); wxASSERT(correction == correction0);
overlap = true;
} }
} }
@ -598,26 +612,32 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
return true; return true;
} }
WaveCache *oldCache = mWaveCache; std::auto_ptr<WaveCache> oldCache(mWaveCache);
mWaveCache = 0;
mWaveCache = new WaveCache(numPixels);
mWaveCache->pps = pixelsPerSecond;
mWaveCache->rate = mRate;
mWaveCache->start = t0;
double tstep = 1.0 / pixelsPerSecond;
double samplesPerPixel = mRate * tstep;
double oldWhere0 = 0; double oldWhere0 = 0;
double denom = 0; double denom = 0;
long oldX0 = 0, oldXLast = 0; long oldX0 = 0, oldXLast = 0;
double correction = 0.0; double correction = 0.0;
bool overlap = false;
const double tstep = 1.0 / pixelsPerSecond;
const double samplesPerPixel = mRate * tstep;
if (match && if (match &&
oldCache->len > 0) { oldCache->len > 0) {
findCorrection(oldCache->where, oldCache->len, mWaveCache->len, findCorrection(oldCache->where, oldCache->len, numPixels,
t0, mRate, samplesPerPixel, t0, mRate, samplesPerPixel,
oldWhere0, denom, oldX0, oldXLast, correction); oldWhere0, denom, oldX0, oldXLast, correction, overlap);
} }
if (!overlap)
oldCache.reset(0);
mWaveCache = new WaveCache(numPixels);
mWaveCache->pps = pixelsPerSecond;
mWaveCache->rate = mRate;
mWaveCache->start = t0;
fillWhere(mWaveCache->where, mWaveCache->len, 0.0, correction, fillWhere(mWaveCache->where, mWaveCache->len, 0.0, correction,
t0, mRate, samplesPerPixel); t0, mRate, samplesPerPixel);
@ -630,7 +650,7 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
// Optimization: if the old cache is good and overlaps // Optimization: if the old cache is good and overlaps
// with the current one, re-use as much of the cache as // with the current one, re-use as much of the cache as
// possible // possible
if (match && if (match && overlap &&
denom >= 0.5 && denom >= 0.5 &&
oldX0 < oldCache->len && oldX0 < oldCache->len &&
oldXLast > oldCache->start) { oldXLast > oldCache->start) {
@ -763,7 +783,6 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
} }
mWaveCache->dirty = mDirty; mWaveCache->dirty = mDirty;
delete oldCache;
memcpy(min, mWaveCache->min, numPixels*sizeof(float)); memcpy(min, mWaveCache->min, numPixels*sizeof(float));
memcpy(max, mWaveCache->max, numPixels*sizeof(float)); memcpy(max, mWaveCache->max, numPixels*sizeof(float));
@ -781,67 +800,6 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
return true; 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;
}
}
void WaveClip::ComputeSpectrogramGainFactors(int fftLen, int frequencyGain, std::vector<float> &gainFactors) void WaveClip::ComputeSpectrogramGainFactors(int fftLen, int frequencyGain, std::vector<float> &gainFactors)
{ {
if (frequencyGain > 0) { if (frequencyGain > 0) {
@ -867,20 +825,26 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
double t0, double pixelsPerSecond, double t0, double pixelsPerSecond,
bool autocorrelation) bool autocorrelation)
{ {
int minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L); const SpectrogramSettings &settings = SpectrogramSettings::defaults();
int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);
int range = gPrefs->Read(wxT("/Spectrum/Range"), 80L);
int gain = gPrefs->Read(wxT("/Spectrum/Gain"), 20L);
int frequencyGain = gPrefs->Read(wxT("/Spectrum/FrequencyGain"), 0L);
int windowType;
int windowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int fftSkipPoints = gPrefs->Read(wxT("/Spectrum/FFTSkipPoints"), 0L); int fftSkipPoints = settings.fftSkipPoints;
int fftSkipPoints1 = fftSkipPoints+1; int fftSkipPoints1 = fftSkipPoints + 1;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
const int zeroPaddingFactor =
autocorrelation ? 1 : gPrefs->Read(wxT("/Spectrum/ZeroPaddingFactor"), 1); const int &frequencyGain = settings.frequencyGain;
gPrefs->Read(wxT("/Spectrum/WindowType"), &windowType, 3); const int &windowSize = settings.windowSize;
const int &windowType = settings.windowType;
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
const int &zeroPaddingFactor = autocorrelation ? 1 : settings.zeroPaddingFactor;
#else
const int zeroPaddingFactor = 1;
#endif
#ifdef EXPERIMENTAL_USE_REALFFTF
settings.CacheWindows();
const HFFT &hFFT = settings.hFFT;
const float *const &window = settings.window;
#endif
// FFT length may be longer than the window of samples that affect results // FFT length may be longer than the window of samples that affect results
// because of zero padding done for increased frequency resolution // because of zero padding done for increased frequency resolution
@ -888,37 +852,15 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
const int half = fftLen / 2; const int half = fftLen / 2;
const int padding = (windowSize * (zeroPaddingFactor - 1)) / 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) || (fftLen != hFFT->Points * 2)
|| (mZeroPaddingFactor != zeroPaddingFactor)) {
mWindowType = windowType;
mWindowSize = windowSize;
if(hFFT != NULL)
EndFFT(hFFT);
hFFT = InitializeFFT(fftLen);
double scale;
RecreateWindow(mWindow, WINDOW, fftLen, padding, mWindowType, mWindowSize, scale);
}
#endif // EXPERIMENTAL_USE_REALFFTF
mZeroPaddingFactor = zeroPaddingFactor;
const bool match = const bool match =
mSpecCache && mSpecCache &&
mSpecCache->dirty == mDirty && mSpecCache->dirty == mDirty &&
mSpecCache->minFreqOld == minFreq && mSpecCache->windowType == windowType &&
mSpecCache->maxFreqOld == maxFreq && mSpecCache->windowSize == windowSize &&
mSpecCache->rangeOld == range && mSpecCache->zeroPaddingFactor == zeroPaddingFactor &&
mSpecCache->gainOld == gain && mSpecCache->frequencyGain == frequencyGain &&
mSpecCache->windowTypeOld == windowType &&
mSpecCache->windowSizeOld == windowSize &&
mSpecCache->zeroPaddingFactorOld == zeroPaddingFactor &&
mSpecCache->frequencyGainOld == frequencyGain &&
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
mSpecCache->fftSkipPointsOld == fftSkipPoints && mSpecCache->fftSkipPoints == fftSkipPoints &&
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
mSpecCache->ac == autocorrelation && mSpecCache->ac == autocorrelation &&
mSpecCache->pps == pixelsPerSecond; mSpecCache->pps == pixelsPerSecond;
@ -931,14 +873,11 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
return false; //hit cache completely return false; //hit cache completely
} }
SpecCache *oldCache = mSpecCache; std::auto_ptr<SpecCache> oldCache(mSpecCache);
mSpecCache = 0;
mSpecCache = new SpecCache(numPixels, half, autocorrelation); bool *recalc = new bool[numPixels + 1];
mSpecCache->pps = pixelsPerSecond; std::fill(&recalc[0], &recalc[numPixels + 1], true);
mSpecCache->start = t0;
bool *recalc = new bool[mSpecCache->len + 1];
std::fill(&recalc[0], &recalc[mSpecCache->len + 1], true);
const double tstep = 1.0 / pixelsPerSecond; const double tstep = 1.0 / pixelsPerSecond;
const double samplesPerPixel = mRate * tstep; const double samplesPerPixel = mRate * tstep;
@ -949,21 +888,33 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
double denom = 0; double denom = 0;
long oldX0 = 0, oldXLast = 0; long oldX0 = 0, oldXLast = 0;
double correction = 0.0; double correction = 0.0;
bool overlap = false;
if (match && if (match &&
oldCache->len > 0) { oldCache->len > 0) {
findCorrection(oldCache->where, oldCache->len, mSpecCache->len, findCorrection(oldCache->where, oldCache->len, numPixels,
t0, mRate, samplesPerPixel, t0, mRate, samplesPerPixel,
oldWhere0, denom, oldX0, oldXLast, correction); oldWhere0, denom, oldX0, oldXLast, correction, overlap);
} }
if (!overlap)
oldCache.reset(0);
mSpecCache = new SpecCache(
numPixels, autocorrelation, pixelsPerSecond, t0,
windowType, windowSize, zeroPaddingFactor, frequencyGain
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
, fftSkipPoints
#endif
);
fillWhere(mSpecCache->where, mSpecCache->len, 0.5, correction, fillWhere(mSpecCache->where, mSpecCache->len, 0.5, correction,
t0, mRate, samplesPerPixel); t0, mRate, samplesPerPixel);
// Optimization: if the old cache is good and overlaps // Optimization: if the old cache is good and overlaps
// with the current one, re-use as much of the cache as // with the current one, re-use as much of the cache as
// possible // possible
if (match && if (match && overlap &&
denom >= 0.5 && denom >= 0.5 &&
oldX0 < oldCache->len && oldX0 < oldCache->len &&
oldXLast > oldCache->start) { oldXLast > oldCache->start) {
@ -988,7 +939,6 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
float *useBuffer = 0; float *useBuffer = 0;
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
float *buffer = new float[fftLen*fftSkipPoints1]; float *buffer = new float[fftLen*fftSkipPoints1];
mSpecCache->fftSkipPointsOld = fftSkipPoints;
#else //!EXPERIMENTAL_FFT_SKIP_POINTS #else //!EXPERIMENTAL_FFT_SKIP_POINTS
float *buffer = new float[fftLen]; float *buffer = new float[fftLen];
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
@ -998,15 +948,6 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
buffer[fftLen - ii - 1] = 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;
std::vector<float> gainFactors; std::vector<float> gainFactors;
ComputeSpectrogramGainFactors(fftLen, frequencyGain, gainFactors); ComputeSpectrogramGainFactors(fftLen, frequencyGain, gainFactors);
@ -1084,7 +1025,7 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
mRate, &mSpecCache->freq[half * x], mRate, &mSpecCache->freq[half * x],
autocorrelation, windowType); autocorrelation, windowType);
} else { } else {
ComputeSpectrumUsingRealFFTf(useBuffer, hFFT, mWindow, fftLen, &mSpecCache->freq[half * x]); ComputeSpectrumUsingRealFFTf(useBuffer, hFFT, window, fftLen, &mSpecCache->freq[half * x]);
} }
#else // EXPERIMENTAL_USE_REALFFTF #else // EXPERIMENTAL_USE_REALFFTF
ComputeSpectrum(buffer, windowSize, windowSize, ComputeSpectrum(buffer, windowSize, windowSize,
@ -1101,7 +1042,6 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
delete[]buffer; delete[]buffer;
delete[]recalc; delete[]recalc;
delete oldCache;
mSpecCache->dirty = mDirty; mSpecCache->dirty = mDirty;
memcpy(freq, mSpecCache->freq, numPixels*half*sizeof(float)); memcpy(freq, mSpecCache->freq, numPixels*half*sizeof(float));
@ -1759,7 +1699,7 @@ bool WaveClip::Resample(int rate, ProgressDialog *progress)
// Invalidate the spectrum display cache // Invalidate the spectrum display cache
if (mSpecCache) if (mSpecCache)
delete mSpecCache; delete mSpecCache;
mSpecCache = new SpecCache(0, 1, false); mSpecCache = new SpecCache();
} }
return !error; return !error;

View File

@ -43,6 +43,7 @@ public:
len = cacheLen; len = cacheLen;
values = new float[len]; values = new float[len];
valid = false; valid = false;
range = gain = -1;
} }
~SpecPxCache() ~SpecPxCache()
@ -53,6 +54,9 @@ public:
sampleCount len; sampleCount len;
float *values; float *values;
bool valid; bool valid;
int range;
int gain;
}; };
class WaveClip; class WaveClip;
@ -303,14 +307,6 @@ protected:
WaveCache *mWaveCache; WaveCache *mWaveCache;
ODLock mWaveCacheMutex; ODLock mWaveCacheMutex;
SpecCache *mSpecCache; SpecCache *mSpecCache;
#ifdef EXPERIMENTAL_USE_REALFFTF
// Variables used for computing the spectrum
HFFT hFFT;
float *mWindow;
int mWindowType;
int mWindowSize;
#endif
int mZeroPaddingFactor;
samplePtr mAppendBuffer; samplePtr mAppendBuffer;
sampleCount mAppendBufferLen; sampleCount mAppendBufferLen;

View File

@ -15,6 +15,7 @@
*//*******************************************************************/ *//*******************************************************************/
#include "../Audacity.h" #include "../Audacity.h"
#include "SpectrumPrefs.h"
#include <wx/defs.h> #include <wx/defs.h>
#include <wx/intl.h> #include <wx/intl.h>
@ -23,7 +24,6 @@
#include "../Prefs.h" #include "../Prefs.h"
#include "../Project.h" #include "../Project.h"
#include "../ShuttleGui.h" #include "../ShuttleGui.h"
#include "SpectrumPrefs.h"
#include "../FFT.h" #include "../FFT.h"
SpectrumPrefs::SpectrumPrefs(wxWindow * parent) SpectrumPrefs::SpectrumPrefs(wxWindow * parent)
@ -352,10 +352,12 @@ bool SpectrumPrefs::Apply()
ShuttleGui S(this, eIsSavingToPrefs); ShuttleGui S(this, eIsSavingToPrefs);
PopulateOrExchange(S); PopulateOrExchange(S);
SpectrogramSettings::defaults().UpdatePrefs();
return true; return true;
} }
void SpectrumPrefs::OnWindowSize(wxCommandEvent &event) void SpectrumPrefs::OnWindowSize(wxCommandEvent &)
{ {
wxChoice *const pWindowSizeControl = wxChoice *const pWindowSizeControl =
static_cast<wxChoice*>(wxWindow::FindWindowById(ID_WINDOW_SIZE, this)); static_cast<wxChoice*>(wxWindow::FindWindowById(ID_WINDOW_SIZE, this));
@ -366,3 +368,179 @@ void SpectrumPrefs::OnWindowSize(wxCommandEvent &event)
BEGIN_EVENT_TABLE(SpectrumPrefs, PrefsPanel) BEGIN_EVENT_TABLE(SpectrumPrefs, PrefsPanel)
EVT_CHOICE(ID_WINDOW_SIZE, SpectrumPrefs::OnWindowSize) EVT_CHOICE(ID_WINDOW_SIZE, SpectrumPrefs::OnWindowSize)
END_EVENT_TABLE() END_EVENT_TABLE()
SpectrogramSettings::SpectrogramSettings()
: hFFT(0)
, window(0)
{
UpdatePrefs();
}
SpectrogramSettings& SpectrogramSettings::defaults()
{
static SpectrogramSettings instance;
return instance;
}
void SpectrogramSettings::UpdatePrefs()
{
bool destroy = false;
minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), -1L);
maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);
// These preferences are not written anywhere in the program as of now,
// but I keep this legacy here. Who knows, someone might edit prefs files
// directly. PRL
logMaxFreq = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), -1);
if (logMaxFreq < 0)
logMaxFreq = maxFreq;
logMinFreq = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), -1);
if (logMinFreq < 0)
logMinFreq = minFreq;
if (logMinFreq < 1)
logMinFreq = 1;
range = gPrefs->Read(wxT("/Spectrum/Range"), 80L);
gain = gPrefs->Read(wxT("/Spectrum/Gain"), 20L);
frequencyGain = gPrefs->Read(wxT("/Spectrum/FrequencyGain"), 0L);
const int newWindowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256);
if (newWindowSize != windowSize) {
destroy = true;
windowSize = newWindowSize;
}
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
const int newZeroPaddingFactor = gPrefs->Read(wxT("/Spectrum/ZeroPaddingFactor"), 1);
if (newZeroPaddingFactor != zeroPaddingFactor) {
destroy = true;
zeroPaddingFactor = newZeroPaddingFactor;
}
#endif
int newWindowType;
gPrefs->Read(wxT("/Spectrum/WindowType"), &newWindowType, 3);
if (newWindowType != windowType) {
destroy = true;
windowType = newWindowType;
}
isGrayscale = (gPrefs->Read(wxT("/Spectrum/Grayscale"), 0L) != 0);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
fftSkipPoints = gPrefs->Read(wxT("/Spectrum/FFTSkipPoints"), 0L);
#endif
#ifdef EXPERIMENTAL_FFT_Y_GRID
fftYGrid = (gPrefs->Read(wxT("/Spectrum/FFTYGrid"), 0L) != 0);
#endif //EXPERIMENTAL_FFT_Y_GRID
#ifdef EXPERIMENTAL_FIND_NOTES
fftFindNotes = (gPrefs->Read(wxT("/Spectrum/FFTFindNotes"), 0L) != 0);
findNotesMinA = gPrefs->Read(wxT("/Spectrum/FindNotesMinA"), -30.0);
numberOfMaxima = gPrefs->Read(wxT("/Spectrum/FindNotesN"), 5L);
findNotesQuantize = (gPrefs->Read(wxT("/Spectrum/FindNotesQuantize"), 0L) != 0);
#endif //EXPERIMENTAL_FIND_NOTES
if (destroy)
DestroyWindows();
}
SpectrogramSettings::~SpectrogramSettings()
{
DestroyWindows();
}
void SpectrogramSettings::DestroyWindows()
{
#ifdef EXPERIMENTAL_USE_REALFFTF
if (hFFT != NULL) {
EndFFT(hFFT);
hFFT = NULL;
}
if (window != NULL) {
delete[] window;
window = NULL;
}
#endif
}
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;
}
}
void SpectrogramSettings::CacheWindows() const
{
#ifdef EXPERIMENTAL_USE_REALFFTF
if (hFFT == NULL || window == NULL) {
double scale;
const int fftLen = windowSize * zeroPaddingFactor;
const int padding = (windowSize * (zeroPaddingFactor - 1)) / 2;
if (hFFT != NULL)
EndFFT(hFFT);
hFFT = InitializeFFT(fftLen);
RecreateWindow(window, WINDOW, fftLen, padding, windowType, windowSize, scale);
}
#endif // EXPERIMENTAL_USE_REALFFTF
}

View File

@ -30,6 +30,8 @@
#include "PrefsPanel.h" #include "PrefsPanel.h"
struct FFTParam;
class SpectrumPrefs:public PrefsPanel class SpectrumPrefs:public PrefsPanel
{ {
public: public:
@ -71,4 +73,57 @@ class SpectrumPrefs:public PrefsPanel
#endif #endif
}; };
struct SpectrogramSettings
{
static SpectrogramSettings &defaults();
SpectrogramSettings();
~SpectrogramSettings();
void UpdatePrefs();
void DestroyWindows();
void CacheWindows() const;
int minFreq;
int maxFreq;
int logMinFreq;
int logMaxFreq;
int range;
int gain;
int frequencyGain;
int windowType;
int windowSize;
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
int zeroPaddingFactor;
#endif
bool isGrayscale;
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int fftSkipPoints;
#endif
#ifdef EXPERIMENTAL_FFT_Y_GRID
bool fftYGrid;
#endif //EXPERIMENTAL_FFT_Y_GRID
#ifdef EXPERIMENTAL_FIND_NOTES
bool fftFindNotes;
bool findNotesMinA;
bool numberOfMaxima;
bool findNotesQuantize;
#endif //EXPERIMENTAL_FIND_NOTES
// Following fields are derived from preferences.
#ifdef EXPERIMENTAL_USE_REALFFTF
// Variables used for computing the spectrum
mutable FFTParam *hFFT;
mutable float *window;
#endif
};
#endif #endif