mirror of
https://github.com/cookiengineer/audacity
synced 2025-09-09 00:50:01 +02:00
Merge: Bug999 and various cleanups of some waveform and spectrogram drawing code
Bug999: top bin of spectrogram display After drawing stripes, restore the pen Lifted code that draws multi-tool sliders -- it's per track, not clip Simplified passing of min/min/rmx/bl/where values for drawing waveforms Simplified some management of WaveClip caches Split apart the loops that update the spectrogram cache and that use it Functions for common parts of waveform and spectrogram display update Abstracted common code out of waveform and spectrum drawing routines Simplified optional profiling code in TrackArtist
This commit is contained in:
commit
7e007ad551
@ -189,6 +189,42 @@ audio tracks.
|
||||
#endif
|
||||
double gWaveformTimeTotal = 0;
|
||||
int gWaveformTimeCount = 0;
|
||||
|
||||
namespace {
|
||||
struct Profiler {
|
||||
Profiler()
|
||||
{
|
||||
# ifdef __WXMSW__
|
||||
_time64(&tv0);
|
||||
# else
|
||||
gettimeofday(&tv0, NULL);
|
||||
# endif
|
||||
}
|
||||
|
||||
~Profiler()
|
||||
{
|
||||
# ifdef __WXMSW__
|
||||
_time64(&tv1);
|
||||
double elapsed = _difftime64(tv1, tv0);
|
||||
# else
|
||||
gettimeofday(&tv1, NULL);
|
||||
double elapsed =
|
||||
(tv1.tv_sec + tv1.tv_usec*0.000001) -
|
||||
(tv0.tv_sec + tv0.tv_usec*0.000001);
|
||||
# endif
|
||||
gWaveformTimeTotal += elapsed;
|
||||
gWaveformTimeCount++;
|
||||
wxPrintf(wxT("Avg waveform drawing time: %f\n"),
|
||||
gWaveformTimeTotal / gWaveformTimeCount);
|
||||
}
|
||||
|
||||
# ifdef __WXMSW__
|
||||
__time64_t tv0, tv1;
|
||||
#else
|
||||
struct timeval tv0, tv1;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_MIDI
|
||||
@ -1070,15 +1106,18 @@ void TrackArtist::DrawWaveformBackground(wxDC &dc, const wxRect &r, const double
|
||||
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
||||
void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect &r, const double env[],
|
||||
float zoomMin, float zoomMax, bool dB,
|
||||
const float min[], const float max[], const float rms[],
|
||||
const int bl[], bool showProgress, bool muted, const float gain)
|
||||
const WaveDisplay &display, bool showProgress, bool muted, const float gain)
|
||||
#else
|
||||
void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect &r, const double env[],
|
||||
float zoomMin, float zoomMax, bool dB,
|
||||
const float min[], const float max[], const float rms[],
|
||||
const int bl[], bool WXUNUSED(showProgress), bool muted)
|
||||
const WaveDisplay &display, bool WXUNUSED(showProgress), bool muted)
|
||||
#endif
|
||||
{
|
||||
const float *const min = display.min;
|
||||
const float *const max = display.max;
|
||||
const float *const rms = display.rms;
|
||||
const int *const bl = display.bl;
|
||||
|
||||
// Display a line representing the
|
||||
// min and max of the samples in this region
|
||||
int lasth1 = std::numeric_limits<int>::max();
|
||||
@ -1195,12 +1234,16 @@ void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect &r, const double env[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the pen for remaining pixel columns!
|
||||
dc.SetPen(muted ? muteSamplePen : samplePen);
|
||||
}
|
||||
else {
|
||||
AColor::Line(dc, xx, r.y + h2, xx, r.y + h1);
|
||||
}
|
||||
}
|
||||
|
||||
// Stroke rms over the min-max
|
||||
dc.SetPen(muted ? muteRmsPen : rmsPen);
|
||||
for (int x = 0; x < r.width; x++) {
|
||||
int xx = r.x + x;
|
||||
@ -1409,7 +1452,7 @@ void TrackArtist::DrawWaveform(WaveTrack *track,
|
||||
|
||||
for (WaveClipList::compatibility_iterator it = track->GetClipIterator(); it; it = it->GetNext())
|
||||
DrawClipWaveform(track, it->GetData(), dc, r, viewInfo,
|
||||
drawEnvelope, drawSamples, drawSliders,
|
||||
drawEnvelope, drawSamples,
|
||||
dB, muted);
|
||||
|
||||
// Update cache for locations, e.g. cutlines and merge points
|
||||
@ -1432,59 +1475,58 @@ void TrackArtist::DrawWaveform(WaveTrack *track,
|
||||
AColor::Line(dc, (int) (r.x + x + 1), r.y, (int) (r.x + x + 1), r.y + r.height);
|
||||
}
|
||||
}
|
||||
|
||||
if (drawSliders) {
|
||||
DrawTimeSlider(track, dc, r, viewInfo, true); // directed right
|
||||
DrawTimeSlider(track, dc, r, viewInfo, false); // directed left
|
||||
}
|
||||
}
|
||||
|
||||
void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
WaveClip *clip,
|
||||
wxDC & dc,
|
||||
const wxRect & r,
|
||||
const ViewInfo *viewInfo,
|
||||
bool drawEnvelope,
|
||||
bool drawSamples,
|
||||
bool drawSliders,
|
||||
bool dB,
|
||||
bool muted)
|
||||
|
||||
namespace {
|
||||
struct ClipParameters
|
||||
{
|
||||
#if PROFILE_WAVEFORM
|
||||
# ifdef __WXMSW__
|
||||
__time64_t tv0, tv1;
|
||||
_time64(&tv0);
|
||||
# else
|
||||
struct timeval tv0, tv1;
|
||||
gettimeofday(&tv0, NULL);
|
||||
# endif
|
||||
#endif
|
||||
double h = viewInfo->h; //The horizontal position in seconds
|
||||
double pps = viewInfo->zoom; //points-per-second--the zoom level
|
||||
double sel0 = viewInfo->selectedRegion.t0(); //left selection bound
|
||||
double sel1 = viewInfo->selectedRegion.t1(); //right selection bound
|
||||
double trackLen = clip->GetEndTime() - clip->GetStartTime();
|
||||
double tOffset = clip->GetOffset();
|
||||
double rate = clip->GetRate();
|
||||
double sps = 1./rate; //seconds-per-sample
|
||||
// Do a bunch of calculations common to waveform and spectrum drawing.
|
||||
ClipParameters
|
||||
(bool spectrum, const WaveTrack *track, const WaveClip *clip, const wxRect &r,
|
||||
const SelectedRegion &selectedRegion, const ViewInfo &viewInfo)
|
||||
{
|
||||
selectedRegion;
|
||||
|
||||
tOffset = clip->GetOffset();
|
||||
rate = clip->GetRate();
|
||||
|
||||
h = viewInfo.h; //The horizontal position in seconds
|
||||
pps = viewInfo.zoom; //points-per-second--the zoom level
|
||||
|
||||
double sel0 = viewInfo.selectedRegion.t0(); //left selection bound
|
||||
double sel1 = viewInfo.selectedRegion.t1(); //right selection bound
|
||||
|
||||
//If the track isn't selected, make the selection empty
|
||||
if (!track->GetSelected() && !track->IsSyncLockSelected()) {
|
||||
if (!track->GetSelected() &&
|
||||
(spectrum || !track->IsSyncLockSelected())) { // PRL: why was there a difference for spectrum?
|
||||
sel0 = sel1 = 0.0;
|
||||
}
|
||||
|
||||
//Some bookkeeping time variables:
|
||||
double tstep = 1.0 / pps; // Seconds per point
|
||||
double tpre = h - tOffset; // offset corrected time of
|
||||
const double trackLen = clip->GetEndTime() - clip->GetStartTime();
|
||||
|
||||
tstep = 1.0 / pps; // Seconds per point
|
||||
tpre = h - tOffset; // offset corrected time of
|
||||
// left edge of display
|
||||
double tpost = tpre + (r.width * tstep); // offset corrected time of
|
||||
tpost = tpre + (r.width * tstep); // offset corrected time of
|
||||
// right edge of display
|
||||
|
||||
const double sps = 1. / rate; //seconds-per-sample
|
||||
|
||||
// Determine whether we should show individual samples
|
||||
// or draw circular points as well
|
||||
bool showIndividualSamples = (pps / rate > 0.5); //zoomed in a lot
|
||||
bool showPoints = (pps / rate > 3.0); //zoomed in even more
|
||||
showIndividualSamples = (pps / rate > 0.5); //zoomed in a lot
|
||||
showPoints = (pps / rate > 3.0); //zoomed in even more
|
||||
|
||||
// Calculate actual selection bounds so that t0 > 0 and t1 < the
|
||||
// end of the track
|
||||
|
||||
double t0 = (tpre >= 0.0 ? tpre : 0.0);
|
||||
double t1 = (tpost < trackLen - sps * .99 ? tpost : trackLen - sps * .99);
|
||||
t0 = (tpre >= 0.0 ? tpre : 0.0);
|
||||
t1 = (tpost < trackLen - sps * .99 ? tpost : trackLen - sps * .99);
|
||||
if (showIndividualSamples) {
|
||||
// adjustment so that the last circular point doesn't appear
|
||||
// to be hanging off the end
|
||||
@ -1501,11 +1543,15 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
t0 = t1;
|
||||
}
|
||||
|
||||
// Calculate sample-based offset-corrected selection
|
||||
|
||||
// Use the WaveTrack method to show what is selected and 'should' be copied, pasted etc.
|
||||
sampleCount ssel0 = wxMax(0, track->TimeToLongSamples(sel0 - tOffset));
|
||||
sampleCount ssel1 = wxMax(0, track->TimeToLongSamples(sel1 - tOffset));
|
||||
ssel0 = std::max(sampleCount(0), spectrum
|
||||
? sampleCount((sel0 - tOffset) * rate + .99) // PRL: why?
|
||||
: track->TimeToLongSamples(sel0 - tOffset)
|
||||
);
|
||||
ssel1 = std::max(sampleCount(0), spectrum
|
||||
? sampleCount((sel1 - tOffset) * rate + .99) // PRL: why?
|
||||
: track->TimeToLongSamples(sel1 - tOffset)
|
||||
);
|
||||
|
||||
//trim selection so that it only contains the actual samples
|
||||
if (ssel0 != ssel1 && ssel1 > (sampleCount)(0.5 + trackLen * rate)) {
|
||||
@ -1515,18 +1561,21 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
// The variable "mid" will be the rectangle containing the
|
||||
// actual waveform, as opposed to any blank area before
|
||||
// or after the track.
|
||||
wxRect mid = r;
|
||||
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
mid = r;
|
||||
|
||||
// If the left edge of the track is to the right of the left
|
||||
// edge of the display, then there's some blank area to the
|
||||
// left of the track. Reduce the "mid"
|
||||
// rect by size of the blank area.
|
||||
if (tpre < 0) {
|
||||
// Fill in the area to the left of the track
|
||||
double delta = r.width;
|
||||
if (t0 < tpost) {
|
||||
delta = (int)((t0 - tpre) * pps);
|
||||
}
|
||||
|
||||
// Offset the rectangle containing the waveform by the width
|
||||
// of the area we just erased.
|
||||
mid.x += (int)delta;
|
||||
mid.width -= (int)delta;
|
||||
}
|
||||
@ -1541,29 +1590,74 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
post.x += (int)((t1 - tpre) * pps);
|
||||
}
|
||||
post.width = r.width - (post.x - r.x);
|
||||
// Reduce the rectangle containing the waveform by the width
|
||||
// of the area we just erased.
|
||||
mid.width -= post.width;
|
||||
}
|
||||
}
|
||||
|
||||
double tOffset;
|
||||
double rate;
|
||||
double h; // absolute time of left edge of display
|
||||
double tstep;
|
||||
double tpre; // offset corrected time of left edge of display
|
||||
// double h1;
|
||||
double tpost; // offset corrected time of right edge of display
|
||||
|
||||
// Calculate actual selection bounds so that t0 > 0 and t1 < the
|
||||
// end of the track
|
||||
double t0;
|
||||
double t1;
|
||||
|
||||
double pps;
|
||||
bool showIndividualSamples, showPoints;
|
||||
|
||||
sampleCount ssel0;
|
||||
sampleCount ssel1;
|
||||
|
||||
wxRect mid;
|
||||
};
|
||||
}
|
||||
|
||||
void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
WaveClip *clip,
|
||||
wxDC & dc,
|
||||
const wxRect & r,
|
||||
const ViewInfo *viewInfo,
|
||||
bool drawEnvelope,
|
||||
bool drawSamples,
|
||||
bool dB,
|
||||
bool muted)
|
||||
{
|
||||
#ifdef PROFILE_WAVEFORM
|
||||
Profiler profiler;
|
||||
#endif
|
||||
|
||||
const ClipParameters params(false, track, clip, r, viewInfo->selectedRegion, *viewInfo);
|
||||
const wxRect &mid = params.mid;
|
||||
// The "mid" rect contains the part of the display actually
|
||||
// containing the waveform. If it's empty, we're done.
|
||||
if (mid.width <= 0) {
|
||||
#if PROFILE_WAVEFORM
|
||||
# ifdef __WXMSW__
|
||||
_time64(&tv1);
|
||||
double elapsed = _difftime64(tv1, tv0);
|
||||
# else
|
||||
gettimeofday(&tv1, NULL);
|
||||
double elapsed =
|
||||
(tv1.tv_sec + tv1.tv_usec*0.000001) -
|
||||
(tv0.tv_sec + tv0.tv_usec*0.000001);
|
||||
# endif
|
||||
gWaveformTimeTotal += elapsed;
|
||||
gWaveformTimeCount++;
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const double &t0 = params.t0;
|
||||
const double &pps = params.pps;
|
||||
const double &tOffset = params.tOffset;
|
||||
const double &tstep = params.tstep;
|
||||
const double &ssel0 = params.ssel0;
|
||||
const double &ssel1 = params.ssel1;
|
||||
const bool &showIndividualSamples = params.showIndividualSamples;
|
||||
const bool &showPoints = params.showPoints;
|
||||
const double &h = params.h;
|
||||
const double &tpre = params.tpre;
|
||||
const double &tpost = params.tpost;
|
||||
const double &t1 = params.t1;
|
||||
|
||||
// Calculate sample-based offset-corrected selection
|
||||
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
|
||||
// If we get to this point, the clip is actually visible on the
|
||||
// screen, so remember the display rectangle.
|
||||
clip->SetDisplayRect(mid);
|
||||
@ -1573,26 +1667,16 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
float zoomMin, zoomMax;
|
||||
track->GetDisplayBounds(&zoomMin, &zoomMax);
|
||||
|
||||
// Arrays containing the shape of the waveform - each array
|
||||
// has one value per pixel.
|
||||
float *min = new float[mid.width];
|
||||
float *max = new float[mid.width];
|
||||
float *rms = new float[mid.width];
|
||||
sampleCount *where = new sampleCount[mid.width + 1];
|
||||
int *bl = new int[mid.width];
|
||||
WaveDisplay display;
|
||||
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.
|
||||
if (!clip->GetWaveDisplay(min, max, rms, bl, where,
|
||||
mid.width, t0, pps, isLoadingOD)) {
|
||||
delete[] min;
|
||||
delete[] max;
|
||||
delete[] rms;
|
||||
delete[] where;
|
||||
delete[] bl;
|
||||
display.Allocate(mid.width);
|
||||
if (!clip->GetWaveDisplay(display,
|
||||
t0, pps, isLoadingOD)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1607,7 +1691,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,
|
||||
where, ssel0, ssel1, drawEnvelope,
|
||||
display.where, ssel0, ssel1, drawEnvelope,
|
||||
!track->GetSelected());
|
||||
|
||||
if (!showIndividualSamples) {
|
||||
@ -1616,7 +1700,7 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
min, max, rms, bl, isLoadingOD, muted, track->GetChannelGain(track->GetChannel()));
|
||||
#else
|
||||
DrawMinMaxRMS(dc, mid, envValues, zoomMin, zoomMax, dB,
|
||||
min, max, rms, bl, isLoadingOD, muted);
|
||||
display, isLoadingOD, muted);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
@ -1631,11 +1715,6 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
}
|
||||
|
||||
delete[] envValues;
|
||||
delete[] min;
|
||||
delete[] max;
|
||||
delete[] rms;
|
||||
delete[] where;
|
||||
delete[] bl;
|
||||
|
||||
// Draw arrows on the left side if the track extends to the left of the
|
||||
// beginning of time. :)
|
||||
@ -1643,11 +1722,6 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
DrawNegativeOffsetTrackArrows(dc, r);
|
||||
}
|
||||
|
||||
if (drawSliders) {
|
||||
DrawTimeSlider(track, dc, r, viewInfo, true); // directed right
|
||||
DrawTimeSlider(track, dc, r, viewInfo, false); // directed left
|
||||
}
|
||||
|
||||
// Draw clip edges
|
||||
dc.SetPen(*wxGREY_PEN);
|
||||
if (tpre < 0) {
|
||||
@ -1660,22 +1734,6 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
||||
mid.x + mid.width, mid.y,
|
||||
mid.x + mid.width, mid.y + r.height);
|
||||
}
|
||||
|
||||
#if PROFILE_WAVEFORM
|
||||
# ifdef __WXMSW__
|
||||
_time64(&tv1);
|
||||
double elapsed = _difftime64(tv1, tv0);
|
||||
# else
|
||||
gettimeofday(&tv1, NULL);
|
||||
double elapsed =
|
||||
(tv1.tv_sec + tv1.tv_usec*0.000001) -
|
||||
(tv0.tv_sec + tv0.tv_usec*0.000001);
|
||||
# endif
|
||||
gWaveformTimeTotal += elapsed;
|
||||
gWaveformTimeCount++;
|
||||
wxPrintf(wxT("Avg waveform drawing time: %f\n"),
|
||||
gWaveformTimeTotal / gWaveformTimeCount);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -1755,24 +1813,22 @@ void TrackArtist::DrawSpectrum(WaveTrack *track,
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
static float sumFreqValues(
|
||||
const float *freq, int x, int half, float bin0, float bin1,
|
||||
bool autocorrelation, int range, int gain)
|
||||
static inline float findValue
|
||||
(float *spectrum, float bin0, float bin1, int half,
|
||||
bool autocorrelation, int gain, int range)
|
||||
{
|
||||
const int x0 = x * half;
|
||||
float value;
|
||||
#if 0
|
||||
// Averaging method
|
||||
if (int(bin1) == int(bin0)) {
|
||||
value = freq[x0+int(bin0)];
|
||||
value = spectrum[int(bin0)];
|
||||
} else {
|
||||
float binwidth= bin1 - bin0;
|
||||
value = freq[x0 + int(bin0)] * (1.f - bin0 + (int)bin0);
|
||||
value = spectrum[int(bin0)] * (1.f - bin0 + (int)bin0);
|
||||
|
||||
bin0 = 1 + int (bin0);
|
||||
while (bin0 < int(bin1)) {
|
||||
value += freq[x0 + int(bin0)];
|
||||
value += spectrum[int(bin0)];
|
||||
bin0 += 1.0;
|
||||
}
|
||||
// Do not reference past end of freq array.
|
||||
@ -1780,17 +1836,18 @@ static float sumFreqValues(
|
||||
bin1 -= 1.0;
|
||||
}
|
||||
|
||||
value += freq[x0 + int(bin1)] * (bin1 - int(bin1));
|
||||
value += spectrum[int(bin1)] * (bin1 - int(bin1));
|
||||
value /= binwidth;
|
||||
}
|
||||
#else
|
||||
half;
|
||||
// Maximum method, and no apportionment of any single bins over multiple pixel rows
|
||||
// See Bug971
|
||||
int bin = floor(0.5 + bin0);
|
||||
const int limitBin = floor(0.5 + bin1);
|
||||
value = freq[x0 + bin];
|
||||
int bin = std::min(half - 1, int(floor(0.5 + bin0)));
|
||||
const int limitBin = std::min(half, int(floor(0.5 + bin1)));
|
||||
value = spectrum[bin];
|
||||
while (++bin < limitBin)
|
||||
value = std::max(value, freq[x0 + bin]);
|
||||
value = std::max(value, spectrum[bin]);
|
||||
#endif
|
||||
if (!autocorrelation) {
|
||||
// Last step converts dB to a 0.0-1.0 range
|
||||
@ -1831,24 +1888,29 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
bool autocorrelation,
|
||||
bool logF)
|
||||
{
|
||||
#ifdef PROFILE_WAVEFORM
|
||||
Profiler profiler;
|
||||
#endif
|
||||
|
||||
const WaveTrack *const track = cache.GetTrack();
|
||||
|
||||
enum { MONOCHROME_LINE = 230, COLORED_LINE = 0 };
|
||||
enum { DASH_LENGTH = 10 /* pixels */ };
|
||||
|
||||
#if PROFILE_WAVEFORM
|
||||
# ifdef __WXMSW__
|
||||
__time64_t tv0, tv1;
|
||||
_time64(&tv0);
|
||||
# else
|
||||
struct timeval tv0, tv1;
|
||||
gettimeofday(&tv0, NULL);
|
||||
# endif
|
||||
#endif
|
||||
double h = viewInfo->h;
|
||||
double pps = viewInfo->zoom;
|
||||
double sel0 = viewInfo->selectedRegion.t0();
|
||||
double sel1 = viewInfo->selectedRegion.t1();
|
||||
const ClipParameters params(true, track, clip, r, viewInfo->selectedRegion, *viewInfo);
|
||||
const wxRect &mid = params.mid;
|
||||
// The "mid" rect contains the part of the display actually
|
||||
// containing the waveform. If it's empty, we're done.
|
||||
if (mid.width <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const double &t0 = params.t0;
|
||||
const double &pps = params.pps;
|
||||
const double &tstep = params.tstep;
|
||||
const double &ssel0 = params.ssel0;
|
||||
const double &ssel1 = params.ssel1;
|
||||
const double &rate = params.rate;
|
||||
|
||||
double freqLo = SelectedRegion::UndefinedFrequency;
|
||||
double freqHi = SelectedRegion::UndefinedFrequency;
|
||||
@ -1859,99 +1921,12 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
}
|
||||
#endif
|
||||
|
||||
double tOffset = clip->GetOffset();
|
||||
double rate = clip->GetRate();
|
||||
double sps = 1./rate;
|
||||
|
||||
int range = gPrefs->Read(wxT("/Spectrum/Range"), 80L);
|
||||
int gain = gPrefs->Read(wxT("/Spectrum/Gain"), 20L);
|
||||
|
||||
if (!track->GetSelected())
|
||||
sel0 = sel1 = 0.0;
|
||||
|
||||
double tpre = h - tOffset;
|
||||
double tstep = 1.0 / pps;
|
||||
double tpost = tpre + (r.width * tstep);
|
||||
double trackLen = clip->GetEndTime() - clip->GetStartTime();
|
||||
|
||||
bool showIndividualSamples = (pps / rate > 0.5); //zoomed in a lot
|
||||
double t0 = (tpre >= 0.0 ? tpre : 0.0);
|
||||
double t1 = (tpost < trackLen - sps*.99 ? tpost : trackLen - sps*.99);
|
||||
if(showIndividualSamples) t1+=2./pps; // for display consistency
|
||||
// with Waveform display
|
||||
|
||||
// Make sure t1 (the right bound) is greater than 0
|
||||
if (t1 < 0.0)
|
||||
t1 = 0.0;
|
||||
|
||||
// Make sure t1 is greater than t0
|
||||
if (t0 > t1)
|
||||
t0 = t1;
|
||||
|
||||
sampleCount ssel0 = wxMax(0, sampleCount((sel0 - tOffset) * rate + .99));
|
||||
sampleCount ssel1 = wxMax(0, sampleCount((sel1 - tOffset) * rate + .99));
|
||||
|
||||
//trim selection so that it only contains the actual samples
|
||||
if (ssel0 != ssel1 && ssel1 > (sampleCount)(0.5+trackLen*rate))
|
||||
ssel1 = (sampleCount)(0.5+trackLen*rate);
|
||||
|
||||
// The variable "mid" will be the rectangle containing the
|
||||
// actual waveform, as opposed to any blank area before
|
||||
// or after the track.
|
||||
wxRect mid = r;
|
||||
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
|
||||
// If the left edge of the track is to the right of the left
|
||||
// edge of the display, then there's some blank area to the
|
||||
// left of the track. Reduce the "mid"
|
||||
// rect by size of the blank area.
|
||||
if (tpre < 0) {
|
||||
// Fill in the area to the left of the track
|
||||
wxRect pre = r;
|
||||
if (t0 < tpost)
|
||||
pre.width = (int) ((t0 - tpre) * pps);
|
||||
|
||||
// Offset the rectangle containing the waveform by the width
|
||||
// of the area we just erased.
|
||||
mid.x += pre.width;
|
||||
mid.width -= pre.width;
|
||||
}
|
||||
|
||||
// If the right edge of the track is to the left of the the right
|
||||
// edge of the display, then there's some blank area to the right
|
||||
// of the track. Reduce the "mid" rect by the
|
||||
// size of the blank area.
|
||||
if (tpost > t1) {
|
||||
wxRect post = r;
|
||||
if (t1 > tpre)
|
||||
post.x += (int) ((t1 - tpre) * pps);
|
||||
post.width = r.width - (post.x - r.x);
|
||||
|
||||
// Reduce the rectangle containing the waveform by the width
|
||||
// of the area we just erased.
|
||||
mid.width -= post.width;
|
||||
}
|
||||
|
||||
// The "mid" rect contains the part of the display actually
|
||||
// containing the waveform. If it's empty, we're done.
|
||||
if (mid.width <= 0) {
|
||||
#if PROFILE_WAVEFORM
|
||||
# ifdef __WXMSW__
|
||||
_time64(&tv1);
|
||||
double elapsed = _difftime64(tv1, tv0);
|
||||
# else
|
||||
gettimeofday(&tv1, NULL);
|
||||
double elapsed =
|
||||
(tv1.tv_sec + tv1.tv_usec*0.000001) -
|
||||
(tv0.tv_sec + tv0.tv_usec*0.000001);
|
||||
# endif
|
||||
gWaveformTimeTotal += elapsed;
|
||||
gWaveformTimeCount++;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// We draw directly to a bit image in memory,
|
||||
// and then paint this directly to our offscreen
|
||||
// bitmap. Note that this could be optimized even
|
||||
@ -1967,6 +1942,12 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
|
||||
bool updated = clip->GetSpectrogram(cache, freq, where, mid.width,
|
||||
t0, pps, autocorrelation);
|
||||
|
||||
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
|
||||
int fftSkipPoints = gPrefs->Read(wxT("/Spectrum/FFTSkipPoints"), 0L);
|
||||
int fftSkipPoints1 = fftSkipPoints + 1;
|
||||
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
|
||||
|
||||
int ifreq = lrint(rate/2);
|
||||
|
||||
int maxFreq;
|
||||
@ -1990,67 +1971,21 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
minFreq = 1.0;
|
||||
}
|
||||
|
||||
bool usePxCache = false;
|
||||
|
||||
if( !updated && clip->mSpecPxCache->valid && (clip->mSpecPxCache->len == mid.height * mid.width)
|
||||
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
||||
&& mFftYGrid==fftYGridOld
|
||||
#endif //EXPERIMENTAL_FFT_Y_GRID
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
&& mFftFindNotes==fftFindNotesOld
|
||||
&& mFindNotesMinA==findNotesMinAOld
|
||||
&& mNumberOfMaxima==findNotesNOld
|
||||
&& mFindNotesQuantize==findNotesQuantizeOld
|
||||
#endif
|
||||
) {
|
||||
usePxCache = true;
|
||||
}
|
||||
else {
|
||||
delete clip->mSpecPxCache;
|
||||
clip->mSpecPxCache = new SpecPxCache(mid.width * mid.height);
|
||||
usePxCache = false;
|
||||
clip->mSpecPxCache->valid = true;
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
fftFindNotesOld=mFftFindNotes;
|
||||
findNotesMinAOld=mFindNotesMinA;
|
||||
findNotesNOld=mNumberOfMaxima;
|
||||
findNotesQuantizeOld=mFindNotesQuantize;
|
||||
#endif
|
||||
}
|
||||
|
||||
// PRL: Must the following two be integers?
|
||||
int minSamples = int((double)minFreq * (double)windowSize / rate + 0.5); // units are fft bins
|
||||
int maxSamples = int((double)maxFreq * (double)windowSize / rate + 0.5);
|
||||
float binPerPx = float(maxSamples - minSamples) / float(mid.height);
|
||||
float selBinLo = freqLo * (double)windowSize / rate;
|
||||
float selBinHi = freqHi * (double)windowSize / rate;
|
||||
float selBinCenter =
|
||||
((freqLo < 0 || freqHi < 0) ? -1 : sqrt(freqLo * freqHi))
|
||||
* (double)windowSize / rate;
|
||||
|
||||
int x = 0;
|
||||
sampleCount w1 = (sampleCount) ((t0*rate + x *rate *tstep) + .5);
|
||||
|
||||
const float
|
||||
// e=exp(1.0f),
|
||||
f = rate / 2.0f / half,
|
||||
lmin = logf(float(minFreq)),
|
||||
lmax = logf(float(maxFreq)),
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
log2=logf(2.0f),
|
||||
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
|
||||
lmins=logf(float(minFreq)/(mFftSkipPoints+1)),
|
||||
lmaxs=logf(float(maxFreq)/(mFftSkipPoints+1)),
|
||||
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
|
||||
lmins=lmin,
|
||||
lmaxs=lmax,
|
||||
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
|
||||
#endif //EXPERIMENTAL_FIND_NOTES
|
||||
scale=lmax-lmin /*,
|
||||
expo=exp(scale)*/ ;
|
||||
scale = lmax - lmin;
|
||||
|
||||
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
||||
const float
|
||||
log2 = logf(2.0f),
|
||||
scale2 = (lmax - lmin) / log2,
|
||||
lmin2 = lmin / log2;
|
||||
|
||||
@ -2070,6 +2005,42 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
}
|
||||
#endif //EXPERIMENTAL_FFT_Y_GRID
|
||||
|
||||
if (!updated && clip->mSpecPxCache->valid && (clip->mSpecPxCache->len == mid.height * mid.width)
|
||||
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
||||
&& mFftYGrid==fftYGridOld
|
||||
#endif //EXPERIMENTAL_FFT_Y_GRID
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
&& mFftFindNotes==fftFindNotesOld
|
||||
&& mFindNotesMinA==findNotesMinAOld
|
||||
&& mNumberOfMaxima==findNotesNOld
|
||||
&& mFindNotesQuantize==findNotesQuantizeOld
|
||||
#endif
|
||||
) {
|
||||
// cache is up to date
|
||||
}
|
||||
else {
|
||||
delete clip->mSpecPxCache;
|
||||
clip->mSpecPxCache = new SpecPxCache(mid.width * mid.height);
|
||||
clip->mSpecPxCache->valid = true;
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
fftFindNotesOld=mFftFindNotes;
|
||||
findNotesMinAOld=mFindNotesMinA;
|
||||
findNotesNOld=mNumberOfMaxima;
|
||||
findNotesQuantizeOld=mFindNotesQuantize;
|
||||
#endif
|
||||
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
const float
|
||||
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
|
||||
lmins = logf(float(minFreq) / (mFftSkipPoints + 1)),
|
||||
lmaxs = logf(float(maxFreq) / (mFftSkipPoints + 1)),
|
||||
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
|
||||
lmins = lmin,
|
||||
lmaxs = lmax,
|
||||
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
|
||||
;
|
||||
#endif //EXPERIMENTAL_FIND_NOTES
|
||||
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
int maxima[128];
|
||||
float maxima0[128], maxima1[128];
|
||||
@ -2088,58 +2059,18 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
int *indexes = new int[maxTableSize];
|
||||
#endif //EXPERIMENTAL_FIND_NOTES
|
||||
|
||||
while (x < mid.width)
|
||||
{
|
||||
sampleCount w0 = w1;
|
||||
w1 = (sampleCount) ((t0*rate + (x+1) *rate *tstep) + .5);
|
||||
|
||||
// TODO: The logF and non-logF case are very similar.
|
||||
// They should be merged and simplified.
|
||||
if (!logF)
|
||||
for (int x = 0; x < mid.width; ++x)
|
||||
{
|
||||
if (!logF) {
|
||||
for (int yy = 0; yy < mid.height; yy++) {
|
||||
float bin0 = float(yy) * binPerPx + minSamples;
|
||||
float bin1 = float(yy + 1) * binPerPx + minSamples;
|
||||
|
||||
// For spectral selection, determine what colour
|
||||
// set to use. We use a darker selection if
|
||||
// in both spectral range and time range.
|
||||
|
||||
AColor::ColorGradientChoice selected =
|
||||
AColor::ColorGradientUnselected;
|
||||
// If we are in the time selected range, then we may use a different color set.
|
||||
if (ssel0 <= w0 && w1 < ssel1)
|
||||
{
|
||||
bool isSpectral = ((track->GetDisplay() == WaveTrack::SpectralSelectionDisplay) ||
|
||||
(track->GetDisplay() == WaveTrack::SpectralSelectionLogDisplay));
|
||||
selected = ChooseColorSet( bin0, bin1, selBinLo, selBinCenter, selBinHi, x/DASH_LENGTH, isSpectral );
|
||||
}
|
||||
|
||||
|
||||
unsigned char rv, gv, bv;
|
||||
float value;
|
||||
|
||||
if(!usePxCache) {
|
||||
value = sumFreqValues(freq, x, half, bin0, bin1,
|
||||
autocorrelation, range, gain);
|
||||
const float value = findValue
|
||||
(freq + half * x, bin0, bin1, half, autocorrelation, gain, range);
|
||||
clip->mSpecPxCache->values[x * mid.height + yy] = value;
|
||||
}
|
||||
else
|
||||
value = clip->mSpecPxCache->values[x * mid.height + yy];
|
||||
|
||||
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv);
|
||||
|
||||
int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
|
||||
data[px++] = rv;
|
||||
data[px++] = gv;
|
||||
data[px] = bv;
|
||||
}
|
||||
}
|
||||
else //logF
|
||||
{
|
||||
unsigned char rv, gv, bv;
|
||||
float value;
|
||||
|
||||
else {
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
int maximas=0;
|
||||
if (!usePxCache && mFftFindNotes) {
|
||||
@ -2199,6 +2130,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
}
|
||||
}
|
||||
int it=0;
|
||||
int oldBin0=-1;
|
||||
bool inMaximum = false;
|
||||
#endif //EXPERIMENTAL_FIND_NOTES
|
||||
|
||||
@ -2218,18 +2150,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
if (yy3<0)
|
||||
yy3=0;
|
||||
float bin1 = float(yy3);
|
||||
|
||||
AColor::ColorGradientChoice selected =
|
||||
AColor::ColorGradientUnselected;
|
||||
// If we are in the time selected range, then we may use a different color set.
|
||||
if (ssel0 <= w0 && w1 < ssel1)
|
||||
{
|
||||
bool isSpectral = ((track->GetDisplay() == WaveTrack::SpectralSelectionDisplay) ||
|
||||
(track->GetDisplay() == WaveTrack::SpectralSelectionLogDisplay));
|
||||
selected = ChooseColorSet( bin0, bin1, selBinLo, selBinCenter, selBinHi, x/DASH_LENGTH, isSpectral );
|
||||
}
|
||||
|
||||
if(!usePxCache) {
|
||||
float value;
|
||||
|
||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||
if (mFftFindNotes) {
|
||||
@ -2258,14 +2179,101 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
value = minColor;
|
||||
} else
|
||||
#endif //EXPERIMENTAL_FIND_NOTES
|
||||
value=sumFreqValues(freq, x, half, bin0, bin1,
|
||||
autocorrelation, range, gain);
|
||||
clip->mSpecPxCache->values[x * mid.height + yy] = value;
|
||||
{
|
||||
value = findValue
|
||||
(freq + half * x, bin0, bin1, half, autocorrelation, gain, range);
|
||||
}
|
||||
else
|
||||
value = clip->mSpecPxCache->values[x * mid.height + yy];
|
||||
clip->mSpecPxCache->values[x * mid.height + yy] = value;
|
||||
yy2 = yy2_base;
|
||||
} // each y
|
||||
} // is logF
|
||||
} // each x
|
||||
|
||||
|
||||
} // updating cache
|
||||
|
||||
float selBinLo = freqLo * (double)windowSize / rate;
|
||||
float selBinHi = freqHi * (double)windowSize / rate;
|
||||
float selBinCenter =
|
||||
((freqLo < 0 || freqHi < 0) ? -1 : sqrt(freqLo * freqHi))
|
||||
* (double)windowSize / rate;
|
||||
|
||||
sampleCount w1 = sampleCount(0.5 + rate *
|
||||
t0
|
||||
);
|
||||
|
||||
for (int x = 0; x < mid.width; ++x)
|
||||
{
|
||||
sampleCount w0 = w1;
|
||||
w1 = sampleCount(0.5 + rate *
|
||||
(t0 + (x+1) * tstep)
|
||||
);
|
||||
|
||||
// TODO: The logF and non-logF case are very similar.
|
||||
// They should be merged and simplified.
|
||||
if (!logF)
|
||||
{
|
||||
for (int yy = 0; yy < mid.height; yy++) {
|
||||
float bin0 = float (yy) * binPerPx + minSamples;
|
||||
float bin1 = float (yy + 1) * binPerPx + minSamples;
|
||||
|
||||
// For spectral selection, determine what colour
|
||||
// set to use. We use a darker selection if
|
||||
// in both spectral range and time range.
|
||||
|
||||
AColor::ColorGradientChoice selected =
|
||||
AColor::ColorGradientUnselected;
|
||||
// If we are in the time selected range, then we may use a different color set.
|
||||
if (ssel0 <= w0 && w1 < ssel1)
|
||||
{
|
||||
bool isSpectral = ((track->GetDisplay() == WaveTrack::SpectralSelectionDisplay) ||
|
||||
(track->GetDisplay() == WaveTrack::SpectralSelectionLogDisplay));
|
||||
selected = ChooseColorSet( bin0, bin1, selBinLo, selBinCenter, selBinHi, x/DASH_LENGTH, isSpectral );
|
||||
}
|
||||
|
||||
unsigned char rv, gv, bv;
|
||||
const float value =
|
||||
clip->mSpecPxCache->values[x * mid.height + yy];
|
||||
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv);
|
||||
int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
|
||||
data[px++] = rv;
|
||||
data[px++] = gv;
|
||||
data[px] = bv;
|
||||
}
|
||||
}
|
||||
else //logF
|
||||
{
|
||||
double yy2_base=exp(lmin)/f;
|
||||
float yy2 = yy2_base;
|
||||
double exp_scale_per_height = exp(scale/mid.height);
|
||||
for (int yy = 0; yy < mid.height; yy++) {
|
||||
if (int(yy2)>=half)
|
||||
yy2=half-1;
|
||||
if (yy2<0)
|
||||
yy2=0;
|
||||
float bin0 = float(yy2);
|
||||
yy2_base *= exp_scale_per_height;
|
||||
float yy3 = yy2_base;
|
||||
if (int(yy3)>=half)
|
||||
yy3=half-1;
|
||||
if (yy3<0)
|
||||
yy3=0;
|
||||
float bin1 = float(yy3);
|
||||
|
||||
AColor::ColorGradientChoice selected =
|
||||
AColor::ColorGradientUnselected;
|
||||
// If we are in the time selected range, then we may use a different color set.
|
||||
if (ssel0 <= w0 && w1 < ssel1)
|
||||
{
|
||||
bool isSpectral = ((track->GetDisplay() == WaveTrack::SpectralSelectionDisplay) ||
|
||||
(track->GetDisplay() == WaveTrack::SpectralSelectionLogDisplay));
|
||||
selected = ChooseColorSet( bin0, bin1, selBinLo, selBinCenter, selBinHi, x/DASH_LENGTH, isSpectral );
|
||||
}
|
||||
|
||||
const float value = clip->mSpecPxCache->values[x * mid.height + yy];
|
||||
yy2 = yy2_base;
|
||||
|
||||
unsigned char rv, gv, bv;
|
||||
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv);
|
||||
|
||||
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
||||
@ -2280,10 +2288,9 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
||||
data[px++] = rv;
|
||||
data[px++] = gv;
|
||||
data[px] = bv;
|
||||
}
|
||||
}
|
||||
x++;
|
||||
}
|
||||
} // each y
|
||||
} // logF
|
||||
} // each x
|
||||
|
||||
// If we get to this point, the clip is actually visible on the
|
||||
// screen, so remember the display rectangle.
|
||||
|
@ -28,6 +28,7 @@ class wxRect;
|
||||
class wxHashTable;
|
||||
|
||||
class Track;
|
||||
class WaveDisplay;
|
||||
class WaveTrack;
|
||||
class WaveTrackCache;
|
||||
class WaveClip;
|
||||
@ -139,7 +140,7 @@ class AUDACITY_DLL_API TrackArtist {
|
||||
|
||||
void DrawClipWaveform(WaveTrack *track, WaveClip *clip,
|
||||
wxDC & dc, const wxRect & r, const ViewInfo *viewInfo,
|
||||
bool drawEnvelope, bool drawSamples, bool drawSliders,
|
||||
bool drawEnvelope, bool drawSamples,
|
||||
bool dB, bool muted);
|
||||
|
||||
void DrawClipSpectrum(WaveTrackCache &cache, WaveClip *clip,
|
||||
@ -156,13 +157,11 @@ class AUDACITY_DLL_API TrackArtist {
|
||||
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
||||
void DrawMinMaxRMS(wxDC & dc, const wxRect & r, const double env[],
|
||||
float zoomMin, float zoomMax, bool dB,
|
||||
const float min[], const float max[], const float rms[],
|
||||
const int bl[], bool showProgress, bool muted, const float gain);
|
||||
const WaveDisplay &display, bool showProgress, bool muted, const float gain);
|
||||
#else
|
||||
void DrawMinMaxRMS(wxDC & dc, const wxRect & r, const double env[],
|
||||
float zoomMin, float zoomMax, bool dB,
|
||||
const float min[], const float max[], const float rms[],
|
||||
const int bl[], bool showProgress, bool muted);
|
||||
const WaveDisplay &display, bool showProgress, bool muted);
|
||||
#endif
|
||||
void DrawIndividualSamples(wxDC & dc, const wxRect & r,
|
||||
float zoomMin, float zoomMax, bool dB,
|
||||
|
280
src/WaveClip.cpp
280
src/WaveClip.cpp
@ -29,6 +29,7 @@ drawing). Cache's the Spectrogram frequency samples.
|
||||
|
||||
#include <math.h>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <wx/log.h>
|
||||
|
||||
@ -41,14 +42,23 @@ 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)
|
||||
{
|
||||
dirty = -1;
|
||||
start = -1.0;
|
||||
pps = 0.0;
|
||||
len = cacheLen;
|
||||
min = len ? new float[len] : 0;
|
||||
max = len ? new float[len] : 0;
|
||||
rms = len ? new float[len] : 0;
|
||||
@ -70,7 +80,7 @@ public:
|
||||
}
|
||||
|
||||
int dirty;
|
||||
sampleCount len;
|
||||
const sampleCount len;
|
||||
double start;
|
||||
double pps;
|
||||
int rate;
|
||||
@ -121,7 +131,7 @@ public:
|
||||
invalEnd = len;
|
||||
|
||||
|
||||
mRegionsMutex.Lock();
|
||||
ODLocker locker(mRegionsMutex);
|
||||
|
||||
//look thru the region array for a place to insert. We could make this more spiffy than a linear search
|
||||
//but right now it is not needed since there will usually only be one region (which grows) for OD loading.
|
||||
@ -187,18 +197,12 @@ public:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mRegionsMutex.Unlock();
|
||||
}
|
||||
|
||||
//lock before calling these in a section. unlock after finished.
|
||||
int GetNumInvalidRegions(){return mRegions.size();}
|
||||
int GetInvalidRegionStart(int i){return mRegions[i]->start;}
|
||||
int GetInvalidRegionEnd(int i){return mRegions[i]->end;}
|
||||
|
||||
void LockInvalidRegions(){mRegionsMutex.Lock();}
|
||||
void UnlockInvalidRegions(){mRegionsMutex.Unlock();}
|
||||
int GetNumInvalidRegions() const {return mRegions.size();}
|
||||
int GetInvalidRegionStart(int i) const {return mRegions[i]->start;}
|
||||
int GetInvalidRegionEnd(int i) const {return mRegions[i]->end;}
|
||||
|
||||
void ClearInvalidRegions()
|
||||
{
|
||||
@ -209,6 +213,38 @@ public:
|
||||
mRegions.clear();
|
||||
}
|
||||
|
||||
void LoadInvalidRegion(int ii, Sequence *sequence, bool updateODCount)
|
||||
{
|
||||
const int invStart = GetInvalidRegionStart(ii);
|
||||
const int invEnd = GetInvalidRegionEnd(ii);
|
||||
|
||||
//before check number of ODPixels
|
||||
int regionODPixels = 0;
|
||||
if (updateODCount)
|
||||
regionODPixels = CountODPixels(bl, invStart, invEnd);
|
||||
|
||||
sequence->GetWaveDisplay(&min[invStart],
|
||||
&max[invStart],
|
||||
&rms[invStart],
|
||||
&bl[invStart],
|
||||
invEnd - invStart,
|
||||
&where[invStart]);
|
||||
|
||||
//after check number of ODPixels
|
||||
if (updateODCount)
|
||||
{
|
||||
const int regionODPixelsAfter = CountODPixels(bl, invStart, invEnd);
|
||||
numODPixels -= (regionODPixels - regionODPixelsAfter);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadInvalidRegions(Sequence *sequence, bool updateODCount)
|
||||
{
|
||||
//invalid regions are kept in a sorted array.
|
||||
for (int i = 0; i < GetNumInvalidRegions(); i++)
|
||||
LoadInvalidRegion(i, sequence, updateODCount);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
std::vector<InvalidRegion*> mRegions;
|
||||
@ -444,20 +480,72 @@ bool WaveClip::AfterClip(double t) const
|
||||
///Delete the wave cache - force redraw. Thread-safe
|
||||
void WaveClip::DeleteWaveCache()
|
||||
{
|
||||
mWaveCacheMutex.Lock();
|
||||
ODLocker locker(mWaveCacheMutex);
|
||||
if(mWaveCache!=NULL)
|
||||
delete mWaveCache;
|
||||
mWaveCache = new WaveCache(0);
|
||||
mWaveCacheMutex.Unlock();
|
||||
}
|
||||
|
||||
///Adds an invalid region to the wavecache so it redraws that portion only.
|
||||
void WaveClip::AddInvalidRegion(long startSample, long endSample)
|
||||
{
|
||||
mWaveCacheMutex.Lock();
|
||||
ODLocker locker(mWaveCacheMutex);
|
||||
if(mWaveCache!=NULL)
|
||||
mWaveCache->AddInvalidRegion(startSample,endSample);
|
||||
mWaveCacheMutex.Unlock();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline
|
||||
void findCorrection(const sampleCount oldWhere[], int oldLen, int newLen,
|
||||
double t0, double rate, double samplesPerPixel,
|
||||
double &oldWhere0, double &denom, int &oldX0, int &oldXLast, 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 oldWhereLast = oldWhere0 + oldLen * samplesPerPixel;
|
||||
// Find the length in samples of the old cache.
|
||||
denom = oldWhereLast - oldWhere0;
|
||||
|
||||
// Skip unless denom rounds off to at least 1.
|
||||
if (denom >= 0.5)
|
||||
{
|
||||
// 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 correction is needed to align the new cache with the old?
|
||||
correction = where0 - guessWhere0;
|
||||
wxASSERT(-samplesPerPixel <= correction && correction <= 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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
fillWhere(sampleCount where[], int len, double bias, double correction,
|
||||
double t0, double rate, double samplesPerPixel)
|
||||
{
|
||||
// Be careful to make the first value non-negative
|
||||
correction += 0.5 + bias;
|
||||
where[0] = sampleCount(std::max(0.0, floor(correction + t0 * rate)));
|
||||
for (sampleCount x = 1; x < len + 1; x++)
|
||||
where[x] = sampleCount(
|
||||
floor(correction + t0 * rate + double(x) * samplesPerPixel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
@ -465,12 +553,17 @@ void WaveClip::AddInvalidRegion(long startSample, long endSample)
|
||||
// clipping calculations
|
||||
//
|
||||
|
||||
bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
||||
sampleCount *where,
|
||||
int numPixels, double t0,
|
||||
bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
|
||||
double pixelsPerSecond, bool &isLoadingOD)
|
||||
{
|
||||
mWaveCacheMutex.Lock();
|
||||
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 =
|
||||
@ -482,40 +575,7 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
||||
mWaveCache->start == t0 &&
|
||||
mWaveCache->len >= numPixels) {
|
||||
|
||||
//check for invalid regions, and make the bottom if an else if.
|
||||
//invalid regions are kept in a sorted array.
|
||||
for(int i=0;i<mWaveCache->GetNumInvalidRegions();i++)
|
||||
{
|
||||
int invStart;
|
||||
invStart = mWaveCache->GetInvalidRegionStart(i);
|
||||
int invEnd;
|
||||
invEnd = mWaveCache->GetInvalidRegionEnd(i);
|
||||
|
||||
int regionODPixels;
|
||||
regionODPixels =0;
|
||||
int regionODPixelsAfter;
|
||||
regionODPixelsAfter =0;
|
||||
//before check number of ODPixels
|
||||
for(int j=invStart;j<invEnd;j++)
|
||||
{
|
||||
if(mWaveCache->bl[j]<0)
|
||||
regionODPixels++;
|
||||
}
|
||||
mSequence->GetWaveDisplay(&mWaveCache->min[invStart],
|
||||
&mWaveCache->max[invStart],
|
||||
&mWaveCache->rms[invStart],
|
||||
&mWaveCache->bl[invStart],
|
||||
invEnd-invStart,
|
||||
&mWaveCache->where[invStart]);
|
||||
//after check number of ODPixels
|
||||
for(int j=invStart;j<invEnd;j++)
|
||||
{
|
||||
if(mWaveCache->bl[j]<0)
|
||||
regionODPixelsAfter++;
|
||||
}
|
||||
//decrement the number of od pixels.
|
||||
mWaveCache->numODPixels -= (regionODPixels - regionODPixelsAfter);
|
||||
}
|
||||
mWaveCache->LoadInvalidRegions(mSequence, true);
|
||||
mWaveCache->ClearInvalidRegions();
|
||||
|
||||
|
||||
@ -525,7 +585,6 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
||||
memcpy(bl, mWaveCache->bl, numPixels*sizeof(int));
|
||||
memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount));
|
||||
isLoadingOD = mWaveCache->numODPixels>0;
|
||||
mWaveCacheMutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -541,47 +600,16 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
||||
double oldWhere0 = 0;
|
||||
double denom = 0;
|
||||
int oldX0 = 0, oldXLast = 0;
|
||||
double error = 0.0;
|
||||
double correction = 0.0;
|
||||
if (match &&
|
||||
oldCache->len > 0) {
|
||||
// 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 = oldCache->where[1] - samplesPerPixel;
|
||||
const double oldWhereLast = oldWhere0 + oldCache->len * samplesPerPixel;
|
||||
// Find the length in samples of the old cache.
|
||||
denom = oldWhereLast - oldWhere0;
|
||||
|
||||
// Skip unless denom rounds off to at least 1.
|
||||
if (denom >= 0.5)
|
||||
{
|
||||
// What sample would go in where[0] with no correction?
|
||||
const double guessWhere0 = t0 * mRate;
|
||||
// What integer position in the old cache array does that map to?
|
||||
// (even if it is out of bounds)
|
||||
oldX0 = floor(0.5 + oldCache->len * (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?
|
||||
error = where0 - guessWhere0;
|
||||
wxASSERT(-samplesPerPixel <= error && error <= samplesPerPixel);
|
||||
// What integer position in the old cache array does our last column
|
||||
// map to? (even if out of bounds)
|
||||
oldXLast = floor(0.5 + oldCache->len * (
|
||||
(where0 + double(mWaveCache->len) * samplesPerPixel - oldWhere0)
|
||||
/ denom
|
||||
));
|
||||
}
|
||||
findCorrection(oldCache->where, oldCache->len, mWaveCache->len,
|
||||
t0, mRate, samplesPerPixel,
|
||||
oldWhere0, denom, oldX0, oldXLast, correction);
|
||||
}
|
||||
|
||||
// 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)
|
||||
);
|
||||
fillWhere(mWaveCache->where, mWaveCache->len, 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];
|
||||
@ -603,24 +631,10 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
||||
p0 = mWaveCache->len;
|
||||
p1 = 0;
|
||||
|
||||
//check for invalid regions, and make the bottom if an else if.
|
||||
//invalid regions are keep in a sorted array.
|
||||
//TODO:integrate into below for loop so that we only load inval regions if
|
||||
//TODO: only load inval regions if
|
||||
//necessary. (usually is the case, so no rush.)
|
||||
//also, we should be updating the NEW cache, but here we are patching the old one up.
|
||||
for(int i=0;i<oldCache->GetNumInvalidRegions();i++)
|
||||
{
|
||||
int invStart;
|
||||
invStart = oldCache->GetInvalidRegionStart(i);
|
||||
int invEnd;
|
||||
invEnd = oldCache->GetInvalidRegionEnd(i);
|
||||
mSequence->GetWaveDisplay(&oldCache->min[invStart],
|
||||
&oldCache->max[invStart],
|
||||
&oldCache->rms[invStart],
|
||||
&oldCache->bl[invStart],
|
||||
invEnd-invStart,
|
||||
&oldCache->where[invStart]);
|
||||
}
|
||||
oldCache->LoadInvalidRegions(mSequence, false);
|
||||
oldCache->ClearInvalidRegions();
|
||||
|
||||
for (sampleCount x = 0; x < mWaveCache->len; x++)
|
||||
@ -733,7 +747,6 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
||||
&mWaveCache->where[p0]))
|
||||
{
|
||||
isLoadingOD=false;
|
||||
mWaveCacheMutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -755,7 +768,6 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
||||
mWaveCache->numODPixels++;
|
||||
|
||||
isLoadingOD = mWaveCache->numODPixels>0;
|
||||
mWaveCacheMutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -897,6 +909,7 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
|
||||
mSpecCache->start = t0;
|
||||
|
||||
bool *recalc = new bool[mSpecCache->len + 1];
|
||||
std::fill(&recalc[0], &recalc[mSpecCache->len + 1], true);
|
||||
|
||||
const double tstep = 1.0 / pixelsPerSecond;
|
||||
const double samplesPerPixel = mRate * tstep;
|
||||
@ -906,52 +919,17 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
|
||||
double oldWhere0 = 0;
|
||||
double denom = 0;
|
||||
int oldX0 = 0, oldXLast = 0;
|
||||
double error = 0.0;
|
||||
double correction = 0.0;
|
||||
|
||||
if (match &&
|
||||
oldCache->len > 0) {
|
||||
// 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 = oldCache->where[1] - samplesPerPixel;
|
||||
const double oldWhereLast = oldWhere0 + oldCache->len * samplesPerPixel;
|
||||
// Find the length in samples of the old cache.
|
||||
denom = oldWhereLast - oldWhere0;
|
||||
|
||||
// Skip unless denom rounds off to at least 1.
|
||||
if (denom >= 0.5)
|
||||
{
|
||||
// What sample would go in where[0] with no correction?
|
||||
const double guessWhere0 = t0 * mRate;
|
||||
// What integer position in the old cache array does that map to?
|
||||
// (even if it is out of bounds)
|
||||
oldX0 = floor(0.5 + oldCache->len * (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?
|
||||
error = where0 - guessWhere0;
|
||||
wxASSERT(-samplesPerPixel <= error && error <= samplesPerPixel);
|
||||
// What integer position in the old cache array does our last column
|
||||
// map to? (even if out of bounds)
|
||||
oldXLast = floor(0.5 + oldCache->len * (
|
||||
(where0 + double(mWaveCache->len) * samplesPerPixel - oldWhere0)
|
||||
/ denom
|
||||
));
|
||||
}
|
||||
findCorrection(oldCache->where, oldCache->len, mSpecCache->len,
|
||||
t0, mRate, samplesPerPixel,
|
||||
oldWhere0, denom, oldX0, oldXLast, correction);
|
||||
}
|
||||
|
||||
// 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(1.0 + error + t0 * mRate + double(x) * samplesPerPixel));
|
||||
}
|
||||
fillWhere(mSpecCache->where, mSpecCache->len, 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
|
||||
|
@ -58,6 +58,51 @@ class WaveClip;
|
||||
WX_DECLARE_USER_EXPORTED_LIST(WaveClip, WaveClipList, AUDACITY_DLL_API);
|
||||
WX_DEFINE_USER_EXPORTED_ARRAY_PTR(WaveClip*, WaveClipArray, class AUDACITY_DLL_API);
|
||||
|
||||
struct WaveDisplay
|
||||
{
|
||||
int width;
|
||||
sampleCount *where;
|
||||
float *min, *max, *rms;
|
||||
int* bl;
|
||||
|
||||
WaveDisplay()
|
||||
: width(0), where(0), min(0), max(0), rms(0), bl(0)
|
||||
{
|
||||
}
|
||||
|
||||
~WaveDisplay()
|
||||
{
|
||||
Deallocate();
|
||||
}
|
||||
|
||||
void WaveDisplay::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 WaveDisplay::Deallocate()
|
||||
{
|
||||
delete[] where;
|
||||
where = 0;
|
||||
delete[] min;
|
||||
min = 0;
|
||||
delete[] max;
|
||||
max = 0;
|
||||
delete[] rms;
|
||||
rms = 0;
|
||||
delete[] bl;
|
||||
bl = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class AUDACITY_DLL_API WaveClip : public XMLTagHandler
|
||||
{
|
||||
private:
|
||||
@ -139,8 +184,8 @@ public:
|
||||
|
||||
/** Getting high-level data from the for screen display and clipping
|
||||
* calculations and Contrast */
|
||||
bool GetWaveDisplay(float *min, float *max, float *rms,int* bl, sampleCount *where,
|
||||
int numPixels, double t0, double pixelsPerSecond, bool &isLoadingOD);
|
||||
bool GetWaveDisplay(WaveDisplay &display,
|
||||
double t0, double pixelsPerSecond, bool &isLoadingOD);
|
||||
bool GetSpectrogram(WaveTrackCache &cache,
|
||||
float *buffer, sampleCount *where,
|
||||
int numPixels,
|
||||
|
@ -166,5 +166,26 @@ protected:
|
||||
|
||||
#endif // __WXMAC__
|
||||
|
||||
// Like wxMutexLocker
|
||||
// So you can use the RAII idiom with ODLock, on whatever platform
|
||||
class ODLocker
|
||||
{
|
||||
public:
|
||||
ODLocker(ODLock &lock)
|
||||
: mLock(lock)
|
||||
{
|
||||
mLock.Lock();
|
||||
}
|
||||
|
||||
~ODLocker()
|
||||
{
|
||||
mLock.Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
ODLock &mLock;
|
||||
};
|
||||
|
||||
#endif //__AUDACITY_ODTASKTHREAD__
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user