34#include <wx/wxcrtvararg.h>
37#include <wx/textfile.h>
47 , mDefaultValue { ClampValue(defaultValue) }
57 bool consistent =
true;
62 for (
size_t ii = 0, count =
mEnv.size(); ii < count; ) {
64 const double thisT =
mEnv[ii].GetT();
67 while ( nextI < count && thisT == ( nextT =
mEnv[nextI].GetT() ) )
70 if ( nextI < count && nextT < thisT )
73 while ( nextI - ii > 2 ) {
95 std::stable_sort(
mEnv.begin(),
mEnv.end(),
97 { return a.GetT() < b.GetT(); } );
118 double factor = (
mDefaultValue - oldMinValue) / (oldMaxValue - oldMinValue);
122 for(
unsigned int i = 0; i <
mEnv.size(); i++ ) {
123 factor = (
mEnv[i].GetVal() - oldMinValue) / (oldMaxValue - oldMinValue);
154 static const double big = std::numeric_limits<double>::max();
189 double limitLo = 0.0;
199 std::max(limitLo,
std::min(limitHi, newWhen));
204 dragPoint.
SetVal(
this, value );
220 for(
unsigned int i = 0; i <
mEnv.size(); i++ )
221 mEnv[i].SetVal(
this,
mEnv[i].GetVal() );
234 auto nn =
mEnv.size() - 1;
235 while ( nn >= 2 &&
mEnv[ nn - 2 ].GetT() == t ) {
238 mEnv.erase(
mEnv.begin() + nn - 1 );
245 , mMinValue(orig.mMinValue)
246 , mMaxValue(orig.mMaxValue)
247 , mDefaultValue(orig.mDefaultValue)
254 CopyRange(orig, range1.first, range2.second);
259 , mMinValue(orig.mMinValue)
260 , mMaxValue(orig.mMaxValue)
261 , mDefaultValue(orig.mDefaultValue)
270 size_t len = orig.
mEnv.size();
278 for (; i <
end; ++i) {
293static double Limit(
double Lo,
double Value,
double Hi )
306 if (tag !=
"envelope")
311 for (
auto pair : attrs)
313 auto attr = pair.first;
314 auto value = pair.second;
316 if (attr ==
"numpoints")
317 value.TryGet(numPoints);
324 mEnv.reserve(numPoints);
330 if (tag !=
"controlpoint")
342 xmlFile.StartTag(wxT(
"envelope"));
343 xmlFile.WriteAttr(wxT(
"numpoints"), mEnv.size());
345 for (ctrlPt = 0; ctrlPt < mEnv.size(); ctrlPt++) {
346 const EnvPoint &point = mEnv[ctrlPt];
347 xmlFile.StartTag(wxT(
"controlpoint"));
348 xmlFile.WriteAttr(wxT(
"t"), point.
GetT(), 12);
349 xmlFile.WriteAttr(wxT(
"val"), point.
GetVal(), 12);
350 xmlFile.EndTag(wxT(
"controlpoint"));
353 xmlFile.EndTag(wxT(
"envelope"));
363 mEnv.insert(
mEnv.begin() + point, p);
383 const auto epsilon = sampleDur / 2;
386 bool leftPoint =
true, rightPoint =
true;
390 auto begin = range0.first;
391 if (
begin == range0.second ) {
392 if ( t0 > epsilon ) {
410 auto end = range1.second;
411 if ( range1.first ==
end ) {
433 auto len =
mEnv.size();
434 for (
size_t i =
begin; i < len; ++i ) {
435 auto &point =
mEnv[i];
436 if (rightPoint && (
int)i ==
begin)
442 point.SetT( point.GetT() - (t1 - t0) );
462 const bool wasEmpty = (this->
mEnv.size() == 0);
463 auto otherSize = e->
mEnv.size();
465 const auto otherOffset = e->
mOffset;
466 const auto deltat = otherOffset + otherDur;
468 if ( otherSize == 0 && wasEmpty && e->
mDefaultValue == this->mDefaultValue )
488 auto index = range.first;
489 if ( index + 2 == range.second &&
490 ( newT0 =
mEnv[ index ].GetT() ) ==
mEnv[ 1 + index ].GetT() )
499 const auto range =
ExpandRegion( t0, deltat, &leftVal, &rightVal );
502 auto insertAt = range.first + 1;
506 if ( otherSize != 0 && e->
mEnv[ otherSize - 1 ].GetT() == otherDur )
510 if ( otherSize != 0 && otherOffset == 0.0 && e->
mEnv[ 0 ].GetT() == 0.0 )
511 ++
begin, --otherSize;
515 for (
size_t index = insertAt, last = insertAt + otherSize;
516 index < last; ++index ) {
517 auto &point =
mEnv[ index ];
524 point.SetT( point.GetT() + t0 );
544 (
size_t startAt,
bool rightward,
bool testNeighbors )
550 auto isDiscontinuity = [
this](
size_t index ) {
554 return point1.
GetT() == point2.
GetT() &&
558 auto remove = [
this](
size_t index,
bool leftLimit ) {
560 const auto &point =
mEnv[ index ];
561 auto when = point.GetT();
562 auto val = point.GetVal();
574 auto len =
mEnv.size();
577 !rightward && startAt + 1 < len && isDiscontinuity( startAt );
579 bool removed = remove( startAt, leftLimit );
585 if ( !testNeighbors )
591 int index = startAt + ( rightward ? 1 : -1 );
592 while ( index >= 0 && index < (
int)len ) {
594 if ( index > 0 && isDiscontinuity( index - 1 ) )
596 if ( (index + 1) < (int)len && isDiscontinuity( index ) )
599 if ( ! remove( index,
false ) )
610 (
double t0,
double tlen,
double *pLeftVal,
double *pRightVal )
618 int index = 1 + range.first;
619 if ( index <= range.second )
628 auto len =
mEnv.size();
629 for (
unsigned int ii = index; ii < len; ++ii ) {
630 auto &point =
mEnv[ ii ];
631 point.SetT( point.GetT() + tlen );
637 if ( index < range.second )
656 return { 1 + range.first, index };
673 int len =
mEnv.size();
678 while (i < len && when >
mEnv[i].GetT())
681 if (i >= len || when <
mEnv[i].GetT())
684 mEnv[i].SetVal(
this, value );
702 for (i = 0; i < n; i++) {
704 bufferValue[i] =
mEnv[i].GetVal();
711 if ( range.first == range.second )
730 msg = wxString::Format(wxT(
"when %.20f mTrackLen %.20f diff %.20f"), when,
mTrackLen, when-
mTrackLen);
736 msg = wxString::Format(wxT(
"when %.20f mTrackLen %.20f"), when,
mTrackLen);
737 wxASSERT_MSG(when >= 0, msg);
744 int index = range.first;
746 if ( index < range.second )
749 mEnv[ index ].SetVal(
this, value );
763 const auto tolerance = sampleDur / 2;
766 auto first = std::lower_bound(
770 {
return point1.
GetT() < point2.GetT(); }
773 while ( after !=
end && after->GetT() <= when + tolerance )
790 auto range =
EqualRange( trackLen, sampleDur );
791 bool needPoint = ( range.first == range.second && trackLen <
mTrackLen );
800 int newLen =
std::min( 1 + range.first, range.second );
801 mEnv.resize( newLen );
811 for (
auto &point :
mEnv )
816 for (
auto &point :
mEnv )
817 point.SetT( point.GetT() * ratio );
874 while (Hi > (Lo + 1)) {
875 int mid = (Lo + Hi) / 2;
877 if (t <
mEnv[mid].GetT())
882 wxASSERT( Hi == ( Lo+1 ));
896 while (Hi > (Lo + 1)) {
897 int mid = (Lo + Hi) / 2;
899 if (t <=
mEnv[mid].GetT())
904 wxASSERT( Hi == ( Lo+1 ));
916 double v =
mEnv[ iPoint ].GetVal();
924 double t0,
double tstep )
const
932 (
double *buffer,
int bufferLen,
double t0,
double tstep,
bool leftLimit)
938 const auto epsilon = tstep / 2;
939 int len =
mEnv.size();
942 double increment = 0;
943 if ( len > 1 && t <=
mEnv[0].GetT() &&
mEnv[0].GetT() ==
mEnv[1].GetT() )
944 increment = leftLimit ? -epsilon : epsilon;
946 double tprev, vprev, tnext = 0, vnext, vstep = 0;
948 for (
int b = 0; b < bufferLen; b++) {
958 auto tplus = t + increment;
961 if ( leftLimit ? tplus <=
mEnv[0].GetT() : tplus <
mEnv[0].GetT() ) {
962 buffer[b] =
mEnv[0].GetVal();
968 ? tplus >
mEnv[len - 1].GetT() : tplus >=
mEnv[len - 1].GetT() ) {
969 buffer[b] =
mEnv[len - 1].GetVal();
976 ( leftLimit ? tplus > tnext : tplus >= tnext ) ) {
991 wxASSERT( lo >= 0 && hi <= len - 1 );
993 tprev =
mEnv[lo].GetT();
994 tnext =
mEnv[hi].GetT();
996 if ( hi + 1 < len && tnext ==
mEnv[ hi + 1 ].GetT() )
1005 increment = leftLimit ? -epsilon : epsilon;
1013 double dt = (tnext - tprev);
1014 double to = t - tprev;
1018 v = (vprev * (dt - to) + vnext * to) / dt;
1019 vstep = (vnext - vprev) * tstep / dt;
1031 vstep = pow( 10.0, vstep );
1037 buffer[b] = buffer[b - 1] * vstep;
1039 buffer[b] = buffer[b - 1] + vstep;
1053 return mEnv.size() - hi;
1061 if (hi >= (
int)
mEnv.size())
1064 return mEnv[hi].GetT();
1072 return Integral( t0, t1 ) / (t1 - t0);
1097 return exp(log(y1) * (1.0 - factor) + log(y2) * factor);
1099 return y1 * (1.0 - factor) + y2 * factor;
1112 double l = log(y1 / y2);
1113 if(fabs(l) < 1.0e-5)
1114 return (y1 + y2) * 0.5 * time;
1115 return (y1 - y2) / l * time;
1119 return (y1 + y2) * 0.5 * time;
1131 double l = log(y1 / y2);
1132 if(fabs(l) < 1.0e-5)
1133 return 2.0 / (y1 + y2) * time;
1135 return (y1 - y2) / (l * y1 * y2) * time;
1137 return l / (y1 - y2) * time;
1143 double a = area / time, res;
1146 double l = log(y1 / y2);
1147 if(fabs(l) < 1.0e-5)
1148 res = a * (y1 + y2) * 0.5;
1149 else if(1.0 + a * y1 * l <= 0.0)
1152 res = log1p(a * y1 * l) / l;
1156 if(fabs(y2 - y1) < 1.0e-5)
1157 res = a * (y1 + y2) * 0.5;
1159 res = y1 * expm1(a * (y2 - y1)) / (y2 - y1);
1161 return std::max(0.0,
std::min(1.0, res)) * time;
1175 unsigned int count =
mEnv.size();
1182 double total = 0.0, lastT, lastVal;
1184 if(t0 <
mEnv[0].GetT())
1186 if(t1 <=
mEnv[0].GetT())
1187 return (t1 - t0) *
mEnv[0].GetVal();
1189 lastT =
mEnv[0].GetT();
1190 lastVal =
mEnv[0].GetVal();
1191 total += (lastT - t0) * lastVal;
1193 else if(t0 >=
mEnv[count - 1].GetT())
1195 return (t1 - t0) *
mEnv[count - 1].GetVal();
1212 return total + (t1 - lastT) * lastVal;
1214 else if(
mEnv[i].GetT() >= t1)
1222 lastT =
mEnv[i].GetT();
1223 lastVal =
mEnv[i].GetVal();
1238 unsigned int count =
mEnv.size();
1245 double total = 0.0, lastT, lastVal;
1247 if(t0 <
mEnv[0].GetT())
1249 if(t1 <=
mEnv[0].GetT())
1250 return (t1 - t0) /
mEnv[0].GetVal();
1252 lastT =
mEnv[0].GetT();
1253 lastVal =
mEnv[0].GetVal();
1254 total += (lastT - t0) / lastVal;
1256 else if(t0 >=
mEnv[count - 1].GetT())
1258 return (t1 - t0) /
mEnv[count - 1].GetVal();
1275 return total + (t1 - lastT) / lastVal;
1277 else if(
mEnv[i].GetT() >= t1)
1285 lastT =
mEnv[i].GetT();
1286 lastVal =
mEnv[i].GetVal();
1297 const auto count =
mEnv.size();
1305 double lastT, lastVal;
1307 if(t0 <
mEnv[0].GetT())
1310 return t0 + area *
mEnv[0].GetVal();
1314 lastT =
mEnv[0].GetT();
1315 lastVal =
mEnv[0].GetVal();
1316 double added = (lastT - t0) / lastVal;
1318 return t0 + area *
mEnv[0].GetVal();
1322 else if(t0 >=
mEnv[count - 1].GetT())
1326 lastT =
mEnv[count - 1].GetT();
1327 lastVal =
mEnv[count - 1].GetVal();
1328 double added = (lastT - t0) / lastVal;
1330 return t0 + area *
mEnv[count - 1].GetVal();
1334 return t0 + area *
mEnv[count - 1].GetVal();
1357 return lastT + area * lastVal;
1366 lastT =
mEnv[i].GetT();
1367 lastVal =
mEnv[i].GetVal();
1378 return lastT + area * lastVal;
1386 lastT =
mEnv[i].GetT();
1387 lastVal =
mEnv[i].GetVal();
1397 for(
unsigned int i = 0; i <
mEnv.size(); i++ )
1398 wxPrintf(
"(%.2f, %.2f)\n",
mEnv[i].GetT(),
mEnv[i].GetVal() );
1403 if( (a-b > 0 ? a-b : b-a) > 0.0000001 )
1405 wxPrintf(
"Envelope: Result #%d is: %f, should be %f\n", n, a, b );
static double InterpolatePoints(double y1, double y2, double factor, bool logarithmic)
static void checkResult(int n, double a, double b)
static double IntegrateInverseInterpolated(double y1, double y2, double time, bool logarithmic)
static double SolveIntegrateInverseInterpolated(double y1, double y2, double time, double area, bool logarithmic)
static double IntegrateInterpolated(double y1, double y2, double time, bool logarithmic)
static const double VALUE_TOLERANCE
std::vector< Attribute > AttributesList
EnvPoint, derived from XMLTagHandler, provides Envelope with a draggable point type.
void SetVal(Envelope *pEnvelope, double val)
Piecewise linear or piecewise exponential function from double to double.
double SolveIntegralOfInverse(double t0, double area) const
Envelope(bool exponential, double minValue, double maxValue, double defaultValue)
int NumberOfPointsAfter(double t) const
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
double NextPointAfter(double t) const
void AddPointAtEnd(double t, double val)
double GetValue(double t, double sampleDur=0) const
Get envelope value at time t.
double mOffset
The time at which the envelope starts, i.e. the start offset.
double Integral(double t0, double t1) const
void PasteEnvelope(double t0, const Envelope *e, double sampleDur)
double Average(double t0, double t1) const
void SetTrackLen(double trackLen, double sampleDur=0.0)
void SetDragPoint(int dragPoint)
void SetOffset(double newOffset)
void WriteXML(XMLWriter &xmlFile) const
double ClampValue(double value)
void RescaleTimes(double newLength)
void GetValues(double *buffer, int len, double t0, double tstep) const
Get many envelope points at once.
void CollapseRegion(double t0, double t1, double sampleDur)
size_t GetNumberOfPoints() const
Return number of points.
void SetExponential(bool db)
std::pair< int, int > EqualRange(double when, double sampleDur) const
void BinarySearchForTime(int &Lo, int &Hi, double t) const
double IntegralOfInverse(double t0, double t1) const
void Cap(double sampleDur)
void GetValuesRelative(double *buffer, int len, double t0, double tstep, bool leftLimit=false) const
void MoveDragPoint(double newWhen, double value)
void InsertSpace(double t0, double tlen)
void BinarySearchForTime_LeftLimit(int &Lo, int &Hi, double t) const
void Delete(int point)
DELETE a point by its position in array.
double GetInterpolationStartValueAtPoint(int iPoint) const
double AverageOfInverse(double t0, double t1) const
int Reassign(double when, double value)
Move a point at when to value.
double mTrackLen
The length of the envelope, which is the same as the length of the underlying track (normally)
std::pair< int, int > ExpandRegion(double t0, double tlen, double *pLeftVal, double *pRightVal)
int InsertOrReplaceRelative(double when, double value)
Add a control point to the envelope.
void RescaleValues(double minValue, double maxValue)
void GetPoints(double *bufferWhen, double *bufferValue, int bufferLen) const
Returns the sets of when and value pairs.
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
void SetDragPointValid(bool valid)
void RemoveUnneededPoints(size_t startAt, bool rightward, bool testNeighbors=true)
void CopyRange(const Envelope &orig, size_t begin, size_t end)
void Insert(int point, const EnvPoint &p)
insert a point
double GetValueRelative(double t, bool leftLimit=false) const
void SetRange(double minValue, double maxValue)
void Flatten(double value)
This class is an interface which should be implemented by classes which wish to be able to load and s...
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.