21namespace tracktion {
inline namespace core
319[[ nodiscard ]] TimeRange
toTime (
const tempo::Sequence&, BeatRange);
322[[ nodiscard ]] BeatRange
toBeats (
const tempo::Sequence&, TimeRange);
352inline double BarsAndBeats::getTotalBars()
const {
return bars + (beats.inBeats() / numerator); }
353inline int BarsAndBeats::getWholeBeats()
const {
return (
int)
std::floor (beats.inBeats()); }
354inline BeatDuration BarsAndBeats::getFractionalBeats()
const {
return BeatDuration::fromBeats (beats.inBeats() -
std::floor (beats.inBeats())); }
358struct Sequence::Section
368 int barNumberOfFirstBar, numerator, prevNumerator, denominator;
377 for (
int i = (
int) sections.
size(); --i > 0;)
379 auto& it = sections[(
size_t) i];
381 if (it.startTime <= time)
382 return it.startBeat + (
time - it.startTime) * it.beatsPerSecond;
385 auto& it = sections[0];
386 return it.startBeat + ((
time - it.startTime) * it.beatsPerSecond);
391 for (
int i = (
int) sections.
size(); --i >= 0;)
393 auto& it = sections[(
size_t) i];
395 if (toPosition (beats - it.startBeat) >= BeatPosition())
396 return it.startTime + it.secondsPerBeat * (beats - it.startBeat);
399 auto& it = sections[0];
400 return it.startTime + it.secondsPerBeat * (beats - it.startBeat);
405 for (
int i = (
int) sections.
size(); --i >= 0;)
407 const auto& it = sections[(
size_t) i];
409 if (it.barNumberOfFirstBar == barsBeats.bars + 1
410 && barsBeats.beats.inBeats() >= it.prevNumerator - it.beatsUntilFirstBar.inBeats())
411 return it.timeOfFirstBar - it.secondsPerBeat * (BeatDuration::fromBeats (it.prevNumerator) - barsBeats.beats);
413 if (it.barNumberOfFirstBar <= barsBeats.bars || i == 0)
414 return it.timeOfFirstBar + it.secondsPerBeat * (BeatDuration::fromBeats (((barsBeats.bars - it.barNumberOfFirstBar) * it.numerator)) + barsBeats.beats);
422 for (
int i = (
int) sections.
size(); --i >= 0;)
424 auto& it = sections[(
size_t) i];
426 if (it.startTime <= time || i == 0)
428 const auto beatsSinceFirstBar = ((
time - it.timeOfFirstBar) * it.beatsPerSecond).inBeats();
430 if (beatsSinceFirstBar < 0)
431 return { it.barNumberOfFirstBar + (
int)
std::floor (beatsSinceFirstBar / it.numerator),
432 BeatDuration::fromBeats (
std::fmod (
std::fmod (beatsSinceFirstBar, it.numerator) + it.numerator, it.numerator)),
435 return { it.barNumberOfFirstBar + (
int)
std::floor (beatsSinceFirstBar / it.numerator),
436 BeatDuration::fromBeats (
std::fmod (beatsSinceFirstBar, it.numerator)),
447inline double Sequence::calcCurveBpm (
double beat,
const TempoChange t1,
const TempoChange t2)
449 const auto b1 = t1.startBeat.inBeats();
450 const auto b2 = t2.startBeat.inBeats();
451 const auto bpm1 = t1.bpm;
452 const auto bpm2 = t2.bpm;
453 const auto c = t1.curve;
455 const auto [x, y] = getBezierPoint (b1, bpm1, b2, bpm2, c);
457 if (c >= -0.5 && c <= 0.5)
458 return getBezierYFromX (beat,
459 b1, bpm1, x, y, b2, bpm2);
466 getBezierEnds (b1, bpm1, b2, bpm2, c,
467 x1end, y1end, x2end, y2end);
469 if (beat >= b1 && beat <= x1end)
472 if (beat >= x2end && beat <= b2)
475 return getBezierYFromX (beat, x1end, y1end, x, y, x2end, y2end);
482 :
Sequence (
std::move (tempos),
std::move (timeSigs), {}, lengthOfOneBeat)
501 for (
const auto& tempo : tempos)
503 beatsWithChanges.
push_back (tempo.startBeat);
510 for (
const auto& timeSig : timeSigs)
512 beatsWithChanges.
push_back (timeSig.startBeat);
520 for (
const auto& keyChange : keys)
522 beatsWithChanges.
push_back (keyChange.startBeat);
531 beatsWithChanges.
end());
537 size_t timeSigIdx = 0;
541 auto currTempo = tempos[tempoIdx++];
542 auto currTimeSig = timeSigs[timeSigIdx++];
543 auto currKey = keys[keyIdx++];
545 const bool useDenominator = lengthOfOneBeat == LengthOfOneBeat::dependsOnTimeSignature;
547 for (
size_t i = 0; i < beatsWithChanges.
size(); ++i)
549 const auto currentBeat = beatsWithChanges[i];
550 assert (std::abs ((currentBeat - beatNum).inBeats()) < 0.001);
552 while (tempoIdx < tempos.
size() && tempos[tempoIdx].startBeat == currentBeat)
553 currTempo = tempos[tempoIdx++];
555 if (timeSigIdx < timeSigs.
size() && timeSigs[timeSigIdx].startBeat == currentBeat)
556 currTimeSig = timeSigs[timeSigIdx++];
558 if (keyIdx < keys.
size() && keys[keyIdx].startBeat == currentBeat)
559 currKey = keys[keyIdx++];
561 const bool nextTempoValid = tempoIdx < tempos.
size();
562 double bpm = nextTempoValid ? calcCurveBpm (currTempo.startBeat.inBeats(), currTempo, tempos[tempoIdx])
565 int numSubdivisions = 1;
567 if (nextTempoValid && (currTempo.curve != -1.0f && currTempo.curve != 1.0f))
568 numSubdivisions =
static_cast<int> (
std::clamp (4.0 * (tempos[tempoIdx].startBeat - currentBeat).inBeats(), 1.0, 100.0));
571 ? ((beatsWithChanges[i + 1] - currentBeat).inBeats() / (
double) numSubdivisions)
574 for (
int k = 0; k < numSubdivisions; ++k)
579 it.numerator = currTimeSig.numerator;
580 it.prevNumerator = it.numerator;
581 it.denominator = currTimeSig.denominator;
582 it.triplets = currTimeSig.triplets;
584 it.startBeat = beatNum;
586 it.secondsPerBeat =
SecondsPerBeat { useDenominator ? (240.0 / (bpm * it.denominator))
588 it.beatsPerSecond = 1.0 / it.secondsPerBeat;
591 ppq += 4 * numBeats.inBeats() / it.denominator;
593 it.key = currKey.key;
595 if (sections.
empty())
597 it.barNumberOfFirstBar = 0;
598 it.beatsUntilFirstBar = {};
599 it.timeOfFirstBar = {};
603 const auto& prevSection = sections[sections.
size() - 1];
605 const auto beatsSincePreviousBarUntilStart = (
time - prevSection.timeOfFirstBar) * prevSection.beatsPerSecond;
606 const auto barsSincePrevBar = (
int)
std::ceil (beatsSincePreviousBarUntilStart.inBeats() / prevSection.numerator - 1.0e-5);
608 it.barNumberOfFirstBar = prevSection.barNumberOfFirstBar + barsSincePrevBar;
611 + (barsSincePrevBar * prevSection.numerator));
613 it.beatsUntilFirstBar = beatNumInEditOfNextBar - it.startBeat;
614 it.timeOfFirstBar =
time + it.beatsUntilFirstBar * it.secondsPerBeat;
616 for (
int j = (
int) sections.
size(); --j >= 0;)
618 auto& tempo = sections[(
size_t) j];
620 if (tempo.barNumberOfFirstBar < it.barNumberOfFirstBar)
622 it.prevNumerator = tempo.numerator;
630 time =
time + numBeats * it.secondsPerBeat;
631 beatNum = beatNum + numBeats;
633 bpm = nextTempoValid ? calcCurveBpm (beatNum.
inBeats(), currTempo, tempos[tempoIdx])
640 : sections (o.sections), hashCode (o.hashCode)
646 : sections (
std::move (o.sections)), hashCode (o.hashCode)
653 sections = o.sections;
655 hashCode = o.hashCode;
661 sections = std::move (o.sections);
663 hashCode = o.hashCode;
670 return details::toBeats (sections,
time);
675 return toBeats (details::toTime (sections, barsAndBeats));
680 return details::toTime (sections, beats);
685 return details::toTime (sections, barsAndBeats);
690 return details::toBarsAndBeats (sections, t);
696 for (
int i = (
int) sections.
size(); --i >= 0;)
698 auto& it = sections[(
size_t) i];
700 if (it.startTime <= t || i == 0)
710 for (
int i = (
int) sections.
size(); --i >= 0;)
712 auto& it = sections[(
size_t) i];
714 if (it.startTime <= t || i == 0)
724 for (
int i = (
int) sections.
size(); --i >= 0;)
726 auto& it = sections[(
size_t) i];
728 if (it.startTime <= t || i == 0)
729 return { .numerator = it.numerator, .denominator = it.denominator };
738 for (
int i = (
int) sections.
size(); --i >= 0;)
740 auto& it = sections[(
size_t) i];
742 if (it.startTime <= t || i == 0)
743 return it.beatsPerSecond;
759 : sequence (sequenceToUse)
765 : sequence (o.sequence)
772 const auto& it = sequence.sections[index];
773 return it.startBeat + (
time - it.startTime) * it.beatsPerSecond;
778 const auto& it = sequence.sections[index];
779 const auto beatsSinceFirstBar = ((
time - it.timeOfFirstBar) * it.beatsPerSecond).inBeats();
781 if (beatsSinceFirstBar < 0)
782 return { it.barNumberOfFirstBar + (
int)
std::floor (beatsSinceFirstBar / it.numerator),
786 return { it.barNumberOfFirstBar + (
int)
std::floor (beatsSinceFirstBar / it.numerator),
793 return sequence.sections[index].bpm;
798 auto& it = sequence.sections[index];
799 return { it.numerator, it.denominator };
804 auto& it = sequence.sections[index];
811 const auto maxIndex = sequence.sections.size() - 1;
813 if (index > maxIndex)
816 time = sequence.sections[index].startTime;
821 while (index < maxIndex && sequence.sections[index + 1].startTime <= t)
826 while (index > 0 && sequence.sections[index].startTime > t)
835 set (details::toTime (sequence.sections, t));
841 set (details::toTime (sequence.sections, t));
863 const auto maxIndex = sequence.sections.size() - 1;
865 if (index == maxIndex)
868 time = sequence.sections[++index].startTime;
878 auto maxIndex = sequence.sections.size() - 1;
879 const auto& it = sequence.sections[index];
880 auto beatTime = it.secondsPerBeat * beats;
882 if (index >= maxIndex
883 || sequence.sections[index + 1].startTime > (
time + beatTime))
890 auto nextStart = sequence.sections[index].startTime;
891 beats = beats - (nextStart -
time) * it.beatsPerSecond;
899 const auto& it = sequence.sections[index];
900 const auto beatTime = it.secondsPerBeat * beats;
903 || sequence.sections[index].startTime <= (
time + beatTime))
909 beats = beats + (
time - it.startTime) * it.beatsPerSecond;
910 time = sequence.sections[index].startTime;
927 assert (sequence.sections.size() > 0);
928 const auto currentSectionTime = sequence.sections[index].startTime;
930 for (
auto i = index + 1; i < sequence.sections.size(); ++i)
931 if (sequence.sections[i].startTime > currentSectionTime)
932 return sequence.sections[i].startTime;
934 return currentSectionTime;
939 assert (sequence.sections.size() > 0);
940 const auto currentSectionBeat = sequence.sections[index].startBeat;
942 for (
auto i = index + 1; i < sequence.sections.size(); ++i)
943 if (sequence.sections[i].startBeat > currentSectionBeat)
944 return sequence.sections[i].startBeat;
946 return currentSectionBeat;
952 for (
int i = (
int) sequence.sections.size(); --i >= 0;)
956 if (sequence.sections[index].ppqAtStart <= ppq)
960 const auto& it = sequence.sections[index];
962 time = (beatsSinceStart * it.secondsPerBeat) +
toDuration (it.startTime);
967 const auto& it = sequence.sections[index];
968 const auto beatsSinceStart = (
time - it.startTime) * it.beatsPerSecond;
970 return it.ppqAtStart + 4.0 * beatsSinceStart.inBeats() / it.denominator;
975 for (
int i =
int (index) + 1; --i >= 0;)
977 const auto& it = sequence.sections[(
size_t) i];
978 const auto beatsSinceFirstBar = ((
time - it.timeOfFirstBar) * it.beatsPerSecond).inBeats();
980 if (beatsSinceFirstBar >= -it.beatsUntilFirstBar.inBeats() || i == 0)
982 const double beatNumberOfLastBarSinceFirstBar = it.numerator *
std::floor (beatsSinceFirstBar / it.numerator);
984 return it.ppqAtStart + 4.0 * (it.beatsUntilFirstBar.inBeats() + beatNumberOfLastBarSinceFirstBar) / it.denominator;
BeatRange toBeats(const tempo::Sequence &, TimeRange)
Converts a TimeRange to a BeatRange.
void hash_combine(size_t &seed, const T &v)
Hashes a type with a given seed, modifying the seed.
constexpr TimeDuration toDuration(TimePosition)
Converts a TimePosition to a TimeDuration.
TimeRange toTime(const tempo::Sequence &, BeatRange)
Converts a BeatRange to a TimeRange.
Represents a duration in beats.
static constexpr BeatDuration fromBeats(T durationInBeats)
Create a BeatPosition from a number of beats.
Represents a position in beats.
constexpr double inBeats() const
Returns the position as a number of beats.
static constexpr BeatPosition fromBeats(T positionInBeats)
Create a BeatPosition from a number of beats.
Describes a range of two positions with a duration separating them.
constexpr Position getEnd() const
Returns the end of the range.
constexpr Position getStart() const
Returns the start of the range.
Represents a duration in real-life time.
static constexpr TimeDuration fromSeconds(T positionInSeconds)
Create a TimeDuration from a number of seconds.
Represents a position in real-life time.
static constexpr TimePosition fromSeconds(T positionInSeconds)
Create a TimePosition from a number of seconds.
Represents a number of bars and then beats in that bar.
int getWholeBeats() const
Returns the number of whole beats.
BeatDuration beats
The number of beats in the current bar.
BeatDuration getFractionalBeats() const
Returns the number of fractional beats.
int numerator
The number of beats in the current bar.
int bars
The number of whole bars.
double getTotalBars() const
Returns the number bars elapsed.
A strong typedef for BPM values.
A strong typedef for beats-per-second values.
Represents a change in key at a specific beat.
BeatPosition startBeat
The position of the change.
Key key
The key at this position.
Represents a musical key.
int pitch
The pitch in MIDI note number (0, 127).
A strong typedef for seconds-per-beat values.
A Sequence::Position is an iterator through a Sequence.
Position(const Sequence &)
Creates a Position from a sequence.
double getPPQTimeOfBarStart() const noexcept
Returns the PPQ start time of the bar the position is in.
double getPPQTime() const noexcept
Returns the position as a PPQ time.
BeatPosition getBeatOfNextChange() const
Returns the time of the next change.
Key getKey() const
Returns the Key of the Position.
bool next()
Moves the position to the next change.
TimeSignature getTimeSignature() const
Returns the current TimeSignature of the Position.
TimePosition add(TimeDuration)
Increments the position by a time duration.
BeatPosition getBeats() const
Returns the current beats of the Position.
TimePosition addBars(int bars)
Increments the position by a number of bars.
TimePosition getTime() const
Returns the current time of the Position.
void setPPQTime(double ppq)
Sets the position to a PPQ.
void set(TimePosition)
Sets the Position to a new time.
BarsAndBeats getBarsBeats() const
Returns the current bars and beats of the Position.
double getTempo() const
Returns the current tempo of the Position.
TimePosition getTimeOfNextChange() const
Returns the time of the next change.
Represents a tempo map with at least one TempoChange and TimeSigChange.
double getBpmAt(TimePosition) const
Returns the tempo at a position.
TimeSignature getTimeSignatureAt(TimePosition) const
Returns the TimeSignature at a position.
size_t hash() const
Returns a unique hash of this sequence.
Sequence(std::vector< TempoChange >, std::vector< TimeSigChange >, LengthOfOneBeat)
Creates a Sequence for at least one TempoChange and at least one TimeSigChange.
BeatsPerSecond getBeatsPerSecondAt(TimePosition) const
Returns the number of beats per second at a position.
BeatPosition toBeats(TimePosition) const
Converts a time to a number of beats.
Key getKeyAt(TimePosition) const
Returns the key at a position.
TimePosition toTime(BeatPosition) const
Converts a number of beats a time.
BarsAndBeats toBarsAndBeats(TimePosition) const
Converts a time to a number of BarsAndBeats.
Sequence & operator=(const Sequence &)
Copies another Sequence.
Represents a change in tempo at a specific beat.
float curve
The curve of the change.
BeatPosition startBeat
The position of the change.
double bpm
The tempo of the change in beats per minute.
Represents a change in time signature at a specific beat.
int denominator
The denominator of the time signature.
bool triplets
Whether the time signature represents triplets.
int numerator
The numerator of the time signature.
BeatPosition startBeat
The position of the change.
int denominator
The denominator part of the signature.
int numerator
The numerator part of the signature.
LengthOfOneBeat
Used to determine the length of a beat in beat <-> time conversions.
@ dependsOnTimeSignature
Signifies that the length (in seconds) of one "beat" at any point in an edit is considered to be the ...
@ isAlwaysACrotchet
Signifies the length of one beat always depends only the current BPM at that point in the edit,...