1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-28 14:18:41 +02:00

Merge: More reorganization of wave and spectrum caches, and performance...

This commit is contained in:
Paul Licameli 2015-06-06 11:45:04 -04:00
commit 324816e3fc
3 changed files with 379 additions and 383 deletions

View File

@ -1681,14 +1681,13 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
float zoomMin, zoomMax; float zoomMin, zoomMax;
track->GetDisplayBounds(&zoomMin, &zoomMax); track->GetDisplayBounds(&zoomMin, &zoomMax);
WaveDisplay display; WaveDisplay display(mid.width);
bool isLoadingOD = false;//true if loading on demand block in sequence. bool isLoadingOD = false;//true if loading on demand block in sequence.
// The WaveClip class handles the details of computing the shape // The WaveClip class handles the details of computing the shape
// of the waveform. The only way GetWaveDisplay will fail is if // of the waveform. The only way GetWaveDisplay will fail is if
// there's a serious error, like some of the waveform data can't // there's a serious error, like some of the waveform data can't
// be loaded. So if the function returns false, we can just exit. // be loaded. So if the function returns false, we can just exit.
display.Allocate(mid.width);
if (!clip->GetWaveDisplay(display, if (!clip->GetWaveDisplay(display,
t0, pps, isLoadingOD)) { t0, pps, isLoadingOD)) {
return; return;
@ -1705,7 +1704,7 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
// the envelope and using a colored pen for the selected // the envelope and using a colored pen for the selected
// part of the waveform // part of the waveform
DrawWaveformBackground(dc, mid, envValues, zoomMin, zoomMax, dB, DrawWaveformBackground(dc, mid, envValues, zoomMin, zoomMax, dB,
display.where, ssel0, ssel1, drawEnvelope, &display.where[0], ssel0, ssel1, drawEnvelope,
!track->GetSelected()); !track->GetSelected());
if (!showIndividualSamples) { if (!showIndividualSamples) {
@ -1824,7 +1823,7 @@ void TrackArtist::DrawSpectrum(WaveTrack *track,
} }
static inline float findValue static inline float findValue
(float *spectrum, float bin0, float bin1, int half, (const float *spectrum, float bin0, float bin1, int half,
bool autocorrelation, int gain, int range) bool autocorrelation, int gain, int range)
{ {
float value; float value;
@ -1959,8 +1958,8 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
int windowSize = GetSpectrumWindowSize(!autocorrelation); int windowSize = GetSpectrumWindowSize(!autocorrelation);
const int half = windowSize / 2; const int half = windowSize / 2;
float *freq = new float[mid.width * half]; const float *freq = 0;
sampleCount *where = new sampleCount[mid.width+1]; const sampleCount *where = 0;
bool updated = clip->GetSpectrogram(cache, freq, where, mid.width, bool updated = clip->GetSpectrogram(cache, freq, where, mid.width,
t0, pps, autocorrelation); t0, pps, autocorrelation);
@ -2330,8 +2329,6 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
dc.Blit(mid.x, mid.y, mid.width, mid.height, &memDC, 0, 0, wxCOPY, FALSE); dc.Blit(mid.x, mid.y, mid.width, mid.height, &memDC, 0, 0, wxCOPY, FALSE);
delete image; delete image;
delete[] where;
delete[] freq;
#ifdef EXPERIMENTAL_FFT_Y_GRID #ifdef EXPERIMENTAL_FFT_Y_GRID
delete[] yGrid; delete[] yGrid;
#endif //EXPERIMENTAL_FFT_Y_GRID #endif //EXPERIMENTAL_FFT_Y_GRID

View File

@ -44,53 +44,56 @@ drawing). Cache's the Spectrogram frequency samples.
#include <wx/listimpl.cpp> #include <wx/listimpl.cpp>
WX_DEFINE_LIST(WaveClipList); WX_DEFINE_LIST(WaveClipList);
namespace {
inline int CountODPixels(int *bl, int start, int end)
{
using namespace std;
return count_if(bl + start, bl + end, bind2nd(less<int>(), 0));
}
}
class WaveCache { class WaveCache {
public: public:
WaveCache(int cacheLen) WaveCache()
: len(cacheLen) : dirty(-1)
, len(-1)
, start(-1)
, pps(0)
, rate(-1)
, where(0)
, min(0)
, max(0)
, rms(0)
, bl(0)
, numODPixels(0)
{ {
dirty = -1; }
start = -1.0;
pps = 0.0; WaveCache(int len_, double pixelsPerSecond, double rate_, double t0)
min = len ? new float[len] : 0; : dirty(-1)
max = len ? new float[len] : 0; , len(len_)
rms = len ? new float[len] : 0; , start(t0)
bl = len ? new int[len] : 0; , pps(pixelsPerSecond)
where = new sampleCount[len+1]; , rate(rate_)
where[0] = 0; , where(1 + len)
numODPixels=0; , min(len)
, max(len)
, rms(len)
, bl(len)
, numODPixels(0)
{
//find the number of OD pixels - the only way to do this is by recounting since we've lost some old cache.
numODPixels = CountODPixels(0, len);
} }
~WaveCache() ~WaveCache()
{ {
delete[] min;
delete[] max;
delete[] rms;
delete[] bl;
delete[] where;
ClearInvalidRegions(); ClearInvalidRegions();
} }
int dirty; int dirty;
const sampleCount len; const int len; // counts pixels, not samples
double start; const double start;
double pps; const double pps;
int rate; const int rate;
sampleCount *where; std::vector<sampleCount> where;
float *min; std::vector<float> min;
float *max; std::vector<float> max;
float *rms; std::vector<float> rms;
int *bl; std::vector<int> bl;
int numODPixels; int numODPixels;
class InvalidRegion class InvalidRegion
@ -223,7 +226,7 @@ public:
//before check number of ODPixels //before check number of ODPixels
int regionODPixels = 0; int regionODPixels = 0;
if (updateODCount) if (updateODCount)
regionODPixels = CountODPixels(bl, invStart, invEnd); regionODPixels = CountODPixels(invStart, invEnd);
sequence->GetWaveDisplay(&min[invStart], sequence->GetWaveDisplay(&min[invStart],
&max[invStart], &max[invStart],
@ -235,7 +238,7 @@ public:
//after check number of ODPixels //after check number of ODPixels
if (updateODCount) if (updateODCount)
{ {
const int regionODPixelsAfter = CountODPixels(bl, invStart, invEnd); const int regionODPixelsAfter = CountODPixels(invStart, invEnd);
numODPixels -= (regionODPixels - regionODPixelsAfter); numODPixels -= (regionODPixels - regionODPixelsAfter);
} }
} }
@ -247,10 +250,16 @@ public:
LoadInvalidRegion(i, sequence, updateODCount); LoadInvalidRegion(i, sequence, updateODCount);
} }
int CountODPixels(int start, int end)
{
using namespace std;
const int *begin = &bl[0];
return count_if(begin + start, begin + end, bind2nd(less<int>(), 0));
}
protected: protected:
std::vector<InvalidRegion*> mRegions; std::vector<InvalidRegion*> mRegions;
ODLock mRegionsMutex; ODLock mRegionsMutex;
}; };
@ -301,10 +310,10 @@ public:
// len columns, and so many rows, column-major. // len columns, and so many rows, column-major.
// Don't take column literally -- this isn't pixel data yet, it's the // Don't take column literally -- this isn't pixel data yet, it's the
// raw data to be mapped onto the display. // raw data to be mapped onto the display.
, freq(len ? new float[len * ((windowSize * zeroPaddingFactor) / 2)] : 0) , freq(len * ((windowSize * zeroPaddingFactor) / 2))
// Sample counts corresponding to the columns, and to one past the end. // Sample counts corresponding to the columns, and to one past the end.
, where(new sampleCount[len + 1]) , where(len + 1)
, dirty(-1) , dirty(-1)
{ {
@ -313,11 +322,27 @@ public:
~SpecCache() ~SpecCache()
{ {
delete[] freq;
delete[] where;
} }
const sampleCount len; bool Matches(int dirty_, bool autocorrelation, double pixelsPerSecond,
const SpectrogramSettings &settings) const;
void CalculateOneSpectrum
(const SpectrogramSettings &settings,
WaveTrackCache &waveTrackCache,
int xx, sampleCount numSamples,
double offset, double rate,
bool autocorrelation, const std::vector<float> &gainFactors,
float *scratch);
void Populate
(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache,
int copyBegin, int copyEnd, int numPixels,
sampleCount numSamples,
double offset, double rate,
bool autocorrelation);
const int len; // counts pixels, not samples
const bool ac; const bool ac;
const double pps; const double pps;
const double start; const double start;
@ -328,8 +353,8 @@ public:
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
const int fftSkipPoints; const int fftSkipPoints;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
float *const freq; std::vector<float> freq;
sampleCount *const where; std::vector<sampleCount> where;
int dirty; int dirty;
}; };
@ -370,7 +395,7 @@ WaveClip::WaveClip(DirManager *projDirManager, sampleFormat format, int rate)
mRate = rate; mRate = rate;
mSequence = new Sequence(projDirManager, format); mSequence = new Sequence(projDirManager, format);
mEnvelope = new Envelope(); mEnvelope = new Envelope();
mWaveCache = new WaveCache(0); mWaveCache = new WaveCache();
mSpecCache = new SpecCache(); mSpecCache = new SpecCache();
mSpecPxCache = new SpecPxCache(1); mSpecPxCache = new SpecPxCache(1);
mAppendBuffer = NULL; mAppendBuffer = NULL;
@ -392,7 +417,7 @@ WaveClip::WaveClip(const WaveClip& orig, DirManager *projDirManager)
mEnvelope->Paste(0.0, orig.mEnvelope); mEnvelope->Paste(0.0, orig.mEnvelope);
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();
mSpecCache = new SpecCache(); mSpecCache = new SpecCache();
mSpecPxCache = new SpecPxCache(1); mSpecPxCache = new SpecPxCache(1);
@ -494,7 +519,7 @@ void WaveClip::DeleteWaveCache()
ODLocker locker(mWaveCacheMutex); ODLocker locker(mWaveCacheMutex);
if(mWaveCache!=NULL) if(mWaveCache!=NULL)
delete mWaveCache; delete mWaveCache;
mWaveCache = new WaveCache(0); mWaveCache = new WaveCache();
} }
///Adds an invalid region to the wavecache so it redraws that portion only. ///Adds an invalid region to the wavecache so it redraws that portion only.
@ -508,57 +533,50 @@ void WaveClip::AddInvalidRegion(long startSample, long endSample)
namespace { namespace {
inline inline
void findCorrection(const sampleCount oldWhere[], int oldLen, int newLen, void findCorrection(const std::vector<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, int &oldX0, 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.
// Look at the loop that populates "where" below to understand this. // Look at the loop that populates "where" below to understand this.
// Find the sample position that is the origin in the old cache. // Find the sample position that is the origin in the old cache.
oldWhere0 = oldWhere[1] - samplesPerPixel; const double oldWhere0 = oldWhere[1] - samplesPerPixel;
const double oldWhereLast = oldWhere0 + oldLen * samplesPerPixel; const double oldWhereLast = oldWhere0 + oldLen * samplesPerPixel;
// Find the length in samples of the old cache. // Find the length in samples of the old cache.
denom = oldWhereLast - oldWhere0; const double denom = oldWhereLast - oldWhere0;
// What sample would go in where[0] with no correction? // What sample would go in where[0] with no correction?
const double guessWhere0 = t0 * rate; const double guessWhere0 = t0 * rate;
// What integer position in the old cache array does that map to?
// (even if it is out of bounds)
oldX0 = floor(0.5 + oldLen * (guessWhere0 - oldWhere0) / denom);
// What sample count would the old cache have put there?
const double where0 = oldWhere0 + double(oldX0) * samplesPerPixel;
// What integer position in the old cache array does our last column
// map to? (even if out of bounds)
oldXLast = floor(0.5 + oldLen * (
(where0 + double(newLen) * samplesPerPixel - oldWhere0)
/ denom
));
if ( // Skip if old and new are disjoint: if ( // Skip if old and new are disjoint:
oldWhereLast <= guessWhere0 || oldWhereLast <= guessWhere0 ||
guessWhere0 + newLen * samplesPerPixel <= oldWhere0 || guessWhere0 + newLen * samplesPerPixel <= oldWhere0 ||
// Skip unless denom rounds off to at least 1. // Skip unless denom rounds off to at least 1.
denom < 0.5) denom < 0.5)
{ {
// The computation of oldX0 in the other branch
// may underflow and the assertion would be violated.
oldX0 = oldLen;
correction = 0.0; correction = 0.0;
overlap = false;
} }
else else
{ {
// What integer position in the old cache array does that map to?
// (even if it is out of bounds)
oldX0 = floor(0.5 + oldLen * (guessWhere0 - oldWhere0) / denom);
// What sample count would the old cache have put there?
const double where0 = oldWhere0 + double(oldX0) * samplesPerPixel;
// What correction is needed to align the new cache with the old? // What correction is needed to align the new cache with the old?
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;
} }
} }
inline void inline void
fillWhere(sampleCount where[], int len, double bias, double correction, fillWhere(std::vector<sampleCount> &where, int len, double bias, double correction,
double t0, double rate, double samplesPerPixel) double t0, double rate, double samplesPerPixel)
{ {
// Be careful to make the first value non-negative // Be careful to make the first value non-negative
@ -581,85 +599,76 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
double pixelsPerSecond, bool &isLoadingOD) double pixelsPerSecond, bool &isLoadingOD)
{ {
int numPixels = display.width; int numPixels = display.width;
float *const min = display.min;
float *const max = display.max;
float *const rms = display.rms;
int *const bl = display.bl;
sampleCount *const where = display.where;
ODLocker locker(mWaveCacheMutex); ODLocker locker(mWaveCacheMutex);
const bool match = const bool match =
mWaveCache && mWaveCache &&
mWaveCache->len > 0 &&
mWaveCache->dirty == mDirty && mWaveCache->dirty == mDirty &&
mWaveCache->pps == pixelsPerSecond; mWaveCache->pps == pixelsPerSecond;
if (match && if (match &&
mWaveCache->start == t0 && mWaveCache->start == t0 &&
mWaveCache->len >= numPixels) { mWaveCache->len >= numPixels) {
mWaveCache->LoadInvalidRegions(mSequence, true); mWaveCache->LoadInvalidRegions(mSequence, true);
mWaveCache->ClearInvalidRegions(); mWaveCache->ClearInvalidRegions();
// Satisfy the request completely from the cache
memcpy(min, mWaveCache->min, numPixels*sizeof(float)); display.min = &mWaveCache->min[0];
memcpy(max, mWaveCache->max, numPixels*sizeof(float)); display.max = &mWaveCache->max[0];
memcpy(rms, mWaveCache->rms, numPixels*sizeof(float)); display.rms = &mWaveCache->rms[0];
memcpy(bl, mWaveCache->bl, numPixels*sizeof(int)); display.bl = &mWaveCache->bl[0];
memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount)); display.where = &mWaveCache->where[0];
isLoadingOD = mWaveCache->numODPixels>0; isLoadingOD = mWaveCache->numODPixels > 0;
return true; return true;
} }
std::auto_ptr<WaveCache> oldCache(mWaveCache); std::auto_ptr<WaveCache> oldCache(mWaveCache);
mWaveCache = 0; mWaveCache = 0;
double oldWhere0 = 0;
double denom = 0;
long oldX0 = 0, oldXLast = 0;
double correction = 0.0;
bool overlap = false;
const double tstep = 1.0 / pixelsPerSecond; const double tstep = 1.0 / pixelsPerSecond;
const double samplesPerPixel = mRate * tstep; const double samplesPerPixel = mRate * tstep;
if (match && int oldX0 = 0;
oldCache->len > 0) { double correction = 0.0;
int copyBegin = 0, copyEnd = 0;
if (match) {
findCorrection(oldCache->where, oldCache->len, numPixels, findCorrection(oldCache->where, oldCache->len, numPixels,
t0, mRate, samplesPerPixel, t0, mRate, samplesPerPixel,
oldWhere0, denom, oldX0, oldXLast, correction, overlap); oldX0, correction);
// Remember our first pixel maps to oldX0 in the old cache,
// possibly out of bounds.
// For what range of pixels can data be copied?
copyBegin = std::min(numPixels, std::max(0, -oldX0));
copyEnd = std::min(numPixels,
copyBegin + oldCache->len - std::max(0, oldX0)
);
} }
if (!overlap) if (!(copyEnd > copyBegin))
oldCache.reset(0); oldCache.reset(0);
mWaveCache = new WaveCache(numPixels); mWaveCache = new WaveCache(numPixels, pixelsPerSecond, mRate, t0);
mWaveCache->pps = pixelsPerSecond; float *const min = &mWaveCache->min[0];
mWaveCache->rate = mRate; float *const max = &mWaveCache->max[0];
mWaveCache->start = t0; float *const rms = &mWaveCache->rms[0];
int *const bl = &mWaveCache->bl[0];
std::vector<sampleCount> &where = mWaveCache->where;
fillWhere(mWaveCache->where, mWaveCache->len, 0.0, correction, fillWhere(where, numPixels, 0.0, correction,
t0, mRate, samplesPerPixel); t0, mRate, samplesPerPixel);
//mchinen: I think s0 - s1 represents the range of samples that we will need to look up. likewise p0-p1 the number of pixels. // The range of pixels we must fetch from the Sequence:
sampleCount s0 = mWaveCache->where[0]; const int p0 = (copyBegin > 0) ? 0 : copyEnd;
sampleCount s1 = mWaveCache->where[mWaveCache->len]; int p1 = (copyEnd >= numPixels) ? copyBegin : numPixels;
int p0 = 0;
int p1 = mWaveCache->len;
// 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 && overlap &&
denom >= 0.5 &&
oldX0 < oldCache->len &&
oldXLast > oldCache->start) {
//now we are assuming the entire range is covered by the old cache and reducing s1/s0 as we find out otherwise. if (oldCache.get()) {
s0 = mWaveCache->where[mWaveCache->len]; //mchinen:s0 is the min sample covered up to by the wave cache. will shrink if old doen't overlap
s1 = mWaveCache->where[0]; //mchinen - same, but the maximum sample covered.
p0 = mWaveCache->len;
p1 = 0;
//TODO: only load inval regions if //TODO: only load inval regions if
//necessary. (usually is the case, so no rush.) //necessary. (usually is the case, so no rush.)
@ -667,30 +676,14 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
oldCache->LoadInvalidRegions(mSequence, false); oldCache->LoadInvalidRegions(mSequence, false);
oldCache->ClearInvalidRegions(); oldCache->ClearInvalidRegions();
for (sampleCount x = 0; x < mWaveCache->len; x++) // Copy what we can from the old cache.
{ const int length = copyEnd - copyBegin;
//if we hit a cached column, load it up. const size_t sizeFloats = length * sizeof(float);
const double whereX = t0 * mRate + ((double)x) * samplesPerPixel; const int srcIdx = copyBegin + oldX0;
const double oxd = (double(oldCache->len) * (whereX - oldWhere0)) / denom; memcpy(&min[copyBegin], &oldCache->min[srcIdx], sizeFloats);
int ox = floor(0.5 + oxd); memcpy(&max[copyBegin], &oldCache->max[srcIdx], sizeFloats);
memcpy(&rms[copyBegin], &oldCache->rms[srcIdx], sizeFloats);
//below is regular cache access. memcpy(&bl[copyBegin], &oldCache->bl[srcIdx], length * sizeof(int));
if (ox >= 0 && ox < oldCache->len) {
mWaveCache->min[x] = oldCache->min[ox];
mWaveCache->max[x] = oldCache->max[ox];
mWaveCache->rms[x] = oldCache->rms[ox];
mWaveCache->bl[x] = oldCache->bl[ox];
} else {
if (mWaveCache->where[x] < s0) {
s0 = mWaveCache->where[x];
p0 = x;
}
if (mWaveCache->where[x + 1] > s1) {
s1 = mWaveCache->where[x + 1];
p1 = x + 1;
}
}
}
} }
if (p1 > p0) { if (p1 > p0) {
@ -699,9 +692,8 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
int numSamples = mSequence->GetNumSamples(); int numSamples = mSequence->GetNumSamples();
int a; int a;
for(a = p0; a < p1; ++a)
for(a=p0; a<p1; a++) if (where[a+1] > numSamples)
if (mWaveCache->where[a+1] > numSamples)
break; break;
//compute the values that are outside the overlap from scratch. //compute the values that are outside the overlap from scratch.
@ -712,9 +704,9 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
bool didUpdate = false; bool didUpdate = false;
for(i=a; i<p1; i++) { for(i=a; i<p1; i++) {
sampleCount left; sampleCount left;
left = mWaveCache->where[i] - numSamples; left = where[i] - numSamples;
sampleCount right; sampleCount right;
right = mWaveCache->where[i+1] - numSamples; right = where[i+1] - numSamples;
//wxCriticalSectionLocker locker(mAppendCriticalSection); //wxCriticalSectionLocker locker(mAppendCriticalSection);
@ -737,22 +729,23 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
(samplePtr)b, floatSample, len); (samplePtr)b, floatSample, len);
} }
float max = b[0]; float theMax, theMin, sumsq;
float min = b[0]; {
float sumsq = b[0] * b[0]; const float val = b[0];
theMax = theMin = val;
sumsq = val * val;
}
for(j=1; j<len; j++) { for(j=1; j<len; j++) {
if (b[j] > max) const float val = b[j];
max = b[j]; theMax = std::max(theMax, val);
if (b[j] < min) theMin = std::min(theMin, val);
min = b[j]; sumsq += val * val;
sumsq += b[j]*b[j];
} }
mWaveCache->min[i] = min; min[i] = theMin;
mWaveCache->max[i] = max; max[i] = theMax;
mWaveCache->rms[i] = (float)sqrt(sumsq / len); rms[i] = (float)sqrt(sumsq / len);
mWaveCache->bl[i] = 1; //for now just fake it. bl[i] = 1; //for now just fake it.
if (seqFormat != floatSample) if (seqFormat != floatSample)
delete[] b; delete[] b;
@ -761,20 +754,18 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
} }
} }
// So that the sequence doesn't try to write any // Shrink the right end of the range to fetch from Sequence
// of these values
//mchinen: but only do this if we've updated pixels in the cache.
if(didUpdate) if(didUpdate)
p1 = a; p1 = a;
} }
if (p1 > p0) { if (p1 > p0) {
if (!mSequence->GetWaveDisplay(&mWaveCache->min[p0], if (!mSequence->GetWaveDisplay(&min[p0],
&mWaveCache->max[p0], &max[p0],
&mWaveCache->rms[p0], &rms[p0],
&mWaveCache->bl[p0], &bl[p0],
p1-p0, p1-p0,
&mWaveCache->where[p0])) &where[p0]))
{ {
isLoadingOD=false; isLoadingOD=false;
return false; return false;
@ -784,30 +775,27 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
mWaveCache->dirty = mDirty; mWaveCache->dirty = mDirty;
memcpy(min, mWaveCache->min, numPixels*sizeof(float)); // Now report the results
memcpy(max, mWaveCache->max, numPixels*sizeof(float)); display.min = min;
memcpy(rms, mWaveCache->rms, numPixels*sizeof(float)); display.max = max;
memcpy(bl, mWaveCache->bl, numPixels*sizeof(int)); display.rms = rms;
memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount)); display.bl = bl;
display.where = &where[0];
//find the number of OD pixels - the only way to do this is by recounting since we've lost some old cache. isLoadingOD = mWaveCache->numODPixels > 0;
mWaveCache->numODPixels = 0;
for(int j=0;j<mWaveCache->len;j++)
if(mWaveCache->bl[j]<0)
mWaveCache->numODPixels++;
isLoadingOD = mWaveCache->numODPixels>0;
return true; return true;
} }
void WaveClip::ComputeSpectrogramGainFactors(int fftLen, int frequencyGain, std::vector<float> &gainFactors) namespace {
void ComputeSpectrogramGainFactors
(int fftLen, double rate, int frequencyGain, std::vector<float> &gainFactors)
{ {
if (frequencyGain > 0) { if (frequencyGain > 0) {
// Compute a frequency-dependent gain factor // Compute a frequency-dependent gain factor
// scaled such that 1000 Hz gets a gain of 0dB // scaled such that 1000 Hz gets a gain of 0dB
// This is the reciprocal of the bin number of 1000 Hz: // This is the reciprocal of the bin number of 1000 Hz:
const double factor = ((double)mRate / (double)fftLen) / 1000.0; const double factor = ((double)rate / (double)fftLen) / 1000.0;
const int half = fftLen / 2; const int half = fftLen / 2;
gainFactors.reserve(half); gainFactors.reserve(half);
@ -819,8 +807,187 @@ void WaveClip::ComputeSpectrogramGainFactors(int fftLen, int frequencyGain, std:
} }
} }
}
bool SpecCache::Matches
(int dirty_, bool autocorrelation, double pixelsPerSecond,
const SpectrogramSettings &settings) const
{
return
dirty == dirty_ &&
windowType == settings.windowType &&
windowSize == settings.windowSize &&
zeroPaddingFactor == settings.zeroPaddingFactor &&
frequencyGain == settings.frequencyGain &&
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
fftSkipPoints == settings.fftSkipPoints &&
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
ac == autocorrelation &&
pps == pixelsPerSecond;
}
void SpecCache::CalculateOneSpectrum
(const SpectrogramSettings &settings,
WaveTrackCache &waveTrackCache,
int xx, sampleCount numSamples,
double offset, double rate,
bool autocorrelation, const std::vector<float> &gainFactors,
float *scratch)
{
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int fftSkipPoints = settings.fftSkipPoints;
int fftSkipPoints1 = fftSkipPoints + 1;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
const int windowSize = settings.windowSize;
sampleCount start = where[xx];
const int zeroPaddingFactor = (autocorrelation ? 1 : settings.zeroPaddingFactor);
const int padding = (windowSize * (zeroPaddingFactor - 1)) / 2;
const int fftLen = windowSize * zeroPaddingFactor;
const int half = fftLen / 2;
float *const results = &freq[half * xx];
sampleCount len = windowSize;
if (start <= 0 || start >= numSamples) {
std::fill(results, results + half, 0.0f);
}
else {
bool copy = !autocorrelation || (padding > 0);
float *useBuffer = 0;
float *adj = scratch + padding;
start -= windowSize >> 1;
if (start < 0) {
for (sampleCount ii = start; ii < 0; ++ii)
*adj++ = 0;
len += start;
start = 0;
copy = true;
}
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
copy = true;
if (start + len*fftSkipPoints1 > numSamples) {
int newlen = (numSamples - start) / fftSkipPoints1;
for (int i = newlen*fftSkipPoints1; i < (sampleCount)len*fftSkipPoints1; i++)
adj[i] = 0;
len = newlen;
}
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
if (start + len > numSamples) {
int newlen = numSamples - start;
for (sampleCount ii = newlen; ii < (sampleCount)len; ++ii)
adj[ii] = 0;
len = newlen;
copy = true;
}
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
if (len > 0) {
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
useBuffer = (float*)(waveTrackCache.Get(floatSample,
floor(0.5 + start + offset * rate), len*fftSkipPoints1));
memcpy(adj, useBuffer, len * fftSkipPoints1 * sizeof(float));
if (fftSkipPoints) {
// TODO: (maybe) alternatively change Get to include skipping of points
int j = 0;
for (int i = 0; i < len; i++) {
adj[i] = adj[j];
j += fftSkipPoints1;
}
}
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
useBuffer = (float*)(waveTrackCache.Get(floatSample,
floor(0.5 + start + offset * rate), len));
if (copy)
memcpy(adj, useBuffer, len * sizeof(float));
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
}
if (copy)
useBuffer = scratch;
#ifdef EXPERIMENTAL_USE_REALFFTF
if (autocorrelation)
ComputeSpectrum(useBuffer, windowSize, windowSize,
rate, results,
autocorrelation, settings.windowType);
else
ComputeSpectrumUsingRealFFTf
(useBuffer, settings.hFFT, settings.window, fftLen, results);
#else // EXPERIMENTAL_USE_REALFFTF
ComputeSpectrum(buffer, windowSize, windowSize,
rate, results,
autocorrelation, settings.windowType);
#endif // EXPERIMENTAL_USE_REALFFTF
if (!gainFactors.empty()) {
// Apply a frequency-dependant gain factor
for (int ii = 0; ii < half; ++ii)
results[ii] += gainFactors[ii];
}
}
}
void SpecCache::Populate
(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache,
int copyBegin, int copyEnd, int numPixels,
sampleCount numSamples,
double offset, double rate,
bool autocorrelation)
{
#ifdef EXPERIMENTAL_USE_REALFFTF
settings.CacheWindows();
#endif
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int fftSkipPoints = settings.fftSkipPoints;
int fftSkipPoints1 = fftSkipPoints + 1;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
const int &frequencyGain = settings.frequencyGain;
const int &windowSize = settings.windowSize;
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
const int &zeroPaddingFactor = autocorrelation ? 1 : settings.zeroPaddingFactor;
#else
const int zeroPaddingFactor = 1;
#endif
// 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 padding = (windowSize * (zeroPaddingFactor - 1)) / 2;
std::vector<float> buffer(
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
fftLen*fftSkipPoints1
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
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;
}
std::vector<float> gainFactors;
ComputeSpectrogramGainFactors(fftLen, rate, frequencyGain, gainFactors);
// Loop over the ranges before and after the copied portion and compute anew.
// One of the ranges may be empty.
for (sampleCount xx = 0; xx < copyBegin; ++xx)
CalculateOneSpectrum(
settings, waveTrackCache, xx, numSamples,
offset, rate, autocorrelation, gainFactors, &buffer[0]);
for (sampleCount xx = copyEnd; xx < numPixels; ++xx)
CalculateOneSpectrum(
settings, waveTrackCache, xx, numSamples,
offset, rate, autocorrelation, gainFactors, &buffer[0]);
}
bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
float *freq, sampleCount *where, const float *& spectrogram, const sampleCount *& where,
int numPixels, int numPixels,
double t0, double pixelsPerSecond, double t0, double pixelsPerSecond,
bool autocorrelation) bool autocorrelation)
@ -829,7 +996,6 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS #ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int fftSkipPoints = settings.fftSkipPoints; int fftSkipPoints = settings.fftSkipPoints;
int fftSkipPoints1 = fftSkipPoints + 1;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS #endif //EXPERIMENTAL_FFT_SKIP_POINTS
const int &frequencyGain = settings.frequencyGain; const int &frequencyGain = settings.frequencyGain;
@ -840,64 +1006,49 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
#else #else
const int zeroPaddingFactor = 1; const int zeroPaddingFactor = 1;
#endif #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
const int fftLen = windowSize * zeroPaddingFactor; const int fftLen = windowSize * zeroPaddingFactor;
const int half = fftLen / 2; const int half = fftLen / 2;
const int padding = (windowSize * (zeroPaddingFactor - 1)) / 2;
const bool match = const bool match =
mSpecCache && mSpecCache &&
mSpecCache->dirty == mDirty && mSpecCache->len > 0 &&
mSpecCache->windowType == windowType && mSpecCache->Matches(mDirty, autocorrelation, pixelsPerSecond, settings);
mSpecCache->windowSize == windowSize &&
mSpecCache->zeroPaddingFactor == zeroPaddingFactor &&
mSpecCache->frequencyGain == frequencyGain &&
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
mSpecCache->fftSkipPoints == fftSkipPoints &&
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
mSpecCache->ac == autocorrelation &&
mSpecCache->pps == pixelsPerSecond;
if (match && if (match &&
mSpecCache->start == t0 && mSpecCache->start == t0 &&
mSpecCache->len >= numPixels) { mSpecCache->len >= numPixels) {
memcpy(freq, mSpecCache->freq, numPixels*half*sizeof(float)); spectrogram = &mSpecCache->freq[0];
memcpy(where, mSpecCache->where, (numPixels+1)*sizeof(sampleCount)); where = &mSpecCache->where[0];
return false; //hit cache completely return false; //hit cache completely
} }
std::auto_ptr<SpecCache> oldCache(mSpecCache); std::auto_ptr<SpecCache> oldCache(mSpecCache);
mSpecCache = 0; mSpecCache = 0;
bool *recalc = new bool[numPixels + 1];
std::fill(&recalc[0], &recalc[numPixels + 1], true);
const double tstep = 1.0 / pixelsPerSecond; const double tstep = 1.0 / pixelsPerSecond;
const double samplesPerPixel = mRate * tstep; const double samplesPerPixel = mRate * tstep;
// To do: eliminate duplicate logic with the wave clip code for cache int oldX0 = 0;
// reuse and finding corrections
double oldWhere0 = 0;
double denom = 0;
long oldX0 = 0, oldXLast = 0;
double correction = 0.0; double correction = 0.0;
bool overlap = false;
if (match && int copyBegin = 0, copyEnd = 0;
oldCache->len > 0) { if (match) {
findCorrection(oldCache->where, oldCache->len, numPixels, findCorrection(oldCache->where, oldCache->len, numPixels,
t0, mRate, samplesPerPixel, t0, mRate, samplesPerPixel,
oldWhere0, denom, oldX0, oldXLast, correction, overlap); oldX0, correction);
// Remember our first pixel maps to oldX0 in the old cache,
// possibly out of bounds.
// For what range of pixels can data be copied?
copyBegin = std::min(numPixels, std::max(0, -oldX0));
copyEnd = std::min(numPixels,
copyBegin + oldCache->len - std::max(0, oldX0)
);
} }
if (!overlap) if (!(copyEnd > copyBegin))
oldCache.reset(0); oldCache.reset(0);
mSpecCache = new SpecCache( mSpecCache = new SpecCache(
@ -908,144 +1059,25 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
#endif #endif
); );
fillWhere(mSpecCache->where, mSpecCache->len, 0.5, correction, fillWhere(mSpecCache->where, numPixels, 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 && overlap && if (oldCache.get()) {
denom >= 0.5 && memcpy(&mSpecCache->freq[half * copyBegin],
oldX0 < oldCache->len && &oldCache->freq[half * (copyBegin + oldX0)],
oldXLast > oldCache->start) { half * (copyEnd - copyBegin) * sizeof(float));
for (sampleCount x = 0; x < mSpecCache->len; x++) {
//if we hit a cached column, load it up.
const double whereX = t0 * mRate + ((double)x) * samplesPerPixel;
const double oxd = (double(oldCache->len) * (whereX - oldWhere0)) / denom;
int ox = floor(0.5 + oxd);
//below is regular cache access.
if (ox >= 0 && ox < oldCache->len) {
if (mSpecCache->where[x] >= oldCache->where[0] &&
mSpecCache->where[x] <= oldCache->where[oldCache->len]) {
for (sampleCount i = 0; i < (sampleCount)half; i++)
mSpecCache->freq[half * x + i] = oldCache->freq[half * ox + i];
recalc[x] = false;
}
}
}
} }
float *useBuffer = 0; mSpecCache->Populate
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS (settings, waveTrackCache, copyBegin, copyEnd, numPixels,
float *buffer = new float[fftLen*fftSkipPoints1]; mSequence->GetNumSamples(), mOffset, mRate, autocorrelation);
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
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;
}
std::vector<float> gainFactors;
ComputeSpectrogramGainFactors(fftLen, frequencyGain, gainFactors);
for (sampleCount x = 0; x < mSpecCache->len; x++)
if (recalc[x]) {
sampleCount start = mSpecCache->where[x];
sampleCount len = windowSize;
sampleCount i;
if (start <= 0 || start >= mSequence->GetNumSamples()) {
for (i = 0; i < (sampleCount)half; i++)
mSpecCache->freq[half * x + i] = 0;
}
else {
bool copy = !autocorrelation || (padding > 0);
float *adj = buffer + padding;
start -= windowSize >> 1;
if (start < 0) {
for (i = start; i < 0; i++)
*adj++ = 0;
len += start;
start = 0;
copy = true;
}
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
copy = true;
if (start + len*fftSkipPoints1 > mSequence->GetNumSamples()) {
int newlen = (mSequence->GetNumSamples() - start)/fftSkipPoints1;
for (i = newlen*fftSkipPoints1; i < (sampleCount)len*fftSkipPoints1; i++)
adj[i] = 0;
len = newlen;
}
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
if (start + len > mSequence->GetNumSamples()) {
int newlen = mSequence->GetNumSamples() - start;
for (i = newlen; i < (sampleCount)len; i++)
adj[i] = 0;
len = newlen;
copy = true;
}
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
if (len > 0) {
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
useBuffer = (float*)(waveTrackCache.Get(floatSample,
floor(0.5 + start + mOffset * mRate),
len * fftSkipPoints1));
memmove(adj, useBuffer, len * fftSkipPoints1 * sizeof(float));
if (fftSkipPoints) {
// TODO: (maybe) alternatively change Get to include skipping of points
int j=0;
for (int i=0; i < len; i++) {
adj[i]=adj[j];
j+=fftSkipPoints1;
}
}
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
useBuffer = (float*)(waveTrackCache.Get(floatSample,
floor(0.5 + start + mOffset * mRate), len));
if (copy)
memmove(adj, useBuffer, len * sizeof(float));
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
}
if (copy)
useBuffer = buffer;
#ifdef EXPERIMENTAL_USE_REALFFTF
if(autocorrelation) {
ComputeSpectrum(useBuffer, windowSize, windowSize,
mRate, &mSpecCache->freq[half * x],
autocorrelation, windowType);
} else {
ComputeSpectrumUsingRealFFTf(useBuffer, hFFT, window, fftLen, &mSpecCache->freq[half * x]);
}
#else // EXPERIMENTAL_USE_REALFFTF
ComputeSpectrum(buffer, windowSize, windowSize,
mRate, &mSpecCache->freq[half * x],
autocorrelation, windowType);
#endif // EXPERIMENTAL_USE_REALFFTF
if (!gainFactors.empty()) {
// Apply a frequency-dependent gain factor
for(i=0; i<half; i++)
mSpecCache->freq[half * x + i] += gainFactors[i];
}
}
}
delete[]buffer;
delete[]recalc;
mSpecCache->dirty = mDirty; mSpecCache->dirty = mDirty;
memcpy(freq, mSpecCache->freq, numPixels*half*sizeof(float)); spectrogram = &mSpecCache->freq[0];
memcpy(where, mSpecCache->where, (numPixels+1)*sizeof(sampleCount)); where = &mSpecCache->where[0];
return true; return true;
} }
@ -1695,7 +1727,7 @@ bool WaveClip::Resample(int rate, ProgressDialog *progress)
delete mWaveCache; delete mWaveCache;
mWaveCache = NULL; mWaveCache = NULL;
} }
mWaveCache = new WaveCache(0); mWaveCache = new WaveCache();
// Invalidate the spectrum display cache // Invalidate the spectrum display cache
if (mSpecCache) if (mSpecCache)
delete mSpecCache; delete mSpecCache;

