diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 0543d08ec..8485fc55c 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -48,11 +48,12 @@ public: start = -1.0; pps = 0.0; len = cacheLen; - min = new float[len]; - max = new float[len]; - rms = new float[len]; - bl = new int[len]; + min = len ? new float[len] : 0; + max = len ? new float[len] : 0; + rms = len ? new float[len] : 0; + bl = len ? new int[len] : 0; where = new sampleCount[len+1]; + where[0] = 0; numODPixels=0; } @@ -233,8 +234,9 @@ public: pps = 0.0; len = cacheLen; ac = autocorrelation; - freq = new float[len*half]; + freq = len ? new float[len*half] : 0; where = new sampleCount[len+1]; + where[0] = 0; } ~SpecCache() @@ -297,14 +299,14 @@ WaveClip::WaveClip(DirManager *projDirManager, sampleFormat format, int rate) mRate = rate; mSequence = new Sequence(projDirManager, format); mEnvelope = new Envelope(); - mWaveCache = new WaveCache(1); + mWaveCache = new WaveCache(0); #ifdef EXPERIMENTAL_USE_REALFFTF mWindowType = -1; mWindowSize = -1; hFFT = NULL; mWindow = NULL; #endif - mSpecCache = new SpecCache(1, 1, false); + mSpecCache = new SpecCache(0, 1, false); mSpecPxCache = new SpecPxCache(1); mAppendBuffer = NULL; mAppendBufferLen = 0; @@ -325,14 +327,14 @@ WaveClip::WaveClip(const WaveClip& orig, DirManager *projDirManager) mEnvelope->Paste(0.0, orig.mEnvelope); mEnvelope->SetOffset(orig.GetOffset()); mEnvelope->SetTrackLen(((double)orig.mSequence->GetNumSamples()) / orig.mRate); - mWaveCache = new WaveCache(1); + mWaveCache = new WaveCache(0); #ifdef EXPERIMENTAL_USE_REALFFTF mWindowType = -1; mWindowSize = -1; hFFT = NULL; mWindow = NULL; #endif - mSpecCache = new SpecCache(1, 1, false); + mSpecCache = new SpecCache(0, 1, false); mSpecPxCache = new SpecPxCache(1); for (WaveClipList::compatibility_iterator it=orig.mCutLines.GetFirst(); it; it=it->GetNext()) @@ -439,7 +441,7 @@ void WaveClip::DeleteWaveCache() mWaveCacheMutex.Lock(); if(mWaveCache!=NULL) delete mWaveCache; - mWaveCache = new WaveCache(1); + mWaveCache = new WaveCache(0); mWaveCacheMutex.Unlock(); } @@ -526,15 +528,43 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, mWaveCache->rate = mRate; mWaveCache->start = t0; double tstep = 1.0 / pixelsPerSecond; + double samplesPerPixel = mRate * tstep; - sampleCount x; + sampleCount oldWhere0 = 0; + sampleCount denom = 0; + int oldX0 = 0, oldXLast = 0; + double error = 0.0; + if (oldCache->len > 0) { + oldWhere0 = oldCache->where[1] - samplesPerPixel; + const sampleCount oldWhereLast(floor(0.5 + + oldWhere0 + oldCache->len * samplesPerPixel + )); + denom = oldWhereLast - oldWhere0; - for (x = 0; x < mWaveCache->len + 1; x++) { - mWaveCache->where[x] = - (sampleCount) floor(t0 * mRate + - ((double) x) * mRate * tstep + 0.5); + // Mitigate the accumulation of location errors + // in copies of copies of ... of caches. + if (denom > 0) + { + const double guessWhere0 = t0 * mRate; + oldX0 = floor(0.5 + oldCache->len * (guessWhere0 - oldWhere0) / denom); + const double where0 = + (sampleCount)floor(0.5 + oldWhere0 + double(oldX0) * samplesPerPixel); + error = where0 - guessWhere0; // should be in (-samplesPerPixel, samplesPerPixel) + wxASSERT(-samplesPerPixel <= error && error <= samplesPerPixel); + oldXLast = floor(0.5 + oldCache->len * ( + (where0 + double(mWaveCache->len) * samplesPerPixel - oldWhere0) + / denom + )); + } } + // Be careful to make the first value non-negative + mWaveCache->where[0] = sampleCount(std::max(0.0, floor(0.5 + error + t0 * mRate))); + for (sampleCount x = 1; x < mWaveCache->len + 1; x++) + mWaveCache->where[x] = sampleCount( + floor(0.5 + error + t0 * mRate + double(x) * 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. sampleCount s0 = mWaveCache->where[0]; sampleCount s1 = mWaveCache->where[mWaveCache->len]; @@ -544,10 +574,11 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, // Optimization: if the old cache is good and overlaps // with the current one, re-use as much of the cache as // possible - if (oldCache->dirty == mDirty && + if (denom > 0 && + oldCache->dirty == mDirty && oldCache->pps == pixelsPerSecond && - oldCache->where[0] < mWaveCache->where[mWaveCache->len] && - oldCache->where[oldCache->len] > mWaveCache->where[0]) { + 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. 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 @@ -576,23 +607,15 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, } oldCache->ClearInvalidRegions(); - for (x = 0; x < mWaveCache->len; x++) + for (sampleCount x = 0; x < mWaveCache->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 (mWaveCache->where[x] >= oldCache->where[0] && - mWaveCache->where[x] <= oldCache->where[oldCache->len - 1]) { - - //if we hit an invalid region, load it up. - - int ox = - int ((double (oldCache->len) * - (mWaveCache->where[x] - - oldCache->where[0])) /(oldCache->where[oldCache->len] - - oldCache->where[0]) + 0.5); - + if (ox >= 0 && ox < oldCache->len) { mWaveCache->min[x] = oldCache->min[ox]; mWaveCache->max[x] = oldCache->max[ox]; mWaveCache->rms[x] = oldCache->rms[ox]; @@ -795,22 +818,55 @@ bool WaveClip::GetSpectrogram(float *freq, sampleCount *where, mSpecCache->pps = pixelsPerSecond; mSpecCache->start = t0; - sampleCount x; - bool *recalc = new bool[mSpecCache->len + 1]; - for (x = 0; x < mSpecCache->len + 1; x++) { + const double tstep = 1.0 / pixelsPerSecond; + const double samplesPerPixel = mRate * tstep; + + sampleCount oldWhere0 = 0; + sampleCount denom = 0; + int oldX0 = 0, oldXLast = 0; + double error = 0.0; + + if (oldCache->len > 0) { + oldWhere0 = oldCache->where[1] - samplesPerPixel; + const sampleCount oldWhereLast(floor(0.5 + + oldWhere0 + oldCache->len * samplesPerPixel + )); + denom = oldWhereLast - oldWhere0; + + // Mitigate the accumulation of location errors + // in copies of copies of ... of caches. + if (denom > 0) + { + const double guessWhere0 = t0 * mRate; + oldX0 = floor(0.5 + oldCache->len * (guessWhere0 - oldWhere0) / denom); + const double where0 = + (sampleCount)floor(0.5 + oldWhere0 + double(oldX0) * samplesPerPixel); + error = where0 - guessWhere0; // should be in (-samplesPerPixel, samplesPerPixel) + oldXLast = floor(0.5 + oldCache->len * ( + (where0 + double(mWaveCache->len) * samplesPerPixel - oldWhere0) + / denom + )); + } + } + + // Be careful to make the first value non-negative + recalc[0] = true; + mSpecCache->where[0] = sampleCount(std::max(0.0, floor(1.0 + error + t0 * mRate))); + for (sampleCount x = 1; x < mSpecCache->len + 1; x++) { recalc[x] = true; // purposely offset the display 1/2 bin to the left (as compared // to waveform display to properly center response of the FFT mSpecCache->where[x] = - (sampleCount)floor((t0*mRate) + (x*mRate/pixelsPerSecond) + 1.); + sampleCount(floor(1.0 + error + t0 * mRate + double(x) * samplesPerPixel)); } // Optimization: if the old cache is good and overlaps // with the current one, re-use as much of the cache as // possible - if (oldCache->dirty == mDirty && + if (denom > 0 && + oldCache->dirty == mDirty && oldCache->minFreqOld == minFreq && oldCache->maxFreqOld == maxFreq && oldCache->rangeOld == range && @@ -823,27 +879,24 @@ bool WaveClip::GetSpectrogram(float *freq, sampleCount *where, #endif //EXPERIMENTAL_FFT_SKIP_POINTS oldCache->pps == pixelsPerSecond && oldCache->ac == autocorrelation && - oldCache->where[0] < mSpecCache->where[mSpecCache->len] && - oldCache->where[oldCache->len] > mSpecCache->where[0]) { - - for (x = 0; x < mSpecCache->len; x++) - if (mSpecCache->where[x] >= oldCache->where[0] && - mSpecCache->where[x] <= oldCache->where[oldCache->len]) { - - int ox = (int) ((double (oldCache->len) * - (mSpecCache->where[x] - oldCache->where[0])) - / (oldCache->where[oldCache->len] - - oldCache->where[0]) + 0.5); - if (ox >= 0 && ox < oldCache->len && - mSpecCache->where[x] == oldCache->where[ox]) { + oldX0 < oldCache->len && + oldXLast > oldCache->start) { + 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]; - + mSpecCache->freq[half * x + i] = oldCache->freq[half * ox + i]; recalc[x] = false; } } + } } #ifdef EXPERIMENTAL_FFT_SKIP_POINTS @@ -866,12 +919,12 @@ bool WaveClip::GetSpectrogram(float *freq, sampleCount *where, // scaled such that 1000 Hz gets a gain of 0dB double factor = 0.001*(double)mRate/(double)windowSize; gainfactor = new float[half]; - for(x = 0; x < half; x++) { + for(sampleCount x = 0; x < half; x++) { gainfactor[x] = frequencygain*log10(factor * x); } } - for (x = 0; x < mSpecCache->len; x++) + for (sampleCount x = 0; x < mSpecCache->len; x++) if (recalc[x]) { sampleCount start = mSpecCache->where[x]; @@ -1602,11 +1655,11 @@ bool WaveClip::Resample(int rate, ProgressDialog *progress) delete mWaveCache; mWaveCache = NULL; } - mWaveCache = new WaveCache(1); + mWaveCache = new WaveCache(0); // Invalidate the spectrum display cache if (mSpecCache) delete mSpecCache; - mSpecCache = new SpecCache(1, 1, false); + mSpecCache = new SpecCache(0, 1, false); } return !error;