From 3752f3b4be736831e4a105acfab0a7b98430e8c8 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 13:35:34 -0400 Subject: [PATCH 1/9] Simplified optional profiling code in TrackArtist --- src/TrackArtist.cpp | 102 ++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 60 deletions(-) diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index 6031181c6..cb13aeec5 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -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 @@ -1445,14 +1481,8 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track, bool dB, bool muted) { -#if PROFILE_WAVEFORM -# ifdef __WXMSW__ - __time64_t tv0, tv1; - _time64(&tv0); -# else - struct timeval tv0, tv1; - gettimeofday(&tv0, NULL); -# endif +#ifdef PROFILE_WAVEFORM + Profiler profiler; #endif double h = viewInfo->h; //The horizontal position in seconds double pps = viewInfo->zoom; //points-per-second--the zoom level @@ -1547,20 +1577,6 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track, // 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; } @@ -1660,22 +1676,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 } @@ -1831,20 +1831,15 @@ 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(); @@ -1936,19 +1931,6 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, // 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; } From 8ef354aba0c758828a894e760a8047fc0e2c36b1 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 12:48:37 -0400 Subject: [PATCH 2/9] Abstracted common code out of waveform and spectrum drawing routines --- src/TrackArtist.cpp | 339 ++++++++++++++++++++++---------------------- 1 file changed, 171 insertions(+), 168 deletions(-) diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index cb13aeec5..ed63133d6 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -1470,6 +1470,143 @@ void TrackArtist::DrawWaveform(WaveTrack *track, } } + +namespace { +struct ClipParameters +{ + // 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() && + (spectrum || !track->IsSyncLockSelected())) { // PRL: why was there a difference for spectrum? + sel0 = sel1 = 0.0; + } + + 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 + 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 + 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 + 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 + t1 += 2. / pps; + } + + // 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; + } + + // Use the WaveTrack method to show what is selected and 'should' be copied, pasted etc. + 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)) { + 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. + 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; + } + + // 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; + } + } + + 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, @@ -1484,102 +1621,32 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track, #ifdef PROFILE_WAVEFORM Profiler profiler; #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 - - //If the track isn't selected, make the selection empty - if (!track->GetSelected() && !track->IsSyncLockSelected()) { - sel0 = sel1 = 0.0; - } - - //Some bookkeeping time variables: - double tstep = 1.0 / pps; // Seconds per point - double tpre = h - tOffset; // offset corrected time of - // left edge of display - double tpost = tpre + (r.width * tstep); // offset corrected time of - // right edge of display - - // 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 - - // 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); - if (showIndividualSamples) { - // adjustment so that the last circular point doesn't appear - // to be hanging off the end - t1 += 2. / pps; - } - - // 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; - } - - // 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)); - - //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" - if (tpre < 0) { - double delta = r.width; - if (t0 < tpost) { - delta = (int) ((t0 - tpre) * pps); - } - mid.x += (int)delta; - mid.width -= (int)delta; - } - - // 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); - mid.width -= post.width; - } + 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) { - return; + 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); @@ -1840,10 +1907,20 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, enum { MONOCHROME_LINE = 230, COLORED_LINE = 0 }; enum { DASH_LENGTH = 10 /* pixels */ }; - 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; @@ -1854,86 +1931,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) { - 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 From ee600ce30b468dfb92d39710053305c11fb92309 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 20:57:11 -0400 Subject: [PATCH 3/9] Functions for common parts of waveform and spectrogram display update --- src/WaveClip.cpp | 145 ++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 78 deletions(-) diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 48f2f5e16..f8b65302e 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -460,6 +460,60 @@ void WaveClip::AddInvalidRegion(long startSample, long 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) + ); +} + +} + // // Getting high-level data from the track for screen display and // clipping calculations @@ -541,47 +595,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]; @@ -897,6 +920,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 +930,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 From ecb97e9c58b657fe065844434a8729de9d1c792f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 16:27:42 -0400 Subject: [PATCH 4/9] Split apart the loops that update the spectrogram cache and that use it --- src/TrackArtist.cpp | 457 +++++++++++++++++++++++--------------------- 1 file changed, 244 insertions(+), 213 deletions(-) diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index ed63133d6..eb7f9f96c 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -1822,24 +1822,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. @@ -1847,17 +1845,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]; + 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 @@ -1952,6 +1951,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; @@ -1975,9 +1980,41 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, minFreq = 1.0; } - bool usePxCache = false; + // 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); - if( !updated && clip->mSpecPxCache->valid && (clip->mSpecPxCache->len == mid.height * mid.width) + const float + // e=exp(1.0f), + f = rate / 2.0f / half, + lmin = logf(float(minFreq)), + lmax = logf(float(maxFreq)), + scale = lmax - lmin; + +#ifdef EXPERIMENTAL_FFT_Y_GRID + const float + log2 = logf(2.0f), + scale2 = (lmax - lmin) / log2, + lmin2 = lmin / log2; + + bool *yGrid; + yGrid = new bool[mid.height]; + for (int y = 0; y < mid.height; y++) { + float n = (float(y) / mid.height*scale2 - lmin2) * 12; + float n2 = (float(y + 1) / mid.height*scale2 - lmin2) * 12; + float f = float(minFreq) / (mFftSkipPoints + 1)*powf(2.0f, n / 12.0f + lmin2); + float f2 = float(minFreq) / (mFftSkipPoints + 1)*powf(2.0f, n2 / 12.0f + lmin2); + n = logf(f / 440) / log2 * 12; + n2 = logf(f2 / 440) / log2 * 12; + if (floor(n) < floor(n2)) + yGrid[y] = true; + else + yGrid[y] = false; + } +#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 @@ -1988,12 +2025,11 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, && mFindNotesQuantize==findNotesQuantizeOld #endif ) { - usePxCache = true; + // cache is up to date } else { delete clip->mSpecPxCache; clip->mSpecPxCache = new SpecPxCache(mid.width * mid.height); - usePxCache = false; clip->mSpecPxCache->valid = true; #ifdef EXPERIMENTAL_FIND_NOTES fftFindNotesOld=mFftFindNotes; @@ -2001,220 +2037,129 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, 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)*/ ; - -#ifdef EXPERIMENTAL_FFT_Y_GRID - const float - scale2=(lmax-lmin)/log2, - lmin2=lmin/log2; - - bool *yGrid; - yGrid=new bool[mid.height]; - for (int y = 0; y < mid.height; y++) { - float n =(float(y )/mid.height*scale2-lmin2)*12; - float n2=(float(y+1)/mid.height*scale2-lmin2)*12; - float f =float(minFreq)/(mFftSkipPoints+1)*powf(2.0f, n /12.0f+lmin2); - float f2=float(minFreq)/(mFftSkipPoints+1)*powf(2.0f, n2/12.0f+lmin2); - n =logf(f /440)/log2*12; - n2=logf(f2/440)/log2*12; - if (floor(n) < floor(n2)) - yGrid[y]=true; - else - yGrid[y]=false; - } -#endif //EXPERIMENTAL_FFT_Y_GRID #ifdef EXPERIMENTAL_FIND_NOTES - int maxima[128]; - float maxima0[128], maxima1[128]; - const float + const float #ifdef EXPERIMENTAL_FFT_SKIP_POINTS - f2bin = half/(rate/2.0f/(mFftSkipPoints+1)), + lmins = logf(float(minFreq) / (mFftSkipPoints + 1)), + lmaxs = logf(float(maxFreq) / (mFftSkipPoints + 1)), #else //!EXPERIMENTAL_FFT_SKIP_POINTS - f2bin = half/(rate/2.0f), + lmins = lmin, + lmaxs = lmax, #endif //EXPERIMENTAL_FFT_SKIP_POINTS - bin2f = 1.0f/f2bin, - minDistance = powf(2.0f, 2.0f/12.0f), - i0=expf(lmin)/f, - i1=expf(scale+lmin)/f, - minColor=0.0f; - const int maxTableSize=1024; - 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); +#ifdef EXPERIMENTAL_FIND_NOTES + int maxima[128]; + float maxima0[128], maxima1[128]; + const float +#ifdef EXPERIMENTAL_FFT_SKIP_POINTS + f2bin = half / (rate / 2.0f / (mFftSkipPoints + 1)), +#else //!EXPERIMENTAL_FFT_SKIP_POINTS + f2bin = half / (rate / 2.0f), +#endif //EXPERIMENTAL_FFT_SKIP_POINTS + bin2f = 1.0f / f2bin, + minDistance = powf(2.0f, 2.0f / 12.0f), + i0 = expf(lmin) / f, + i1 = expf(scale + lmin) / f, + minColor = 0.0f; + const int maxTableSize = 1024; + int *indexes = new int[maxTableSize]; +#endif //EXPERIMENTAL_FIND_NOTES - // 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) { - 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); + if (!logF) { + for (int yy = 0; yy < mid.height; yy++) { + float bin0 = float(yy) * binPerPx + minSamples; + float bin1 = float(yy + 1) * binPerPx + minSamples; + 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]; + } + else { +#ifdef EXPERIMENTAL_FIND_NOTES + int maximas=0; + if (!usePxCache && mFftFindNotes) { + for (int i = maxTableSize-1; i >= 0; i--) + indexes[i]=-1; - GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv); + // Build a table of (most) values, put the index in it. + for (int i = int(i0); i < int(i1); i++) { + float freqi=freq[x0+int(i)]; + int value=int((freqi+gain+range)/range*(maxTableSize-1)); + if (value < 0) + value=0; + if (value >= maxTableSize) + value=maxTableSize-1; + indexes[value]=i; + } + // Build from the indices an array of maxima. + for (int i = maxTableSize-1; i >= 0; i--) { + int index=indexes[i]; + if (index >= 0) { + float freqi=freq[x0+index]; + if (freqi < mFindNotesMinA) + break; - int px = ((mid.height - 1 - yy) * mid.width + x) * 3; - data[px++] = rv; - data[px++] = gv; - data[px] = bv; + bool ok=true; + for (int m=0; m < maximas; m++) { + // Avoid to store very close maxima. + float maxm = maxima[m]; + if (maxm/index < minDistance && index/maxm < minDistance) { + ok=false; + break; + } + } + if (ok) { + maxima[maximas++] = index; + if (maximas >= mNumberOfMaxima) + break; + } } } - else //logF - { - unsigned char rv, gv, bv; - float value; - -#ifdef EXPERIMENTAL_FIND_NOTES - int maximas=0; - if (!usePxCache && mFftFindNotes) { - for (int i = maxTableSize-1; i >= 0; i--) - indexes[i]=-1; - - // Build a table of (most) values, put the index in it. - for (int i = int(i0); i < int(i1); i++) { - float freqi=freq[x0+int(i)]; - int value=int((freqi+gain+range)/range*(maxTableSize-1)); - if (value < 0) - value=0; - if (value >= maxTableSize) - value=maxTableSize-1; - indexes[value]=i; - } - // Build from the indices an array of maxima. - for (int i = maxTableSize-1; i >= 0; i--) { - int index=indexes[i]; - if (index >= 0) { - float freqi=freq[x0+index]; - if (freqi < mFindNotesMinA) - break; - - bool ok=true; - for (int m=0; m < maximas; m++) { - // Avoid to store very close maxima. - float maxm = maxima[m]; - if (maxm/index < minDistance && index/maxm < minDistance) { - ok=false; - break; - } - } - if (ok) { - maxima[maximas++] = index; - if (maximas >= mNumberOfMaxima) - break; - } - } - } // The f2pix helper macro converts a frequency into a pixel coordinate. #define f2pix(f) (logf(f)-lmins)/(lmaxs-lmins)*mid.height - // Possibly quantize the maxima frequencies and create the pixel block limits. - for (int i=0; i < maximas; i++) { - int index=maxima[i]; - float f = float(index)*bin2f; - if (mFindNotesQuantize) - { f = expf(int(log(f/440)/log2*12-0.5)/12.0f*log2)*440; + // Possibly quantize the maxima frequencies and create the pixel block limits. + for (int i=0; i < maximas; i++) { + int index=maxima[i]; + float f = float(index)*bin2f; + if (mFindNotesQuantize) + { f = expf(int(log(f/440)/log2*12-0.5)/12.0f*log2)*440; maxima[i] = f*f2bin; + } + float f0 = expf((log(f/440)/log2*24-1)/24.0f*log2)*440; + maxima0[i] = f2pix(f0); + float f1 = expf((log(f/440)/log2*24+1)/24.0f*log2)*440; + maxima1[i] = f2pix(f1); } - float f0 = expf((log(f/440)/log2*24-1)/24.0f*log2)*440; - maxima0[i] = f2pix(f0); - float f1 = expf((log(f/440)/log2*24+1)/24.0f*log2)*440; - maxima1[i] = f2pix(f1); } - } - int it=0; - bool inMaximum = false; + int it=0; + int oldBin0=-1; + bool inMaximum = false; #endif //EXPERIMENTAL_FIND_NOTES - 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 ); - } - - if(!usePxCache) { + 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); + float value; #ifdef EXPERIMENTAL_FIND_NOTES if (mFftFindNotes) { @@ -2243,14 +2188,101 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, value = minColor; } else #endif //EXPERIMENTAL_FIND_NOTES - value=sumFreqValues(freq, x, half, bin0, bin1, - autocorrelation, range, gain); + { + value = findValue + (freq + half * x, bin0, bin1, half, autocorrelation, gain, range); + } 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 ); } - else - value = clip->mSpecPxCache->values[x * mid.height + yy]; + + 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 @@ -2265,10 +2297,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. From 34b09053b4c777a477555e1b7baebf489d40c50f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 18:51:39 -0400 Subject: [PATCH 5/9] Simplified some management of WaveClip caches --- src/WaveClip.cpp | 124 ++++++++++++++++-------------------- src/ondemand/ODTaskThread.h | 21 ++++++ 2 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index f8b65302e..ca623f0b0 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -29,6 +29,7 @@ drawing). Cache's the Spectrogram frequency samples. #include #include +#include #include #include @@ -41,14 +42,23 @@ drawing). Cache's the Spectrogram frequency samples. #include 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(), 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 mRegions; @@ -444,20 +480,18 @@ 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 { @@ -524,7 +558,7 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, int numPixels, double t0, double pixelsPerSecond, bool &isLoadingOD) { - mWaveCacheMutex.Lock(); + ODLocker locker(mWaveCacheMutex); const bool match = @@ -536,40 +570,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;iGetNumInvalidRegions();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;jbl[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;jbl[j]<0) - regionODPixelsAfter++; - } - //decrement the number of od pixels. - mWaveCache->numODPixels -= (regionODPixels - regionODPixelsAfter); - } + mWaveCache->LoadInvalidRegions(mSequence, true); mWaveCache->ClearInvalidRegions(); @@ -579,7 +580,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; } @@ -626,24 +626,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;iGetNumInvalidRegions();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++) @@ -756,7 +742,6 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, &mWaveCache->where[p0])) { isLoadingOD=false; - mWaveCacheMutex.Unlock(); return false; } } @@ -778,7 +763,6 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, mWaveCache->numODPixels++; isLoadingOD = mWaveCache->numODPixels>0; - mWaveCacheMutex.Unlock(); return true; } diff --git a/src/ondemand/ODTaskThread.h b/src/ondemand/ODTaskThread.h index 4ca533162..aecdefa92 100644 --- a/src/ondemand/ODTaskThread.h +++ b/src/ondemand/ODTaskThread.h @@ -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__ From 29fb5a2ffef981ba3db11a25ff360507e363981c Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 19:50:28 -0400 Subject: [PATCH 6/9] Simplified passing of min/min/rmx/bl/where values for drawing waveforms --- src/TrackArtist.cpp | 38 ++++++++++++----------------------- src/TrackArtist.h | 7 +++---- src/WaveClip.cpp | 11 +++++++--- src/WaveClip.h | 49 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 71 insertions(+), 34 deletions(-) diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index eb7f9f96c..a2ab1ffc4 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -1106,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::max(); @@ -1656,26 +1659,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; } @@ -1690,7 +1683,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) { @@ -1699,7 +1692,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 { @@ -1714,11 +1707,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. :) diff --git a/src/TrackArtist.h b/src/TrackArtist.h index 467c775cb..032c5e26f 100644 --- a/src/TrackArtist.h +++ b/src/TrackArtist.h @@ -28,6 +28,7 @@ class wxRect; class wxHashTable; class Track; +class WaveDisplay; class WaveTrack; class WaveTrackCache; class WaveClip; @@ -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, diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index ca623f0b0..a46f91766 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -553,11 +553,16 @@ fillWhere(sampleCount where[], int len, double bias, double correction, // 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) { + 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); diff --git a/src/WaveClip.h b/src/WaveClip.h index 1d2d581ab..bdaefa08b 100644 --- a/src/WaveClip.h +++ b/src/WaveClip.h @@ -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, From 8aea6638824ce2039aff3d40c21691edcef0c337 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 18:45:08 -0400 Subject: [PATCH 7/9] Lifted code that draws multi-tool sliders -- it's per track, not clip --- src/TrackArtist.cpp | 13 ++++++------- src/TrackArtist.h | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index a2ab1ffc4..a119dc79e 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -1448,7 +1448,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 @@ -1471,6 +1471,11 @@ 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 + } } @@ -1617,7 +1622,6 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track, const ViewInfo *viewInfo, bool drawEnvelope, bool drawSamples, - bool drawSliders, bool dB, bool muted) { @@ -1714,11 +1718,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) { diff --git a/src/TrackArtist.h b/src/TrackArtist.h index 032c5e26f..b833f2768 100644 --- a/src/TrackArtist.h +++ b/src/TrackArtist.h @@ -140,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, From d74e99502426f6ef97f08c210b5d8e15ca193c31 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 18:35:39 -0400 Subject: [PATCH 8/9] After drawing stripes, restore the pen --- src/TrackArtist.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index a119dc79e..55973da27 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -1234,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; From 5f71e334dc7b7e7f60229ed5c69a950a2ca52b95 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Jun 2015 16:53:39 -0400 Subject: [PATCH 9/9] Bug999: top bin of spectrogram display --- src/TrackArtist.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index 55973da27..b72181c7d 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -1843,8 +1843,8 @@ static inline float findValue 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); + 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, spectrum[bin]);