View File

@ -72,41 +72,13 @@ public:
float *min, *max, *rms; float *min, *max, *rms;
int* bl; int* bl;
WaveDisplay() WaveDisplay(int w)
: width(0), where(0), min(0), max(0), rms(0), bl(0) : width(w), where(0), min(0), max(0), rms(0), bl(0)
{ {
} }
~WaveDisplay() ~WaveDisplay()
{ {
Deallocate();
}
void Allocate(int w)
{
width = w;
// One more to hold the past-the-end sample count:
where = new sampleCount[1 + width];
min = new float[width];
max = new float[width];
rms = new float[width];
bl = new int[width];
}
void Deallocate()
{
delete[] where;
where = 0;
delete[] min;
min = 0;
delete[] max;
max = 0;
delete[] rms;
rms = 0;
delete[] bl;
bl = 0;
} }
}; };
@ -193,13 +165,8 @@ public:
* calculations and Contrast */ * calculations and Contrast */
bool GetWaveDisplay(WaveDisplay &display, bool GetWaveDisplay(WaveDisplay &display,
double t0, double pixelsPerSecond, bool &isLoadingOD); double t0, double pixelsPerSecond, bool &isLoadingOD);
void ComputeSpectrogramGainFactors(
int fftLen,
int frequencyGain, // db/decade
std::vector<float> &gainFactors
);
bool GetSpectrogram(WaveTrackCache &cache, bool GetSpectrogram(WaveTrackCache &cache,
float *buffer, sampleCount *where, const float *& spectrogram, const sampleCount *& where,
int numPixels, int numPixels,
double t0, double pixelsPerSecond, double t0, double pixelsPerSecond,
bool autocorrelation); bool autocorrelation);