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

Simplify the partial copying of wave and spectrum caches.  Use memcpy for speed.

Don't memcpy out of the caches into temporary buffers for TrackArtist,
just pass
pointers.

More vectors in the cache classes, fewer deletes.

Pulled big loop, and its body, out of the spectrogram routine into functions.
This commit is contained in:
Paul Licameli 2015-06-05 09:47:56 -04:00
parent 9b8244635d
commit 976eb6164f
3 changed files with 379 additions and 383 deletions

View File

@ -1681,14 +1681,13 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
float zoomMin, zoomMax;
track->GetDisplayBounds(&zoomMin, &zoomMax);
WaveDisplay display;
WaveDisplay display(mid.width);
bool isLoadingOD = false;//true if loading on demand block in sequence.
// The WaveClip class handles the details of computing the shape
// of the waveform. The only way GetWaveDisplay will fail is if
// 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.
display.Allocate(mid.width);
if (!clip->GetWaveDisplay(display,
t0, pps, isLoadingOD)) {
return;
@ -1705,7 +1704,7 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
// the envelope and using a colored pen for the selected
// part of the waveform
DrawWaveformBackground(dc, mid, envValues, zoomMin, zoomMax, dB,
display.where, ssel0, ssel1, drawEnvelope,
&display.where[0], ssel0, ssel1, drawEnvelope,
!track->GetSelected());
if (!showIndividualSamples) {
@ -1824,7 +1823,7 @@ void TrackArtist::DrawSpectrum(WaveTrack *track,
}
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)
{
float value;
@ -1959,8 +1958,8 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
int windowSize = GetSpectrumWindowSize(!autocorrelation);
const int half = windowSize / 2;
float *freq = new float[mid.width * half];
sampleCount *where = new sampleCount[mid.width+1];
const float *freq = 0;
const sampleCount *where = 0;
bool updated = clip->GetSpectrogram(cache, freq, where, mid.width,
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);
delete image;
delete[] where;
delete[] freq;
#ifdef EXPERIMENTAL_FFT_Y_GRID
delete[] yGrid;
#endif //EXPERIMENTAL_FFT_Y_GRID

View File

@ -44,53 +44,56 @@ drawing). Cache's the Spectrogram frequency samples.
#include <wx/listimpl.cpp>
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 {
public:
WaveCache(int cacheLen)
: len(cacheLen)
WaveCache()
: 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;
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;
}
WaveCache(int len_, double pixelsPerSecond, double rate_, double t0)
: dirty(-1)
, len(len_)
, start(t0)
, pps(pixelsPerSecond)
, rate(rate_)
, where(1 + len)
, 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()
{
delete[] min;
delete[] max;
delete[] rms;
delete[] bl;
delete[] where;
ClearInvalidRegions();
}
int dirty;
const sampleCount len;
double start;
double pps;
int rate;
sampleCount *where;
float *min;
float *max;
float *rms;
int *bl;
const int len; // counts pixels, not samples
const double start;
const double pps;
const int rate;
std::vector<sampleCount> where;
std::vector<float> min;
std::vector<float> max;
std::vector<float> rms;
std::vector<int> bl;
int numODPixels;
class InvalidRegion
@ -223,7 +226,7 @@ public:
//before check number of ODPixels
int regionODPixels = 0;
if (updateODCount)
regionODPixels = CountODPixels(bl, invStart, invEnd);
regionODPixels = CountODPixels(invStart, invEnd);
sequence->GetWaveDisplay(&min[invStart],
&max[invStart],
@ -235,7 +238,7 @@ public:
//after check number of ODPixels
if (updateODCount)
{
const int regionODPixelsAfter = CountODPixels(bl, invStart, invEnd);
const int regionODPixelsAfter = CountODPixels(invStart, invEnd);
numODPixels -= (regionODPixels - regionODPixelsAfter);
}
}
@ -247,10 +250,16 @@ public:
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:
std::vector<InvalidRegion*> mRegions;
ODLock mRegionsMutex;
ODLock mRegionsMutex;
};
@ -301,10 +310,10 @@ public:
// 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)
, freq(len * ((windowSize * zeroPaddingFactor) / 2))
// Sample counts corresponding to the columns, and to one past the end.
, where(new sampleCount[len + 1])
, where(len + 1)
, dirty(-1)
{
@ -313,11 +322,27 @@ public:
~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 double pps;
const double start;
@ -328,8 +353,8 @@ public:
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
const int fftSkipPoints;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
float *const freq;
sampleCount *const where;
std::vector<float> freq;
std::vector<sampleCount> where;
int dirty;
};
@ -370,7 +395,7 @@ WaveClip::WaveClip(DirManager *projDirManager, sampleFormat format, int rate)
mRate = rate;
mSequence = new Sequence(projDirManager, format);
mEnvelope = new Envelope();
mWaveCache = new WaveCache(0);
mWaveCache = new WaveCache();
mSpecCache = new SpecCache();
mSpecPxCache = new SpecPxCache(1);
mAppendBuffer = NULL;
@ -392,7 +417,7 @@ 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(0);
mWaveCache = new WaveCache();
mSpecCache = new SpecCache();
mSpecPxCache = new SpecPxCache(1);
@ -494,7 +519,7 @@ void WaveClip::DeleteWaveCache()
ODLocker locker(mWaveCacheMutex);
if(mWaveCache!=NULL)
delete mWaveCache;
mWaveCache = new WaveCache(0);
mWaveCache = new WaveCache();
}
///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 {
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 &oldWhere0, double &denom, long &oldX0, long &oldXLast, double &correction,
bool &overlap)
int &oldX0, double &correction)
{
// Mitigate the accumulation of location errors
// in copies of copies of ... of caches.
// Look at the loop that populates "where" below to understand this.
// 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;
// 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?
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:
oldWhereLast <= guessWhere0 ||
guessWhere0 + newLen * samplesPerPixel <= oldWhere0 ||
// Skip unless denom rounds off to at least 1.
denom < 0.5)
{
// The computation of oldX0 in the other branch
// may underflow and the assertion would be violated.
oldX0 = oldLen;
correction = 0.0;
overlap = false;
}
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?
const double correction0 = where0 - guessWhere0;
correction = std::max(-samplesPerPixel, std::min(samplesPerPixel, correction0));
wxASSERT(correction == correction0);
overlap = true;
}
}
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)
{
// Be careful to make the first value non-negative
@ -581,85 +599,76 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
double pixelsPerSecond, bool &isLoadingOD)
{
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);
const bool match =
mWaveCache &&
mWaveCache->len > 0 &&
mWaveCache->dirty == mDirty &&
mWaveCache->pps == pixelsPerSecond;
if (match &&
mWaveCache->start == t0 &&
mWaveCache->len >= numPixels) {
mWaveCache->LoadInvalidRegions(mSequence, true);
mWaveCache->ClearInvalidRegions();
memcpy(min, mWaveCache->min, numPixels*sizeof(float));
memcpy(max, mWaveCache->max, numPixels*sizeof(float));
memcpy(rms, mWaveCache->rms, numPixels*sizeof(float));
memcpy(bl, mWaveCache->bl, numPixels*sizeof(int));
memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount));
isLoadingOD = mWaveCache->numODPixels>0;
// Satisfy the request completely from the cache
display.min = &mWaveCache->min[0];
display.max = &mWaveCache->max[0];
display.rms = &mWaveCache->rms[0];
display.bl = &mWaveCache->bl[0];
display.where = &mWaveCache->where[0];
isLoadingOD = mWaveCache->numODPixels > 0;
return true;
}
std::auto_ptr<WaveCache> oldCache(mWaveCache);
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 samplesPerPixel = mRate * tstep;
if (match &&
oldCache->len > 0) {
int oldX0 = 0;
double correction = 0.0;
int copyBegin = 0, copyEnd = 0;
if (match) {
findCorrection(oldCache->where, oldCache->len, numPixels,
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);
mWaveCache = new WaveCache(numPixels);
mWaveCache->pps = pixelsPerSecond;
mWaveCache->rate = mRate;
mWaveCache->start = t0;
mWaveCache = new WaveCache(numPixels, pixelsPerSecond, mRate, t0);
float *const min = &mWaveCache->min[0];
float *const max = &mWaveCache->max[0];
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);
//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];
int p0 = 0;
int p1 = mWaveCache->len;
// The range of pixels we must fetch from the Sequence:
const int p0 = (copyBegin > 0) ? 0 : copyEnd;
int p1 = (copyEnd >= numPixels) ? copyBegin : numPixels;
// Optimization: if the old cache is good and overlaps
// with the current one, re-use as much of the cache as
// 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.
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;
if (oldCache.get()) {
//TODO: only load inval regions if
//necessary. (usually is the case, so no rush.)
@ -667,30 +676,14 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
oldCache->LoadInvalidRegions(mSequence, false);
oldCache->ClearInvalidRegions();
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 (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;
}
}
}
// Copy what we can from the old cache.
const int length = copyEnd - copyBegin;
const size_t sizeFloats = length * sizeof(float);
const int srcIdx = copyBegin + oldX0;
memcpy(&min[copyBegin], &oldCache->min[srcIdx], sizeFloats);
memcpy(&max[copyBegin], &oldCache->max[srcIdx], sizeFloats);
memcpy(&rms[copyBegin], &oldCache->rms[srcIdx], sizeFloats);
memcpy(&bl[copyBegin], &oldCache->bl[srcIdx], length * sizeof(int));
}
if (p1 > p0) {
@ -699,9 +692,8 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
int numSamples = mSequence->GetNumSamples();
int a;
for(a=p0; a<p1; a++)
if (mWaveCache->where[a+1] > numSamples)
for(a = p0; a < p1; ++a)
if (where[a+1] > numSamples)
break;
//compute the values that are outside the overlap from scratch.
@ -712,9 +704,9 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
bool didUpdate = false;
for(i=a; i<p1; i++) {
sampleCount left;
left = mWaveCache->where[i] - numSamples;
left = where[i] - numSamples;
sampleCount right;
right = mWaveCache->where[i+1] - numSamples;
right = where[i+1] - numSamples;
//wxCriticalSectionLocker locker(mAppendCriticalSection);
@ -737,22 +729,23 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
(samplePtr)b, floatSample, len);
}
float max = b[0];
float min = b[0];
float sumsq = b[0] * b[0];
float theMax, theMin, sumsq;
{
const float val = b[0];
theMax = theMin = val;
sumsq = val * val;
}
for(j=1; j<len; j++) {
if (b[j] > max)
max = b[j];
if (b[j] < min)
min = b[j];
sumsq += b[j]*b[j];
const float val = b[j];
theMax = std::max(theMax, val);
theMin = std::min(theMin, val);
sumsq += val * val;
}
mWaveCache->min[i] = min;
mWaveCache->max[i] = max;
mWaveCache->rms[i] = (float)sqrt(sumsq / len);
mWaveCache->bl[i] = 1; //for now just fake it.
min[i] = theMin;
max[i] = theMax;
rms[i] = (float)sqrt(sumsq / len);
bl[i] = 1; //for now just fake it.
if (seqFormat != floatSample)
delete[] b;
@ -761,20 +754,18 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
}
}
// So that the sequence doesn't try to write any
// of these values
//mchinen: but only do this if we've updated pixels in the cache.
// Shrink the right end of the range to fetch from Sequence
if(didUpdate)
p1 = a;
}
if (p1 > p0) {
if (!mSequence->GetWaveDisplay(&mWaveCache->min[p0],
&mWaveCache->max[p0],
&mWaveCache->rms[p0],
&mWaveCache->bl[p0],
if (!mSequence->GetWaveDisplay(&min[p0],
&max[p0],
&rms[p0],
&bl[p0],
p1-p0,
&mWaveCache->where[p0]))
&where[p0]))
{
isLoadingOD=false;
return false;
@ -784,30 +775,27 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
mWaveCache->dirty = mDirty;
memcpy(min, mWaveCache->min, numPixels*sizeof(float));
memcpy(max, mWaveCache->max, numPixels*sizeof(float));
memcpy(rms, mWaveCache->rms, numPixels*sizeof(float));
memcpy(bl, mWaveCache->bl, numPixels*sizeof(int));
memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount));
//find the number of OD pixels - the only way to do this is by recounting since we've lost some old cache.
mWaveCache->numODPixels = 0;
for(int j=0;j<mWaveCache->len;j++)
if(mWaveCache->bl[j]<0)
mWaveCache->numODPixels++;
isLoadingOD = mWaveCache->numODPixels>0;
// Now report the results
display.min = min;
display.max = max;
display.rms = rms;
display.bl = bl;
display.where = &where[0];
isLoadingOD = mWaveCache->numODPixels > 0;
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) {
// Compute a frequency-dependent gain factor
// scaled such that 1000 Hz gets a gain of 0dB
// 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;
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,
float *freq, sampleCount *where,
const float *& spectrogram, const sampleCount *& where,
int numPixels,
double t0, double pixelsPerSecond,
bool autocorrelation)
@ -829,7 +996,6 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
int fftSkipPoints = settings.fftSkipPoints;
int fftSkipPoints1 = fftSkipPoints + 1;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
const int &frequencyGain = settings.frequencyGain;
@ -840,64 +1006,49 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
#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
// because of zero padding done for increased frequency resolution
const int fftLen = windowSize * zeroPaddingFactor;
const int half = fftLen / 2;
const int padding = (windowSize * (zeroPaddingFactor - 1)) / 2;
const bool match =
mSpecCache &&
mSpecCache->dirty == mDirty &&
mSpecCache->windowType == windowType &&
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;
mSpecCache->len > 0 &&
mSpecCache->Matches(mDirty, autocorrelation, pixelsPerSecond, settings);
if (match &&
mSpecCache->start == t0 &&
mSpecCache->len >= numPixels) {
memcpy(freq, mSpecCache->freq, numPixels*half*sizeof(float));
memcpy(where, mSpecCache->where, (numPixels+1)*sizeof(sampleCount));
spectrogram = &mSpecCache->freq[0];
where = &mSpecCache->where[0];
return false; //hit cache completely
}
std::auto_ptr<SpecCache> oldCache(mSpecCache);
mSpecCache = 0;
bool *recalc = new bool[numPixels + 1];
std::fill(&recalc[0], &recalc[numPixels + 1], true);
const double tstep = 1.0 / pixelsPerSecond;
const double samplesPerPixel = mRate * tstep;
// To do: eliminate duplicate logic with the wave clip code for cache
// reuse and finding corrections
double oldWhere0 = 0;
double denom = 0;
long oldX0 = 0, oldXLast = 0;
int oldX0 = 0;
double correction = 0.0;
bool overlap = false;
if (match &&
oldCache->len > 0) {
int copyBegin = 0, copyEnd = 0;
if (match) {
findCorrection(oldCache->where, oldCache->len, numPixels,
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);
mSpecCache = new SpecCache(
@ -908,144 +1059,25 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
#endif
);
fillWhere(mSpecCache->where, mSpecCache->len, 0.5, correction,
fillWhere(mSpecCache->where, numPixels, 0.5, correction,
t0, mRate, samplesPerPixel);
// Optimization: if the old cache is good and overlaps
// with the current one, re-use as much of the cache as
// possible
if (match && overlap &&
denom >= 0.5 &&
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];
recalc[x] = false;
}
}
}
if (oldCache.get()) {
memcpy(&mSpecCache->freq[half * copyBegin],
&oldCache->freq[half * (copyBegin + oldX0)],
half * (copyEnd - copyBegin) * sizeof(float));
}
float *useBuffer = 0;
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
float *buffer = new float[fftLen*fftSkipPoints1];
#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->Populate
(settings, waveTrackCache, copyBegin, copyEnd, numPixels,
mSequence->GetNumSamples(), mOffset, mRate, autocorrelation);
mSpecCache->dirty = mDirty;
memcpy(freq, mSpecCache->freq, numPixels*half*sizeof(float));
memcpy(where, mSpecCache->where, (numPixels+1)*sizeof(sampleCount));
spectrogram = &mSpecCache->freq[0];
where = &mSpecCache->where[0];
return true;
}
@ -1695,7 +1727,7 @@ bool WaveClip::Resample(int rate, ProgressDialog *progress)
delete mWaveCache;
mWaveCache = NULL;
}
mWaveCache = new WaveCache(0);
mWaveCache = new WaveCache();
// Invalidate the spectrum display cache
if (mSpecCache)
delete mSpecCache;

View File

@ -72,41 +72,13 @@ public:
float *min, *max, *rms;
int* bl;
WaveDisplay()
: width(0), where(0), min(0), max(0), rms(0), bl(0)
WaveDisplay(int w)
: width(w), where(0), min(0), max(0), rms(0), bl(0)
{
}
~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 */
bool GetWaveDisplay(WaveDisplay &display,
double t0, double pixelsPerSecond, bool &isLoadingOD);
void ComputeSpectrogramGainFactors(
int fftLen,
int frequencyGain, // db/decade
std::vector<float> &gainFactors
);
bool GetSpectrogram(WaveTrackCache &cache,
float *buffer, sampleCount *where,
const float *& spectrogram, const sampleCount *& where,
int numPixels,
double t0, double pixelsPerSecond,
bool autocorrelation);