mirror of
https://github.com/cookiengineer/audacity
synced 2025-09-09 08:53:23 +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
|
#endif
|
||||||
double gWaveformTimeTotal = 0;
|
double gWaveformTimeTotal = 0;
|
||||||
int gWaveformTimeCount = 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
|
#endif
|
||||||
|
|
||||||
#ifdef USE_MIDI
|
#ifdef USE_MIDI
|
||||||
@ -1070,15 +1106,18 @@ void TrackArtist::DrawWaveformBackground(wxDC &dc, const wxRect &r, const double
|
|||||||
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
||||||
void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect &r, const double env[],
|
void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect &r, const double env[],
|
||||||
float zoomMin, float zoomMax, bool dB,
|
float zoomMin, float zoomMax, bool dB,
|
||||||
const float min[], const float max[], const float rms[],
|
const WaveDisplay &display, bool showProgress, bool muted, const float gain)
|
||||||
const int bl[], bool showProgress, bool muted, const float gain)
|
|
||||||
#else
|
#else
|
||||||
void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect &r, const double env[],
|
void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect &r, const double env[],
|
||||||
float zoomMin, float zoomMax, bool dB,
|
float zoomMin, float zoomMax, bool dB,
|
||||||
const float min[], const float max[], const float rms[],
|
const WaveDisplay &display, bool WXUNUSED(showProgress), bool muted)
|
||||||
const int bl[], bool WXUNUSED(showProgress), bool muted)
|
|
||||||
#endif
|
#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
|
// Display a line representing the
|
||||||
// min and max of the samples in this region
|
// min and max of the samples in this region
|
||||||
int lasth1 = std::numeric_limits<int>::max();
|
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 {
|
else {
|
||||||
AColor::Line(dc, xx, r.y + h2, xx, r.y + h1);
|
AColor::Line(dc, xx, r.y + h2, xx, r.y + h1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stroke rms over the min-max
|
||||||
dc.SetPen(muted ? muteRmsPen : rmsPen);
|
dc.SetPen(muted ? muteRmsPen : rmsPen);
|
||||||
for (int x = 0; x < r.width; x++) {
|
for (int x = 0; x < r.width; x++) {
|
||||||
int xx = r.x + 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())
|
for (WaveClipList::compatibility_iterator it = track->GetClipIterator(); it; it = it->GetNext())
|
||||||
DrawClipWaveform(track, it->GetData(), dc, r, viewInfo,
|
DrawClipWaveform(track, it->GetData(), dc, r, viewInfo,
|
||||||
drawEnvelope, drawSamples, drawSliders,
|
drawEnvelope, drawSamples,
|
||||||
dB, muted);
|
dB, muted);
|
||||||
|
|
||||||
// Update cache for locations, e.g. cutlines and merge points
|
// 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);
|
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,
|
namespace {
|
||||||
wxDC & dc,
|
struct ClipParameters
|
||||||
const wxRect & r,
|
|
||||||
const ViewInfo *viewInfo,
|
|
||||||
bool drawEnvelope,
|
|
||||||
bool drawSamples,
|
|
||||||
bool drawSliders,
|
|
||||||
bool dB,
|
|
||||||
bool muted)
|
|
||||||
{
|
{
|
||||||
#if PROFILE_WAVEFORM
|
// Do a bunch of calculations common to waveform and spectrum drawing.
|
||||||
# ifdef __WXMSW__
|
ClipParameters
|
||||||
__time64_t tv0, tv1;
|
(bool spectrum, const WaveTrack *track, const WaveClip *clip, const wxRect &r,
|
||||||
_time64(&tv0);
|
const SelectedRegion &selectedRegion, const ViewInfo &viewInfo)
|
||||||
# else
|
{
|
||||||
struct timeval tv0, tv1;
|
selectedRegion;
|
||||||
gettimeofday(&tv0, NULL);
|
|
||||||
# endif
|
tOffset = clip->GetOffset();
|
||||||
#endif
|
rate = clip->GetRate();
|
||||||
double h = viewInfo->h; //The horizontal position in seconds
|
|
||||||
double pps = viewInfo->zoom; //points-per-second--the zoom level
|
h = viewInfo.h; //The horizontal position in seconds
|
||||||
double sel0 = viewInfo->selectedRegion.t0(); //left selection bound
|
pps = viewInfo.zoom; //points-per-second--the zoom level
|
||||||
double sel1 = viewInfo->selectedRegion.t1(); //right selection bound
|
|
||||||
double trackLen = clip->GetEndTime() - clip->GetStartTime();
|
double sel0 = viewInfo.selectedRegion.t0(); //left selection bound
|
||||||
double tOffset = clip->GetOffset();
|
double sel1 = viewInfo.selectedRegion.t1(); //right selection bound
|
||||||
double rate = clip->GetRate();
|
|
||||||
double sps = 1./rate; //seconds-per-sample
|
|
||||||
|
|
||||||
//If the track isn't selected, make the selection empty
|
//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;
|
sel0 = sel1 = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Some bookkeeping time variables:
|
const double trackLen = clip->GetEndTime() - clip->GetStartTime();
|
||||||
double tstep = 1.0 / pps; // Seconds per point
|
|
||||||
double tpre = h - tOffset; // offset corrected time of
|
tstep = 1.0 / pps; // Seconds per point
|
||||||
|
tpre = h - tOffset; // offset corrected time of
|
||||||
// left edge of display
|
// 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
|
// right edge of display
|
||||||
|
|
||||||
|
const double sps = 1. / rate; //seconds-per-sample
|
||||||
|
|
||||||
// Determine whether we should show individual samples
|
// Determine whether we should show individual samples
|
||||||
// or draw circular points as well
|
// or draw circular points as well
|
||||||
bool showIndividualSamples = (pps / rate > 0.5); //zoomed in a lot
|
showIndividualSamples = (pps / rate > 0.5); //zoomed in a lot
|
||||||
bool showPoints = (pps / rate > 3.0); //zoomed in even more
|
showPoints = (pps / rate > 3.0); //zoomed in even more
|
||||||
|
|
||||||
// Calculate actual selection bounds so that t0 > 0 and t1 < the
|
// Calculate actual selection bounds so that t0 > 0 and t1 < the
|
||||||
// end of the track
|
// end of the track
|
||||||
|
t0 = (tpre >= 0.0 ? tpre : 0.0);
|
||||||
double t0 = (tpre >= 0.0 ? tpre : 0.0);
|
t1 = (tpost < trackLen - sps * .99 ? tpost : trackLen - sps * .99);
|
||||||
double t1 = (tpost < trackLen - sps * .99 ? tpost : trackLen - sps * .99);
|
|
||||||
if (showIndividualSamples) {
|
if (showIndividualSamples) {
|
||||||
// adjustment so that the last circular point doesn't appear
|
// adjustment so that the last circular point doesn't appear
|
||||||
// to be hanging off the end
|
// to be hanging off the end
|
||||||
@ -1501,11 +1543,15 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
|||||||
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.
|
// Use the WaveTrack method to show what is selected and 'should' be copied, pasted etc.
|
||||||
sampleCount ssel0 = wxMax(0, track->TimeToLongSamples(sel0 - tOffset));
|
ssel0 = std::max(sampleCount(0), spectrum
|
||||||
sampleCount ssel1 = wxMax(0, track->TimeToLongSamples(sel1 - tOffset));
|
? 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
|
//trim selection so that it only contains the actual samples
|
||||||
if (ssel0 != ssel1 && ssel1 > (sampleCount)(0.5 + trackLen * rate)) {
|
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
|
// The variable "mid" will be the rectangle containing the
|
||||||
// actual waveform, as opposed to any blank area before
|
// actual waveform, as opposed to any blank area before
|
||||||
// or after the track.
|
// or after the track.
|
||||||
wxRect mid = r;
|
mid = r;
|
||||||
|
|
||||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
|
||||||
|
|
||||||
// If the left edge of the track is to the right of the left
|
// 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
|
// edge of the display, then there's some blank area to the
|
||||||
// left of the track. Reduce the "mid"
|
// left of the track. Reduce the "mid"
|
||||||
|
// rect by size of the blank area.
|
||||||
if (tpre < 0) {
|
if (tpre < 0) {
|
||||||
|
// Fill in the area to the left of the track
|
||||||
double delta = r.width;
|
double delta = r.width;
|
||||||
if (t0 < tpost) {
|
if (t0 < tpost) {
|
||||||
delta = (int) ((t0 - tpre) * pps);
|
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.x += (int)delta;
|
||||||
mid.width -= (int)delta;
|
mid.width -= (int)delta;
|
||||||
}
|
}
|
||||||
@ -1538,32 +1587,77 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
|||||||
if (tpost > t1) {
|
if (tpost > t1) {
|
||||||
wxRect post = r;
|
wxRect post = r;
|
||||||
if (t1 > tpre) {
|
if (t1 > tpre) {
|
||||||
post.x += (int) ((t1 - tpre) * pps);
|
post.x += (int)((t1 - tpre) * pps);
|
||||||
}
|
}
|
||||||
post.width = r.width - (post.x - r.x);
|
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;
|
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
|
// The "mid" rect contains the part of the display actually
|
||||||
// containing the waveform. If it's empty, we're done.
|
// containing the waveform. If it's empty, we're done.
|
||||||
if (mid.width <= 0) {
|
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;
|
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
|
// If we get to this point, the clip is actually visible on the
|
||||||
// screen, so remember the display rectangle.
|
// screen, so remember the display rectangle.
|
||||||
clip->SetDisplayRect(mid);
|
clip->SetDisplayRect(mid);
|
||||||
@ -1573,26 +1667,16 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
|||||||
float zoomMin, zoomMax;
|
float zoomMin, zoomMax;
|
||||||
track->GetDisplayBounds(&zoomMin, &zoomMax);
|
track->GetDisplayBounds(&zoomMin, &zoomMax);
|
||||||
|
|
||||||
// Arrays containing the shape of the waveform - each array
|
WaveDisplay display;
|
||||||
// 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];
|
|
||||||
bool isLoadingOD = false;//true if loading on demand block in sequence.
|
bool isLoadingOD = false;//true if loading on demand block in sequence.
|
||||||
|
|
||||||
// The WaveClip class handles the details of computing the shape
|
// The WaveClip class handles the details of computing the shape
|
||||||
// of the waveform. The only way GetWaveDisplay will fail is if
|
// of the waveform. The only way GetWaveDisplay will fail is if
|
||||||
// there's a serious error, like some of the waveform data can't
|
// there's a serious error, like some of the waveform data can't
|
||||||
// be loaded. So if the function returns false, we can just exit.
|
// be loaded. So if the function returns false, we can just exit.
|
||||||
if (!clip->GetWaveDisplay(min, max, rms, bl, where,
|
display.Allocate(mid.width);
|
||||||
mid.width, t0, pps, isLoadingOD)) {
|
if (!clip->GetWaveDisplay(display,
|
||||||
delete[] min;
|
t0, pps, isLoadingOD)) {
|
||||||
delete[] max;
|
|
||||||
delete[] rms;
|
|
||||||
delete[] where;
|
|
||||||
delete[] bl;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1607,7 +1691,7 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
|||||||
// the envelope and using a colored pen for the selected
|
// the envelope and using a colored pen for the selected
|
||||||
// part of the waveform
|
// part of the waveform
|
||||||
DrawWaveformBackground(dc, mid, envValues, zoomMin, zoomMax, dB,
|
DrawWaveformBackground(dc, mid, envValues, zoomMin, zoomMax, dB,
|
||||||
where, ssel0, ssel1, drawEnvelope,
|
display.where, ssel0, ssel1, drawEnvelope,
|
||||||
!track->GetSelected());
|
!track->GetSelected());
|
||||||
|
|
||||||
if (!showIndividualSamples) {
|
if (!showIndividualSamples) {
|
||||||
@ -1616,7 +1700,7 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
|||||||
min, max, rms, bl, isLoadingOD, muted, track->GetChannelGain(track->GetChannel()));
|
min, max, rms, bl, isLoadingOD, muted, track->GetChannelGain(track->GetChannel()));
|
||||||
#else
|
#else
|
||||||
DrawMinMaxRMS(dc, mid, envValues, zoomMin, zoomMax, dB,
|
DrawMinMaxRMS(dc, mid, envValues, zoomMin, zoomMax, dB,
|
||||||
min, max, rms, bl, isLoadingOD, muted);
|
display, isLoadingOD, muted);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1631,11 +1715,6 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete[] envValues;
|
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
|
// Draw arrows on the left side if the track extends to the left of the
|
||||||
// beginning of time. :)
|
// beginning of time. :)
|
||||||
@ -1643,11 +1722,6 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
|||||||
DrawNegativeOffsetTrackArrows(dc, r);
|
DrawNegativeOffsetTrackArrows(dc, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drawSliders) {
|
|
||||||
DrawTimeSlider(track, dc, r, viewInfo, true); // directed right
|
|
||||||
DrawTimeSlider(track, dc, r, viewInfo, false); // directed left
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw clip edges
|
// Draw clip edges
|
||||||
dc.SetPen(*wxGREY_PEN);
|
dc.SetPen(*wxGREY_PEN);
|
||||||
if (tpre < 0) {
|
if (tpre < 0) {
|
||||||
@ -1660,22 +1734,6 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track,
|
|||||||
mid.x + mid.width, mid.y,
|
mid.x + mid.width, mid.y,
|
||||||
mid.x + mid.width, mid.y + r.height);
|
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 inline float findValue
|
||||||
static float sumFreqValues(
|
(float *spectrum, float bin0, float bin1, int half,
|
||||||
const float *freq, int x, int half, float bin0, float bin1,
|
bool autocorrelation, int gain, int range)
|
||||||
bool autocorrelation, int range, int gain)
|
|
||||||
{
|
{
|
||||||
const int x0 = x * half;
|
|
||||||
float value;
|
float value;
|
||||||
#if 0
|
#if 0
|
||||||
// Averaging method
|
// Averaging method
|
||||||
if (int(bin1) == int(bin0)) {
|
if (int(bin1) == int(bin0)) {
|
||||||
value = freq[x0+int(bin0)];
|
value = spectrum[int(bin0)];
|
||||||
} else {
|
} else {
|
||||||
float binwidth= bin1 - bin0;
|
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);
|
bin0 = 1 + int (bin0);
|
||||||
while (bin0 < int(bin1)) {
|
while (bin0 < int(bin1)) {
|
||||||
value += freq[x0 + int(bin0)];
|
value += spectrum[int(bin0)];
|
||||||
bin0 += 1.0;
|
bin0 += 1.0;
|
||||||
}
|
}
|
||||||
// Do not reference past end of freq array.
|
// Do not reference past end of freq array.
|
||||||
@ -1780,17 +1836,18 @@ static float sumFreqValues(
|
|||||||
bin1 -= 1.0;
|
bin1 -= 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
value += freq[x0 + int(bin1)] * (bin1 - int(bin1));
|
value += spectrum[int(bin1)] * (bin1 - int(bin1));
|
||||||
value /= binwidth;
|
value /= binwidth;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
half;
|
||||||
// Maximum method, and no apportionment of any single bins over multiple pixel rows
|
// Maximum method, and no apportionment of any single bins over multiple pixel rows
|
||||||
// See Bug971
|
// See Bug971
|
||||||
int bin = floor(0.5 + bin0);
|
int bin = std::min(half - 1, int(floor(0.5 + bin0)));
|
||||||
const int limitBin = floor(0.5 + bin1);
|
const int limitBin = std::min(half, int(floor(0.5 + bin1)));
|
||||||
value = freq[x0 + bin];
|
value = spectrum[bin];
|
||||||
while (++bin < limitBin)
|
while (++bin < limitBin)
|
||||||
value = std::max(value, freq[x0 + bin]);
|
value = std::max(value, spectrum[bin]);
|
||||||
#endif
|
#endif
|
||||||
if (!autocorrelation) {
|
if (!autocorrelation) {
|
||||||
// Last step converts dB to a 0.0-1.0 range
|
// Last step converts dB to a 0.0-1.0 range
|
||||||
@ -1831,24 +1888,29 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
bool autocorrelation,
|
bool autocorrelation,
|
||||||
bool logF)
|
bool logF)
|
||||||
{
|
{
|
||||||
|
#ifdef PROFILE_WAVEFORM
|
||||||
|
Profiler profiler;
|
||||||
|
#endif
|
||||||
|
|
||||||
const WaveTrack *const track = cache.GetTrack();
|
const WaveTrack *const track = cache.GetTrack();
|
||||||
|
|
||||||
enum { MONOCHROME_LINE = 230, COLORED_LINE = 0 };
|
enum { MONOCHROME_LINE = 230, COLORED_LINE = 0 };
|
||||||
enum { DASH_LENGTH = 10 /* pixels */ };
|
enum { DASH_LENGTH = 10 /* pixels */ };
|
||||||
|
|
||||||
#if PROFILE_WAVEFORM
|
const ClipParameters params(true, track, clip, r, viewInfo->selectedRegion, *viewInfo);
|
||||||
# ifdef __WXMSW__
|
const wxRect &mid = params.mid;
|
||||||
__time64_t tv0, tv1;
|
// The "mid" rect contains the part of the display actually
|
||||||
_time64(&tv0);
|
// containing the waveform. If it's empty, we're done.
|
||||||
# else
|
if (mid.width <= 0) {
|
||||||
struct timeval tv0, tv1;
|
return;
|
||||||
gettimeofday(&tv0, NULL);
|
}
|
||||||
# endif
|
|
||||||
#endif
|
const double &t0 = params.t0;
|
||||||
double h = viewInfo->h;
|
const double &pps = params.pps;
|
||||||
double pps = viewInfo->zoom;
|
const double &tstep = params.tstep;
|
||||||
double sel0 = viewInfo->selectedRegion.t0();
|
const double &ssel0 = params.ssel0;
|
||||||
double sel1 = viewInfo->selectedRegion.t1();
|
const double &ssel1 = params.ssel1;
|
||||||
|
const double &rate = params.rate;
|
||||||
|
|
||||||
double freqLo = SelectedRegion::UndefinedFrequency;
|
double freqLo = SelectedRegion::UndefinedFrequency;
|
||||||
double freqHi = SelectedRegion::UndefinedFrequency;
|
double freqHi = SelectedRegion::UndefinedFrequency;
|
||||||
@ -1859,99 +1921,12 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
double tOffset = clip->GetOffset();
|
|
||||||
double rate = clip->GetRate();
|
|
||||||
double sps = 1./rate;
|
|
||||||
|
|
||||||
int range = gPrefs->Read(wxT("/Spectrum/Range"), 80L);
|
int range = gPrefs->Read(wxT("/Spectrum/Range"), 80L);
|
||||||
int gain = gPrefs->Read(wxT("/Spectrum/Gain"), 20L);
|
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);
|
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,
|
// We draw directly to a bit image in memory,
|
||||||
// and then paint this directly to our offscreen
|
// and then paint this directly to our offscreen
|
||||||
// bitmap. Note that this could be optimized even
|
// 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,
|
bool updated = clip->GetSpectrogram(cache, freq, where, mid.width,
|
||||||
t0, pps, autocorrelation);
|
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 ifreq = lrint(rate/2);
|
||||||
|
|
||||||
int maxFreq;
|
int maxFreq;
|
||||||
@ -1990,9 +1971,41 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
minFreq = 1.0;
|
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
|
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
||||||
&& mFftYGrid==fftYGridOld
|
&& mFftYGrid==fftYGridOld
|
||||||
#endif //EXPERIMENTAL_FFT_Y_GRID
|
#endif //EXPERIMENTAL_FFT_Y_GRID
|
||||||
@ -2003,12 +2016,11 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
&& mFindNotesQuantize==findNotesQuantizeOld
|
&& mFindNotesQuantize==findNotesQuantizeOld
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
usePxCache = true;
|
// cache is up to date
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete clip->mSpecPxCache;
|
delete clip->mSpecPxCache;
|
||||||
clip->mSpecPxCache = new SpecPxCache(mid.width * mid.height);
|
clip->mSpecPxCache = new SpecPxCache(mid.width * mid.height);
|
||||||
usePxCache = false;
|
|
||||||
clip->mSpecPxCache->valid = true;
|
clip->mSpecPxCache->valid = true;
|
||||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||||
fftFindNotesOld=mFftFindNotes;
|
fftFindNotesOld=mFftFindNotes;
|
||||||
@ -2016,130 +2028,49 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
findNotesNOld=mNumberOfMaxima;
|
findNotesNOld=mNumberOfMaxima;
|
||||||
findNotesQuantizeOld=mFindNotesQuantize;
|
findNotesQuantizeOld=mFindNotesQuantize;
|
||||||
#endif
|
#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
|
#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
|
const float
|
||||||
scale2=(lmax-lmin)/log2,
|
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
|
||||||
lmin2=lmin/log2;
|
lmins = logf(float(minFreq) / (mFftSkipPoints + 1)),
|
||||||
|
lmaxs = logf(float(maxFreq) / (mFftSkipPoints + 1)),
|
||||||
bool *yGrid;
|
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
|
||||||
yGrid=new bool[mid.height];
|
lmins = lmin,
|
||||||
for (int y = 0; y < mid.height; y++) {
|
lmaxs = lmax,
|
||||||
float n =(float(y )/mid.height*scale2-lmin2)*12;
|
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
|
||||||
float n2=(float(y+1)/mid.height*scale2-lmin2)*12;
|
;
|
||||||
float f =float(minFreq)/(mFftSkipPoints+1)*powf(2.0f, n /12.0f+lmin2);
|
#endif //EXPERIMENTAL_FIND_NOTES
|
||||||
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
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||||
int maxima[128];
|
int maxima[128];
|
||||||
float maxima0[128], maxima1[128];
|
float maxima0[128], maxima1[128];
|
||||||
const float
|
const float
|
||||||
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
|
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
|
||||||
f2bin = half/(rate/2.0f/(mFftSkipPoints+1)),
|
f2bin = half / (rate / 2.0f / (mFftSkipPoints + 1)),
|
||||||
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
|
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
|
||||||
f2bin = half/(rate/2.0f),
|
f2bin = half / (rate / 2.0f),
|
||||||
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
|
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
|
||||||
bin2f = 1.0f/f2bin,
|
bin2f = 1.0f / f2bin,
|
||||||
minDistance = powf(2.0f, 2.0f/12.0f),
|
minDistance = powf(2.0f, 2.0f / 12.0f),
|
||||||
i0=expf(lmin)/f,
|
i0 = expf(lmin) / f,
|
||||||
i1=expf(scale+lmin)/f,
|
i1 = expf(scale + lmin) / f,
|
||||||
minColor=0.0f;
|
minColor = 0.0f;
|
||||||
const int maxTableSize=1024;
|
const int maxTableSize = 1024;
|
||||||
int *indexes=new int[maxTableSize];
|
int *indexes = new int[maxTableSize];
|
||||||
#endif //EXPERIMENTAL_FIND_NOTES
|
#endif //EXPERIMENTAL_FIND_NOTES
|
||||||
|
|
||||||
while (x < mid.width)
|
for (int x = 0; x < mid.width; ++x)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
if (!logF) {
|
||||||
for (int yy = 0; yy < mid.height; yy++) {
|
for (int yy = 0; yy < mid.height; yy++) {
|
||||||
float bin0 = float (yy) * binPerPx + minSamples;
|
float bin0 = float(yy) * binPerPx + minSamples;
|
||||||
float bin1 = float (yy + 1) * binPerPx + minSamples;
|
float bin1 = float(yy + 1) * binPerPx + minSamples;
|
||||||
|
const float value = findValue
|
||||||
// For spectral selection, determine what colour
|
(freq + half * x, bin0, bin1, half, autocorrelation, gain, range);
|
||||||
// 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);
|
|
||||||
clip->mSpecPxCache->values[x * mid.height + yy] = value;
|
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 {
|
||||||
else //logF
|
|
||||||
{
|
|
||||||
unsigned char rv, gv, bv;
|
|
||||||
float value;
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||||
int maximas=0;
|
int maximas=0;
|
||||||
if (!usePxCache && mFftFindNotes) {
|
if (!usePxCache && mFftFindNotes) {
|
||||||
@ -2199,14 +2130,15 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
int it=0;
|
int it=0;
|
||||||
|
int oldBin0=-1;
|
||||||
bool inMaximum = false;
|
bool inMaximum = false;
|
||||||
#endif //EXPERIMENTAL_FIND_NOTES
|
#endif //EXPERIMENTAL_FIND_NOTES
|
||||||
|
|
||||||
double yy2_base=exp(lmin)/f;
|
double yy2_base = exp(lmin) / f;
|
||||||
float yy2 = yy2_base;
|
float yy2 = yy2_base;
|
||||||
double exp_scale_per_height = exp(scale/mid.height);
|
double exp_scale_per_height = exp(scale / mid.height);
|
||||||
for (int yy = 0; yy < mid.height; yy++) {
|
for (int yy = 0; yy < mid.height; yy++) {
|
||||||
if (int(yy2)>=half)
|
if (int(yy2) >= half)
|
||||||
yy2=half-1;
|
yy2=half-1;
|
||||||
if (yy2<0)
|
if (yy2<0)
|
||||||
yy2=0;
|
yy2=0;
|
||||||
@ -2218,18 +2150,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
if (yy3<0)
|
if (yy3<0)
|
||||||
yy3=0;
|
yy3=0;
|
||||||
float bin1 = float(yy3);
|
float bin1 = float(yy3);
|
||||||
|
float value;
|
||||||
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) {
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||||
if (mFftFindNotes) {
|
if (mFftFindNotes) {
|
||||||
@ -2258,14 +2179,101 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
value = minColor;
|
value = minColor;
|
||||||
} else
|
} else
|
||||||
#endif //EXPERIMENTAL_FIND_NOTES
|
#endif //EXPERIMENTAL_FIND_NOTES
|
||||||
value=sumFreqValues(freq, x, half, bin0, bin1,
|
{
|
||||||
autocorrelation, range, gain);
|
value = findValue
|
||||||
clip->mSpecPxCache->values[x * mid.height + yy] = value;
|
(freq + half * x, bin0, bin1, half, autocorrelation, gain, range);
|
||||||
}
|
}
|
||||||
else
|
clip->mSpecPxCache->values[x * mid.height + yy] = value;
|
||||||
value = clip->mSpecPxCache->values[x * mid.height + yy];
|
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;
|
yy2 = yy2_base;
|
||||||
|
|
||||||
|
unsigned char rv, gv, bv;
|
||||||
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv);
|
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv);
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
||||||
@ -2280,10 +2288,9 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache,
|
|||||||
data[px++] = rv;
|
data[px++] = rv;
|
||||||
data[px++] = gv;
|
data[px++] = gv;
|
||||||
data[px] = bv;
|
data[px] = bv;
|
||||||
}
|
} // each y
|
||||||
}
|
} // logF
|
||||||
x++;
|
} // each x
|
||||||
}
|
|
||||||
|
|
||||||
// If we get to this point, the clip is actually visible on the
|
// If we get to this point, the clip is actually visible on the
|
||||||
// screen, so remember the display rectangle.
|
// screen, so remember the display rectangle.
|
||||||
|
@ -28,6 +28,7 @@ class wxRect;
|
|||||||
class wxHashTable;
|
class wxHashTable;
|
||||||
|
|
||||||
class Track;
|
class Track;
|
||||||
|
class WaveDisplay;
|
||||||
class WaveTrack;
|
class WaveTrack;
|
||||||
class WaveTrackCache;
|
class WaveTrackCache;
|
||||||
class WaveClip;
|
class WaveClip;
|
||||||
@ -139,7 +140,7 @@ class AUDACITY_DLL_API TrackArtist {
|
|||||||
|
|
||||||
void DrawClipWaveform(WaveTrack *track, WaveClip *clip,
|
void DrawClipWaveform(WaveTrack *track, WaveClip *clip,
|
||||||
wxDC & dc, const wxRect & r, const ViewInfo *viewInfo,
|
wxDC & dc, const wxRect & r, const ViewInfo *viewInfo,
|
||||||
bool drawEnvelope, bool drawSamples, bool drawSliders,
|
bool drawEnvelope, bool drawSamples,
|
||||||
bool dB, bool muted);
|
bool dB, bool muted);
|
||||||
|
|
||||||
void DrawClipSpectrum(WaveTrackCache &cache, WaveClip *clip,
|
void DrawClipSpectrum(WaveTrackCache &cache, WaveClip *clip,
|
||||||
@ -156,13 +157,11 @@ class AUDACITY_DLL_API TrackArtist {
|
|||||||
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
||||||
void DrawMinMaxRMS(wxDC & dc, const wxRect & r, const double env[],
|
void DrawMinMaxRMS(wxDC & dc, const wxRect & r, const double env[],
|
||||||
float zoomMin, float zoomMax, bool dB,
|
float zoomMin, float zoomMax, bool dB,
|
||||||
const float min[], const float max[], const float rms[],
|
const WaveDisplay &display, bool showProgress, bool muted, const float gain);
|
||||||
const int bl[], bool showProgress, bool muted, const float gain);
|
|
||||||
#else
|
#else
|
||||||
void DrawMinMaxRMS(wxDC & dc, const wxRect & r, const double env[],
|
void DrawMinMaxRMS(wxDC & dc, const wxRect & r, const double env[],
|
||||||
float zoomMin, float zoomMax, bool dB,
|
float zoomMin, float zoomMax, bool dB,
|
||||||
const float min[], const float max[], const float rms[],
|
const WaveDisplay &display, bool showProgress, bool muted);
|
||||||
const int bl[], bool showProgress, bool muted);
|
|
||||||
#endif
|
#endif
|
||||||
void DrawIndividualSamples(wxDC & dc, const wxRect & r,
|
void DrawIndividualSamples(wxDC & dc, const wxRect & r,
|
||||||
float zoomMin, float zoomMax, bool dB,
|
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 <math.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <wx/log.h>
|
#include <wx/log.h>
|
||||||
|
|
||||||
@ -41,14 +42,23 @@ drawing). Cache's the Spectrogram frequency samples.
|
|||||||
#include <wx/listimpl.cpp>
|
#include <wx/listimpl.cpp>
|
||||||
WX_DEFINE_LIST(WaveClipList);
|
WX_DEFINE_LIST(WaveClipList);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
inline int CountODPixels(int *bl, int start, int end)
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
return count_if(bl + start, bl + end, bind2nd(less<int>(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class WaveCache {
|
class WaveCache {
|
||||||
public:
|
public:
|
||||||
WaveCache(int cacheLen)
|
WaveCache(int cacheLen)
|
||||||
|
: len(cacheLen)
|
||||||
{
|
{
|
||||||
dirty = -1;
|
dirty = -1;
|
||||||
start = -1.0;
|
start = -1.0;
|
||||||
pps = 0.0;
|
pps = 0.0;
|
||||||
len = cacheLen;
|
|
||||||
min = len ? new float[len] : 0;
|
min = len ? new float[len] : 0;
|
||||||
max = len ? new float[len] : 0;
|
max = len ? new float[len] : 0;
|
||||||
rms = len ? new float[len] : 0;
|
rms = len ? new float[len] : 0;
|
||||||
@ -70,7 +80,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dirty;
|
int dirty;
|
||||||
sampleCount len;
|
const sampleCount len;
|
||||||
double start;
|
double start;
|
||||||
double pps;
|
double pps;
|
||||||
int rate;
|
int rate;
|
||||||
@ -121,7 +131,7 @@ public:
|
|||||||
invalEnd = len;
|
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
|
//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.
|
//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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mRegionsMutex.Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//lock before calling these in a section. unlock after finished.
|
//lock before calling these in a section. unlock after finished.
|
||||||
int GetNumInvalidRegions(){return mRegions.size();}
|
int GetNumInvalidRegions() const {return mRegions.size();}
|
||||||
int GetInvalidRegionStart(int i){return mRegions[i]->start;}
|
int GetInvalidRegionStart(int i) const {return mRegions[i]->start;}
|
||||||
int GetInvalidRegionEnd(int i){return mRegions[i]->end;}
|
int GetInvalidRegionEnd(int i) const {return mRegions[i]->end;}
|
||||||
|
|
||||||
void LockInvalidRegions(){mRegionsMutex.Lock();}
|
|
||||||
void UnlockInvalidRegions(){mRegionsMutex.Unlock();}
|
|
||||||
|
|
||||||
void ClearInvalidRegions()
|
void ClearInvalidRegions()
|
||||||
{
|
{
|
||||||
@ -209,6 +213,38 @@ public:
|
|||||||
mRegions.clear();
|
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:
|
protected:
|
||||||
std::vector<InvalidRegion*> mRegions;
|
std::vector<InvalidRegion*> mRegions;
|
||||||
@ -444,20 +480,72 @@ bool WaveClip::AfterClip(double t) const
|
|||||||
///Delete the wave cache - force redraw. Thread-safe
|
///Delete the wave cache - force redraw. Thread-safe
|
||||||
void WaveClip::DeleteWaveCache()
|
void WaveClip::DeleteWaveCache()
|
||||||
{
|
{
|
||||||
mWaveCacheMutex.Lock();
|
ODLocker locker(mWaveCacheMutex);
|
||||||
if(mWaveCache!=NULL)
|
if(mWaveCache!=NULL)
|
||||||
delete mWaveCache;
|
delete mWaveCache;
|
||||||
mWaveCache = new WaveCache(0);
|
mWaveCache = new WaveCache(0);
|
||||||
mWaveCacheMutex.Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///Adds an invalid region to the wavecache so it redraws that portion only.
|
///Adds an invalid region to the wavecache so it redraws that portion only.
|
||||||
void WaveClip::AddInvalidRegion(long startSample, long endSample)
|
void WaveClip::AddInvalidRegion(long startSample, long endSample)
|
||||||
{
|
{
|
||||||
mWaveCacheMutex.Lock();
|
ODLocker locker(mWaveCacheMutex);
|
||||||
if(mWaveCache!=NULL)
|
if(mWaveCache!=NULL)
|
||||||
mWaveCache->AddInvalidRegion(startSample,endSample);
|
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
|
// clipping calculations
|
||||||
//
|
//
|
||||||
|
|
||||||
bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
|
||||||
sampleCount *where,
|
|
||||||
int numPixels, double t0,
|
|
||||||
double pixelsPerSecond, bool &isLoadingOD)
|
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 =
|
const bool match =
|
||||||
@ -482,40 +575,7 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
|||||||
mWaveCache->start == t0 &&
|
mWaveCache->start == t0 &&
|
||||||
mWaveCache->len >= numPixels) {
|
mWaveCache->len >= numPixels) {
|
||||||
|
|
||||||
//check for invalid regions, and make the bottom if an else if.
|
mWaveCache->LoadInvalidRegions(mSequence, true);
|
||||||
//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->ClearInvalidRegions();
|
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(bl, mWaveCache->bl, numPixels*sizeof(int));
|
||||||
memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount));
|
memcpy(where, mWaveCache->where, (numPixels+1)*sizeof(sampleCount));
|
||||||
isLoadingOD = mWaveCache->numODPixels>0;
|
isLoadingOD = mWaveCache->numODPixels>0;
|
||||||
mWaveCacheMutex.Unlock();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,47 +600,16 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
|||||||
double oldWhere0 = 0;
|
double oldWhere0 = 0;
|
||||||
double denom = 0;
|
double denom = 0;
|
||||||
int oldX0 = 0, oldXLast = 0;
|
int oldX0 = 0, oldXLast = 0;
|
||||||
double error = 0.0;
|
double correction = 0.0;
|
||||||
if (match &&
|
if (match &&
|
||||||
oldCache->len > 0) {
|
oldCache->len > 0) {
|
||||||
// Mitigate the accumulation of location errors
|
findCorrection(oldCache->where, oldCache->len, mWaveCache->len,
|
||||||
// in copies of copies of ... of caches.
|
t0, mRate, samplesPerPixel,
|
||||||
// Look at the loop that populates "where" below to understand this.
|
oldWhere0, denom, oldX0, oldXLast, correction);
|
||||||
|
|
||||||
// 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
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Be careful to make the first value non-negative
|
fillWhere(mWaveCache->where, mWaveCache->len, 0.0, correction,
|
||||||
mWaveCache->where[0] = sampleCount(std::max(0.0, floor(0.5 + error + t0 * mRate)));
|
t0, mRate, samplesPerPixel);
|
||||||
for (sampleCount x = 1; x < mWaveCache->len + 1; x++)
|
|
||||||
mWaveCache->where[x] = sampleCount(
|
|
||||||
floor(0.5 + error + t0 * mRate + double(x) * samplesPerPixel)
|
|
||||||
);
|
|
||||||
|
|
||||||
//mchinen: I think s0 - s1 represents the range of samples that we will need to look up. likewise p0-p1 the number of pixels.
|
//mchinen: I think s0 - s1 represents the range of samples that we will need to look up. likewise p0-p1 the number of pixels.
|
||||||
sampleCount s0 = mWaveCache->where[0];
|
sampleCount s0 = mWaveCache->where[0];
|
||||||
@ -603,24 +631,10 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
|||||||
p0 = mWaveCache->len;
|
p0 = mWaveCache->len;
|
||||||
p1 = 0;
|
p1 = 0;
|
||||||
|
|
||||||
//check for invalid regions, and make the bottom if an else if.
|
//TODO: only load inval regions if
|
||||||
//invalid regions are keep in a sorted array.
|
|
||||||
//TODO:integrate into below for loop so that we only load inval regions if
|
|
||||||
//necessary. (usually is the case, so no rush.)
|
//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.
|
//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++)
|
oldCache->LoadInvalidRegions(mSequence, false);
|
||||||
{
|
|
||||||
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->ClearInvalidRegions();
|
oldCache->ClearInvalidRegions();
|
||||||
|
|
||||||
for (sampleCount x = 0; x < mWaveCache->len; x++)
|
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]))
|
&mWaveCache->where[p0]))
|
||||||
{
|
{
|
||||||
isLoadingOD=false;
|
isLoadingOD=false;
|
||||||
mWaveCacheMutex.Unlock();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -755,7 +768,6 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl,
|
|||||||
mWaveCache->numODPixels++;
|
mWaveCache->numODPixels++;
|
||||||
|
|
||||||
isLoadingOD = mWaveCache->numODPixels>0;
|
isLoadingOD = mWaveCache->numODPixels>0;
|
||||||
mWaveCacheMutex.Unlock();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -897,6 +909,7 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
|
|||||||
mSpecCache->start = t0;
|
mSpecCache->start = t0;
|
||||||
|
|
||||||
bool *recalc = new bool[mSpecCache->len + 1];
|
bool *recalc = new bool[mSpecCache->len + 1];
|
||||||
|
std::fill(&recalc[0], &recalc[mSpecCache->len + 1], true);
|
||||||
|
|
||||||
const double tstep = 1.0 / pixelsPerSecond;
|
const double tstep = 1.0 / pixelsPerSecond;
|
||||||
const double samplesPerPixel = mRate * tstep;
|
const double samplesPerPixel = mRate * tstep;
|
||||||
@ -906,52 +919,17 @@ bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache,
|
|||||||
double oldWhere0 = 0;
|
double oldWhere0 = 0;
|
||||||
double denom = 0;
|
double denom = 0;
|
||||||
int oldX0 = 0, oldXLast = 0;
|
int oldX0 = 0, oldXLast = 0;
|
||||||
double error = 0.0;
|
double correction = 0.0;
|
||||||
|
|
||||||
if (match &&
|
if (match &&
|
||||||
oldCache->len > 0) {
|
oldCache->len > 0) {
|
||||||
// Mitigate the accumulation of location errors
|
findCorrection(oldCache->where, oldCache->len, mSpecCache->len,
|
||||||
// in copies of copies of ... of caches.
|
t0, mRate, samplesPerPixel,
|
||||||
// Look at the loop that populates "where" below to understand this.
|
oldWhere0, denom, oldX0, oldXLast, correction);
|
||||||
|
|
||||||
// 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
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Be careful to make the first value non-negative
|
fillWhere(mSpecCache->where, mSpecCache->len, 0.5, correction,
|
||||||
recalc[0] = true;
|
t0, mRate, samplesPerPixel);
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimization: if the old cache is good and overlaps
|
// Optimization: if the old cache is good and overlaps
|
||||||
// with the current one, re-use as much of the cache as
|
// with the current one, re-use as much of the cache as
|
||||||
|
@ -58,6 +58,51 @@ class WaveClip;
|
|||||||
WX_DECLARE_USER_EXPORTED_LIST(WaveClip, WaveClipList, AUDACITY_DLL_API);
|
WX_DECLARE_USER_EXPORTED_LIST(WaveClip, WaveClipList, AUDACITY_DLL_API);
|
||||||
WX_DEFINE_USER_EXPORTED_ARRAY_PTR(WaveClip*, WaveClipArray, class 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
|
class AUDACITY_DLL_API WaveClip : public XMLTagHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -139,8 +184,8 @@ public:
|
|||||||
|
|
||||||
/** Getting high-level data from the for screen display and clipping
|
/** Getting high-level data from the for screen display and clipping
|
||||||
* calculations and Contrast */
|
* calculations and Contrast */
|
||||||
bool GetWaveDisplay(float *min, float *max, float *rms,int* bl, sampleCount *where,
|
bool GetWaveDisplay(WaveDisplay &display,
|
||||||
int numPixels, double t0, double pixelsPerSecond, bool &isLoadingOD);
|
double t0, double pixelsPerSecond, bool &isLoadingOD);
|
||||||
bool GetSpectrogram(WaveTrackCache &cache,
|
bool GetSpectrogram(WaveTrackCache &cache,
|
||||||
float *buffer, sampleCount *where,
|
float *buffer, sampleCount *where,
|
||||||
int numPixels,
|
int numPixels,
|
||||||
|
@ -166,5 +166,26 @@ protected:
|
|||||||
|
|
||||||
#endif // __WXMAC__
|
#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__
|
#endif //__AUDACITY_ODTASKTHREAD__
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user