34#include <wx/wxcrtvararg.h>
37#include <wx/textfile.h>
47 , mDefaultValue { ClampValue(defaultValue) }
62 bool consistent =
true;
67 for (
size_t ii = 0, count =
mEnv.size(); ii < count; ) {
69 const double thisT =
mEnv[ii].GetT();
72 while ( nextI < count && thisT == ( nextT =
mEnv[nextI].GetT() ) )
75 if ( nextI < count && nextT < thisT )
78 while ( nextI - ii > 2 ) {
100 std::stable_sort(
mEnv.begin(),
mEnv.end(),
102 { return a.GetT() < b.GetT(); } );
104 }
while ( disorder );
123 double factor = (
mDefaultValue - oldMinValue) / (oldMaxValue - oldMinValue);
127 for(
unsigned int i = 0; i <
mEnv.size(); i++ ) {
128 factor = (
mEnv[i].GetVal() - oldMinValue) / (oldMaxValue - oldMinValue);
159 static const double big = std::numeric_limits<double>::max();
194 double limitLo = 0.0;
204 std::max(limitLo,
std::min(limitHi, newWhen));
209 dragPoint.
SetVal(
this, value );
225 for(
unsigned int i = 0; i <
mEnv.size(); i++ )
226 mEnv[i].SetVal(
this,
mEnv[i].GetVal() );
239 auto nn =
mEnv.size() - 1;
240 while ( nn >= 2 &&
mEnv[ nn - 2 ].GetT() == t ) {
243 mEnv.erase(
mEnv.begin() + nn - 1 );
250 , mMinValue(orig.mMinValue)
251 , mMaxValue(orig.mMaxValue)
252 , mDefaultValue(orig.mDefaultValue)
259 CopyRange(orig, range1.first, range2.second);
264 , mMinValue(orig.mMinValue)
265 , mMaxValue(orig.mMaxValue)
266 , mDefaultValue(orig.mDefaultValue)
275 size_t len = orig.
mEnv.size();
283 for (; i <
end; ++i) {
298static double Limit(
double Lo,
double Value,
double Hi )
311 if (tag !=
"envelope")
316 for (
auto pair : attrs)
318 auto attr = pair.first;
319 auto value = pair.second;
321 if (attr ==
"numpoints")
322 value.TryGet(numPoints);
329 mEnv.reserve(numPoints);
335 if (tag !=
"controlpoint")
347 xmlFile.StartTag(
wxT(
"envelope"));
348 xmlFile.WriteAttr(
wxT(
"numpoints"), mEnv.size());
350 for (ctrlPt = 0; ctrlPt < mEnv.size(); ctrlPt++) {
351 const EnvPoint &point = mEnv[ctrlPt];
352 xmlFile.StartTag(
wxT(
"controlpoint"));
353 xmlFile.WriteAttr(
wxT(
"t"), point.
GetT(), 12);
354 xmlFile.WriteAttr(
wxT(
"val"), point.
GetVal(), 12);
355 xmlFile.EndTag(
wxT(
"controlpoint"));
358 xmlFile.EndTag(
wxT(
"envelope"));
368 mEnv.insert(
mEnv.begin() + point, p);
388 const auto epsilon = sampleDur / 2;
391 bool leftPoint =
true, rightPoint =
true;
395 auto begin = range0.first;
396 if (
begin == range0.second ) {
397 if ( t0 > epsilon ) {
415 auto end = range1.second;
416 if ( range1.first ==
end ) {
438 auto len =
mEnv.size();
439 for (
size_t i =
begin; i < len; ++i ) {
440 auto &point =
mEnv[i];
441 if (rightPoint && (
int)i ==
begin)
447 point.SetT( point.GetT() - (t1 - t0) );
467 const bool wasEmpty = (this->
mEnv.size() == 0);
468 auto otherSize = e->
mEnv.size();
470 const auto otherOffset = e->
mOffset;
471 const auto deltat = otherOffset + otherDur;
473 if ( otherSize == 0 && wasEmpty && e->
mDefaultValue == this->mDefaultValue )
493 auto index = range.first;
494 if ( index + 2 == range.second &&
495 ( newT0 =
mEnv[ index ].GetT() ) ==
mEnv[ 1 + index ].GetT() )
504 const auto range =
ExpandRegion( t0, deltat, &leftVal, &rightVal );
507 auto insertAt = range.first + 1;
511 if ( otherSize != 0 && e->
mEnv[ otherSize - 1 ].GetT() == otherDur )
515 if ( otherSize != 0 && otherOffset == 0.0 && e->
mEnv[ 0 ].GetT() == 0.0 )
516 ++
begin, --otherSize;
520 for (
size_t index = insertAt, last = insertAt + otherSize;
521 index < last; ++index ) {
522 auto &point =
mEnv[ index ];
529 point.SetT( point.GetT() + t0 );
549 (
size_t startAt,
bool rightward,
bool testNeighbors )
555 auto isDiscontinuity = [
this](
size_t index ) {
559 return point1.
GetT() == point2.
GetT() &&
563 auto remove = [
this](
size_t index,
bool leftLimit ) {
565 const auto &point =
mEnv[ index ];
566 auto when = point.GetT();
567 auto val = point.GetVal();
579 auto len =
mEnv.size();
582 !rightward && startAt + 1 < len && isDiscontinuity( startAt );
584 bool removed = remove( startAt, leftLimit );
590 if ( !testNeighbors )
596 int index = startAt + ( rightward ? 1 : -1 );
597 while ( index >= 0 && index < (
int)len ) {
599 if ( index > 0 && isDiscontinuity( index - 1 ) )
601 if ( (index + 1) < (int)len && isDiscontinuity( index ) )
604 if ( ! remove( index,
false ) )
615 (
double t0,
double tlen,
double *pLeftVal,
double *pRightVal )
623 int index = 1 + range.first;
624 if ( index <= range.second )
633 auto len =
mEnv.size();
634 for (
unsigned int ii = index; ii < len; ++ii ) {
635 auto &point =
mEnv[ ii ];
636 point.SetT( point.GetT() + tlen );
642 if ( index < range.second )
661 return { 1 + range.first, index };
678 int len =
mEnv.size();
683 while (i < len && when >
mEnv[i].GetT())
686 if (i >= len || when <
mEnv[i].GetT())
689 mEnv[i].SetVal(
this, value );
707 for (i = 0; i < n; i++) {
709 bufferValue[i] =
mEnv[i].GetVal();
716 if ( range.first == range.second )
735 msg = wxString::Format(
wxT(
"when %.20f mTrackLen %.20f diff %.20f"), when,
mTrackLen, when-
mTrackLen);
741 msg = wxString::Format(
wxT(
"when %.20f mTrackLen %.20f"), when,
mTrackLen);
742 wxASSERT_MSG(when >= 0, msg);
749 int index = range.first;
751 if ( index < range.second )
754 mEnv[ index ].SetVal(
this, value );
768 const auto tolerance = sampleDur / 2;
771 auto first = std::lower_bound(
775 {
return point1.
GetT() < point2.GetT(); }
778 while ( after !=
end && after->GetT() <= when + tolerance )
795 auto range =
EqualRange( trackLen, sampleDur );
796 bool needPoint = ( range.first == range.second && trackLen <
mTrackLen );
805 int newLen =
std::min( 1 + range.first, range.second );
806 mEnv.resize( newLen );
816 for (
auto &point :
mEnv )
821 for (
auto &point :
mEnv )
822 point.SetT( point.GetT() * ratio );
879 while (Hi > (Lo + 1)) {
880 int mid = (Lo + Hi) / 2;
882 if (t <
mEnv[mid].GetT())
887 wxASSERT( Hi == ( Lo+1 ));
901 while (Hi > (Lo + 1)) {
902 int mid = (Lo + Hi) / 2;
904 if (t <=
mEnv[mid].GetT())
909 wxASSERT( Hi == ( Lo+1 ));
921 double v =
mEnv[ iPoint ].GetVal();
929 double t0,
double tstep )
const
937 (
double *buffer,
int bufferLen,
double t0,
double tstep,
bool leftLimit)
943 const auto epsilon = tstep / 2;
944 int len =
mEnv.size();
947 double increment = 0;
948 if ( len > 1 && t <=
mEnv[0].GetT() &&
mEnv[0].GetT() ==
mEnv[1].GetT() )
949 increment = leftLimit ? -epsilon : epsilon;
951 double tprev, vprev, tnext = 0, vnext, vstep = 0;
953 for (
int b = 0; b < bufferLen; b++) {
963 auto tplus = t + increment;
966 if ( leftLimit ? tplus <=
mEnv[0].GetT() : tplus <
mEnv[0].GetT() ) {
967 buffer[b] =
mEnv[0].GetVal();
973 ? tplus >
mEnv[len - 1].GetT() : tplus >=
mEnv[len - 1].GetT() ) {
974 buffer[b] =
mEnv[len - 1].GetVal();
981 ( leftLimit ? tplus > tnext : tplus >= tnext ) ) {
996 wxASSERT( lo >= 0 && hi <= len - 1 );
998 tprev =
mEnv[lo].GetT();
999 tnext =
mEnv[hi].GetT();
1001 if ( hi + 1 < len && tnext ==
mEnv[ hi + 1 ].GetT() )
1010 increment = leftLimit ? -epsilon : epsilon;
1018 double dt = (tnext - tprev);
1019 double to = t - tprev;
1023 v = (vprev * (dt - to) + vnext * to) / dt;
1024 vstep = (vnext - vprev) * tstep / dt;
1036 vstep = pow( 10.0, vstep );
1042 buffer[b] = buffer[b - 1] * vstep;
1044 buffer[b] = buffer[b - 1] + vstep;
1058 return mEnv.size() - hi;
1066 if (hi >= (
int)
mEnv.size())
1069 return mEnv[hi].GetT();
1077 return Integral( t0, t1 ) / (t1 - t0);
1102 return exp(log(y1) * (1.0 - factor) + log(y2) * factor);
1104 return y1 * (1.0 - factor) + y2 * factor;
1117 double l = log(y1 / y2);
1118 if(fabs(l) < 1.0e-5)
1119 return (y1 + y2) * 0.5 * time;
1120 return (y1 - y2) / l * time;
1124 return (y1 + y2) * 0.5 * time;
1136 double l = log(y1 / y2);
1137 if(fabs(l) < 1.0e-5)
1138 return 2.0 / (y1 + y2) * time;
1140 return (y1 - y2) / (l * y1 * y2) * time;
1142 return l / (y1 - y2) * time;
1148 double a = area / time, res;
1151 double l = log(y1 / y2);
1152 if(fabs(l) < 1.0e-5)
1153 res = a * (y1 + y2) * 0.5;
1154 else if(1.0 + a * y1 * l <= 0.0)
1157 res = log1p(a * y1 * l) / l;
1161 if(fabs(y2 - y1) < 1.0e-5)
1162 res = a * (y1 + y2) * 0.5;
1164 res = y1 * expm1(a * (y2 - y1)) / (y2 - y1);
1166 return std::max(0.0,
std::min(1.0, res)) * time;
1180 unsigned int count =
mEnv.size();
1187 double total = 0.0, lastT, lastVal;
1189 if(t0 <
mEnv[0].GetT())
1191 if(t1 <=
mEnv[0].GetT())
1192 return (t1 - t0) *
mEnv[0].GetVal();
1194 lastT =
mEnv[0].GetT();
1195 lastVal =
mEnv[0].GetVal();
1196 total += (lastT - t0) * lastVal;
1198 else if(t0 >=
mEnv[count - 1].GetT())
1200 return (t1 - t0) *
mEnv[count - 1].GetVal();
1217 return total + (t1 - lastT) * lastVal;
1219 else if(
mEnv[i].GetT() >= t1)
1227 lastT =
mEnv[i].GetT();
1228 lastVal =
mEnv[i].GetVal();
1243 unsigned int count =
mEnv.size();
1250 double total = 0.0, lastT, lastVal;
1252 if(t0 <
mEnv[0].GetT())
1254 if(t1 <=
mEnv[0].GetT())
1255 return (t1 - t0) /
mEnv[0].GetVal();
1257 lastT =
mEnv[0].GetT();
1258 lastVal =
mEnv[0].GetVal();
1259 total += (lastT - t0) / lastVal;
1261 else if(t0 >=
mEnv[count - 1].GetT())
1263 return (t1 - t0) /
mEnv[count - 1].GetVal();
1280 return total + (t1 - lastT) / lastVal;
1282 else if(
mEnv[i].GetT() >= t1)
1290 lastT =
mEnv[i].GetT();
1291 lastVal =
mEnv[i].GetVal();
1302 const auto count =
mEnv.size();
1310 double lastT, lastVal;
1312 if(t0 <
mEnv[0].GetT())
1315 return t0 + area *
mEnv[0].GetVal();
1319 lastT =
mEnv[0].GetT();
1320 lastVal =
mEnv[0].GetVal();
1321 double added = (lastT - t0) / lastVal;
1323 return t0 + area *
mEnv[0].GetVal();
1327 else if(t0 >=
mEnv[count - 1].GetT())
1331 lastT =
mEnv[count - 1].GetT();
1332 lastVal =
mEnv[count - 1].GetVal();
1333 double added = (lastT - t0) / lastVal;
1335 return t0 + area *
mEnv[count - 1].GetVal();
1339 return t0 + area *
mEnv[count - 1].GetVal();
1362 return lastT + area * lastVal;
1371 lastT =
mEnv[i].GetT();
1372 lastVal =
mEnv[i].GetVal();
1383 return lastT + area * lastVal;
1391 lastT =
mEnv[i].GetT();
1392 lastVal =
mEnv[i].GetVal();
1402 for(
unsigned int i = 0; i <
mEnv.size(); i++ )
1403 wxPrintf(
"(%.2f, %.2f)\n",
mEnv[i].GetT(),
mEnv[i].GetVal() );
1408 if( (a-b > 0 ? a-b : b-a) > 0.0000001 )
1410 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.