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 ) {
102 std::stable_sort(
mEnv.begin(),
mEnv.end(),
104 { return a.GetT() < b.GetT(); } );
106 }
while ( disorder );
125 double factor = (
mDefaultValue - oldMinValue) / (oldMaxValue - oldMinValue);
129 for(
unsigned int i = 0; i <
mEnv.size(); i++ ) {
130 factor = (
mEnv[i].GetVal() - oldMinValue) / (oldMaxValue - oldMinValue);
164 static const double big = std::numeric_limits<double>::max();
201 double limitLo = 0.0;
211 std::max(limitLo,
std::min(limitHi, newWhen));
216 dragPoint.
SetVal(
this, value );
234 for(
unsigned int i = 0; i <
mEnv.size(); i++ )
235 mEnv[i].SetVal(
this,
mEnv[i].GetVal() );
250 auto nn =
mEnv.size() - 1;
251 while ( nn >= 2 &&
mEnv[ nn - 2 ].GetT() == t ) {
254 mEnv.erase(
mEnv.begin() + nn - 1 );
263 , mMinValue(orig.mMinValue)
264 , mMaxValue(orig.mMaxValue)
265 , mDefaultValue(orig.mDefaultValue)
272 CopyRange(orig, range1.first, range2.second);
277 , mMinValue(orig.mMinValue)
278 , mMaxValue(orig.mMaxValue)
279 , mDefaultValue(orig.mDefaultValue)
288 size_t len = orig.
mEnv.size();
296 for (; i <
end; ++i) {
311static double Limit(
double Lo,
double Value,
double Hi )
324 if (tag !=
"envelope")
329 for (
auto pair : attrs)
331 auto attr = pair.first;
332 auto value = pair.second;
334 if (attr ==
"numpoints")
335 value.TryGet(numPoints);
342 mEnv.reserve(numPoints);
348 if (tag !=
"controlpoint")
360 xmlFile.StartTag(
wxT(
"envelope"));
361 xmlFile.WriteAttr(
wxT(
"numpoints"), mEnv.size());
363 for (ctrlPt = 0; ctrlPt < mEnv.size(); ctrlPt++) {
364 const EnvPoint &point = mEnv[ctrlPt];
365 xmlFile.StartTag(
wxT(
"controlpoint"));
366 xmlFile.WriteAttr(
wxT(
"t"), point.
GetT(), 12);
367 xmlFile.WriteAttr(
wxT(
"val"), point.
GetVal(), 12);
368 xmlFile.EndTag(
wxT(
"controlpoint"));
371 xmlFile.EndTag(
wxT(
"envelope"));
383 mEnv.insert(mEnv.begin() + point, p);
407 const auto epsilon = sampleDur / 2;
408 t0 = std::max( 0.0,
std::min( mTrackLen, t0 - mOffset ) );
409 t1 = std::max( 0.0,
std::min( mTrackLen, t1 - mOffset ) );
410 bool leftPoint =
true, rightPoint =
true;
413 auto range0 = EqualRange(t0, 0);
414 auto begin = range0.first;
415 if (
begin == range0.second ) {
416 if ( t0 > epsilon ) {
419 auto val = GetValueRelative( t0 );
420 InsertOrReplaceRelative( t0, val );
433 auto range1 = EqualRange( t1, 0 );
434 auto end = range1.second;
435 if ( range1.first ==
end ) {
436 if ( mTrackLen - t1 > epsilon ) {
438 auto val = GetValueRelative( t1 );
439 InsertOrReplaceRelative( t1, val );
454 mEnv.erase( mEnv.begin() +
begin, mEnv.begin() +
end );
457 auto len = mEnv.size();
458 for (
size_t i =
begin; i < len; ++i ) {
459 auto &point = mEnv[i];
460 if (rightPoint && (
int)i ==
begin)
466 point.SetT( point.GetT() - (t1 - t0) );
471 RemoveUnneededPoints(
begin,
true );
473 RemoveUnneededPoints(
begin - 1,
false );
475 mTrackLen -= (t1 - t0);
488 const bool wasEmpty = (this->
mEnv.size() == 0);
489 auto otherSize = e->
mEnv.size();
491 const auto otherOffset = e->
mOffset;
492 const auto deltat = otherOffset + otherDur;
496 if ( otherSize == 0 && wasEmpty && e->
mDefaultValue == this->mDefaultValue )
516 auto index = range.first;
517 if ( index + 2 == range.second &&
518 ( newT0 =
mEnv[ index ].GetT() ) ==
mEnv[ 1 + index ].GetT() )
527 const auto range =
ExpandRegion( t0, deltat, &leftVal, &rightVal );
530 auto insertAt = range.first + 1;
534 if ( otherSize != 0 && e->
mEnv[ otherSize - 1 ].GetT() == otherDur )
538 if ( otherSize != 0 && otherOffset == 0.0 && e->
mEnv[ 0 ].GetT() == 0.0 )
539 ++
begin, --otherSize;
543 for (
size_t index = insertAt, last = insertAt + otherSize;
544 index < last; ++index ) {
545 auto &point =
mEnv[ index ];
552 point.SetT( point.GetT() + t0 );
572 size_t startAt,
bool rightward,
bool testNeighbors)
noexcept
578 auto isDiscontinuity = [
this](
size_t index )
noexcept {
580 const EnvPoint &point1 = mEnv[ index ];
581 const EnvPoint &point2 = mEnv[ index + 1 ];
582 return point1.
GetT() == point2.
GetT() &&
586 auto remove = [
this](
size_t index,
bool leftLimit )
noexcept {
588 const auto &point = mEnv[ index ];
589 auto when = point.GetT();
590 auto val = point.GetVal();
592 auto val1 = GetValueRelative ( when, leftLimit );
595 Insert( index,
EnvPoint{ when, val } );
605 auto len = mEnv.size();
608 !rightward && startAt + 1 < len && isDiscontinuity( startAt );
610 bool removed = remove( startAt, leftLimit );
616 if ( !testNeighbors )
622 int index = startAt + ( rightward ? 1 : -1 );
623 while ( index >= 0 && index < (
int)len ) {
625 if ( index > 0 && isDiscontinuity( index - 1 ) )
627 if ( (index + 1) < (int)len && isDiscontinuity( index ) )
630 if ( ! remove( index,
false ) )
641 (
double t0,
double tlen,
double *pLeftVal,
double *pRightVal )
649 int index = 1 + range.first;
650 if ( index <= range.second )
659 auto len =
mEnv.size();
660 for (
unsigned int ii = index; ii < len; ++ii ) {
661 auto &point =
mEnv[ ii ];
662 point.SetT( point.GetT() + tlen );
668 if ( index < range.second )
687 return { 1 + range.first, index };
704 int len =
mEnv.size();
709 while (i < len && when >
mEnv[i].GetT())
712 if (i >= len || when <
mEnv[i].GetT())
715 mEnv[i].SetVal(
this, value);
746 for (i = 0; i < n; i++) {
748 bufferValue[i] =
mEnv[i].GetVal();
755 if ( range.first == range.second )
771 if(when > mTrackLen + 0.0000001)
774 msg = wxString::Format(
wxT(
"when %.20f mTrackLen %.20f diff %.20f"), when, mTrackLen, when-mTrackLen);
775 wxASSERT_MSG(when <= (mTrackLen), msg);
780 msg = wxString::Format(
wxT(
"when %.20f mTrackLen %.20f"), when, mTrackLen);
781 wxASSERT_MSG(when >= 0, msg);
785 when = std::max( 0.0,
std::min( mTrackLen, when ) );
787 auto range = EqualRange( when, 0 );
788 int index = range.first;
790 if ( index < range.second )
793 mEnv[ index ].SetVal(
this, value );
796 Insert( index,
EnvPoint { when, value } );
808 const auto tolerance = sampleDur / 2;
809 auto begin = mEnv.begin();
810 auto end = mEnv.end();
811 auto first = std::lower_bound(
815 {
return point1.
GetT() < point2.GetT(); }
818 while ( after !=
end && after->GetT() <= when + tolerance )
835 auto range =
EqualRange( trackLen, sampleDur );
836 bool needPoint = ( range.first == range.second && trackLen <
mTrackLen );
845 int newLen =
std::min( 1 + range.first, range.second );
846 mEnv.resize( newLen );
858 for (
auto &point :
mEnv )
863 for (
auto &point :
mEnv )
864 point.SetT( point.GetT() * ratio );
873 for (
auto& point :
mEnv)
874 point.SetT(point.GetT() * ratio);
893 GetValuesRelative(&temp, 1, t, 0.0, leftLimit);
905 if (mSearchGuess >= 0 && mSearchGuess < (
int)mEnv.size()) {
906 if (t >= mEnv[mSearchGuess].GetT() &&
907 (1 + mSearchGuess == (
int)mEnv.size() ||
908 t < mEnv[1 + mSearchGuess].GetT())) {
910 Hi = 1 + mSearchGuess;
916 if (mSearchGuess >= 0 && mSearchGuess < (
int)mEnv.size()) {
917 if (t >= mEnv[mSearchGuess].GetT() &&
918 (1 + mSearchGuess == (
int)mEnv.size() ||
919 t < mEnv[1 + mSearchGuess].GetT())) {
921 Hi = 1 + mSearchGuess;
931 while (Hi > (Lo + 1)) {
932 int mid = (Lo + Hi) / 2;
934 if (t < mEnv[mid].GetT())
939 wxASSERT( Hi == ( Lo+1 ));
954 while (Hi > (Lo + 1)) {
955 int mid = (Lo + Hi) / 2;
957 if (t <= mEnv[mid].GetT())
962 wxASSERT( Hi == ( Lo+1 ));
974 double v = mEnv[ iPoint ].GetVal();
982 double t0,
double tstep )
const
990 (
double *buffer,
int bufferLen,
double t0,
double tstep,
bool leftLimit)
996 const auto epsilon = tstep / 2;
997 int len = mEnv.size();
1000 double increment = 0;
1001 if ( len > 1 && t <= mEnv[0].GetT() && mEnv[0].GetT() == mEnv[1].GetT() )
1002 increment = leftLimit ? -epsilon : epsilon;
1004 double tprev, vprev, tnext = 0, vnext, vstep = 0;
1006 for (
int b = 0; b < bufferLen; b++) {
1011 buffer[b] = mDefaultValue;
1016 auto tplus = t + increment;
1019 if ( leftLimit ? tplus <= mEnv[0].GetT() : tplus < mEnv[0].GetT() ) {
1020 buffer[b] = mEnv[0].GetVal();
1026 ? tplus > mEnv[len - 1].GetT() : tplus >= mEnv[len - 1].GetT() ) {
1027 buffer[b] = mEnv[len - 1].GetVal();
1034 ( leftLimit ? tplus > tnext : tplus >= tnext ) ) {
1043 BinarySearchForTime_LeftLimit( lo, hi, tplus );
1045 BinarySearchForTime( lo, hi, tplus );
1049 wxASSERT( lo >= 0 && hi <= len - 1 );
1051 tprev = mEnv[lo].GetT();
1052 tnext = mEnv[hi].GetT();
1054 if ( hi + 1 < len && tnext == mEnv[ hi + 1 ].GetT() )
1063 increment = leftLimit ? -epsilon : epsilon;
1067 vprev = GetInterpolationStartValueAtPoint( lo );
1068 vnext = GetInterpolationStartValueAtPoint( hi );
1071 double dt = (tnext - tprev);
1072 double to = t - tprev;
1076 v = (vprev * (dt - to) + vnext * to) / dt;
1077 vstep = (vnext - vprev) * tstep / dt;
1089 vstep = pow( 10.0, vstep );
1095 buffer[b] = buffer[b - 1] * vstep;
1097 buffer[b] = buffer[b - 1] + vstep;
1111 return mEnv.size() - hi;
1119 if (hi >= (
int)
mEnv.size())
1122 return mEnv[hi].GetT();
1130 return Integral( t0, t1 ) / (t1 - t0);
1155 return exp(log(y1) * (1.0 - factor) + log(y2) * factor);
1157 return y1 * (1.0 - factor) + y2 * factor;
1170 double l = log(y1 / y2);
1171 if(fabs(l) < 1.0e-5)
1172 return (y1 + y2) * 0.5 * time;
1173 return (y1 - y2) / l * time;
1177 return (y1 + y2) * 0.5 * time;
1189 double l = log(y1 / y2);
1190 if(fabs(l) < 1.0e-5)
1191 return 2.0 / (y1 + y2) * time;
1193 return (y1 - y2) / (l * y1 * y2) * time;
1195 return l / (y1 - y2) * time;
1201 double a = area / time, res;
1204 double l = log(y1 / y2);
1205 if(fabs(l) < 1.0e-5)
1206 res = a * (y1 + y2) * 0.5;
1207 else if(1.0 + a * y1 * l <= 0.0)
1210 res = log1p(a * y1 * l) / l;
1214 if(fabs(y2 - y1) < 1.0e-5)
1215 res = a * (y1 + y2) * 0.5;
1217 res = y1 * expm1(a * (y2 - y1)) / (y2 - y1);
1219 return std::max(0.0,
std::min(1.0, res)) * time;
1233 unsigned int count =
mEnv.size();
1240 double total = 0.0, lastT, lastVal;
1242 if(t0 <
mEnv[0].GetT())
1244 if(t1 <=
mEnv[0].GetT())
1245 return (t1 - t0) *
mEnv[0].GetVal();
1247 lastT =
mEnv[0].GetT();
1248 lastVal =
mEnv[0].GetVal();
1249 total += (lastT - t0) * lastVal;
1251 else if(t0 >=
mEnv[count - 1].GetT())
1253 return (t1 - t0) *
mEnv[count - 1].GetVal();
1270 return total + (t1 - lastT) * lastVal;
1272 else if(
mEnv[i].GetT() >= t1)
1280 lastT =
mEnv[i].GetT();
1281 lastVal =
mEnv[i].GetVal();
1296 unsigned int count =
mEnv.size();
1303 double total = 0.0, lastT, lastVal;
1305 if(t0 <
mEnv[0].GetT())
1307 if(t1 <=
mEnv[0].GetT())
1308 return (t1 - t0) /
mEnv[0].GetVal();
1310 lastT =
mEnv[0].GetT();
1311 lastVal =
mEnv[0].GetVal();
1312 total += (lastT - t0) / lastVal;
1314 else if(t0 >=
mEnv[count - 1].GetT())
1316 return (t1 - t0) /
mEnv[count - 1].GetVal();
1333 return total + (t1 - lastT) / lastVal;
1335 else if(
mEnv[i].GetT() >= t1)
1343 lastT =
mEnv[i].GetT();
1344 lastVal =
mEnv[i].GetVal();
1355 const auto count =
mEnv.size();
1363 double lastT, lastVal;
1365 if(t0 <
mEnv[0].GetT())
1368 return t0 + area *
mEnv[0].GetVal();
1372 lastT =
mEnv[0].GetT();
1373 lastVal =
mEnv[0].GetVal();
1374 double added = (lastT - t0) / lastVal;
1376 return t0 + area *
mEnv[0].GetVal();
1380 else if(t0 >=
mEnv[count - 1].GetT())
1384 lastT =
mEnv[count - 1].GetT();
1385 lastVal =
mEnv[count - 1].GetVal();
1386 double added = (lastT - t0) / lastVal;
1388 return t0 + area *
mEnv[count - 1].GetVal();
1392 return t0 + area *
mEnv[count - 1].GetVal();
1418 lastT =
mEnv[i].GetT();
1419 lastVal =
mEnv[i].GetVal();
1422 return lastT + area * lastVal;
1426 while (i < (
int)count)
1432 lastT =
mEnv[i].GetT();
1433 lastVal =
mEnv[i].GetVal();
1436 return lastT + area * lastVal;
1443 if( (a-b > 0 ? a-b : b-a) > 0.0000001 )
1445 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.
double GetT() const noexcept
void SetT(double t) noexcept
double GetVal() const noexcept
void SetVal(Envelope *pEnvelope, double val)
Piecewise linear or piecewise exponential function from double to double.
size_t GetVersion() const
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
double GetDefaultValue() const
void Insert(int point, const EnvPoint &p) noexcept
insert a point
void AddPointAtEnd(double t, double val)
std::pair< int, int > EqualRange(double when, double sampleDur) const noexcept
double GetValue(double t, double sampleDur=0) const
Get envelope value at time t.
void CollapseRegion(double t0, double t1, double sampleDur) noexcept
double mOffset
The time at which the envelope starts, i.e. the start offset.
double Integral(double t0, double t1) const
void BinarySearchForTime(int &Lo, int &Hi, double t) const noexcept
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.
size_t GetNumberOfPoints() const
Return number of points.
int InsertOrReplaceRelative(double when, double value) noexcept
Add a control point to the envelope.
void RemoveUnneededPoints(size_t startAt, bool rightward, bool testNeighbors=true) noexcept
double IntegralOfInverse(double t0, double t1) const
void Cap(double sampleDur)
void MoveDragPoint(double newWhen, double value)
void InsertSpace(double t0, double tlen)
void RescaleTimesBy(double ratio)
void Delete(int point)
DELETE a point by its position in array.
double AverageOfInverse(double t0, double t1) const
void BinarySearchForTime_LeftLimit(int &Lo, int &Hi, double t) const noexcept
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)
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 CopyRange(const Envelope &orig, size_t begin, size_t end)
double GetValueRelative(double t, bool leftLimit=false) const noexcept
void SetRange(double minValue, double maxValue)
double GetInterpolationStartValueAtPoint(int iPoint) const noexcept
void GetValuesRelative(double *buffer, int len, double t0, double tstep, bool leftLimit=false) const noexcept
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...
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept