diff --git a/src/Envelope.cpp b/src/Envelope.cpp index d9299dfb7..f039393a9 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -42,28 +42,12 @@ a draggable point type. #include "DirManager.h" #include "TrackArtist.h" -Envelope::Envelope() +Envelope::Envelope(bool exponential, double minValue, double maxValue, double defaultValue) + : mDB(exponential) + , mMinValue(minValue) + , mMaxValue(maxValue) + , mDefaultValue { ClampValue(defaultValue) } { - mOffset = 0.0; - mTrackLen = 0.0; - - // Anything with a sample rate of no more than 200 KHz - // will have samples spaced apart by at least this amount, - // "epsilon". We use this to enforce that there aren't - // allowed to be multiple control points at the same t - // value. - mTrackEpsilon = 1.0 / 200000.0; - - mDB = true; - mDefaultValue = 1.0; - mDragPoint = -1; - - mMinValue = 1.0e-7; - mMaxValue = 2.0; - - mSearchGuess = -1; - - mDragPointValid = false; } Envelope::~Envelope() @@ -196,41 +180,51 @@ EnvPoint *Envelope::AddPointAtEnd( double t, double val ) return &mEnv.back(); } -void Envelope::CopyFrom(const Envelope *e, double t0, double t1) +Envelope::Envelope(const Envelope &orig, double t0, double t1) + : mDB(orig.mDB) + , mMinValue(orig.mMinValue) + , mMaxValue(orig.mMaxValue) + , mDefaultValue(orig.mDefaultValue) { - wxASSERT( t0 <= t1 ); + mOffset = wxMax(t0, orig.mOffset); + mTrackLen = wxMin(t1, orig.mOffset + orig.mTrackLen) - mOffset; - mOffset = wxMax(t0, e->mOffset); - mTrackLen = wxMin(t1, e->mOffset + e->mTrackLen) - mOffset; + auto range1 = orig.EqualRange( t0 - orig.mOffset, 0 ); + auto range2 = orig.EqualRange( t1 - orig.mOffset, 0 ); + CopyRange(orig, range1.first, range2.second); +} - mEnv.clear(); - int len = e->mEnv.size(); - int i = 0; +Envelope::Envelope(const Envelope &orig) + : mDB(orig.mDB) + , mMinValue(orig.mMinValue) + , mMaxValue(orig.mMaxValue) + , mDefaultValue(orig.mDefaultValue) +{ + mOffset = orig.mOffset; + mTrackLen = orig.mTrackLen; + CopyRange(orig, 0, orig.GetNumberOfPoints()); +} - // Skip the points that come before the copied region - while ((i < len) && e->mOffset + e->mEnv[i].GetT() <= t0) - i++; +void Envelope::CopyRange(const Envelope &orig, size_t begin, size_t end) +{ + int len = orig.mEnv.size(); + int i = begin; // Create the point at 0 if it needs interpolated representation - if (i>0) - AddPointAtEnd( 0, e->GetValue(mOffset) ); + if ( i > 0 ) + AddPointAtEnd(0, orig.GetValue(mOffset)); // Copy points from inside the copied region - while (i < len) { - const EnvPoint &point = e->mEnv[i]; - const double when = e->mOffset + point.GetT() - mOffset; - if (when < mTrackLen) { - AddPointAtEnd(when, point.GetVal()); - i++; - } - else - break; + for (; i < end; ++i) { + const EnvPoint &point = orig[i]; + const double when = point.GetT() + (orig.mOffset - mOffset); + AddPointAtEnd(when, point.GetVal()); } // Create the final point if it needs interpolated representation // If the last point of e was exatly at t1, this effectively copies it too. if (mTrackLen > 0 && i < len) - AddPointAtEnd( mTrackLen, e->GetValue(mOffset + mTrackLen)); + AddPointAtEnd( mTrackLen, orig.GetValue(mOffset + mTrackLen)); } /// Limit() limits a double value to a range. @@ -998,6 +992,27 @@ int Envelope::Insert(double when, double value) return i; } +std::pair Envelope::EqualRange( double when, double sampleTime ) const +{ + // Find range of envelope points matching the given time coordinate + // (within an interval of length sampleTime) + // by binary search; if empty, it still indicates where to + // insert. + const auto tolerance = sampleTime / 2; + auto begin = mEnv.begin(); + auto end = mEnv.end(); + auto first = std::lower_bound( + begin, end, + EnvPoint{ const_cast( this ), when - tolerance, 0.0 }, + []( const EnvPoint &point1, const EnvPoint &point2 ) + { return point1.GetT() < point2.GetT(); } + ); + auto after = first; + while ( after != end && after->GetT() < when + tolerance ) + ++after; + return { first - begin, after - begin }; +} + // Control void Envelope::SetOffset(double newOffset) diff --git a/src/Envelope.h b/src/Envelope.h index 97a46baaa..71114f148 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -75,8 +75,15 @@ private: typedef std::vector EnvArray; class Envelope final : public XMLTagHandler { - public: - Envelope(); +public: + // Envelope can define a piecewise linear function, or piecewise exponential. + Envelope(bool exponential, double minValue, double maxValue, double defaultValue); + + Envelope(const Envelope &orig); + + // Create from a subrange of another envelope. + Envelope(const Envelope &orig, double t0, double t1); + void Initialize(int numPoints); virtual ~ Envelope(); @@ -113,8 +120,6 @@ class Envelope final : public XMLTagHandler { // Handling Cut/Copy/Paste events void CollapseRegion(double t0, double t1); - // Takes absolute times, NOT offset-relative: - void CopyFrom(const Envelope * e, double t0, double t1); void Paste(double t0, const Envelope *e); void InsertSpace(double t0, double tlen); void RemoveUnneededPoints(double time = -1, double tolerence = 0.001); @@ -172,11 +177,13 @@ class Envelope final : public XMLTagHandler { private: friend class EnvelopeEditor; /** \brief Accessor for points */ - EnvPoint &operator[] (int index) + const EnvPoint &operator[] (int index) const { return mEnv[index]; } + std::pair EqualRange( double when, double sampleTime ) const; + public: /** \brief Returns the sets of when and value pairs */ void GetPoints(double *bufferWhen, @@ -199,6 +206,7 @@ public: private: EnvPoint * AddPointAtEnd( double t, double val ); + void CopyRange(const Envelope &orig, size_t begin, size_t end); void BinarySearchForTime( int &Lo, int &Hi, double t ) const; double GetInterpolationStartValueAtPoint( int iPoint ) const; @@ -206,25 +214,25 @@ private: EnvArray mEnv; /** \brief The time at which the envelope starts, i.e. the start offset */ - double mOffset; + double mOffset { 0.0 }; /** \brief The length of the envelope, which is the same as the length of the * underlying track (normally) */ - double mTrackLen; + double mTrackLen { 0.0 }; // TODO: mTrackEpsilon based on assumption of 200KHz. Needs review if/when // we support higher sample rates. /** \brief The shortest distance appart that points on an envelope can be * before being considered the same point */ - double mTrackEpsilon; - double mDefaultValue; + double mTrackEpsilon { 1.0 / 200000.0 }; bool mDB; double mMinValue, mMaxValue; + double mDefaultValue; // UI stuff - bool mDragPointValid; - int mDragPoint; + bool mDragPointValid { false }; + int mDragPoint { -1 }; - mutable int mSearchGuess; + mutable int mSearchGuess { -2 }; }; inline EnvPoint::EnvPoint(Envelope *envelope, double t, double val) diff --git a/src/TimeTrack.cpp b/src/TimeTrack.cpp index cfab32eef..08313c374 100644 --- a/src/TimeTrack.cpp +++ b/src/TimeTrack.cpp @@ -45,12 +45,9 @@ TimeTrack::TimeTrack(const std::shared_ptr &projDirManager, const Zo mRangeUpper = 1.1; mDisplayLog = false; - mEnvelope = std::make_unique(); + mEnvelope = std::make_unique(true, TIMETRACK_MIN, TIMETRACK_MAX, 1.0); mEnvelope->SetTrackLen(DBL_MAX); - mEnvelope->SetInterpolateDB(true); - mEnvelope->Flatten(1.0); mEnvelope->SetOffset(0); - mEnvelope->SetRange(TIMETRACK_MIN, TIMETRACK_MAX); SetDefaultName(_("Time Track")); SetName(GetDefaultName()); @@ -67,18 +64,12 @@ TimeTrack::TimeTrack(const TimeTrack &orig, double *pT0, double *pT1) { Init(orig); // this copies the TimeTrack metadata (name, range, etc) - ///@TODO: Give Envelope:: a copy-constructor instead of this? - mEnvelope = std::make_unique(); - mEnvelope->Flatten(1.0); - mEnvelope->SetTrackLen(DBL_MAX); - SetInterpolateLog(orig.GetInterpolateLog()); // this calls Envelope::SetInterpolateDB - mEnvelope->SetOffset(0); - mEnvelope->SetRange(orig.mEnvelope->GetMinValue(), orig.mEnvelope->GetMaxValue()); - if ( pT0 && pT1 ) - // restricted copy - mEnvelope->CopyFrom(orig.mEnvelope.get(), *pT0, *pT1); + if (pT0 && pT1) + mEnvelope = std::make_unique( *orig.mEnvelope, *pT0, *pT1 ); else - mEnvelope->Paste(0.0, orig.mEnvelope.get()); + mEnvelope = std::make_unique( *orig.mEnvelope ); + mEnvelope->SetTrackLen(DBL_MAX); + mEnvelope->SetOffset(0); ///@TODO: Give Ruler:: a copy-constructor instead of this? mRuler = std::make_unique(); diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 71923696f..1775f3f9f 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -309,7 +309,7 @@ WaveClip::WaveClip(const std::shared_ptr &projDirManager, mRate = rate; mSequence = std::make_unique(projDirManager, format); - mEnvelope = std::make_unique(); + mEnvelope = std::make_unique(true, 1e-7, 2.0, 1.0); mWaveCache = std::make_unique(); mSpecCache = std::make_unique(); @@ -328,10 +328,7 @@ WaveClip::WaveClip(const WaveClip& orig, mRate = orig.mRate; mSequence = std::make_unique(*orig.mSequence, projDirManager); - mEnvelope = std::make_unique(); - mEnvelope->Paste(0.0, orig.mEnvelope.get()); - mEnvelope->SetOffset(orig.GetOffset()); - mEnvelope->SetTrackLen((orig.mSequence->GetNumSamples().as_double()) / orig.mRate); + mEnvelope = std::make_unique(*orig.mEnvelope); mWaveCache = std::make_unique(); mSpecCache = std::make_unique(); @@ -345,7 +342,6 @@ WaveClip::WaveClip(const WaveClip& orig, mIsPlaceholder = orig.GetIsPlaceholder(); } -// to do WaveClip::WaveClip(const WaveClip& orig, const std::shared_ptr &projDirManager, bool copyCutlines, @@ -369,10 +365,11 @@ WaveClip::WaveClip(const WaveClip& orig, mSequence = orig.mSequence->Copy(s0, s1); - mEnvelope = std::make_unique(); - mEnvelope->CopyFrom(orig.mEnvelope.get(), - mOffset + s0.as_double()/mRate, - mOffset + s1.as_double()/mRate); + mEnvelope = std::make_unique( + *orig.mEnvelope, + mOffset + s0.as_double()/mRate, + mOffset + s1.as_double()/mRate + ); if ( copyCutlines ) // Copy cutline clips that fall in the range diff --git a/src/effects/Equalization.cpp b/src/effects/Equalization.cpp index 168bd34b8..ba869b119 100644 --- a/src/effects/Equalization.cpp +++ b/src/effects/Equalization.cpp @@ -239,13 +239,15 @@ EffectEqualization::EffectEqualization() mInterpolations.Add(wxGetTranslation(kInterpStrings[i])); } - mLogEnvelope = std::make_unique(); - mLogEnvelope->SetInterpolateDB(false); - mLogEnvelope->SetRange(MIN_dBMin, MAX_dBMax); // MB: this is the highest possible range + mLogEnvelope = std::make_unique + (false, + MIN_dBMin, MAX_dBMax, // MB: this is the highest possible range + 1.0); - mLinEnvelope = std::make_unique(); - mLinEnvelope->SetInterpolateDB(false); - mLinEnvelope->SetRange(MIN_dBMin, MAX_dBMax); // MB: this is the highest possible range + mLinEnvelope = std::make_unique + (false, + MIN_dBMin, MAX_dBMax, // MB: this is the highest possible range + 1.0); mEnvelope = (mLin ? mLinEnvelope : mLogEnvelope).get(); @@ -2308,12 +2310,7 @@ void EffectEqualization::ErrMin(void) double correction = 1.6; bool flag; size_t j=0; - Envelope testEnvelope; - testEnvelope.SetInterpolateDB(false); - testEnvelope.SetRange(-120.0, 60.0); - testEnvelope.Flatten(0.); - testEnvelope.SetTrackLen(1.0); - testEnvelope.CopyFrom(mLogEnvelope.get(), 0.0, 1.0); + Envelope testEnvelope{ *mLogEnvelope }; for(size_t i = 0; i < NUM_PTS; i++) vals[i] = testEnvelope.GetValue(mWhens[i]);