diff --git a/src/Envelope.cpp b/src/Envelope.cpp index fa92b5ebc..9d3aa6c2a 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -606,36 +606,62 @@ bool EnvelopeEditor::MouseEvent(const wxMouseEvent & event, wxRect & r, return false; } -void Envelope::CollapseRegion(double t0, double t1) +void Envelope::CollapseRegion( double t0, double t1, double sampleTime ) // NOFAIL-GUARANTEE { - // This gets called when somebody clears samples. All of the - // control points within the region disappear and the points - // to the right get shifted over. + // This gets called when somebody clears samples. - t0 -= mOffset; - t1 -= mOffset; + // Snip points in the interval (t0, t1), shift values left at times after t1. + // For the boundaries of the interval, preserve the left-side limit at the + // start and right-side limit at the end. - t0 = std::max(0.0, std::min(mTrackLen, t0)); - t1 = std::max(0.0, std::min(mTrackLen, t1)); + const auto epsilon = sampleTime / 2; + t0 = std::max( 0.0, std::min( mTrackLen, t0 - mOffset ) ); + t1 = std::max( 0.0, std::min( mTrackLen, t1 - mOffset ) ); - int len = mEnv.size(); - int i; - - // Remove points in deleted region. - for (i = 0; i < len - 0; i++) - if (mEnv[i].GetT() >= t0 && mEnv[i].GetT() < t1) { - Delete(i); - len--; - i--; + // Determine the start of the range of points to remove from the array. + auto range0 = EqualRange( t0, 0 ); + auto begin = range0.first; + if ( begin == range0.second ) { + if ( t0 > epsilon ) { + // There was no point exactly at t0; + // insert a point to preserve the value. + auto val = GetValueRelative( t0 ); + InsertOrReplaceRelative( t0, val ); + ++begin; } + } + else + // We will keep the first (or only) point that was at t0. + ++begin; + + // We want end to be the index one past the range of points to remove from + // the array. + // At first, find index of the first point after t1: + auto range1 = EqualRange( t1, 0 ); + auto end = range1.second; + if ( range1.first == end ) { + if ( mTrackLen - t1 > epsilon ) { + // There was no point exactly at t1; insert a point to preserve the value. + auto val = GetValueRelative( t1 ); + InsertOrReplaceRelative( t1, val ); + // end is now the index of this NEW point and that is correct. + } + } + else + // We will keep the last (or only) point that was at t1. + --end; + + mEnv.erase( mEnv.begin() + begin, mEnv.begin() + end ); // Shift points left after deleted region. - for (i = 0; i < len; i++) - if (mEnv[i].GetT() >= t1) - mEnv[i].SetT(mEnv[i].GetT() - (t1 - t0)); + auto len = mEnv.size(); + for ( size_t i = begin; i < len; ++i ) { + auto &point = mEnv[i]; + point.SetT( point.GetT() - (t1 - t0) ); + } - mTrackLen -= (t1-t0); + mTrackLen -= ( t1 - t0 ); } // This operation is trickier than it looks; the basic rub is that diff --git a/src/Envelope.h b/src/Envelope.h index 13cec2917..9db7f1dfc 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -118,8 +118,11 @@ public: float zoomMin, float zoomMax, bool mirrored) const; // Handling Cut/Copy/Paste events - void CollapseRegion(double t0, double t1); + // sampleTime determines when the endpoint of the collapse is near enough + // to an endpoint of the domain, that an extra control point is not needed. + void CollapseRegion(double t0, double t1, double sampleTime); void Paste(double t0, const Envelope *e); + void InsertSpace(double t0, double tlen); void RemoveUnneededPoints(double time = -1, double tolerence = 0.001); diff --git a/src/TimeTrack.cpp b/src/TimeTrack.cpp index 4edfac787..bc2260e61 100644 --- a/src/TimeTrack.cpp +++ b/src/TimeTrack.cpp @@ -22,6 +22,7 @@ #include "widgets/Ruler.h" #include "Envelope.h" #include "Prefs.h" +#include "Project.h" #include "Internat.h" #include "ViewInfo.h" #include "AllThemeResources.h" @@ -108,7 +109,8 @@ Track::Holder TimeTrack::Copy( double t0, double t1, bool ) const void TimeTrack::Clear(double t0, double t1) { - mEnvelope->CollapseRegion(t0, t1); + auto sampleTime = 1.0 / GetActiveProject()->GetRate(); + mEnvelope->CollapseRegion( t0, t1, sampleTime ); } void TimeTrack::Paste(double t, const Track * src) diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index b49ece960..ed4e3ea2b 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -1676,7 +1676,8 @@ void WaveClip::Clear(double t0, double t1) } // Collapse envelope - GetEnvelope()->CollapseRegion(t0, t1); + auto sampleTime = 1.0 / GetRate(); + GetEnvelope()->CollapseRegion( t0, t1, sampleTime ); if (t0 < GetStartTime()) Offset(-(GetStartTime() - t0)); @@ -1728,7 +1729,8 @@ void WaveClip::ClearAndAddCutLine(double t0, double t1) GetSequence()->Delete(s0, s1-s0); // Collapse envelope - GetEnvelope()->CollapseRegion(t0, t1); + auto sampleTime = 1.0 / GetRate(); + GetEnvelope()->CollapseRegion( t0, t1, sampleTime ); if (t0 < GetStartTime()) Offset(-(GetStartTime() - t0)); diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index be88dc146..e6a3ac7e1 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -1138,26 +1138,7 @@ void WaveTrack::HandleClear(double t0, double t1, clipsToDelete.push_back( clip.get() ); auto newClip = make_movable( *clip, mDirManager, true ); - /* We are going to DELETE part of the clip here. The clip may - * have envelope points, and we need to ensure that the envelope - * outside of the cleared region is not affected. This means - * putting in "glue" points where the clip enters and leaves the - * region being cleared. If one of the ends of the clip is inside - * the region, then one of the glue points will be redundant. */ // clip->Clear keeps points < t0 and >= t1 via Envelope::CollapseRegion - if (clip->GetEnvelope()->GetNumberOfPoints() > 0) { // don't insert env pts if none exist - double val; - if (clip->WithinClip(t0)) { - // start of region within clip - val = clip->GetEnvelope()->GetValue(t0); - newClip->GetEnvelope()->InsertOrReplace(t0 - 1.0 / clip->GetRate(), val); - } - if (clip->WithinClip(t1)) - { // end of region within clip - val = clip->GetEnvelope()->GetValue(t1); - newClip->GetEnvelope()->InsertOrReplace(t1 , val); - } - } newClip->Clear(t0,t1); newClip->GetEnvelope()->RemoveUnneededPoints(t0);