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:
commit
324816e3fc
@ -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
|
||||||
|
708
src/WaveClip.cpp
708
src/WaveClip.cpp
@ -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,6 +250,12 @@ 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;
|
||||||
@ -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;
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user