From 2e7f806e90ab716d322996a40aac66ccbc2f6cd5 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 7 May 2017 15:28:48 -0400 Subject: [PATCH 1/9] Fix EqualRange for case of zero tolerance --- src/Envelope.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 4a84e361c..319616046 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -1012,7 +1012,7 @@ std::pair Envelope::EqualRange( double when, double sampleTime ) const { return point1.GetT() < point2.GetT(); } ); auto after = first; - while ( after != end && after->GetT() < when + tolerance ) + while ( after != end && after->GetT() <= when + tolerance ) ++after; return { first - begin, after - begin }; } From 01b99f2849b3943efa73b0ee33f3013c4be71e12 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 7 May 2017 19:16:19 -0400 Subject: [PATCH 2/9] Remove pointer back to Envelope from EnvPoint --- src/Envelope.cpp | 27 ++++++++++++--------------- src/Envelope.h | 25 ++++++++++--------------- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 319616046..75f5dfe3d 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -74,7 +74,7 @@ void Envelope::RescaleValues(double minValue, double maxValue) // rescale all points for( unsigned int i = 0; i < mEnv.size(); i++ ) { factor = (mEnv[i].GetVal() - oldMinValue) / (oldMaxValue - oldMinValue); - mEnv[i].SetVal(mMinValue + (mMaxValue - mMinValue) * factor); + mEnv[i].SetVal( this, mMinValue + (mMaxValue - mMinValue) * factor ); } } @@ -112,13 +112,13 @@ void Envelope::SetDragPointValid(bool valid) // off screen and at default height. // temporary state when dragging only! mEnv[mDragPoint].SetT(big); - mEnv[mDragPoint].SetVal(mDefaultValue); + mEnv[mDragPoint].SetVal( this, mDefaultValue ); return; } else if ( mDragPoint + 1 == size ) { // Put the point at the height of the last point, but also off screen. mEnv[mDragPoint].SetT(big); - mEnv[mDragPoint].SetVal(mEnv[ size - 1 ].GetVal()); + mEnv[mDragPoint].SetVal( this, mEnv[ size - 1 ].GetVal() ); } else { // Place it exactly on its right neighbour. @@ -126,7 +126,7 @@ void Envelope::SetDragPointValid(bool valid) // a light dot, as if it were deleted. const auto &neighbor = mEnv[mDragPoint + 1]; mEnv[mDragPoint].SetT(neighbor.GetT()); - mEnv[mDragPoint].SetVal(neighbor.GetVal()); + mEnv[mDragPoint].SetVal( this, neighbor.GetVal() ); } } } @@ -154,7 +154,7 @@ void Envelope::MoveDragPoint(double newWhen, double value) // This might temporary violate the constraint that at most two // points share a time value. dragPoint.SetT(tt); - dragPoint.SetVal(value); + dragPoint.SetVal( this, value ); } void Envelope::ClearDragPoint() @@ -171,12 +171,12 @@ void Envelope::SetRange(double minValue, double maxValue) { mMaxValue = maxValue; mDefaultValue = ClampValue(mDefaultValue); for( unsigned int i = 0; i < mEnv.size(); i++ ) - mEnv[i].SetVal(mEnv[i].GetVal()); // this clamps the value to the NEW range + mEnv[i].SetVal( this, mEnv[i].GetVal() ); // this clamps the value to the NEW range } EnvPoint *Envelope::AddPointAtEnd( double t, double val ) { - mEnv.push_back(EnvPoint(this, t, val)); + mEnv.push_back( EnvPoint{ t, val } ); return &mEnv.back(); } @@ -898,7 +898,7 @@ int Envelope::Reassign(double when, double value) if (i >= len || when < mEnv[i].GetT()) return -1; - mEnv[i].SetVal(value); + mEnv[i].SetVal( this, value ); return 0; } @@ -978,15 +978,12 @@ int Envelope::InsertOrReplaceRelative(double when, double value) while (i < len && when > mEnv[i].GetT()) i++; - if(i < len && when == mEnv[i].GetT()) { - + if(i < len && when == mEnv[i].GetT()) // modify existing - mEnv[i].SetVal(value); - - } + mEnv[i].SetVal( this, value ); else { // Add NEW - EnvPoint e(this, when, value); + EnvPoint e{ when, value }; if (i < len) { Insert(i, e); } else { @@ -1007,7 +1004,7 @@ std::pair Envelope::EqualRange( double when, double sampleTime ) const auto end = mEnv.end(); auto first = std::lower_bound( begin, end, - EnvPoint{ const_cast( this ), when - tolerance, 0.0 }, + EnvPoint{ when - tolerance, 0.0 }, []( const EnvPoint &point1, const EnvPoint &point2 ) { return point1.GetT() < point2.GetT(); } ); diff --git a/src/Envelope.h b/src/Envelope.h index d560b5358..5bec859d1 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -36,12 +36,13 @@ class ZoomInfo; class EnvPoint final : public XMLTagHandler { public: - inline EnvPoint(Envelope *envelope, double t, double val); + EnvPoint() {} + inline EnvPoint( double t, double val ) : mT{ t }, mVal{ val } {} double GetT() const { return mT; } void SetT(double t) { mT = t; } double GetVal() const { return mVal; } - inline void SetVal(double val); + inline void SetVal( Envelope *pEnvelope, double val ); bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override { @@ -52,7 +53,7 @@ public: if (!wxStrcmp(attr, wxT("t"))) SetT(Internat::CompatibleToDouble(value)); else if (!wxStrcmp(attr, wxT("val"))) - SetVal(Internat::CompatibleToDouble(value)); + SetVal( nullptr, Internat::CompatibleToDouble(value) ); } return true; } @@ -66,9 +67,8 @@ public: } private: - Envelope *mEnvelope; - double mT; - double mVal; + double mT {}; + double mVal {}; }; @@ -243,16 +243,11 @@ private: mutable int mSearchGuess { -2 }; }; -inline EnvPoint::EnvPoint(Envelope *envelope, double t, double val) +inline void EnvPoint::SetVal( Envelope *pEnvelope, double val ) { - mEnvelope = envelope; - mT = t; - mVal = mEnvelope->ClampValue(val); -} - -inline void EnvPoint::SetVal(double val) -{ - mVal = mEnvelope->ClampValue(val); + if ( pEnvelope ) + val = pEnvelope->ClampValue(val); + mVal = val; } // A class that holds state for the duration of dragging From f5a7e4ce7cc976719e6d19e303ee3ec1e37986ee Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 7 May 2017 19:18:12 -0400 Subject: [PATCH 3/9] Define private Envelope evaluator functions taking relative time... ... To be very sure we can avoid roundoff errors from adding mOffset and subtracting it again; so that evaluation exactly at a control point time gives the exact value of that point. --- src/Envelope.cpp | 13 +++++++++++++ src/Envelope.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 75f5dfe3d..42b938d93 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -1060,6 +1060,14 @@ double Envelope::GetValue(double t) const return temp; } +double Envelope::GetValueRelative(double t) const +{ + double temp; + + GetValuesRelative(&temp, 1, t, 1.0); + return temp; +} + // relative time /// @param Lo returns last index at or before this time, maybe -1 /// @param Hi returns first index after this time, maybe past the end @@ -1126,7 +1134,12 @@ void Envelope::GetValues(double *buffer, int bufferLen, { // Convert t0 from absolute to clip-relative time t0 -= mOffset; + GetValuesRelative( buffer, bufferLen, t0, tstep); +} +void Envelope::GetValuesRelative(double *buffer, int bufferLen, + double t0, double tstep) const +{ // JC: If bufferLen ==0 we have probably just allocated a zero sized buffer. // wxASSERT( bufferLen > 0 ); diff --git a/src/Envelope.h b/src/Envelope.h index 5bec859d1..2c9d89ce1 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -145,6 +145,9 @@ public: (double *buffer, int bufferLen, int leftOffset, const ZoomInfo &zoomInfo) const; private: + double GetValueRelative(double t) const; + void GetValuesRelative + (double *buffer, int len, double t0, double tstep) const; // relative time int NumberOfPointsAfter(double t) const; // relative time From 945e411e2c3a1475418c39f05730649ee4576f68 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 7 May 2017 12:20:53 -0400 Subject: [PATCH 4/9] Partial range copy of Envelope will not leave coincident points --- src/Envelope.cpp | 20 +++++++++++++++++--- src/Envelope.h | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 42b938d93..5ac20dacd 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -174,10 +174,23 @@ void Envelope::SetRange(double minValue, double maxValue) { mEnv[i].SetVal( this, mEnv[i].GetVal() ); // this clamps the value to the NEW range } -EnvPoint *Envelope::AddPointAtEnd( double t, double val ) +// This is used only during construction of an Envelope by complete or partial +// copy of another. +void Envelope::AddPointAtEnd( double t, double val ) { mEnv.push_back( EnvPoint{ t, val } ); - return &mEnv.back(); + + // Assume copied points were stored by nondecreasing time. + // Allow no more than two points at exactly the same time. + // Maybe that happened, because extra points were inserted at the boundary + // of the copied range, which were not in the source envelope. + auto nn = mEnv.size() - 1; + while ( nn >= 2 && mEnv[ nn - 2 ].GetT() == t ) { + // Of three or more points at the same time, erase one in the middle, + // not the one newly added. + mEnv.erase( mEnv.begin() + nn - 1 ); + --nn; + } } Envelope::Envelope(const Envelope &orig, double t0, double t1) @@ -342,7 +355,8 @@ XMLTagHandler *Envelope::HandleXMLChild(const wxChar *tag) if (wxStrcmp(tag, wxT("controlpoint"))) return NULL; - return AddPointAtEnd(0,0); + mEnv.push_back( EnvPoint{} ); + return &mEnv.back(); } void Envelope::WriteXML(XMLWriter &xmlFile) const diff --git a/src/Envelope.h b/src/Envelope.h index 2c9d89ce1..13cec2917 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -215,7 +215,7 @@ private: void MoveDragPoint(double newWhen, double value); // May delete the drag point. Restores envelope consistency. void ClearDragPoint(); - EnvPoint * AddPointAtEnd( double t, double val ); + void AddPointAtEnd( double t, double val ); void CopyRange(const Envelope &orig, size_t begin, size_t end); // relative time void BinarySearchForTime( int &Lo, int &Hi, double t ) const; From 2d84c65c9446e9039075cb89eb5889540d75d45a Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 4 May 2017 08:56:51 -0400 Subject: [PATCH 5/9] When Envelope domain is shortened, evaluation inside won't change... ... It doesn't happen in practice yet, all present calls to SetTrackLen make the track the same length or longer. But this is for completeness. --- src/Envelope.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 5ac20dacd..fa92b5ebc 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -175,7 +175,7 @@ void Envelope::SetRange(double minValue, double maxValue) { } // This is used only during construction of an Envelope by complete or partial -// copy of another. +// copy of another, or when truncating a track. void Envelope::AddPointAtEnd( double t, double val ) { mEnv.push_back( EnvPoint{ t, val } ); @@ -1037,16 +1037,20 @@ void Envelope::SetOffset(double newOffset) } void Envelope::SetTrackLen(double trackLen) +// NOFAIL-GUARANTEE { - mTrackLen = trackLen; + // Preserve the right-side limit at trackLen. + bool needPoint = ( trackLen < mTrackLen ); + double value; + if ( needPoint ) + value = GetValueRelative( trackLen ); - int len = mEnv.size(); - for (int i = 0; i < len; i++) - if (mEnv[i].GetT() > mTrackLen) { - Delete(i); - len--; - i--; - } + mTrackLen = trackLen; + int newLen = EqualRange( trackLen, 0 ).second; + mEnv.resize( newLen ); + + if ( needPoint ) + AddPointAtEnd( mTrackLen, value ); } void Envelope::RescaleTimes( double newLength ) From aba52bc79ecc10d8625001092b297785e2074d11 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 3 May 2017 08:48:24 -0400 Subject: [PATCH 6/9] Update envelope properly for TimeTrack and WaveTrack editing... ... Formerly this was done correctly only for cut and delete from WaveTrack, paste into WaveTrack, and sync-lock adjustment of WaveTrack (either lengthening or shortening). Now also properly done for TimeTrack cut and paste, and also for: Split cut Split delete Trim --- src/Envelope.cpp | 68 ++++++++++++++++++++++++++++++++--------------- src/Envelope.h | 5 +++- src/TimeTrack.cpp | 4 ++- src/WaveClip.cpp | 6 +++-- src/WaveTrack.cpp | 19 ------------- 5 files changed, 58 insertions(+), 44 deletions(-) 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); From 8c4dc38047ec5f33e33bfdb1c0f8bb6eff824175 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 4 May 2017 14:07:23 -0400 Subject: [PATCH 7/9] WaveTrack::SplitAt no longer needs to insert envelope points... ... because partial-copy construction of the new WaveClip, and CollapseRegion within WaveClip::Clear, handle it. --- src/WaveTrack.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index e6a3ac7e1..a3b6a086b 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -2379,12 +2379,6 @@ void WaveTrack::SplitAt(double t) { double val; t = LongSamplesToTime(TimeToLongSamples(t)); // put t on a sample - val = c->GetEnvelope()->GetValue(t); - //make two envelope points to preserve the value. - //handle the case where we split on the 1st sample (without this we hit an assert) - if(t - 1.0/c->GetRate() >= c->GetOffset()) - c->GetEnvelope()->InsertOrReplace(t - 1.0 / c->GetRate(), val); // frame end points - c->GetEnvelope()->InsertOrReplace(t, val); auto newClip = make_movable( *c, mDirManager, true ); c->Clear(t, c->GetEndTime()); newClip->Clear(c->GetStartTime(), t); From a9160cf80359604974ab5c07186f7535d10deeb4 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 6 May 2017 00:31:43 -0400 Subject: [PATCH 8/9] Envelope::InsertSpace preserves limiting values of the split point --- src/Envelope.cpp | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 9d3aa6c2a..67eff8799 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -909,17 +909,37 @@ void Envelope::RemoveUnneededPoints(double time, double tolerence) } } -void Envelope::InsertSpace(double t0, double tlen) +void Envelope::InsertSpace( double t0, double tlen ) // NOFAIL-GUARANTEE { t0 -= mOffset; - unsigned int len = mEnv.size(); - unsigned int i; + // Preserve the left-side limit at the split. + auto val = GetValueRelative( t0 ); + auto range = EqualRange( t0, 0 ); + + size_t index; + if ( range.first < range.second ) + // There is already a control point. + index = 1 + range.first; + else + // Make a control point. + index = 1 + InsertOrReplaceRelative( t0, val ); + + // Shift points. + auto len = mEnv.size(); + for ( ; index < len; ++index ) { + auto &point = mEnv[ index ]; + point.SetT( point.GetT() + tlen ); + } + + // Preserve the right-side limit. + if ( 1 + range.first < range.second ) + // There was a control point already. + ; + else + InsertOrReplaceRelative( t0 + tlen, val ); - for (i = 0; i < len; i++) - if (mEnv[i].GetT() > t0) - mEnv[i].SetT(mEnv[i].GetT() + tlen); mTrackLen += tlen; } From 327e5d85573f9839e94d8fad77b5aeb186330f00 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 6 May 2017 00:57:38 -0400 Subject: [PATCH 9/9] Correct envelope update for Join that includes a gap between clips --- src/WaveClip.cpp | 29 +++++++++++++++++++++++++++-- src/WaveClip.h | 6 +++++- src/WaveTrack.cpp | 5 ++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index ed4e3ea2b..aea155503 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -1607,7 +1607,7 @@ void WaveClip::Paste(double t0, const WaveClip* other) mCutLines.push_back(std::move(holder)); } -void WaveClip::InsertSilence(double t, double len) +void WaveClip::InsertSilence( double t, double len, double *pEnvelopeValue ) // STRONG-GUARANTEE { sampleCount s0; @@ -1619,10 +1619,35 @@ void WaveClip::InsertSilence(double t, double len) // use NOFAIL-GUARANTEE OffsetCutLines(t, len); - GetEnvelope()->InsertSpace(t, len); + + const auto sampleTime = 1.0 / GetRate(); + auto pEnvelope = GetEnvelope(); + if ( pEnvelopeValue ) { + + // Preserve limit value at the end + auto oldLen = pEnvelope->GetTrackLen(); + auto oldT = pEnvelope->GetOffset() + oldLen; + auto newLen = oldLen + len; + pEnvelope->InsertOrReplace( oldT, pEnvelope->GetValue( oldT ) ); + + // Ramp across the silence to the given value + pEnvelope->SetTrackLen( newLen ); + pEnvelope->InsertOrReplace + ( pEnvelope->GetOffset() + newLen, *pEnvelopeValue ); + } + else + pEnvelope->InsertSpace( t, len ); + MarkChanged(); } +void WaveClip::AppendSilence( double len, double envelopeValue ) +// STRONG-GUARANTEE +{ + auto t = GetEndTime(); + InsertSilence( t, len, &envelopeValue ); +} + void WaveClip::Clear(double t0, double t1) // STRONG-GUARANTEE { diff --git a/src/WaveClip.h b/src/WaveClip.h index 1c45da356..0438b34d8 100644 --- a/src/WaveClip.h +++ b/src/WaveClip.h @@ -321,7 +321,11 @@ public: /** Insert silence - note that this is an efficient operation for large * amounts of silence */ - void InsertSilence(double t, double len); + void InsertSilence( double t, double len, double *pEnvelopeValue = nullptr ); + + /** Insert silence at the end, and causes the envelope to ramp + linearly to the given value */ + void AppendSilence( double len, double envelopeValue ); /// Get access to cut lines list WaveClipHolders &GetCutLines() { return mCutLines; } diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index a3b6a086b..21decc67c 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -1564,12 +1564,15 @@ void WaveTrack::Join(double t0, double t1) if (clip->GetOffset() - t > (1.0 / mRate)) { double addedSilence = (clip->GetOffset() - t); //printf("Adding %.6f seconds of silence\n"); - newClip->InsertSilence(t, addedSilence); + auto offset = clip->GetOffset(); + auto value = clip->GetEnvelope()->GetValue( offset ); + newClip->AppendSilence( addedSilence, value ); t += addedSilence; } //printf("Pasting at %.6f\n", t); newClip->Paste(t, clip); + t = newClip->GetEndTime(); auto it = FindClip(mClips, clip);