35#include <wx/wxcrtvararg.h>
38#include <wx/textfile.h>
48 , mDefaultValue { ClampValue(defaultValue) }
63 bool consistent =
true;
68 for (
size_t ii = 0, count =
mEnv.size(); ii < count; ) {
70 const double thisT =
mEnv[ii].GetT();
73 while ( nextI < count && thisT == ( nextT =
mEnv[nextI].GetT() ) )
76 if ( nextI < count && nextT < thisT )
79 while ( nextI - ii > 2 ) {
101 std::stable_sort(
mEnv.begin(),
mEnv.end(),
103 { return a.GetT() < b.GetT(); } );
105 }
while ( disorder );
124 double factor = (
mDefaultValue - oldMinValue) / (oldMaxValue - oldMinValue);
128 for(
unsigned int i = 0; i <
mEnv.size(); i++ ) {
129 factor = (
mEnv[i].GetVal() - oldMinValue) / (oldMaxValue - oldMinValue);
160 static const double big = std::numeric_limits<double>::max();
195 double limitLo = 0.0;
205 std::max(limitLo,
std::min(limitHi, newWhen));
210 dragPoint.
SetVal(
this, value );
226 for(
unsigned int i = 0; i <
mEnv.size(); i++ )
227 mEnv[i].SetVal(
this,
mEnv[i].GetVal() );
240 auto nn =
mEnv.size() - 1;
241 while ( nn >= 2 &&
mEnv[ nn - 2 ].GetT() == t ) {
244 mEnv.erase(
mEnv.begin() + nn - 1 );
251 , mMinValue(orig.mMinValue)
252 , mMaxValue(orig.mMaxValue)
253 , mDefaultValue(orig.mDefaultValue)
260 CopyRange(orig, range1.first, range2.second);
265 , mMinValue(orig.mMinValue)
266 , mMaxValue(orig.mMaxValue)
267 , mDefaultValue(orig.mDefaultValue)
276 size_t len = orig.
mEnv.size();
284 for (; i <
end; ++i) {
299static double Limit(
double Lo,
double Value,
double Hi )
312 if (tag !=
"envelope")
317 for (
auto pair : attrs)
319 auto attr = pair.first;
320 auto value = pair.second;
322 if (attr ==
"numpoints")
323 value.TryGet(numPoints);
330 mEnv.reserve(numPoints);
336 if (tag !=
"controlpoint")
348 xmlFile.StartTag(
wxT(
"envelope"));
349 xmlFile.WriteAttr(
wxT(
"numpoints"), mEnv.size());
351 for (ctrlPt = 0; ctrlPt < mEnv.size(); ctrlPt++) {
352 const EnvPoint &point = mEnv[ctrlPt];
353 xmlFile.StartTag(
wxT(
"controlpoint"));
354 xmlFile.WriteAttr(
wxT(
"t"), point.
GetT(), 12);
355 xmlFile.WriteAttr(
wxT(
"val"), point.
GetVal(), 12);
356 xmlFile.EndTag(
wxT(
"controlpoint"));
359 xmlFile.EndTag(
wxT(
"envelope"));
369 mEnv.insert(
mEnv.begin() + point, p);
389 const auto epsilon = sampleDur / 2;
392 bool leftPoint =
true, rightPoint =
true;
396 auto begin = range0.first;
397 if (
begin == range0.second ) {
398 if ( t0 > epsilon ) {
416 auto end = range1.second;
417 if ( range1.first ==
end ) {
439 auto len =
mEnv.size();
440 for (
size_t i =
begin; i < len; ++i ) {
441 auto &point =
mEnv[i];
442 if (rightPoint && (
int)i ==
begin)
448 point.SetT( point.GetT() - (t1 - t0) );
468 const bool wasEmpty = (this->
mEnv.size() == 0);
469 auto otherSize = e->
mEnv.size();
471 const auto otherOffset = e->
mOffset;
472 const auto deltat = otherOffset + otherDur;
474 if ( otherSize == 0 && wasEmpty && e->
mDefaultValue == this->mDefaultValue )
494 auto index = range.first;
495 if ( index + 2 == range.second &&
496 ( newT0 =
mEnv[ index ].GetT() ) ==
mEnv[ 1 + index ].GetT() )
505 const auto range =
ExpandRegion( t0, deltat, &leftVal, &rightVal );
508 auto insertAt = range.first + 1;
512 if ( otherSize != 0 && e->
mEnv[ otherSize - 1 ].GetT() == otherDur )
516 if ( otherSize != 0 && otherOffset == 0.0 && e->
mEnv[ 0 ].GetT() == 0.0 )
517 ++
begin, --otherSize;
521 for (
size_t index = insertAt, last = insertAt + otherSize;
522 index < last; ++index ) {
523 auto &point =
mEnv[ index ];
530 point.SetT( point.GetT() + t0 );
550 (
size_t startAt,
bool rightward,
bool testNeighbors )
556 auto isDiscontinuity = [
this](
size_t index ) {
560 return point1.
GetT() == point2.
GetT() &&
564 auto remove = [
this](
size_t index,
bool leftLimit ) {
566 const auto &point =
mEnv[ index ];
567 auto when = point.GetT();
568 auto val = point.GetVal();
580 auto len =
mEnv.size();
583 !rightward && startAt + 1 < len && isDiscontinuity( startAt );
585 bool removed = remove( startAt, leftLimit );
591 if ( !testNeighbors )
597 int index = startAt + ( rightward ? 1 : -1 );
598 while ( index >= 0 && index < (
int)len ) {
600 if ( index > 0 && isDiscontinuity( index - 1 ) )
602 if ( (index + 1) < (int)len && isDiscontinuity( index ) )
605 if ( ! remove( index,
false ) )
616 (
double t0,
double tlen,
double *pLeftVal,
double *pRightVal )
624 int index = 1 + range.first;
625 if ( index <= range.second )
634 auto len =
mEnv.size();
635 for (
unsigned int ii = index; ii < len; ++ii ) {
636 auto &point =
mEnv[ ii ];
637 point.SetT( point.GetT() + tlen );
643 if ( index < range.second )
662 return { 1 + range.first, index };
679 int len =
mEnv.size();
684 while (i < len && when >
mEnv[i].GetT())
687 if (i >= len || when <
mEnv[i].GetT())
690 mEnv[i].SetVal(
this, value );
708 for (i = 0; i < n; i++) {
710 bufferValue[i] =
mEnv[i].GetVal();
717 if ( range.first == range.second )
736 msg = wxString::Format(
wxT(
"when %.20f mTrackLen %.20f diff %.20f"), when,
mTrackLen, when-
mTrackLen);
742 msg = wxString::Format(
wxT(
"when %.20f mTrackLen %.20f"), when,
mTrackLen);
743 wxASSERT_MSG(when >= 0, msg);
750 int index = range.first;
752 if ( index < range.second )
755 mEnv[ index ].SetVal(
this, value );
769 const auto tolerance = sampleDur / 2;
772 auto first = std::lower_bound(
776 {
return point1.
GetT() < point2.GetT(); }
779 while ( after !=
end && after->GetT() <= when + tolerance )
796 auto range =
EqualRange( trackLen, sampleDur );
797 bool needPoint = ( range.first == range.second && trackLen <
mTrackLen );
806 int newLen =
std::min( 1 + range.first, range.second );
807 mEnv.resize( newLen );
817 for (
auto &point :
mEnv )
822 for (
auto &point :
mEnv )
823 point.SetT( point.GetT() * ratio );
830 for (
auto& point :
mEnv)
831 point.SetT(point.GetT() * ratio);
888 while (Hi > (Lo + 1)) {
889 int mid = (Lo + Hi) / 2;
891 if (t <
mEnv[mid].GetT())
896 wxASSERT( Hi == ( Lo+1 ));
910 while (Hi > (Lo + 1)) {
911 int mid = (Lo + Hi) / 2;
913 if (t <=
mEnv[mid].GetT())
918 wxASSERT( Hi == ( Lo+1 ));
930 double v =
mEnv[ iPoint ].GetVal();
938 double t0,
double tstep )
const
946 (
double *buffer,
int bufferLen,
double t0,
double tstep,
bool leftLimit)
952 const auto epsilon = tstep / 2;
953 int len =
mEnv.size();
956 double increment = 0;
957 if ( len > 1 && t <=
mEnv[0].GetT() &&
mEnv[0].GetT() ==
mEnv[1].GetT() )
958 increment = leftLimit ? -epsilon : epsilon;
960 double tprev, vprev, tnext = 0, vnext, vstep = 0;
962 for (
int b = 0; b < bufferLen; b++) {
972 auto tplus = t + increment;
975 if ( leftLimit ? tplus <=
mEnv[0].GetT() : tplus <
mEnv[0].GetT() ) {
976 buffer[b] =
mEnv[0].GetVal();
982 ? tplus >
mEnv[len - 1].GetT() : tplus >=
mEnv[len - 1].GetT() ) {
983 buffer[b] =
mEnv[len - 1].GetVal();
990 ( leftLimit ? tplus > tnext : tplus >= tnext ) ) {
1005 wxASSERT( lo >= 0 && hi <= len - 1 );
1007 tprev =
mEnv[lo].GetT();
1008 tnext =
mEnv[hi].GetT();
1010 if ( hi + 1 < len && tnext ==
mEnv[ hi + 1 ].GetT() )
1019 increment = leftLimit ? -epsilon : epsilon;
1027 double dt = (tnext - tprev);
1028 double to = t - tprev;
1032 v = (vprev * (dt - to) + vnext * to) / dt;
1033 vstep = (vnext - vprev) * tstep / dt;
1045 vstep = pow( 10.0, vstep );
1051 buffer[b] = buffer[b - 1] * vstep;
1053 buffer[b] = buffer[b - 1] + vstep;
1067 return mEnv.size() - hi;
1075 if (hi >= (
int)
mEnv.size())
1078 return mEnv[hi].GetT();
1086 return Integral( t0, t1 ) / (t1 - t0);
1111 return exp(log(y1) * (1.0 - factor) + log(y2) * factor);
1113 return y1 * (1.0 - factor) + y2 * factor;
1126 double l = log(y1 / y2);
1127 if(fabs(l) < 1.0e-5)
1128 return (y1 + y2) * 0.5 * time;
1129 return (y1 - y2) / l * time;
1133 return (y1 + y2) * 0.5 * time;
1145 double l = log(y1 / y2);
1146 if(fabs(l) < 1.0e-5)
1147 return 2.0 / (y1 + y2) * time;
1149 return (y1 - y2) / (l * y1 * y2) * time;
1151 return l / (y1 - y2) * time;
1157 double a = area / time, res;
1160 double l = log(y1 / y2);
1161 if(fabs(l) < 1.0e-5)
1162 res = a * (y1 + y2) * 0.5;
1163 else if(1.0 + a * y1 * l <= 0.0)
1166 res = log1p(a * y1 * l) / l;
1170 if(fabs(y2 - y1) < 1.0e-5)
1171 res = a * (y1 + y2) * 0.5;
1173 res = y1 * expm1(a * (y2 - y1)) / (y2 - y1);
1175 return std::max(0.0,
std::min(1.0, res)) * time;
1189 unsigned int count =
mEnv.size();
1196 double total = 0.0, lastT, lastVal;
1198 if(t0 <
mEnv[0].GetT())
1200 if(t1 <=
mEnv[0].GetT())
1201 return (t1 - t0) *
mEnv[0].GetVal();
1203 lastT =
mEnv[0].GetT();
1204 lastVal =
mEnv[0].GetVal();
1205 total += (lastT - t0) * lastVal;
1207 else if(t0 >=
mEnv[count - 1].GetT())
1209 return (t1 - t0) *
mEnv[count - 1].GetVal();
1226 return total + (t1 - lastT) * lastVal;
1228 else if(
mEnv[i].GetT() >= t1)
1236 lastT =
mEnv[i].GetT();
1237 lastVal =
mEnv[i].GetVal();
1252 unsigned int count =
mEnv.size();
1259 double total = 0.0, lastT, lastVal;
1261 if(t0 <
mEnv[0].GetT())
1263 if(t1 <=
mEnv[0].GetT())
1264 return (t1 - t0) /
mEnv[0].GetVal();
1266 lastT =
mEnv[0].GetT();
1267 lastVal =
mEnv[0].GetVal();
1268 total += (lastT - t0) / lastVal;
1270 else if(t0 >=
mEnv[count - 1].GetT())
1272 return (t1 - t0) /
mEnv[count - 1].GetVal();
1289 return total + (t1 - lastT) / lastVal;
1291 else if(
mEnv[i].GetT() >= t1)
1299 lastT =
mEnv[i].GetT();
1300 lastVal =
mEnv[i].GetVal();
1311 const auto count =
mEnv.size();
1319 double lastT, lastVal;
1321 if(t0 <
mEnv[0].GetT())
1324 return t0 + area *
mEnv[0].GetVal();
1328 lastT =
mEnv[0].GetT();
1329 lastVal =
mEnv[0].GetVal();
1330 double added = (lastT - t0) / lastVal;
1332 return t0 + area *
mEnv[0].GetVal();
1336 else if(t0 >=
mEnv[count - 1].GetT())
1340 lastT =
mEnv[count - 1].GetT();
1341 lastVal =
mEnv[count - 1].GetVal();
1342 double added = (lastT - t0) / lastVal;
1344 return t0 + area *
mEnv[count - 1].GetVal();
1348 return t0 + area *
mEnv[count - 1].GetVal();
1371 return lastT + area * lastVal;
1380 lastT =
mEnv[i].GetT();
1381 lastVal =
mEnv[i].GetVal();
1392 return lastT + area * lastVal;
1400 lastT =
mEnv[i].GetT();
1401 lastVal =
mEnv[i].GetVal();
1411 for(
unsigned int i = 0; i <
mEnv.size(); i++ )
1412 wxPrintf(
"(%.2f, %.2f)\n",
mEnv[i].GetT(),
mEnv[i].GetVal() );
1417 if( (a-b > 0 ? a-b : b-a) > 0.0000001 )
1419 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 RescaleTimesBy(double ratio)
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.