mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-03 06:03:13 +02:00
Various fixes involving Envelope. More to come later.
This commit is contained in:
commit
b6d43e4954
493
src/Envelope.cpp
493
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()
|
||||
@ -76,7 +60,7 @@ Envelope::~Envelope()
|
||||
/// and rescale all envelope points accordingly (unlike SetRange, which clamps the envelope points to the NEW range).
|
||||
/// @minValue - the NEW minimum value
|
||||
/// @maxValue - the NEW maximum value
|
||||
void Envelope::Rescale(double minValue, double maxValue)
|
||||
void Envelope::RescaleValues(double minValue, double maxValue)
|
||||
{
|
||||
double oldMinValue = mMinValue;
|
||||
double oldMaxValue = mMaxValue;
|
||||
@ -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.
|
||||
@ -505,7 +499,7 @@ bool EnvelopeEditor::HandleMouseButtonDown(const wxMouseEvent & event, wxRect &
|
||||
double newVal = ValueOfPixel(clip_y, r.height, upper, dB, dBRange,
|
||||
zoomMin, zoomMax);
|
||||
|
||||
mEnvelope.SetDragPoint(mEnvelope.Insert(when - mEnvelope.GetOffset(), newVal));
|
||||
mEnvelope.SetDragPoint(mEnvelope.InsertOrReplaceRelative(when - mEnvelope.GetOffset(), newVal));
|
||||
mDirty = true;
|
||||
}
|
||||
|
||||
@ -669,9 +663,6 @@ void Envelope::Paste(double t0, const Envelope *e)
|
||||
// get values to perform framing of the insertion
|
||||
double splitval = GetValue(t0 + mOffset);
|
||||
|
||||
if(len != 0) { // Not case 10: there are point/s in the envelope
|
||||
|
||||
|
||||
/*
|
||||
Old analysis of cases:
|
||||
(see discussions on audacity-devel around 19/8/7 - 23/8/7 and beyond, "Envelopes and 'Join'")
|
||||
@ -697,7 +688,7 @@ Old analysis of cases:
|
||||
// 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 discontnuity in the envelope at a boundary between the
|
||||
// 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.
|
||||
@ -709,7 +700,9 @@ Old analysis of cases:
|
||||
// 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
|
||||
// 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;
|
||||
@ -726,57 +719,66 @@ Old analysis of cases:
|
||||
if( (mTrackLen - t0) < mTrackEpsilon )
|
||||
atEnd = true;
|
||||
if(0 > t0)
|
||||
beforeStart = true; // Case 13
|
||||
// Case 13
|
||||
beforeStart = true;
|
||||
if(mTrackLen < t0)
|
||||
afterEnd = true; // Case 12
|
||||
// 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) { // first env point is at LH end
|
||||
mEnv[0].SetT(mEnv[0].GetT() + mTrackEpsilon); // Case 1: move it R slightly to avoid duplicate point
|
||||
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 {
|
||||
Insert(t0 + mTrackEpsilon, splitval); // Case 3: insert a point to maintain the envelope
|
||||
// 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:
|
||||
mEnv[0].SetT(mEnv[0].GetT() - mTrackEpsilon); // move it L slightly to avoid duplicate point
|
||||
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(t0 - mTrackEpsilon, splitval); // insert a point to maintain the envelope
|
||||
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 {
|
||||
if(onPoint) { // Case 7: move the point L and insert a NEW one to the R
|
||||
mEnv[pos].SetT(mEnv[pos].GetT() - mTrackEpsilon);
|
||||
Insert(t0 + mTrackEpsilon, splitval);
|
||||
someToShift = true;
|
||||
//wxLogDebug(wxT("Case 7"));
|
||||
}
|
||||
else {
|
||||
if( !beforeStart && !afterEnd ) {// Case 5: Insert points to L and R
|
||||
Insert(t0 - mTrackEpsilon, splitval);
|
||||
Insert(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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Case 12:
|
||||
//wxLogDebug(wxT("Case 12"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -789,7 +791,8 @@ Old analysis of cases:
|
||||
}
|
||||
mTrackLen += deltat;
|
||||
}
|
||||
else { // Case 10:
|
||||
else {
|
||||
// Case 10:
|
||||
if( mTrackLen == 0 ) // creating a NEW envelope
|
||||
{
|
||||
mTrackLen = e->mTrackLen;
|
||||
@ -811,13 +814,13 @@ Old analysis of cases:
|
||||
// calls for the start and end times will have no effect.
|
||||
const double leftval = e->GetValue(0 + e->mOffset);
|
||||
const double rightval = e->GetValue(e->mTrackLen + e->mOffset);
|
||||
Insert(t0, leftval);
|
||||
Insert(t0 + e->mTrackLen, rightval);
|
||||
InsertOrReplaceRelative(t0, leftval);
|
||||
InsertOrReplaceRelative(t0 + e->mTrackLen, rightval);
|
||||
}
|
||||
|
||||
len = e->mEnv.size();
|
||||
for (i = 0; i < len; i++)
|
||||
Insert(t0 + e->mEnv[i].GetT(), e->mEnv[i].GetVal());
|
||||
InsertOrReplaceRelative(t0 + e->mEnv[i].GetT(), e->mEnv[i].GetVal());
|
||||
|
||||
/* if(len != 0)
|
||||
for (i = 0; i < mEnv.size(); i++)
|
||||
@ -852,7 +855,7 @@ void Envelope::RemoveUnneededPoints(double time, double tolerence)
|
||||
bool bExcludePoint = true;
|
||||
if( fabs(val -val1) > tolerence )
|
||||
{
|
||||
Insert(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.
|
||||
// in which case len needs to shrink i and len, because the array size decreased.
|
||||
@ -869,6 +872,8 @@ void Envelope::RemoveUnneededPoints(double time, double tolerence)
|
||||
void Envelope::InsertSpace(double t0, double tlen)
|
||||
// NOFAIL-GUARANTEE
|
||||
{
|
||||
t0 -= mOffset;
|
||||
|
||||
unsigned int len = mEnv.size();
|
||||
unsigned int i;
|
||||
|
||||
@ -878,8 +883,10 @@ void Envelope::InsertSpace(double t0, double tlen)
|
||||
mTrackLen += tlen;
|
||||
}
|
||||
|
||||
int Envelope::Move(double when, double value)
|
||||
int Envelope::Reassign(double when, double value)
|
||||
{
|
||||
when -= mOffset;
|
||||
|
||||
int len = mEnv.size();
|
||||
if (len == 0)
|
||||
return -1;
|
||||
@ -910,7 +917,7 @@ void Envelope::GetPoints(double *bufferWhen,
|
||||
n = bufferLen;
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
bufferWhen[i] = mEnv[i].GetT();
|
||||
bufferWhen[i] = mEnv[i].GetT() - mOffset;
|
||||
bufferValue[i] = mEnv[i].GetVal();
|
||||
}
|
||||
}
|
||||
@ -932,20 +939,11 @@ void Envelope::GetPoints(double *bufferWhen,
|
||||
|
||||
/** @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 value the envelope value to use at the given point.
|
||||
* @return the index of the NEW envelope point within array of envelope points.
|
||||
*/
|
||||
int Envelope::Insert(double when, double value)
|
||||
int Envelope::InsertOrReplaceRelative(double when, double value)
|
||||
{
|
||||
#if defined(__WXDEBUG__)
|
||||
// in debug builds, do a spot of argument checking
|
||||
@ -998,6 +996,27 @@ int Envelope::Insert(double when, double value)
|
||||
return i;
|
||||
}
|
||||
|
||||
std::pair<int, int> 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<Envelope*>( 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)
|
||||
@ -1019,6 +1038,21 @@ void Envelope::SetTrackLen(double trackLen)
|
||||
}
|
||||
}
|
||||
|
||||
void Envelope::RescaleTimes( double newLength )
|
||||
// NOFAIL-GUARANTEE
|
||||
{
|
||||
if ( mTrackLen == 0 ) {
|
||||
for ( auto &point : mEnv )
|
||||
point.SetT( 0 );
|
||||
}
|
||||
else {
|
||||
auto ratio = newLength / mTrackLen;
|
||||
for ( auto &point : mEnv )
|
||||
point.SetT( point.GetT() * ratio );
|
||||
}
|
||||
mTrackLen = newLength;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
double Envelope::GetValue(double t) const
|
||||
{
|
||||
@ -1029,21 +1063,18 @@ double Envelope::GetValue(double t) const
|
||||
return temp;
|
||||
}
|
||||
|
||||
/// @param Lo returns last index at or before this time.
|
||||
/// @param Hi returns first index after this time.
|
||||
// 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
|
||||
void Envelope::BinarySearchForTime( int &Lo, int &Hi, double t ) const
|
||||
{
|
||||
Lo = 0;
|
||||
Hi = mEnv.size() - 1;
|
||||
// JC: Do we have a problem if the envelope only has one point??
|
||||
wxASSERT(Hi > Lo);
|
||||
|
||||
// Optimizations for the usual pattern of repeated calls with
|
||||
// small increases of t.
|
||||
{
|
||||
if (mSearchGuess >= 0 && mSearchGuess < (int)(mEnv.size()) - 1) {
|
||||
if (mSearchGuess >= 0 && mSearchGuess < mEnv.size()) {
|
||||
if (t >= mEnv[mSearchGuess].GetT() &&
|
||||
t < mEnv[1 + mSearchGuess].GetT()) {
|
||||
(1 + mSearchGuess == mEnv.size() ||
|
||||
t < mEnv[1 + mSearchGuess].GetT())) {
|
||||
Lo = mSearchGuess;
|
||||
Hi = 1 + mSearchGuess;
|
||||
return;
|
||||
@ -1051,9 +1082,10 @@ void Envelope::BinarySearchForTime( int &Lo, int &Hi, double t ) const
|
||||
}
|
||||
|
||||
++mSearchGuess;
|
||||
if (mSearchGuess >= 0 && mSearchGuess < (int)(mEnv.size()) - 1) {
|
||||
if (mSearchGuess >= 0 && mSearchGuess < mEnv.size()) {
|
||||
if (t >= mEnv[mSearchGuess].GetT() &&
|
||||
t < mEnv[1 + mSearchGuess].GetT()) {
|
||||
(1 + mSearchGuess == mEnv.size() ||
|
||||
t < mEnv[1 + mSearchGuess].GetT())) {
|
||||
Lo = mSearchGuess;
|
||||
Hi = 1 + mSearchGuess;
|
||||
return;
|
||||
@ -1061,8 +1093,13 @@ void Envelope::BinarySearchForTime( int &Lo, int &Hi, double t ) const
|
||||
}
|
||||
}
|
||||
|
||||
Lo = -1;
|
||||
Hi = mEnv.size();
|
||||
|
||||
// Invariants: Lo is not less than -1, Hi not more than size
|
||||
while (Hi > (Lo + 1)) {
|
||||
int mid = (Lo + Hi) / 2;
|
||||
// mid must be strictly between Lo and Hi, therefore a valid index
|
||||
if (t < mEnv[mid].GetT())
|
||||
Hi = mid;
|
||||
else
|
||||
@ -1132,6 +1169,8 @@ void Envelope::GetValues(double *buffer, int bufferLen,
|
||||
|
||||
int lo,hi;
|
||||
BinarySearchForTime( lo, hi, t );
|
||||
// mEnv[0] is before t because of eliminations above, therefore lo >= 0
|
||||
// mEnv[len - 1] is after t, therefore hi <= len - 1
|
||||
tprev = mEnv[lo].GetT();
|
||||
tnext = mEnv[hi].GetT();
|
||||
|
||||
@ -1180,39 +1219,24 @@ void Envelope::GetValues
|
||||
buffer[xx] = GetValue(zoomInfo.PositionToTime(xx, -leftOffset));
|
||||
}
|
||||
|
||||
// relative time
|
||||
int Envelope::NumberOfPointsAfter(double t) const
|
||||
{
|
||||
if( t >= mEnv[mEnv.size()-1].GetT() )
|
||||
return 0;
|
||||
else if( t < mEnv[0].GetT() )
|
||||
return mEnv.size();
|
||||
else
|
||||
{
|
||||
int lo,hi;
|
||||
BinarySearchForTime( lo, hi, t );
|
||||
int lo,hi;
|
||||
BinarySearchForTime( lo, hi, t );
|
||||
|
||||
if( mEnv[hi].GetT() == t )
|
||||
return mEnv.size() - (hi+1);
|
||||
else
|
||||
return mEnv.size() - hi;
|
||||
}
|
||||
return mEnv.size() - hi;
|
||||
}
|
||||
|
||||
// relative time
|
||||
double Envelope::NextPointAfter(double t) const
|
||||
{
|
||||
if( mEnv[mEnv.size()-1].GetT() < t )
|
||||
int lo,hi;
|
||||
BinarySearchForTime( lo, hi, t );
|
||||
if (hi >= mEnv.size())
|
||||
return t;
|
||||
else if( t < mEnv[0].GetT() )
|
||||
return mEnv[0].GetT();
|
||||
else
|
||||
{
|
||||
int lo,hi;
|
||||
BinarySearchForTime( lo, hi, t );
|
||||
if( mEnv[hi].GetT() == t )
|
||||
return mEnv[hi+1].GetT();
|
||||
else
|
||||
return mEnv[hi].GetT();
|
||||
}
|
||||
return mEnv[hi].GetT();
|
||||
}
|
||||
|
||||
double Envelope::Average( double t0, double t1 ) const
|
||||
@ -1327,6 +1351,9 @@ double Envelope::Integral( double t0, double t1 ) const
|
||||
if(count == 0) // 'empty' envelope
|
||||
return (t1 - t0) * mDefaultValue;
|
||||
|
||||
t0 -= mOffset;
|
||||
t1 -= mOffset;
|
||||
|
||||
double total = 0.0, lastT, lastVal;
|
||||
unsigned int i; // this is the next point to check
|
||||
if(t0 < mEnv[0].GetT()) // t0 preceding the first point
|
||||
@ -1338,7 +1365,7 @@ double Envelope::Integral( double t0, double t1 ) const
|
||||
lastVal = mEnv[0].GetVal();
|
||||
total += (lastT - t0) * lastVal;
|
||||
}
|
||||
else if(t0 >= mEnv[count - 1].GetT()) // t0 following the last point
|
||||
else if(t0 >= mEnv[count - 1].GetT()) // t0 at or following the last point
|
||||
{
|
||||
return (t1 - t0) * mEnv[count - 1].GetVal();
|
||||
}
|
||||
@ -1364,7 +1391,7 @@ double Envelope::Integral( double t0, double t1 ) const
|
||||
double thisVal = InterpolatePoints(mEnv[i - 1].GetVal(), mEnv[i].GetVal(), (t1 - mEnv[i - 1].GetT()) / (mEnv[i].GetT() - mEnv[i - 1].GetT()), mDB);
|
||||
return total + IntegrateInterpolated(lastVal, thisVal, t1 - lastT, mDB);
|
||||
}
|
||||
else // this point preceeds the end of the range
|
||||
else // this point precedes the end of the range
|
||||
{
|
||||
total += IntegrateInterpolated(lastVal, mEnv[i].GetVal(), mEnv[i].GetT() - lastT, mDB);
|
||||
lastT = mEnv[i].GetT();
|
||||
@ -1387,6 +1414,9 @@ double Envelope::IntegralOfInverse( double t0, double t1 ) const
|
||||
if(count == 0) // 'empty' envelope
|
||||
return (t1 - t0) / mDefaultValue;
|
||||
|
||||
t0 -= mOffset;
|
||||
t1 -= mOffset;
|
||||
|
||||
double total = 0.0, lastT, lastVal;
|
||||
unsigned int i; // this is the next point to check
|
||||
if(t0 < mEnv[0].GetT()) // t0 preceding the first point
|
||||
@ -1398,7 +1428,7 @@ double Envelope::IntegralOfInverse( double t0, double t1 ) const
|
||||
lastVal = mEnv[0].GetVal();
|
||||
total += (lastT - t0) / lastVal;
|
||||
}
|
||||
else if(t0 >= mEnv[count - 1].GetT()) // t0 following the last point
|
||||
else if(t0 >= mEnv[count - 1].GetT()) // t0 at or following the last point
|
||||
{
|
||||
return (t1 - t0) / mEnv[count - 1].GetVal();
|
||||
}
|
||||
@ -1424,7 +1454,7 @@ double Envelope::IntegralOfInverse( double t0, double t1 ) const
|
||||
double thisVal = InterpolatePoints(mEnv[i - 1].GetVal(), mEnv[i].GetVal(), (t1 - mEnv[i - 1].GetT()) / (mEnv[i].GetT() - mEnv[i - 1].GetT()), mDB);
|
||||
return total + IntegrateInverseInterpolated(lastVal, thisVal, t1 - lastT, mDB);
|
||||
}
|
||||
else // this point preceeds the end of the range
|
||||
else // this point precedes the end of the range
|
||||
{
|
||||
total += IntegrateInverseInterpolated(lastVal, mEnv[i].GetVal(), mEnv[i].GetT() - lastT, mDB);
|
||||
lastT = mEnv[i].GetT();
|
||||
@ -1443,93 +1473,98 @@ double Envelope::SolveIntegralOfInverse( double t0, double area ) const
|
||||
if(count == 0) // 'empty' envelope
|
||||
return t0 + area * mDefaultValue;
|
||||
|
||||
double lastT, lastVal;
|
||||
int i; // this is the next point to check
|
||||
if(t0 < mEnv[0].GetT()) // t0 preceding the first point
|
||||
{
|
||||
if (area < 0) {
|
||||
return t0 + area * mEnv[0].GetVal();
|
||||
}
|
||||
else {
|
||||
i = 1;
|
||||
lastT = mEnv[0].GetT();
|
||||
lastVal = mEnv[0].GetVal();
|
||||
double added = (lastT - t0) / lastVal;
|
||||
if(added >= area)
|
||||
// Correct for offset!
|
||||
t0 -= mOffset;
|
||||
return mOffset + [&] {
|
||||
// Now we can safely assume t0 is relative time!
|
||||
double lastT, lastVal;
|
||||
int i; // this is the next point to check
|
||||
if(t0 < mEnv[0].GetT()) // t0 preceding the first point
|
||||
{
|
||||
if (area < 0) {
|
||||
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
|
||||
{
|
||||
if (area < 0) {
|
||||
i = count - 2;
|
||||
lastT = mEnv[count - 1].GetT();
|
||||
lastVal = mEnv[count - 1].GetVal();
|
||||
double added = (lastT - t0) / lastVal; // negative
|
||||
if(added <= area)
|
||||
else if(t0 >= mEnv[count - 1].GetT()) // t0 at or following the last point
|
||||
{
|
||||
if (area < 0) {
|
||||
i = count - 2;
|
||||
lastT = mEnv[count - 1].GetT();
|
||||
lastVal = mEnv[count - 1].GetVal();
|
||||
double added = (lastT - t0) / lastVal; // negative
|
||||
if(added <= area)
|
||||
return t0 + area * mEnv[count - 1].GetVal();
|
||||
area -= added;
|
||||
}
|
||||
else {
|
||||
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 {
|
||||
return t0 + area * mEnv[count - 1].GetVal();
|
||||
}
|
||||
}
|
||||
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
|
||||
// loop through the rest of the envelope points until we get to t1
|
||||
while (1)
|
||||
{
|
||||
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;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
@ -1551,7 +1586,7 @@ void Envelope::testMe()
|
||||
{
|
||||
double t0=0, t1=0;
|
||||
|
||||
SetInterpolateDB(false);
|
||||
SetExponential(false);
|
||||
|
||||
Flatten(0.5);
|
||||
checkResult( 1, Integral(0.0,100.0), 50);
|
||||
@ -1563,14 +1598,14 @@ void Envelope::testMe()
|
||||
checkResult( 5, Integral(-20.0,-10.0), 5);
|
||||
|
||||
Flatten(0.5);
|
||||
Insert( 5.0, 0.5 );
|
||||
InsertOrReplaceRelative( 5.0, 0.5 );
|
||||
checkResult( 6, Integral(0.0,100.0), 50);
|
||||
checkResult( 7, Integral(-10.0,10.0), 10);
|
||||
|
||||
Flatten(0.0);
|
||||
Insert( 0.0, 0.0 );
|
||||
Insert( 5.0, 1.0 );
|
||||
Insert( 10.0, 0.0 );
|
||||
InsertOrReplaceRelative( 0.0, 0.0 );
|
||||
InsertOrReplaceRelative( 5.0, 1.0 );
|
||||
InsertOrReplaceRelative( 10.0, 0.0 );
|
||||
t0 = 10.0 - .1;
|
||||
t1 = 10.0 + .1;
|
||||
double result = Integral(0.0,t1);
|
||||
@ -1580,9 +1615,9 @@ void Envelope::testMe()
|
||||
checkResult( 8, result - resulta - resultb, 0);
|
||||
|
||||
Flatten(0.0);
|
||||
Insert( 0.0, 0.0 );
|
||||
Insert( 5.0, 1.0 );
|
||||
Insert( 10.0, 0.0 );
|
||||
InsertOrReplaceRelative( 0.0, 0.0 );
|
||||
InsertOrReplaceRelative( 5.0, 1.0 );
|
||||
InsertOrReplaceRelative( 10.0, 0.0 );
|
||||
t0 = 10.0 - .1;
|
||||
t1 = 10.0 + .1;
|
||||
checkResult( 9, Integral(0.0,t1), 5);
|
||||
@ -1590,9 +1625,9 @@ void Envelope::testMe()
|
||||
checkResult( 11, Integral(t0,t1), .001);
|
||||
|
||||
mEnv.clear();
|
||||
Insert( 0.0, 0.0 );
|
||||
Insert( 5.0, 1.0 );
|
||||
Insert( 10.0, 0.0 );
|
||||
InsertOrReplaceRelative( 0.0, 0.0 );
|
||||
InsertOrReplaceRelative( 5.0, 1.0 );
|
||||
InsertOrReplaceRelative( 10.0, 0.0 );
|
||||
checkResult( 12, NumberOfPointsAfter( -1 ), 3 );
|
||||
checkResult( 13, NumberOfPointsAfter( 0 ), 2 );
|
||||
checkResult( 14, NumberOfPointsAfter( 1 ), 2 );
|
||||
|
@ -75,8 +75,15 @@ private:
|
||||
typedef std::vector<EnvPoint> 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();
|
||||
@ -84,9 +91,8 @@ class Envelope final : public XMLTagHandler {
|
||||
double GetOffset() const { return mOffset; }
|
||||
double GetTrackLen() const { return mTrackLen; }
|
||||
|
||||
bool GetInterpolateDB() { return mDB; }
|
||||
void SetInterpolateDB(bool db) { mDB = db; }
|
||||
void Rescale(double minValue, double maxValue);
|
||||
bool GetExponential() const { return mDB; }
|
||||
void SetExponential(bool db) { mDB = db; }
|
||||
|
||||
void Flatten(double value);
|
||||
|
||||
@ -113,8 +119,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);
|
||||
@ -122,6 +126,8 @@ class Envelope final : public XMLTagHandler {
|
||||
// Control
|
||||
void SetOffset(double newOffset);
|
||||
void SetTrackLen(double trackLen);
|
||||
void RescaleValues(double minValue, double maxValue);
|
||||
void RescaleTimes( double newLength );
|
||||
|
||||
// Accessors
|
||||
/** \brief Get envelope value at time t */
|
||||
@ -138,9 +144,13 @@ class Envelope final : public XMLTagHandler {
|
||||
void GetValues
|
||||
(double *buffer, int bufferLen, int leftOffset, const ZoomInfo &zoomInfo) const;
|
||||
|
||||
private:
|
||||
// relative time
|
||||
int NumberOfPointsAfter(double t) const;
|
||||
// relative time
|
||||
double NextPointAfter(double t) const;
|
||||
|
||||
public:
|
||||
double Average( double t0, double t1 ) const;
|
||||
double AverageOfInverse( double t0, double t1 ) const;
|
||||
double Integral( double t0, double t1 ) const;
|
||||
@ -152,13 +162,14 @@ class Envelope final : public XMLTagHandler {
|
||||
|
||||
bool IsDirty() const;
|
||||
|
||||
/** \brief Add a point at a particular spot */
|
||||
int Insert(double when, double value);
|
||||
/** \brief Add a point at a particular absolute time coordinate */
|
||||
int InsertOrReplace(double when, double value)
|
||||
{ return InsertOrReplaceRelative( when - mOffset, value ); }
|
||||
|
||||
/** \brief Move a point at when to value
|
||||
*
|
||||
* Returns 0 if point moved, -1 if not found.*/
|
||||
int Move(double when, double value);
|
||||
int Reassign(double when, double value);
|
||||
|
||||
/** \brief DELETE a point by its position in array */
|
||||
void Delete(int point);
|
||||
@ -170,13 +181,16 @@ class Envelope final : public XMLTagHandler {
|
||||
size_t GetNumberOfPoints() const;
|
||||
|
||||
private:
|
||||
int InsertOrReplaceRelative(double when, double value);
|
||||
friend class EnvelopeEditor;
|
||||
/** \brief Accessor for points */
|
||||
EnvPoint &operator[] (int index)
|
||||
const EnvPoint &operator[] (int index) const
|
||||
{
|
||||
return mEnv[index];
|
||||
}
|
||||
|
||||
std::pair<int, int> EqualRange( double when, double sampleTime ) const;
|
||||
|
||||
public:
|
||||
/** \brief Returns the sets of when and value pairs */
|
||||
void GetPoints(double *bufferWhen,
|
||||
@ -193,47 +207,40 @@ public:
|
||||
bool GetDragPointValid() const { return mDragPointValid; }
|
||||
// Modify the dragged point and change its value.
|
||||
// But consistency constraints may move it less then you ask for.
|
||||
|
||||
private:
|
||||
void MoveDragPoint(double newWhen, double value);
|
||||
// May delete the drag point. Restores envelope consistency.
|
||||
void ClearDragPoint();
|
||||
|
||||
private:
|
||||
EnvPoint * 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;
|
||||
double GetInterpolationStartValueAtPoint( int iPoint ) const;
|
||||
|
||||
// Possibly inline functions:
|
||||
// This function resets them integral memoizers (call whenever the Envelope changes)
|
||||
void resetIntegralMemoizer() { lastIntegral_t0=0; lastIntegral_t1=0; lastIntegral_result=0; }
|
||||
|
||||
// The list of envelope control points.
|
||||
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;
|
||||
|
||||
// These are memoizing variables for Integral()
|
||||
double lastIntegral_t0;
|
||||
double lastIntegral_t1;
|
||||
double lastIntegral_result;
|
||||
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)
|
||||
|
@ -45,12 +45,9 @@ TimeTrack::TimeTrack(const std::shared_ptr<DirManager> &projDirManager, const Zo
|
||||
mRangeUpper = 1.1;
|
||||
mDisplayLog = false;
|
||||
|
||||
mEnvelope = std::make_unique<Envelope>();
|
||||
mEnvelope = std::make_unique<Envelope>(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<Envelope>();
|
||||
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<Envelope>( *orig.mEnvelope, *pT0, *pT1 );
|
||||
else
|
||||
mEnvelope->Paste(0.0, orig.mEnvelope.get());
|
||||
mEnvelope = std::make_unique<Envelope>( *orig.mEnvelope );
|
||||
mEnvelope->SetTrackLen(DBL_MAX);
|
||||
mEnvelope->SetOffset(0);
|
||||
|
||||
///@TODO: Give Ruler:: a copy-constructor instead of this?
|
||||
mRuler = std::make_unique<Ruler>();
|
||||
@ -145,11 +136,11 @@ Track::Holder TimeTrack::Duplicate() const
|
||||
|
||||
bool TimeTrack::GetInterpolateLog() const
|
||||
{
|
||||
return mEnvelope->GetInterpolateDB();
|
||||
return mEnvelope->GetExponential();
|
||||
}
|
||||
|
||||
void TimeTrack::SetInterpolateLog(bool interpolateLog) {
|
||||
mEnvelope->SetInterpolateDB(interpolateLog);
|
||||
mEnvelope->SetExponential(interpolateLog);
|
||||
}
|
||||
|
||||
//Compute the (average) warp factor between two non-warped time points
|
||||
@ -226,7 +217,7 @@ void TimeTrack::HandleXMLEndTag(const wxChar * WXUNUSED(tag))
|
||||
if(mRescaleXMLValues)
|
||||
{
|
||||
mRescaleXMLValues = false;
|
||||
mEnvelope->Rescale(mRangeLower, mRangeUpper);
|
||||
mEnvelope->RescaleValues(mRangeLower, mRangeUpper);
|
||||
mEnvelope->SetRange(TIMETRACK_MIN, TIMETRACK_MAX);
|
||||
}
|
||||
}
|
||||
@ -308,10 +299,10 @@ void TimeTrack::Draw(wxDC & dc, const wxRect & r, const ZoomInfo &zoomInfo) cons
|
||||
void TimeTrack::testMe()
|
||||
{
|
||||
GetEnvelope()->Flatten(0.0);
|
||||
GetEnvelope()->Insert( 0.0, 0.2 );
|
||||
GetEnvelope()->Insert( 5.0 - 0.001, 0.2 );
|
||||
GetEnvelope()->Insert( 5.0 + 0.001, 1.3 );
|
||||
GetEnvelope()->Insert( 10.0, 1.3 );
|
||||
GetEnvelope()->InsertOrReplace(0.0, 0.2);
|
||||
GetEnvelope()->InsertOrReplace(5.0 - 0.001, 0.2);
|
||||
GetEnvelope()->InsertOrReplace(5.0 + 0.001, 1.3);
|
||||
GetEnvelope()->InsertOrReplace(10.0, 1.3);
|
||||
|
||||
double value1 = GetEnvelope()->Integral(2.0, 13.0);
|
||||
double expected1 = (5.0 - 2.0) * 0.2 + (13.0 - 5.0) * 1.3;
|
||||
|
@ -309,7 +309,7 @@ WaveClip::WaveClip(const std::shared_ptr<DirManager> &projDirManager,
|
||||
mRate = rate;
|
||||
mSequence = std::make_unique<Sequence>(projDirManager, format);
|
||||
|
||||
mEnvelope = std::make_unique<Envelope>();
|
||||
mEnvelope = std::make_unique<Envelope>(true, 1e-7, 2.0, 1.0);
|
||||
|
||||
mWaveCache = std::make_unique<WaveCache>();
|
||||
mSpecCache = std::make_unique<SpecCache>();
|
||||
@ -328,10 +328,7 @@ WaveClip::WaveClip(const WaveClip& orig,
|
||||
mRate = orig.mRate;
|
||||
mSequence = std::make_unique<Sequence>(*orig.mSequence, projDirManager);
|
||||
|
||||
mEnvelope = std::make_unique<Envelope>();
|
||||
mEnvelope->Paste(0.0, orig.mEnvelope.get());
|
||||
mEnvelope->SetOffset(orig.GetOffset());
|
||||
mEnvelope->SetTrackLen((orig.mSequence->GetNumSamples().as_double()) / orig.mRate);
|
||||
mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
|
||||
|
||||
mWaveCache = std::make_unique<WaveCache>();
|
||||
mSpecCache = std::make_unique<SpecCache>();
|
||||
@ -345,7 +342,6 @@ WaveClip::WaveClip(const WaveClip& orig,
|
||||
mIsPlaceholder = orig.GetIsPlaceholder();
|
||||
}
|
||||
|
||||
// to do
|
||||
WaveClip::WaveClip(const WaveClip& orig,
|
||||
const std::shared_ptr<DirManager> &projDirManager,
|
||||
bool copyCutlines,
|
||||
@ -369,10 +365,11 @@ WaveClip::WaveClip(const WaveClip& orig,
|
||||
|
||||
mSequence = orig.mSequence->Copy(s0, s1);
|
||||
|
||||
mEnvelope = std::make_unique<Envelope>();
|
||||
mEnvelope->CopyFrom(orig.mEnvelope.get(),
|
||||
mOffset + s0.as_double()/mRate,
|
||||
mOffset + s1.as_double()/mRate);
|
||||
mEnvelope = std::make_unique<Envelope>(
|
||||
*orig.mEnvelope,
|
||||
mOffset + s0.as_double()/mRate,
|
||||
mOffset + s1.as_double()/mRate
|
||||
);
|
||||
|
||||
if ( copyCutlines )
|
||||
// Copy cutline clips that fall in the range
|
||||
@ -1835,7 +1832,8 @@ void WaveClip::Unlock()
|
||||
void WaveClip::SetRate(int rate)
|
||||
{
|
||||
mRate = rate;
|
||||
UpdateEnvelopeTrackLen();
|
||||
auto newLength = mSequence->GetNumSamples().as_double() / mRate;
|
||||
mEnvelope->RescaleTimes( newLength );
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
|
@ -394,9 +394,14 @@ double WaveTrack::GetRate() const
|
||||
|
||||
void WaveTrack::SetRate(double newRate)
|
||||
{
|
||||
wxASSERT( newRate > 0 );
|
||||
newRate = std::max( 1.0, newRate );
|
||||
auto ratio = mRate / newRate;
|
||||
mRate = (int) newRate;
|
||||
for (const auto &clip : mClips)
|
||||
for (const auto &clip : mClips) {
|
||||
clip->SetRate((int)newRate);
|
||||
clip->SetOffset( clip->GetOffset() * ratio );
|
||||
}
|
||||
}
|
||||
|
||||
float WaveTrack::GetGain() const
|
||||
@ -1145,13 +1150,13 @@ void WaveTrack::HandleClear(double t0, double t1,
|
||||
if (clip->WithinClip(t0)) {
|
||||
// start of region within clip
|
||||
val = clip->GetEnvelope()->GetValue(t0);
|
||||
newClip->GetEnvelope()->Insert(t0 - clip->GetOffset() - 1.0/clip->GetRate(), val);
|
||||
}
|
||||
if (clip->WithinClip(t1)) {
|
||||
// end of region within clip
|
||||
newClip->GetEnvelope()->InsertOrReplace(t0 - clip->GetOffset() - 1.0 / clip->GetRate(), val);
|
||||
}
|
||||
if (clip->WithinClip(t1))
|
||||
{ // end of region within clip
|
||||
val = clip->GetEnvelope()->GetValue(t1);
|
||||
newClip->GetEnvelope()->Insert(t1 - clip->GetOffset(), val);
|
||||
}
|
||||
newClip->GetEnvelope()->InsertOrReplace(t1 - clip->GetOffset(), val);
|
||||
}
|
||||
}
|
||||
newClip->Clear(t0,t1);
|
||||
newClip->GetEnvelope()->RemoveUnneededPoints(t0);
|
||||
@ -2393,8 +2398,8 @@ void WaveTrack::SplitAt(double 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()->Insert(t - c->GetOffset() - 1.0/c->GetRate(), val); // frame end points
|
||||
c->GetEnvelope()->Insert(t - c->GetOffset(), val);
|
||||
c->GetEnvelope()->InsertOrReplace(t - 1.0 / c->GetRate(), val); // frame end points
|
||||
c->GetEnvelope()->InsertOrReplace(t, val);
|
||||
auto newClip = make_movable<WaveClip>( *c, mDirManager, true );
|
||||
c->Clear(t, c->GetEndTime());
|
||||
newClip->Clear(c->GetStartTime(), t);
|
||||
|
@ -239,13 +239,15 @@ EffectEqualization::EffectEqualization()
|
||||
mInterpolations.Add(wxGetTranslation(kInterpStrings[i]));
|
||||
}
|
||||
|
||||
mLogEnvelope = std::make_unique<Envelope>();
|
||||
mLogEnvelope->SetInterpolateDB(false);
|
||||
mLogEnvelope->SetRange(MIN_dBMin, MAX_dBMax); // MB: this is the highest possible range
|
||||
mLogEnvelope = std::make_unique<Envelope>
|
||||
(false,
|
||||
MIN_dBMin, MAX_dBMax, // MB: this is the highest possible range
|
||||
1.0);
|
||||
|
||||
mLinEnvelope = std::make_unique<Envelope>();
|
||||
mLinEnvelope->SetInterpolateDB(false);
|
||||
mLinEnvelope->SetRange(MIN_dBMin, MAX_dBMax); // MB: this is the highest possible range
|
||||
mLinEnvelope = std::make_unique<Envelope>
|
||||
(false,
|
||||
MIN_dBMin, MAX_dBMax, // MB: this is the highest possible range
|
||||
1.0);
|
||||
|
||||
mEnvelope = (mLin ? mLinEnvelope : mLogEnvelope).get();
|
||||
|
||||
@ -1649,7 +1651,7 @@ void EffectEqualization::setCurve(int currentCurve)
|
||||
when = (log10(std::max((double) loFreqI, when)) - loLog)/denom;
|
||||
}
|
||||
value = mCurves[currentCurve].points[0].dB;
|
||||
env->Insert(std::min(1.0, std::max(0.0, when)), value);
|
||||
env->InsertOrReplace(std::min(1.0, std::max(0.0, when)), value);
|
||||
ForceRecalc();
|
||||
return;
|
||||
}
|
||||
@ -1668,17 +1670,18 @@ void EffectEqualization::setCurve(int currentCurve)
|
||||
when = mCurves[currentCurve].points[pointCount].Freq / mHiFreq;
|
||||
value = mCurves[currentCurve].points[pointCount].dB;
|
||||
if(when <= 1) {
|
||||
env->Insert(when, value);
|
||||
env->InsertOrReplace(when, value);
|
||||
if (when == 1)
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// There are more points at higher freqs, so interpolate next one then stop.
|
||||
when = 1.0;
|
||||
double lastF = mCurves[currentCurve].points[pointCount-1].Freq;
|
||||
double nextF = mCurves[currentCurve].points[pointCount].Freq;
|
||||
double lastDB = mCurves[currentCurve].points[pointCount-1].dB;
|
||||
double nextDB = mCurves[currentCurve].points[pointCount].dB;
|
||||
value = lastDB + ((nextDB - lastDB) * ((mHiFreq - lastF) / (nextF - lastF)));
|
||||
env->Insert(when, value);
|
||||
env->InsertOrReplace(when, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1700,7 +1703,7 @@ void EffectEqualization::setCurve(int currentCurve)
|
||||
// All points below 20 Hz, so just use final point.
|
||||
when = 0.0;
|
||||
value = mCurves[currentCurve].points[numPoints-1].dB;
|
||||
env->Insert(when, value);
|
||||
env->InsertOrReplace(when, value);
|
||||
ForceRecalc();
|
||||
return;
|
||||
}
|
||||
@ -1715,7 +1718,7 @@ void EffectEqualization::setCurve(int currentCurve)
|
||||
double nextDB = mCurves[currentCurve].points[firstAbove20Hz].dB;
|
||||
when = 0.0;
|
||||
value = nextDB - ((nextDB - prevDB) * ((nextF - loLog) / (nextF - prevF)));
|
||||
env->Insert(when, value);
|
||||
env->InsertOrReplace(when, value);
|
||||
}
|
||||
|
||||
// Now get the rest.
|
||||
@ -1727,7 +1730,7 @@ void EffectEqualization::setCurve(int currentCurve)
|
||||
when = (flog - loLog)/denom;
|
||||
value = mCurves[currentCurve].points[pointCount].dB;
|
||||
if(when <= 1.0) {
|
||||
env->Insert(when, value);
|
||||
env->InsertOrReplace(when, value);
|
||||
}
|
||||
else {
|
||||
// This looks weird when adjusting curve in Draw mode if
|
||||
@ -1746,7 +1749,7 @@ void EffectEqualization::setCurve(int currentCurve)
|
||||
double logLastF = log10(mCurves[currentCurve].points[pointCount-1].Freq);
|
||||
double lastDB = mCurves[currentCurve].points[pointCount-1].dB;
|
||||
value = lastDB + ((value - lastDB) * ((log10(mHiFreq) - logLastF) / (flog - logLastF)));
|
||||
env->Insert(when, value);
|
||||
env->InsertOrReplace(when, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2175,7 +2178,7 @@ void EffectEqualization::UpdateGraphic()
|
||||
{
|
||||
when = freq/mHiFreq;
|
||||
value = mLinEnvelope->GetValue(when);
|
||||
mLinEnvelope->Insert(when, value);
|
||||
mLinEnvelope->InsertOrReplace(when, value);
|
||||
}
|
||||
|
||||
EnvLinToLog();
|
||||
@ -2247,14 +2250,14 @@ void EffectEqualization::EnvLogToLin(void)
|
||||
mLinEnvelope->Flatten(0.);
|
||||
mLinEnvelope->SetTrackLen(1.0);
|
||||
mLogEnvelope->GetPoints( when.get(), value.get(), numPoints );
|
||||
mLinEnvelope->Move(0., value[0]);
|
||||
mLinEnvelope->Reassign(0., value[0]);
|
||||
double loLog = log10(20.);
|
||||
double hiLog = log10(mHiFreq);
|
||||
double denom = hiLog - loLog;
|
||||
|
||||
for (size_t i = 0; i < numPoints; i++)
|
||||
mLinEnvelope->Insert(pow( 10., ((when[i] * denom) + loLog))/mHiFreq , value[i]);
|
||||
mLinEnvelope->Move(1., value[numPoints-1]);
|
||||
mLinEnvelope->InsertOrReplace(pow( 10., ((when[i] * denom) + loLog))/mHiFreq , value[i]);
|
||||
mLinEnvelope->Reassign(1., value[numPoints-1]);
|
||||
}
|
||||
|
||||
void EffectEqualization::EnvLinToLog(void)
|
||||
@ -2271,7 +2274,7 @@ void EffectEqualization::EnvLinToLog(void)
|
||||
mLogEnvelope->Flatten(0.);
|
||||
mLogEnvelope->SetTrackLen(1.0);
|
||||
mLinEnvelope->GetPoints( when.get(), value.get(), numPoints );
|
||||
mLogEnvelope->Move(0., value[0]);
|
||||
mLogEnvelope->Reassign(0., value[0]);
|
||||
double loLog = log10(20.);
|
||||
double hiLog = log10(mHiFreq);
|
||||
double denom = hiLog - loLog;
|
||||
@ -2284,16 +2287,16 @@ void EffectEqualization::EnvLinToLog(void)
|
||||
// Caution: on Linux, when when == 20, the log calulation rounds
|
||||
// to just under zero, which causes an assert error.
|
||||
double flog = (log10(when[i]*mHiFreq)-loLog)/denom;
|
||||
mLogEnvelope->Insert(std::max(0.0, flog) , value[i]);
|
||||
mLogEnvelope->InsertOrReplace(std::max(0.0, flog) , value[i]);
|
||||
}
|
||||
else
|
||||
{ //get the first point as close as we can to the last point requested
|
||||
changed = true;
|
||||
double v = value[i];
|
||||
mLogEnvelope->Insert(0., v);
|
||||
mLogEnvelope->InsertOrReplace(0., v);
|
||||
}
|
||||
}
|
||||
mLogEnvelope->Move(1., value[numPoints-1]);
|
||||
mLogEnvelope->Reassign(1., value[numPoints - 1]);
|
||||
|
||||
if(changed)
|
||||
EnvelopeUpdated(mLogEnvelope.get(), false);
|
||||
@ -2308,12 +2311,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]);
|
||||
@ -2451,10 +2449,10 @@ void EffectEqualization::GraphicEQ(Envelope *env)
|
||||
}
|
||||
}
|
||||
if(mWhens[i]<=0.)
|
||||
env->Move( 0., value );
|
||||
env->Insert( mWhens[i], value );
|
||||
env->Reassign(0., value);
|
||||
env->InsertOrReplace( mWhens[i], value );
|
||||
}
|
||||
env->Move( 1., value );
|
||||
env->Reassign( 1., value );
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2495,10 +2493,10 @@ void EffectEqualization::GraphicEQ(Envelope *env)
|
||||
}
|
||||
}
|
||||
if(mWhens[i]<=0.)
|
||||
env->Move( 0., value );
|
||||
env->Insert( mWhens[i], value );
|
||||
env->Reassign(0., value);
|
||||
env->InsertOrReplace( mWhens[i], value );
|
||||
}
|
||||
env->Move( 1., value );
|
||||
env->Reassign( 1., value );
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2509,7 +2507,7 @@ void EffectEqualization::GraphicEQ(Envelope *env)
|
||||
spline(mWhenSliders, mEQVals, mBandsInUse+1, y2);
|
||||
for(double xf=0; xf<1.; xf+=1./NUM_PTS)
|
||||
{
|
||||
env->Insert(xf, splint(mWhenSliders, mEQVals, mBandsInUse+1, y2, xf));
|
||||
env->InsertOrReplace(xf, splint(mWhenSliders, mEQVals, mBandsInUse+1, y2, xf));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2738,9 +2736,9 @@ void EffectEqualization::OnInvert(wxCommandEvent & WXUNUSED(event)) // Inverts a
|
||||
for (size_t i = 0; i < numPoints; i++)
|
||||
{
|
||||
if(lin)
|
||||
mLinEnvelope->Move(when[i] , -value[i]);
|
||||
mLinEnvelope->Reassign(when[i] , -value[i]);
|
||||
else
|
||||
mLogEnvelope->Move(when[i] , -value[i]);
|
||||
mLogEnvelope->Reassign(when[i] , -value[i]);
|
||||
}
|
||||
|
||||
// copy it back to the other one (just in case)
|
||||
|
Loading…
x
Reference in New Issue
Block a user