1
0
mirror of https://github.com/cookiengineer/audacity synced 2026-01-13 08:05:52 +01:00

Public Envelope methods all take & return ABSOLUTE time values...

... rather than some of them being relative to the Envelope's offset.

In case of the envelopes used in TimeTrack or Equalization, offset was
always zero, so this doesn't matter, except to make the contract of the
Envelope class more explicit and sensible in isolation.

In case of InsertSpace at least, this does fix an obscure bug, which could
only happen when you have a clip, with an envelope, that starts before zero,
and you select a region overlapping that clip and some other clip, with a void
between, and you use the Join command.

Aren't you relieved that's fixed now?
This commit is contained in:
Paul Licameli
2017-05-03 07:50:21 -04:00
parent 9c683a4f19
commit 26c4d65bd4
3 changed files with 132 additions and 116 deletions

View File

@@ -499,7 +499,7 @@ bool EnvelopeEditor::HandleMouseButtonDown(const wxMouseEvent & event, wxRect &
double newVal = ValueOfPixel(clip_y, r.height, upper, dB, dBRange, double newVal = ValueOfPixel(clip_y, r.height, upper, dB, dBRange,
zoomMin, zoomMax); zoomMin, zoomMax);
mEnvelope.SetDragPoint(mEnvelope.InsertOrReplace(when - mEnvelope.GetOffset(), newVal)); mEnvelope.SetDragPoint(mEnvelope.InsertOrReplaceRelative(when - mEnvelope.GetOffset(), newVal));
mDirty = true; mDirty = true;
} }
@@ -732,7 +732,7 @@ Old analysis of cases:
//wxLogDebug(wxT("Case 1")); //wxLogDebug(wxT("Case 1"));
} }
else { else {
InsertOrReplace(t0 + mTrackEpsilon, splitval); // Case 3: insert a point to maintain the envelope InsertOrReplaceRelative(t0 + mTrackEpsilon, splitval); // Case 3: insert a point to maintain the envelope
someToShift = true; someToShift = true;
//wxLogDebug(wxT("Case 3")); //wxLogDebug(wxT("Case 3"));
} }
@@ -744,21 +744,21 @@ Old analysis of cases:
//wxLogDebug(wxT("Case 2")); //wxLogDebug(wxT("Case 2"));
} }
else { // Case 4: else { // Case 4:
InsertOrReplace(t0 - mTrackEpsilon, splitval); // insert a point to maintain the envelope InsertOrReplaceRelative(t0 - mTrackEpsilon, splitval); // insert a point to maintain the envelope
//wxLogDebug(wxT("Case 4")); //wxLogDebug(wxT("Case 4"));
} }
} }
else { else {
if(onPoint) { // Case 7: move the point L and insert a NEW one to the R if(onPoint) { // Case 7: move the point L and insert a NEW one to the R
mEnv[pos].SetT(mEnv[pos].GetT() - mTrackEpsilon); mEnv[pos].SetT(mEnv[pos].GetT() - mTrackEpsilon);
InsertOrReplace(t0 + mTrackEpsilon, splitval); InsertOrReplaceRelative(t0 + mTrackEpsilon, splitval);
someToShift = true; someToShift = true;
//wxLogDebug(wxT("Case 7")); //wxLogDebug(wxT("Case 7"));
} }
else { else {
if( !beforeStart && !afterEnd ) {// Case 5: Insert points to L and R if( !beforeStart && !afterEnd ) {// Case 5: Insert points to L and R
InsertOrReplace(t0 - mTrackEpsilon, splitval); InsertOrReplaceRelative(t0 - mTrackEpsilon, splitval);
InsertOrReplace(t0 + mTrackEpsilon, splitval); InsertOrReplaceRelative(t0 + mTrackEpsilon, splitval);
someToShift = true; someToShift = true;
//wxLogDebug(wxT("Case 5")); //wxLogDebug(wxT("Case 5"));
} }
@@ -805,13 +805,13 @@ Old analysis of cases:
// calls for the start and end times will have no effect. // calls for the start and end times will have no effect.
const double leftval = e->GetValue(0 + e->mOffset); const double leftval = e->GetValue(0 + e->mOffset);
const double rightval = e->GetValue(e->mTrackLen + e->mOffset); const double rightval = e->GetValue(e->mTrackLen + e->mOffset);
InsertOrReplace(t0, leftval); InsertOrReplaceRelative(t0, leftval);
InsertOrReplace(t0 + e->mTrackLen, rightval); InsertOrReplaceRelative(t0 + e->mTrackLen, rightval);
} }
len = e->mEnv.size(); len = e->mEnv.size();
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
InsertOrReplace(t0 + e->mEnv[i].GetT(), e->mEnv[i].GetVal()); InsertOrReplaceRelative(t0 + e->mEnv[i].GetT(), e->mEnv[i].GetVal());
/* if(len != 0) /* if(len != 0)
for (i = 0; i < mEnv.size(); i++) for (i = 0; i < mEnv.size(); i++)
@@ -846,7 +846,7 @@ void Envelope::RemoveUnneededPoints(double time, double tolerence)
bool bExcludePoint = true; bool bExcludePoint = true;
if( fabs(val -val1) > tolerence ) if( fabs(val -val1) > tolerence )
{ {
InsertOrReplace(when, val); // put it back, we needed it InsertOrReplaceRelative(when, val); // put it back, we needed it
//Insert may have modified instead of inserting, if two points were at the same time. //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. // in which case len needs to shrink i and len, because the array size decreased.
@@ -863,6 +863,8 @@ void Envelope::RemoveUnneededPoints(double time, double tolerence)
void Envelope::InsertSpace(double t0, double tlen) void Envelope::InsertSpace(double t0, double tlen)
// NOFAIL-GUARANTEE // NOFAIL-GUARANTEE
{ {
t0 -= mOffset;
unsigned int len = mEnv.size(); unsigned int len = mEnv.size();
unsigned int i; unsigned int i;
@@ -874,6 +876,8 @@ void Envelope::InsertSpace(double t0, double tlen)
int Envelope::Reassign(double when, double value) int Envelope::Reassign(double when, double value)
{ {
when -= mOffset;
int len = mEnv.size(); int len = mEnv.size();
if (len == 0) if (len == 0)
return -1; return -1;
@@ -904,7 +908,7 @@ void Envelope::GetPoints(double *bufferWhen,
n = bufferLen; n = bufferLen;
int i; int i;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
bufferWhen[i] = mEnv[i].GetT(); bufferWhen[i] = mEnv[i].GetT() - mOffset;
bufferValue[i] = mEnv[i].GetVal(); bufferValue[i] = mEnv[i].GetVal();
} }
} }
@@ -926,20 +930,11 @@ void Envelope::GetPoints(double *bufferWhen,
/** @brief Add a control point to the envelope /** @brief Add a control point to the envelope
* *
* Control point positions start at zero and are measured in seconds from the
* start of the envelope. The position of the envelope on the project-wide
* time scale is store in seconds in Envelope::mOffset.
* This is worth remembering.
* If you call Envelope::Insert() from WaveClip, or anywhere else outside the
* Envelope class that is using project timing, subtract the envelope's mOffset
* from the time.
* If you call Envelope::Insert() from within Envelope, don't subtract mOffset
* because you are working in relative time inside the envelope
* @param when the time in seconds when the envelope point should be created. * @param when the time in seconds when the envelope point should be created.
* @param value the envelope value to use at the given point. * @param value the envelope value to use at the given point.
* @return the index of the NEW envelope point within array of envelope points. * @return the index of the NEW envelope point within array of envelope points.
*/ */
int Envelope::InsertOrReplace(double when, double value) int Envelope::InsertOrReplaceRelative(double when, double value)
{ {
#if defined(__WXDEBUG__) #if defined(__WXDEBUG__)
// in debug builds, do a spot of argument checking // in debug builds, do a spot of argument checking
@@ -1044,6 +1039,7 @@ double Envelope::GetValue(double t) const
return temp; return temp;
} }
// relative time
/// @param Lo returns last index at or before this time. /// @param Lo returns last index at or before this time.
/// @param Hi returns first index after this time. /// @param Hi returns first index after this time.
void Envelope::BinarySearchForTime( int &Lo, int &Hi, double t ) const void Envelope::BinarySearchForTime( int &Lo, int &Hi, double t ) const
@@ -1195,6 +1191,7 @@ void Envelope::GetValues
buffer[xx] = GetValue(zoomInfo.PositionToTime(xx, -leftOffset)); buffer[xx] = GetValue(zoomInfo.PositionToTime(xx, -leftOffset));
} }
// relative time
int Envelope::NumberOfPointsAfter(double t) const int Envelope::NumberOfPointsAfter(double t) const
{ {
if( t >= mEnv[mEnv.size()-1].GetT() ) if( t >= mEnv[mEnv.size()-1].GetT() )
@@ -1213,6 +1210,7 @@ int Envelope::NumberOfPointsAfter(double t) const
} }
} }
// relative time
double Envelope::NextPointAfter(double t) const double Envelope::NextPointAfter(double t) const
{ {
if( mEnv[mEnv.size()-1].GetT() < t ) if( mEnv[mEnv.size()-1].GetT() < t )
@@ -1342,6 +1340,9 @@ double Envelope::Integral( double t0, double t1 ) const
if(count == 0) // 'empty' envelope if(count == 0) // 'empty' envelope
return (t1 - t0) * mDefaultValue; return (t1 - t0) * mDefaultValue;
t0 -= mOffset;
t1 -= mOffset;
double total = 0.0, lastT, lastVal; double total = 0.0, lastT, lastVal;
unsigned int i; // this is the next point to check unsigned int i; // this is the next point to check
if(t0 < mEnv[0].GetT()) // t0 preceding the first point if(t0 < mEnv[0].GetT()) // t0 preceding the first point
@@ -1402,6 +1403,9 @@ double Envelope::IntegralOfInverse( double t0, double t1 ) const
if(count == 0) // 'empty' envelope if(count == 0) // 'empty' envelope
return (t1 - t0) / mDefaultValue; return (t1 - t0) / mDefaultValue;
t0 -= mOffset;
t1 -= mOffset;
double total = 0.0, lastT, lastVal; double total = 0.0, lastT, lastVal;
unsigned int i; // this is the next point to check unsigned int i; // this is the next point to check
if(t0 < mEnv[0].GetT()) // t0 preceding the first point if(t0 < mEnv[0].GetT()) // t0 preceding the first point
@@ -1458,93 +1462,98 @@ double Envelope::SolveIntegralOfInverse( double t0, double area ) const
if(count == 0) // 'empty' envelope if(count == 0) // 'empty' envelope
return t0 + area * mDefaultValue; return t0 + area * mDefaultValue;
double lastT, lastVal; // Correct for offset!
int i; // this is the next point to check t0 -= mOffset;
if(t0 < mEnv[0].GetT()) // t0 preceding the first point return mOffset + [&] {
{ // Now we can safely assume t0 is relative time!
if (area < 0) { double lastT, lastVal;
return t0 + area * mEnv[0].GetVal(); int i; // this is the next point to check
} if(t0 < mEnv[0].GetT()) // t0 preceding the first point
else { {
i = 1; if (area < 0) {
lastT = mEnv[0].GetT();
lastVal = mEnv[0].GetVal();
double added = (lastT - t0) / lastVal;
if(added >= area)
return t0 + area * mEnv[0].GetVal(); return t0 + area * mEnv[0].GetVal();
area -= added; }
else {
i = 1;
lastT = mEnv[0].GetT();
lastVal = mEnv[0].GetVal();
double added = (lastT - t0) / lastVal;
if(added >= area)
return t0 + area * mEnv[0].GetVal();
area -= added;
}
} }
} else if(t0 >= mEnv[count - 1].GetT()) // t0 following the last point
else if(t0 >= mEnv[count - 1].GetT()) // t0 following the last point {
{ if (area < 0) {
if (area < 0) { i = count - 2;
i = count - 2; lastT = mEnv[count - 1].GetT();
lastT = mEnv[count - 1].GetT(); lastVal = mEnv[count - 1].GetVal();
lastVal = mEnv[count - 1].GetVal(); double added = (lastT - t0) / lastVal; // negative
double added = (lastT - t0) / lastVal; // negative if(added <= area)
if(added <= area) return t0 + area * mEnv[count - 1].GetVal();
area -= added;
}
else {
return t0 + area * mEnv[count - 1].GetVal(); return t0 + area * mEnv[count - 1].GetVal();
area -= added; }
}
else // t0 enclosed by points
{
// Skip any points that come before t0 using binary search
int lo, hi;
BinarySearchForTime(lo, hi, t0);
lastVal = InterpolatePoints(mEnv[lo].GetVal(), mEnv[hi].GetVal(), (t0 - mEnv[lo].GetT()) / (mEnv[hi].GetT() - mEnv[lo].GetT()), mDB);
lastT = t0;
if (area < 0)
i = lo;
else
i = hi; // the point immediately after t0.
}
if (area < 0) {
// loop BACKWARDS through the rest of the envelope points until we get to t1
// (which is less than t0)
while (1)
{
if(i < 0) // the requested range extends beyond the leftmost point
{
return lastT + area * lastVal;
}
else
{
double added =
-IntegrateInverseInterpolated(mEnv[i].GetVal(), lastVal, lastT - mEnv[i].GetT(), mDB);
if(added <= area)
return lastT - SolveIntegrateInverseInterpolated(lastVal, mEnv[i].GetVal(), lastT - mEnv[i].GetT(), -area, mDB);
area -= added;
lastT = mEnv[i].GetT();
lastVal = mEnv[i].GetVal();
--i;
}
}
} }
else { else {
return t0 + area * mEnv[count - 1].GetVal(); // loop through the rest of the envelope points until we get to t1
} while (1)
}
else // t0 enclosed by points
{
// Skip any points that come before t0 using binary search
int lo, hi;
BinarySearchForTime(lo, hi, t0);
lastVal = InterpolatePoints(mEnv[lo].GetVal(), mEnv[hi].GetVal(), (t0 - mEnv[lo].GetT()) / (mEnv[hi].GetT() - mEnv[lo].GetT()), mDB);
lastT = t0;
if (area < 0)
i = lo;
else
i = hi; // the point immediately after t0.
}
if (area < 0) {
// loop BACKWARDS through the rest of the envelope points until we get to t1
// (which is less than t0)
while (1)
{
if(i < 0) // the requested range extends beyond the leftmost point
{ {
return lastT + area * lastVal; if(i >= count) // the requested range extends beyond the last point
} {
else return lastT + area * lastVal;
{ }
double added = else
-IntegrateInverseInterpolated(mEnv[i].GetVal(), lastVal, lastT - mEnv[i].GetT(), mDB); {
if(added <= area) double added = IntegrateInverseInterpolated(lastVal, mEnv[i].GetVal(), mEnv[i].GetT() - lastT, mDB);
return lastT - SolveIntegrateInverseInterpolated(lastVal, mEnv[i].GetVal(), lastT - mEnv[i].GetT(), -area, mDB); if(added >= area)
area -= added; return lastT + SolveIntegrateInverseInterpolated(lastVal, mEnv[i].GetVal(), mEnv[i].GetT() - lastT, area, mDB);
lastT = mEnv[i].GetT(); area -= added;
lastVal = mEnv[i].GetVal(); lastT = mEnv[i].GetT();
--i; lastVal = mEnv[i].GetVal();
i++;
}
} }
} }
} }();
else {
// loop through the rest of the envelope points until we get to t1
while (1)
{
if(i >= count) // the requested range extends beyond the last point
{
return lastT + area * lastVal;
}
else
{
double added = IntegrateInverseInterpolated(lastVal, mEnv[i].GetVal(), mEnv[i].GetT() - lastT, mDB);
if(added >= area)
return lastT + SolveIntegrateInverseInterpolated(lastVal, mEnv[i].GetVal(), mEnv[i].GetT() - lastT, area, mDB);
area -= added;
lastT = mEnv[i].GetT();
lastVal = mEnv[i].GetVal();
i++;
}
}
}
} }
void Envelope::print() const void Envelope::print() const
@@ -1578,14 +1587,14 @@ void Envelope::testMe()
checkResult( 5, Integral(-20.0,-10.0), 5); checkResult( 5, Integral(-20.0,-10.0), 5);
Flatten(0.5); Flatten(0.5);
InsertOrReplace( 5.0, 0.5 ); InsertOrReplaceRelative( 5.0, 0.5 );
checkResult( 6, Integral(0.0,100.0), 50); checkResult( 6, Integral(0.0,100.0), 50);
checkResult( 7, Integral(-10.0,10.0), 10); checkResult( 7, Integral(-10.0,10.0), 10);
Flatten(0.0); Flatten(0.0);
InsertOrReplace( 0.0, 0.0 ); InsertOrReplaceRelative( 0.0, 0.0 );
InsertOrReplace( 5.0, 1.0 ); InsertOrReplaceRelative( 5.0, 1.0 );
InsertOrReplace( 10.0, 0.0 ); InsertOrReplaceRelative( 10.0, 0.0 );
t0 = 10.0 - .1; t0 = 10.0 - .1;
t1 = 10.0 + .1; t1 = 10.0 + .1;
double result = Integral(0.0,t1); double result = Integral(0.0,t1);
@@ -1595,9 +1604,9 @@ void Envelope::testMe()
checkResult( 8, result - resulta - resultb, 0); checkResult( 8, result - resulta - resultb, 0);
Flatten(0.0); Flatten(0.0);
InsertOrReplace( 0.0, 0.0 ); InsertOrReplaceRelative( 0.0, 0.0 );
InsertOrReplace( 5.0, 1.0 ); InsertOrReplaceRelative( 5.0, 1.0 );
InsertOrReplace( 10.0, 0.0 ); InsertOrReplaceRelative( 10.0, 0.0 );
t0 = 10.0 - .1; t0 = 10.0 - .1;
t1 = 10.0 + .1; t1 = 10.0 + .1;
checkResult( 9, Integral(0.0,t1), 5); checkResult( 9, Integral(0.0,t1), 5);
@@ -1605,9 +1614,9 @@ void Envelope::testMe()
checkResult( 11, Integral(t0,t1), .001); checkResult( 11, Integral(t0,t1), .001);
mEnv.clear(); mEnv.clear();
InsertOrReplace( 0.0, 0.0 ); InsertOrReplaceRelative( 0.0, 0.0 );
InsertOrReplace( 5.0, 1.0 ); InsertOrReplaceRelative( 5.0, 1.0 );
InsertOrReplace( 10.0, 0.0 ); InsertOrReplaceRelative( 10.0, 0.0 );
checkResult( 12, NumberOfPointsAfter( -1 ), 3 ); checkResult( 12, NumberOfPointsAfter( -1 ), 3 );
checkResult( 13, NumberOfPointsAfter( 0 ), 2 ); checkResult( 13, NumberOfPointsAfter( 0 ), 2 );
checkResult( 14, NumberOfPointsAfter( 1 ), 2 ); checkResult( 14, NumberOfPointsAfter( 1 ), 2 );

