This operation is trickier than it looks; the basic rub is that a track's envelope runs the range from t=0 to t=tracklen; the t=0 envelope point applies to the first sample, but the t=tracklen envelope point applies one-past the last actual sample. t0 should be in the domain of this; if not, it is trimmed. ! @excsafety{No-fail} */
| 484 | // t0 should be in the domain of this; if not, it is trimmed. |
| 485 | /*! @excsafety{No-fail} */ |
| 486 | void Envelope::PasteEnvelope( double t0, const Envelope *e, double sampleDur ) |
| 487 | { |
| 488 | const bool wasEmpty = (this->mEnv.size() == 0); |
| 489 | auto otherSize = e->mEnv.size(); |
| 490 | const double otherDur = e->mTrackLen; |
| 491 | const auto otherOffset = e->mOffset; |
| 492 | const auto deltat = otherOffset + otherDur; |
| 493 | |
| 494 | ++mVersion; |
| 495 | |
| 496 | if ( otherSize == 0 && wasEmpty && e->mDefaultValue == this->mDefaultValue ) |
| 497 | { |
| 498 | // msmeyer: The envelope is empty and has the same default value, so |
| 499 | // there is nothing that must be inserted, just return. This avoids |
| 500 | // the creation of unnecessary duplicate control points |
| 501 | // MJS: but the envelope does get longer |
| 502 | // PRL: Assuming t0 is in the domain of the envelope |
| 503 | mTrackLen += deltat; |
| 504 | return; |
| 505 | } |
| 506 | |
| 507 | // Make t0 relative to the offset of the envelope we are pasting into, |
| 508 | // and trim it to the domain of this |
| 509 | t0 = std::min( mTrackLen, std::max( 0.0, t0 - mOffset ) ); |
| 510 | |
| 511 | // Adjust if the insertion point rounds off near a discontinuity in this |
| 512 | if ( true ) |
| 513 | { |
| 514 | double newT0; |
| 515 | auto range = EqualRange( t0, sampleDur ); |
| 516 | auto index = range.first; |
| 517 | if ( index + 2 == range.second && |
| 518 | ( newT0 = mEnv[ index ].GetT() ) == mEnv[ 1 + index ].GetT() ) |
| 519 | t0 = newT0; |
| 520 | } |
| 521 | |
| 522 | // Open up a space |
| 523 | double leftVal = e->GetValue( 0 ); |
| 524 | double rightVal = e->GetValueRelative( otherDur ); |
| 525 | // This range includes the right-side limit of the left end of the space, |
| 526 | // and the left-side limit of the right end: |
| 527 | const auto range = ExpandRegion( t0, deltat, &leftVal, &rightVal ); |
| 528 | // Where to put the copied points from e -- after the first of the |
| 529 | // two points in range: |
| 530 | auto insertAt = range.first + 1; |
| 531 | |
| 532 | // Copy points from e -- maybe skipping those at the extremes |
| 533 | auto end = e->mEnv.end(); |
| 534 | if ( otherSize != 0 && e->mEnv[ otherSize - 1 ].GetT() == otherDur ) |
| 535 | // ExpandRegion already made an equivalent limit point |
| 536 | --end, --otherSize; |
| 537 | auto begin = e->mEnv.begin(); |
| 538 | if ( otherSize != 0 && otherOffset == 0.0 && e->mEnv[ 0 ].GetT() == 0.0 ) |
| 539 | ++begin, --otherSize; |
| 540 | mEnv.insert( mEnv.begin() + insertAt, begin, end ); |
| 541 | |
| 542 | // Adjust their times |
| 543 | for ( size_t index = insertAt, last = insertAt + otherSize; |