From 0126918fb523d05ed8a142d8dd67300a9bb5931b Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 9 May 2017 12:13:08 -0400 Subject: [PATCH 1/5] Simplify InsertOrReplaceRelative, always trim when to domain... ... Comment in it says that it does not check for discontinuities if it replaces! This will not matter in the uses of it internal to the Envelope class, which will in fact always be proper insertions, having checked first for the presence of a point. --- src/Envelope.cpp | 37 +++++++++++-------------------------- src/Envelope.h | 1 + 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index b2144e764..a4777bd4c 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -1107,35 +1107,20 @@ int Envelope::InsertOrReplaceRelative(double when, double value) #endif int len = mEnv.size(); + when = std::max( 0.0, std::min( mTrackLen, when ) ); - if (len && when < 0.0) - return 0; - if ((len > 1) && when > mTrackLen) - return len - 1; + auto range = EqualRange( when, 0 ); + int index = range.first; - if (when < 0.0) - when = 0.0; - if ((len>1) && when > mTrackLen) - when = mTrackLen; - - int i = 0; - - while (i < len && when > mEnv[i].GetT()) - i++; - - if(i < len && when == mEnv[i].GetT()) - // modify existing - mEnv[i].SetVal( this, value ); - else { + if ( index < range.second ) + // modify existing + // In case of a discontinuity, ALWAYS CHANGING LEFT LIMIT ONLY! + mEnv[ index ].SetVal( this, value ); + else // Add NEW - EnvPoint e{ when, value }; - if (i < len) { - Insert(i, e); - } else { - mEnv.push_back(e); - } - } - return i; + Insert( index, EnvPoint { when, value } ); + + return index; } std::pair Envelope::EqualRange( double when, double sampleDur ) const diff --git a/src/Envelope.h b/src/Envelope.h index eb358ec0f..37e2d4598 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -196,6 +196,7 @@ public: private: int InsertOrReplaceRelative(double when, double value); + friend class EnvelopeEditor; /** \brief Accessor for points */ const EnvPoint &operator[] (int index) const From bd4d2dc31d675a6660a0fbda2515a987380ee884 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 28 May 2017 12:31:35 -0400 Subject: [PATCH 2/5] Rewrite Envelope::InsertSpace --- src/Envelope.cpp | 59 +++++++++++++++++++++++++++++++++++------------- src/Envelope.h | 3 +++ 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index a4777bd4c..a0a9b84d1 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -999,39 +999,66 @@ void Envelope::RemoveUnneededPoints(double time, double tolerence) } } -void Envelope::InsertSpace( double t0, double tlen ) +std::pair< int, int > Envelope::ExpandRegion + ( double t0, double tlen, double *pLeftVal, double *pRightVal ) // NOFAIL-GUARANTEE { - t0 -= mOffset; + // t0 is relative time - // Preserve the left-side limit at the split. - auto val = GetValueRelative( t0 ); - auto range = EqualRange( t0, 0 ); + double val; + const auto range = EqualRange( t0, 0 ); - size_t index; - if ( range.first < range.second ) + // Preserve the left-side limit. + int index = 1 + range.first; + if ( index <= range.second ) // There is already a control point. - index = 1 + range.first; - else + ; + else { // Make a control point. - index = 1 + InsertOrReplaceRelative( t0, val ); + val = GetValueRelative( t0 ); + Insert( range.first, EnvPoint{ t0, val } ); + } // Shift points. auto len = mEnv.size(); - for ( ; index < len; ++index ) { - auto &point = mEnv[ index ]; + for ( int ii = index; ii < len; ++ii ) { + auto &point = mEnv[ ii ]; point.SetT( point.GetT() + tlen ); } - // increase track len, before insert or replace, - // since it range chacks the values. mTrackLen += tlen; + // Preserve the right-side limit. - if ( 1 + range.first < range.second ) + if ( index < range.second ) // There was a control point already. ; else - InsertOrReplaceRelative( t0 + tlen, val ); + // Make a control point. + Insert( index, EnvPoint{ t0 + tlen, val } ); + + // Make discontinuities at ends, maybe: + + if ( pLeftVal ) + // Make a discontinuity at the left side of the expansion + Insert( index++, EnvPoint{ t0, *pLeftVal } ); + + if ( pRightVal ) + // Make a discontinuity at the right side of the expansion + Insert( index++, EnvPoint{ t0 + tlen, *pRightVal } ); + + // Return the range of indices that includes the inside limiting points, + // none, one, or two + return { 1 + range.first, index }; +} + +void Envelope::InsertSpace( double t0, double tlen ) +// NOFAIL-GUARANTEE +{ + auto range = ExpandRegion( t0 - mOffset, tlen, nullptr, nullptr ); + + // Simplify the boundaries if possible + RemoveUnneededPoints( range.second, true ); + RemoveUnneededPoints( range.first - 1, false ); } int Envelope::Reassign(double when, double value) diff --git a/src/Envelope.h b/src/Envelope.h index 37e2d4598..2e7145717 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -154,6 +154,9 @@ public: void Cap( double sampleDur ); private: + std::pair< int, int > ExpandRegion + ( double t0, double tlen, double *pLeftVal, double *pRightVal ); + void RemoveUnneededPoints( size_t startAt, bool rightward ); double GetValueRelative(double t) const; From 02fe963d2310ee1b6c14b09e84530b853938233c Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 4 May 2017 14:26:54 -0400 Subject: [PATCH 3/5] Define consistency check for Envelope, to be used in Paste --- src/Envelope.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ src/Envelope.h | 6 +++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index a0a9b84d1..0cb07698c 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -56,6 +56,55 @@ Envelope::~Envelope() { } +bool Envelope::ConsistencyCheck() +{ + bool consistent = true; + + bool disorder; + do { + disorder = false; + for ( size_t ii = 0, count = mEnv.size(); ii < count; ) { + // Find range of points with equal T + const double thisT = mEnv[ii].GetT(); + double nextT; + auto nextI = ii + 1; + while ( nextI < count && thisT == ( nextT = mEnv[nextI].GetT() ) ) + ++nextI; + + if ( nextI < count && nextT < thisT ) + disorder = true; + + while ( nextI - ii > 2 ) { + // too many coincident time values + if (ii == mDragPoint || nextI - 1 == mDragPoint) + // forgivable + ; + else { + consistent = false; + // repair it + Delete( nextI - 2 ); + if (mDragPoint >= nextI - 2) + --mDragPoint; + --nextI, --count; + // wxLogError + } + } + + ii = nextI; + } + + if (disorder) { + consistent = false; + // repair it + std::stable_sort( mEnv.begin(), mEnv.end(), + []( const EnvPoint &a, const EnvPoint &b ) + { return a.GetT() < b.GetT(); } ); + } + } while ( disorder ); + + return consistent; +} + /// Rescale function for time tracks (could also be used for other tracks though). /// This is used to load old time track project files where the envelope used a 0 to 1 /// range instead of storing the actual time track values. This function will change the range of the envelope diff --git a/src/Envelope.h b/src/Envelope.h index 2e7145717..4a5ee1af8 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -86,7 +86,11 @@ public: void Initialize(int numPoints); - virtual ~ Envelope(); + virtual ~Envelope(); + + // Return true if violations of point ordering invariants were detected + // and repaired + bool ConsistencyCheck(); double GetOffset() const { return mOffset; } double GetTrackLen() const { return mTrackLen; } From 58e7a942641b2e2cbf57ff6ae79fe7ac1dc09a34 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 5 May 2017 20:43:32 -0400 Subject: [PATCH 4/5] Envelope::Paste takes a time tolerance argument --- src/Envelope.cpp | 2 +- src/Envelope.h | 6 +++++- src/TimeTrack.cpp | 4 +++- src/WaveClip.cpp | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 0cb07698c..d48a62dc1 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -734,7 +734,7 @@ void Envelope::CollapseRegion( double t0, double t1, double sampleDur ) // envelope point applies to the first sample, but the t=tracklen // envelope point applies one-past the last actual sample. // Rather than going to a .5-offset-index, we special case the framing. -void Envelope::Paste(double t0, const Envelope *e) +void Envelope::Paste(double t0, const Envelope *e, double sampleDur) // NOFAIL-GUARANTEE { const bool wasEmpty = (this->mEnv.size() == 0); diff --git a/src/Envelope.h b/src/Envelope.h index 4a5ee1af8..587c59168 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -125,7 +125,11 @@ public: // sampleDur 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 sampleDur); - void Paste(double t0, const Envelope *e); + + // Envelope has no notion of rate and control point times are not quantized; + // but a tolerance is needed in the Paste routine, and better to inform it + // of an appropriate number, than use hidden arbitrary constants. + void Paste(double t0, const Envelope *e, double sampleDur); 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 f6a05ed04..55ec5b346 100644 --- a/src/TimeTrack.cpp +++ b/src/TimeTrack.cpp @@ -122,7 +122,9 @@ void TimeTrack::Paste(double t, const Track * src) // THROW_INCONSISTENCY_EXCEPTION; // ? return; - mEnvelope->Paste(t, static_cast(src)->mEnvelope.get()); + auto sampleTime = 1.0 / GetActiveProject()->GetRate(); + mEnvelope->Paste + (t, static_cast(src)->mEnvelope.get(), sampleTime); } void TimeTrack::Silence(double t0, double t1) diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 238e3798a..5b9a60288 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -1600,7 +1600,9 @@ void WaveClip::Paste(double t0, const WaveClip* other) // Assume NOFAIL-GUARANTEE in the remaining MarkChanged(); - mEnvelope->Paste(s0.as_double()/mRate + mOffset, pastedClip->mEnvelope.get()); + auto sampleTime = 1.0 / GetRate(); + mEnvelope->Paste + (s0.as_double()/mRate + mOffset, pastedClip->mEnvelope.get(), sampleTime); mEnvelope->RemoveUnneededPoints(); OffsetCutLines(t0, pastedClip->GetEndTime() - pastedClip->GetStartTime()); From 4c80a074d3c779511d4e0530ae5b9152be7cdf51 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 28 May 2017 13:40:40 -0400 Subject: [PATCH 5/5] Bug835(cut-then-paste should be no-op): Rewrite Envelope::Paste... ... Simplify. Don't assume e->mOffset is zero. Check consistency afterward. Do not leave responsibility for simplification of discontinuities to higher level code. Allow real discontinuities at the end of the insertion; no more readjustment of times of points. --- src/Envelope.cpp | 282 ++++++++++------------------------------------- src/Envelope.h | 4 +- src/WaveClip.cpp | 1 - 3 files changed, 63 insertions(+), 224 deletions(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index d48a62dc1..3416bcd00 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -733,206 +733,85 @@ void Envelope::CollapseRegion( double t0, double t1, double sampleDur ) // a track's envelope runs the range from t=0 to t=tracklen; the t=0 // envelope point applies to the first sample, but the t=tracklen // envelope point applies one-past the last actual sample. -// Rather than going to a .5-offset-index, we special case the framing. -void Envelope::Paste(double t0, const Envelope *e, double sampleDur) +// t0 should be in the domain of this; if not, it is trimmed. +void Envelope::Paste( double t0, const Envelope *e, double sampleDur ) // NOFAIL-GUARANTEE { const bool wasEmpty = (this->mEnv.size() == 0); + auto otherSize = e->mEnv.size(); + const double otherDur = e->mTrackLen; + const auto otherOffset = e->mOffset; + const auto deltat = otherOffset + otherDur; - // JC: The old analysis of cases and the resulting code here is way more complex than needed. - // TODO: simplify the analysis and simplify the code. - - if (e->mEnv.size() == 0 && wasEmpty && e->mDefaultValue == this->mDefaultValue) + if ( otherSize == 0 && wasEmpty && e->mDefaultValue == this->mDefaultValue ) { // msmeyer: The envelope is empty and has the same default value, so // there is nothing that must be inserted, just return. This avoids // the creation of unnecessary duplicate control points // MJS: but the envelope does get longer - mTrackLen += e->mTrackLen; + // PRL: Assuming t0 is in the domain of the envelope + mTrackLen += deltat; return; } - t0 = wxMin(t0 - mOffset, mTrackLen); // t0 now has origin of zero - double deltat = e->mTrackLen; + // Make t0 relative and trim it to the domain of this + t0 = std::min( mTrackLen, std::max( 0.0, t0 - mOffset ) ); - unsigned int i; - unsigned int pos = 0; - bool someToShift = false; - bool atStart = false; - bool beforeStart = false; - bool atEnd = false; - bool afterEnd = false; - bool onPoint = false; - unsigned int len = mEnv.size(); - - // get values to perform framing of the insertion - const double splitval = GetValueRelative( t0 ); - -/* -Old analysis of cases: -(see discussions on audacity-devel around 19/8/7 - 23/8/7 and beyond, "Envelopes and 'Join'") -1 9 11 2 3 5 7 8 6 4 13 12 -0-----0--0---0 -----0---0------ --(0)---- - -1 The insert point is at the beginning of the current env, and it is a control point. -2 The insert point is at the end of the current env, and it is a control point. -3 The insert point is at the beginning of the current env, and it is not a control point. -4 The insert point is at the end of the current env, and it is not a control point. -5 The insert point is not at a control point, and there is space either side. -6 As 5. -7 The insert point is at a control point, and there is space either side. -8 Same as 7. -9 Same as 5. -10 There are no points in the current envelope (commonly called by the 'undo' stuff, and not in the diagrams). -11 As 7. -12 Insert beyond the RH end of the current envelope (should not happen, at the moment) -13 Insert beyond the LH end of the current envelope (should not happen, at the moment) -*/ - -// JC: Simplified Analysis: -// In pasting in a clip we choose to preserve the envelope so that the loudness of the -// parts is unchanged. -// -// 1) This may introduce a discontinuity in the envelope at a boundary between the -// old and NEW clips. In that case we must ensure there are envelope points -// at sample positions immediately before and immediately after the boundary. -// 2) If the points have the same value we only need one of them. -// 3) If the points have the same value AND it is the same as the value interpolated -// from the rest of the envelope then we don't need it at all. -// -// We do the same for the left and right edge of the NEW clip. -// -// Even simpler: we could always add two points at a boundary and then call -// RemoveUnneededPoints() (provided that function behaves correctly). - - // See if existing points need shifting to the right, and what Case we are in - if(len != 0) { - // Not case 10: there are point/s in the envelope - for (i = 0; i < len; i++) { - if (mEnv[i].GetT() > t0) - someToShift = true; - else { - pos = i; // last point not moved - if ( fabs(mEnv[i].GetT() - t0) - 1/500000.0 < 0.0 ) // close enough to a point - onPoint = true; - } - } - - // In these statements, remember we subtracted mOffset from t0 - if( t0 < mTrackEpsilon ) - atStart = true; - if( (mTrackLen - t0) < mTrackEpsilon ) - atEnd = true; - if(0 > t0) - // Case 13 - beforeStart = true; - if(mTrackLen < t0) - // Case 12 - afterEnd = true; - - // Now test for the various Cases, and try to do the right thing - if(atStart) { - // insertion at the beginning - if(onPoint) { - // Case 1: move it R slightly to avoid duplicate point - // first env point is at LH end - mEnv[0].SetT(mEnv[0].GetT() + mTrackEpsilon); - someToShift = true; // there is now, even if there wasn't before - //wxLogDebug(wxT("Case 1")); - } - else { - // Case 3: insert a point to maintain the envelope - InsertOrReplaceRelative(t0 + mTrackEpsilon, splitval); - someToShift = true; - //wxLogDebug(wxT("Case 3")); - } - } - else { - if(atEnd) { - // insertion at the end - if(onPoint) { - // last env point is at RH end, Case 2: - // move it L slightly to avoid duplicate point - mEnv[0].SetT(mEnv[0].GetT() - mTrackEpsilon); - //wxLogDebug(wxT("Case 2")); - } - else { - // Case 4: - // insert a point to maintain the envelope - InsertOrReplaceRelative(t0 - mTrackEpsilon, splitval); - //wxLogDebug(wxT("Case 4")); - } - } - else if(onPoint) { - // Case 7: move the point L and insert a NEW one to the R - mEnv[pos].SetT(mEnv[pos].GetT() - mTrackEpsilon); - InsertOrReplaceRelative(t0 + mTrackEpsilon, splitval); - someToShift = true; - //wxLogDebug(wxT("Case 7")); - } - else if( !beforeStart && !afterEnd ) { - // Case 5: Insert points to L and R - InsertOrReplaceRelative(t0 - mTrackEpsilon, splitval); - InsertOrReplaceRelative(t0 + mTrackEpsilon, splitval); - someToShift = true; - //wxLogDebug(wxT("Case 5")); - } - else if( beforeStart ) { - // Case 13: - //wxLogDebug(wxT("Case 13")); - } - else { - // Case 12: - //wxLogDebug(wxT("Case 12")); - } - } - - // Now shift existing points to the right, if required - if(someToShift) { - len = mEnv.size(); // it may well have changed - for (i = 0; i < len; i++) - if (mEnv[i].GetT() > t0) - mEnv[i].SetT(mEnv[i].GetT() + deltat); - } - mTrackLen += deltat; - } - else { - // Case 10: - if( mTrackLen == 0 ) // creating a NEW envelope - { - mTrackLen = e->mTrackLen; - mOffset = e->mOffset; - //wxLogDebug(wxT("Case 10, NEW env/clip: mTrackLen %f mOffset %f t0 %f"), mTrackLen, mOffset, t0); - } - else - { - mTrackLen += e->mTrackLen; - //wxLogDebug(wxT("Case 10, paste into current env: mTrackLen %f mOffset %f t0 %f"), mTrackLen, mOffset, t0); - } + // Adjust if the insertion point rounds off near a discontinuity in this + if ( true ) + { + double newT0; + auto range = EqualRange( t0, sampleDur ); + auto index = range.first; + if ( index + 2 == range.second && + ( newT0 = mEnv[ index ].GetT() ) == mEnv[ 1 + index ].GetT() ) + t0 = newT0; } - // Copy points from inside the selection + // Open up a space + double leftVal = e->GetValue( 0 ); + double rightVal = e->GetValueRelative( otherDur ); + // This range includes the right-side limit of the left end of the space, + // and the left-side limit of the right end: + const auto range = ExpandRegion( t0, deltat, &leftVal, &rightVal ); + // Where to put the copied points from e -- after the first of the + // two points in range: + auto insertAt = range.first + 1; - if (!wasEmpty) { - // Add end points in case they are not not in e. - // If they are in e, no harm, because the repeated Insert - // calls for the start and end times will have no effect. - const double leftval = e->GetValueRelative( 0 ); - const double rightval = e->GetValueRelative( e->mTrackLen ); - InsertOrReplaceRelative(t0, leftval); - InsertOrReplaceRelative(t0 + e->mTrackLen, rightval); + // Copy points from e -- maybe skipping those at the extremes + auto end = e->mEnv.end(); + if ( otherSize != 0 && e->mEnv[ otherSize - 1 ].GetT() == otherDur ) + // ExpandRegion already made an equivalent limit point + --end, --otherSize; + auto begin = e->mEnv.begin(); + if ( otherSize != 0 && otherOffset == 0.0 && e->mEnv[ 0 ].GetT() == 0.0 ) + ++begin, --otherSize; + mEnv.insert( mEnv.begin() + insertAt, begin, end ); + + // Adjust their times + for ( size_t index = insertAt, last = insertAt + otherSize; + index < last; ++index ) { + auto &point = mEnv[ index ]; + point.SetT( point.GetT() + otherOffset + t0 ); } - len = e->mEnv.size(); - for (i = 0; i < len; i++) - InsertOrReplaceRelative(t0 + e->mEnv[i].GetT(), e->mEnv[i].GetVal()); + // Treat removable discontinuities + // Right edge outward: + RemoveUnneededPoints( insertAt + otherSize + 1, true ); + // Right edge inward: + RemoveUnneededPoints( insertAt + otherSize, false, false ); -/* if(len != 0) - for (i = 0; i < mEnv.size(); i++) - wxLogDebug(wxT("Fixed i %d when %.18f val %f"),i,mEnv[i].GetT(),mEnv[i].GetVal()); */ + // Left edge inward: + RemoveUnneededPoints( range.first, true, false ); + // Left edge outward: + RemoveUnneededPoints( range.first - 1, false ); + + // Guarantee monotonicity of times, against little round-off mistakes perhaps + ConsistencyCheck(); } -void Envelope::RemoveUnneededPoints( size_t startAt, bool rightward ) +void Envelope::RemoveUnneededPoints + ( size_t startAt, bool rightward, bool testNeighbors ) // NOFAIL-GUARANTEE { // startAt is the index of a recently inserted point which might make no @@ -986,6 +865,9 @@ void Envelope::RemoveUnneededPoints( size_t startAt, bool rightward ) // The given point was removable. Done! return; + if ( !testNeighbors ) + return; + // The given point was not removable. But did its insertion make nearby // points removable? @@ -1006,48 +888,6 @@ void Envelope::RemoveUnneededPoints( size_t startAt, bool rightward ) } } -// Deletes 'unneeded' points, starting from the left. -// If 'time' is set and positive, just deletes points in a small region -// around that value. -// 'Unneeded' means that the envelope doesn't change by more than -// 'tolerence' without the point being there. -void Envelope::RemoveUnneededPoints(double time, double tolerence) -// NOFAIL-GUARANTEE -{ - unsigned int len = mEnv.size(); - unsigned int i; - double when, val, val1; - - if(mEnv.size() == 0) - return; - - for (i = 0; i < len; i++) { - when = mEnv[i].GetT(); - if(time >= 0) - { - if(fabs(when + mOffset - time) > 0.00025) // 2 samples at 8kHz, 11 at 44.1kHz - continue; - } - val = mEnv[i].GetVal(); - Delete(i); // try it to see if it's doing anything - val1 = GetValue(when + mOffset); - bool bExcludePoint = true; - if( fabs(val -val1) > tolerence ) - { - InsertOrReplaceRelative(when, val); // put it back, we needed it - - //Insert may have modified instead of inserting, if two points were at the same time. - // in which case len needs to shrink i and len, because the array size decreased. - bExcludePoint = (mEnv.size() < len); - } - - if( bExcludePoint ) { // it made no difference so leave it out - len--; - i--; - } - } -} - std::pair< int, int > Envelope::ExpandRegion ( double t0, double tlen, double *pLeftVal, double *pRightVal ) // NOFAIL-GUARANTEE diff --git a/src/Envelope.h b/src/Envelope.h index 587c59168..e11982a5e 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -132,7 +132,6 @@ public: void Paste(double t0, const Envelope *e, double sampleDur); void InsertSpace(double t0, double tlen); - void RemoveUnneededPoints(double time = -1, double tolerence = 0.001); // Control void SetOffset(double newOffset); @@ -165,7 +164,8 @@ private: std::pair< int, int > ExpandRegion ( double t0, double tlen, double *pLeftVal, double *pRightVal ); - void RemoveUnneededPoints( size_t startAt, bool rightward ); + void RemoveUnneededPoints + ( size_t startAt, bool rightward, bool testNeighbors = true ); double GetValueRelative(double t) const; void GetValuesRelative diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 5b9a60288..2fad8ed8c 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -1603,7 +1603,6 @@ void WaveClip::Paste(double t0, const WaveClip* other) auto sampleTime = 1.0 / GetRate(); mEnvelope->Paste (s0.as_double()/mRate + mOffset, pastedClip->mEnvelope.get(), sampleTime); - mEnvelope->RemoveUnneededPoints(); OffsetCutLines(t0, pastedClip->GetEndTime() - pastedClip->GetStartTime()); for (auto &holder : newCutlines)