View File

@@ -143,9 +143,13 @@ public:
void GetValues void GetValues
(double *buffer, int bufferLen, int leftOffset, const ZoomInfo &zoomInfo) const; (double *buffer, int bufferLen, int leftOffset, const ZoomInfo &zoomInfo) const;
private:
// relative time
int NumberOfPointsAfter(double t) const; int NumberOfPointsAfter(double t) const;
// relative time
double NextPointAfter(double t) const; double NextPointAfter(double t) const;
public:
double Average( double t0, double t1 ) const; double Average( double t0, double t1 ) const;
double AverageOfInverse( double t0, double t1 ) const; double AverageOfInverse( double t0, double t1 ) const;
double Integral( double t0, double t1 ) const; double Integral( double t0, double t1 ) const;
@@ -157,8 +161,9 @@ public:
bool IsDirty() const; bool IsDirty() const;
/** \brief Add a point at a particular spot */ /** \brief Add a point at a particular absolute time coordinate */
int InsertOrReplace(double when, double value); int InsertOrReplace(double when, double value)
{ return InsertOrReplaceRelative( when - mOffset, value ); }
/** \brief Move a point at when to value /** \brief Move a point at when to value
* *
@@ -175,6 +180,7 @@ public:
size_t GetNumberOfPoints() const; size_t GetNumberOfPoints() const;
private: private:
int InsertOrReplaceRelative(double when, double value);
friend class EnvelopeEditor; friend class EnvelopeEditor;
/** \brief Accessor for points */ /** \brief Accessor for points */
const EnvPoint &operator[] (int index) const const EnvPoint &operator[] (int index) const
@@ -200,13 +206,14 @@ public:
bool GetDragPointValid() const { return mDragPointValid; } bool GetDragPointValid() const { return mDragPointValid; }
// Modify the dragged point and change its value. // Modify the dragged point and change its value.
// But consistency constraints may move it less then you ask for. // But consistency constraints may move it less then you ask for.
private:
void MoveDragPoint(double newWhen, double value); void MoveDragPoint(double newWhen, double value);
// May delete the drag point. Restores envelope consistency. // May delete the drag point. Restores envelope consistency.
void ClearDragPoint(); void ClearDragPoint();
private:
EnvPoint * AddPointAtEnd( double t, double val ); EnvPoint * AddPointAtEnd( double t, double val );
void CopyRange(const Envelope &orig, size_t begin, size_t end); void CopyRange(const Envelope &orig, size_t begin, size_t end);
// relative time
void BinarySearchForTime( int &Lo, int &Hi, double t ) const; void BinarySearchForTime( int &Lo, int &Hi, double t ) const;
double GetInterpolationStartValueAtPoint( int iPoint ) const; double GetInterpolationStartValueAtPoint( int iPoint ) const;

View File

@@ -2393,8 +2393,8 @@ void WaveTrack::SplitAt(double t)
//make two envelope points to preserve the value. //make two envelope points to preserve the value.
//handle the case where we split on the 1st sample (without this we hit an assert) //handle the case where we split on the 1st sample (without this we hit an assert)
if(t - 1.0/c->GetRate() >= c->GetOffset()) if(t - 1.0/c->GetRate() >= c->GetOffset())
c->GetEnvelope()->InsertOrReplace(t - c->GetOffset() - 1.0 / c->GetRate(), val); // frame end points c->GetEnvelope()->InsertOrReplace(t - 1.0 / c->GetRate(), val); // frame end points
c->GetEnvelope()->InsertOrReplace(t - c->GetOffset(), val); c->GetEnvelope()->InsertOrReplace(t, val);
auto newClip = make_movable<WaveClip>( *c, mDirManager, true ); auto newClip = make_movable<WaveClip>( *c, mDirManager, true );
c->Clear(t, c->GetEndTime()); c->Clear(t, c->GetEndTime());
newClip->Clear(c->GetStartTime(), t); newClip->Clear(c->GetStartTime(), t);