tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_Tempo.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11#pragma once
12
13#include <cassert>
14#include <algorithm>
15#include <vector>
16
17#include "tracktion_Time.h"
18#include "tracktion_TimeRange.h"
19#include "tracktion_Bezier.h"
20
21namespace tracktion { inline namespace core
22{
23
24//==============================================================================
25//==============================================================================
26namespace tempo
27{
28 //==============================================================================
31 {
32 int bars = 0;
34 int numerator = 0;
37 double getTotalBars() const;
38
40 int getWholeBeats() const;
41
44 };
45
48 {
49 int numerator = 0;
50 int denominator = 0;
51 };
52
54 struct Key
55 {
56 int pitch = 60;
57 int scale = 0;
58 };
59
61 struct BeatsPerMinute { double v = 0.0; };
62
64 struct BeatsPerSecond { double v = 0.0; };
65
67 struct SecondsPerBeat { double v = 0.0; };
68
69
70 //==============================================================================
71 //==============================================================================
74 {
76 double bpm;
77 float curve;
78 };
79
80 //==============================================================================
89
90 //==============================================================================
97
98 //==============================================================================
112
113 //==============================================================================
118 struct Sequence
119 {
120 struct Section;
121
122 //==============================================================================
126
132
134 Sequence (const Sequence&);
135
137 Sequence (Sequence&&);
138
141
144
145 //==============================================================================
148
151
154
157
160
161 //==============================================================================
163 double getBpmAt (TimePosition) const;
164
167
169 Key getKeyAt (TimePosition) const;
170
173
175 size_t hash() const;
176
177 //==============================================================================
178 //==============================================================================
187 struct Position
188 {
189 //==============================================================================
194 Position (const Sequence&);
195
197 Position (const Position&);
198
199 //==============================================================================
201 TimePosition getTime() const { return time; }
202
204 BeatPosition getBeats() const;
205
208
210 double getTempo() const;
211
214
216 Key getKey() const;
217
218 //==============================================================================
220 void set (TimePosition);
221
224
227
230
233
235 TimePosition addBars (int bars);
236
240 bool next();
241
242 //==============================================================================
249
256
257 //==============================================================================
259 void setPPQTime (double ppq);
260
262 double getPPQTime() const noexcept;
263
265 double getPPQTimeOfBarStart() const noexcept;
266
267 private:
268 const Sequence& sequence;
269 TimePosition time;
270 size_t index = 0;
271 };
272
273 private:
274 std::vector<Section> sections;
275 size_t hashCode = 0;
276
277 static double calcCurveBpm (double beat, const TempoChange, const TempoChange);
278 };
279}
280
282[[ nodiscard ]] constexpr BeatPosition operator* (TimePosition t, tempo::BeatsPerSecond bps) { return BeatPosition::fromBeats (t.inSeconds() * bps.v); }
284[[ nodiscard ]] constexpr BeatDuration operator* (TimeDuration t, tempo::BeatsPerSecond bps) { return BeatDuration::fromBeats (t.inSeconds() * bps.v); }
286[[ nodiscard ]] constexpr BeatPosition operator* (tempo::BeatsPerSecond bps, TimePosition t) { return BeatPosition::fromBeats (t.inSeconds() * bps.v); }
288[[ nodiscard ]] constexpr BeatDuration operator* (tempo::BeatsPerSecond bps, TimeDuration t) { return BeatDuration::fromBeats (t.inSeconds() * bps.v); }
289
291[[ nodiscard ]] constexpr BeatPosition operator* (TimePosition t, tempo::BeatsPerMinute bpm) { return BeatPosition::fromBeats (t.inSeconds() / 60.0 * bpm.v); }
293[[ nodiscard ]] constexpr BeatDuration operator* (TimeDuration t, tempo::BeatsPerMinute bpm) { return BeatDuration::fromBeats (t.inSeconds() / 60.0 * bpm.v); }
295[[ nodiscard ]] constexpr BeatPosition operator* (tempo::BeatsPerMinute bpm, TimePosition t) { return BeatPosition::fromBeats (t.inSeconds() / 60.0 * bpm.v); }
297[[ nodiscard ]] constexpr BeatDuration operator* (tempo::BeatsPerMinute bpm, TimeDuration t) { return BeatDuration::fromBeats (t.inSeconds() / 60.0 * bpm.v); }
298
300[[ nodiscard ]] constexpr TimePosition operator* (BeatPosition t, tempo::SecondsPerBeat spb) { return TimePosition::fromSeconds (t.inBeats() * spb.v); }
302[[ nodiscard ]] constexpr TimeDuration operator* (BeatDuration t, tempo::SecondsPerBeat spb) { return TimeDuration::fromSeconds (t.inBeats() * spb.v); }
304[[ nodiscard ]] constexpr TimePosition operator* (tempo::SecondsPerBeat spb, BeatPosition t) { return TimePosition::fromSeconds (t.inBeats() * spb.v); }
306[[ nodiscard ]] constexpr TimeDuration operator* (tempo::SecondsPerBeat spb, BeatDuration t) { return TimeDuration::fromSeconds (t.inBeats() * spb.v); }
307
309[[ nodiscard ]] constexpr tempo::BeatsPerSecond operator/ (double t, tempo::SecondsPerBeat spb) { return { t / spb.v }; }
311[[ nodiscard ]] constexpr tempo::SecondsPerBeat operator/ (double t, tempo::BeatsPerSecond bps) { return { t / bps.v }; }
312
314[[ nodiscard ]] constexpr bool operator== (tempo::Key k1, tempo::Key k2) { return k1.pitch == k2.pitch && k1.scale == k2.scale; }
316[[ nodiscard ]] constexpr bool operator!= (tempo::Key k1, tempo::Key k2) { return ! (k1 == k2); }
317
319[[ nodiscard ]] TimeRange toTime (const tempo::Sequence&, BeatRange);
320
322[[ nodiscard ]] BeatRange toBeats (const tempo::Sequence&, TimeRange);
323
324
325//==============================================================================
326// _ _ _ _
327// __| | ___ | |_ __ _ (_)| | ___
328// / _` | / _ \| __| / _` || || |/ __|
329// | (_| || __/| |_ | (_| || || |\__ \ _ _ _
330// \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_)
331//
332// Code beyond this point is implementation detail...
333//
334//==============================================================================
335
336inline TimeRange toTime (const tempo::Sequence& seq, BeatRange range)
337{
338 return { seq.toTime (range.getStart()),
339 seq.toTime (range.getEnd()) };
340}
341
342inline BeatRange toBeats (const tempo::Sequence& seq, TimeRange range)
343{
344 return { seq.toBeats (range.getStart()),
345 seq.toBeats (range.getEnd()) };
346}
347
348
349namespace tempo
350{
351
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())); }
355
356//==============================================================================
357//==============================================================================
358struct Sequence::Section
359{
360 double bpm;
361 TimePosition startTime;
362 BeatPosition startBeat;
363 SecondsPerBeat secondsPerBeat;
364 BeatsPerSecond beatsPerSecond;
365 double ppqAtStart;
366 TimePosition timeOfFirstBar;
367 BeatDuration beatsUntilFirstBar;
368 int barNumberOfFirstBar, numerator, prevNumerator, denominator;
369 bool triplets;
370 Key key;
371};
372
373namespace details
374{
376 {
377 for (int i = (int) sections.size(); --i > 0;)
378 {
379 auto& it = sections[(size_t) i];
380
381 if (it.startTime <= time)
382 return it.startBeat + (time - it.startTime) * it.beatsPerSecond;
383 }
384
385 auto& it = sections[0];
386 return it.startBeat + ((time - it.startTime) * it.beatsPerSecond);
387 }
388
389 inline TimePosition toTime (const std::vector<Sequence::Section>& sections, BeatPosition beats)
390 {
391 for (int i = (int) sections.size(); --i >= 0;)
392 {
393 auto& it = sections[(size_t) i];
394
395 if (toPosition (beats - it.startBeat) >= BeatPosition())
396 return it.startTime + it.secondsPerBeat * (beats - it.startBeat);
397 }
398
399 auto& it = sections[0];
400 return it.startTime + it.secondsPerBeat * (beats - it.startBeat);
401 }
402
403 inline TimePosition toTime (const std::vector<Sequence::Section>& sections, BarsAndBeats barsBeats)
404 {
405 for (int i = (int) sections.size(); --i >= 0;)
406 {
407 const auto& it = sections[(size_t) i];
408
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);
412
413 if (it.barNumberOfFirstBar <= barsBeats.bars || i == 0)
414 return it.timeOfFirstBar + it.secondsPerBeat * (BeatDuration::fromBeats (((barsBeats.bars - it.barNumberOfFirstBar) * it.numerator)) + barsBeats.beats);
415 }
416
417 return {};
418 }
419
420 inline BarsAndBeats toBarsAndBeats (const std::vector<Sequence::Section>& sections, TimePosition time)
421 {
422 for (int i = (int) sections.size(); --i >= 0;)
423 {
424 auto& it = sections[(size_t) i];
425
426 if (it.startTime <= time || i == 0)
427 {
428 const auto beatsSinceFirstBar = ((time - it.timeOfFirstBar) * it.beatsPerSecond).inBeats();
429
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)),
433 it.numerator };
434
435 return { it.barNumberOfFirstBar + (int) std::floor (beatsSinceFirstBar / it.numerator),
436 BeatDuration::fromBeats (std::fmod (beatsSinceFirstBar, it.numerator)),
437 it.numerator };
438 }
439 }
440
441 return { 0, {} };
442 }
443}
444
445//==============================================================================
446//==============================================================================
447inline double Sequence::calcCurveBpm (double beat, const TempoChange t1, const TempoChange t2)
448{
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;
454
455 const auto [x, y] = getBezierPoint (b1, bpm1, b2, bpm2, c);
456
457 if (c >= -0.5 && c <= 0.5)
458 return getBezierYFromX (beat,
459 b1, bpm1, x, y, b2, bpm2);
460
461 double x1end = 0;
462 double x2end = 0;
463 double y1end = 0;
464 double y2end = 0;
465
466 getBezierEnds (b1, bpm1, b2, bpm2, c,
467 x1end, y1end, x2end, y2end);
468
469 if (beat >= b1 && beat <= x1end)
470 return y1end;
471
472 if (beat >= x2end && beat <= b2)
473 return y2end;
474
475 return getBezierYFromX (beat, x1end, y1end, x, y, x2end, y2end);
476}
477
478//==============================================================================
479//==============================================================================
480inline Sequence::Sequence (std::vector<TempoChange> tempos, std::vector<TimeSigChange> timeSigs,
481 LengthOfOneBeat lengthOfOneBeat)
482 : Sequence (std::move (tempos), std::move (timeSigs), {}, lengthOfOneBeat)
483{
484}
485
487 LengthOfOneBeat lengthOfOneBeat)
488{
489 if (keys.empty())
490 keys.push_back ({});
491
492 assert (tempos.size() > 0 && timeSigs.size() > 0);
493 assert (tempos[0].startBeat == BeatPosition());
494 assert (timeSigs[0].startBeat == BeatPosition());
495 assert (keys[0].startBeat == BeatPosition());
496
497 // Find the beats with changes
498 std::vector<BeatPosition> beatsWithChanges;
499 beatsWithChanges.reserve (tempos.size() + timeSigs.size() + keys.size());
500
501 for (const auto& tempo : tempos)
502 {
503 beatsWithChanges.push_back (tempo.startBeat);
504
505 hash_combine (hashCode, tempo.startBeat.inBeats());
506 hash_combine (hashCode, tempo.bpm);
507 hash_combine (hashCode, tempo.curve);
508 }
509
510 for (const auto& timeSig : timeSigs)
511 {
512 beatsWithChanges.push_back (timeSig.startBeat);
513
514 hash_combine (hashCode, timeSig.startBeat.inBeats());
515 hash_combine (hashCode, timeSig.numerator);
516 hash_combine (hashCode, timeSig.denominator);
517 hash_combine (hashCode, timeSig.triplets);
518 }
519
520 for (const auto& keyChange : keys)
521 {
522 beatsWithChanges.push_back (keyChange.startBeat);
523
524 hash_combine (hashCode, keyChange.startBeat.inBeats());
525 hash_combine (hashCode, keyChange.key.pitch);
526 hash_combine (hashCode, keyChange.key.scale);
527 }
528
529 std::sort (beatsWithChanges.begin(), beatsWithChanges.end());
530 beatsWithChanges.erase (std::unique (beatsWithChanges.begin(), beatsWithChanges.end()),
531 beatsWithChanges.end());
532
533 // Build the sections
535 BeatPosition beatNum;
536 double ppq = 0.0;
537 size_t timeSigIdx = 0;
538 size_t tempoIdx = 0;
539 size_t keyIdx = 0;
540
541 auto currTempo = tempos[tempoIdx++];
542 auto currTimeSig = timeSigs[timeSigIdx++];
543 auto currKey = keys[keyIdx++];
544
545 const bool useDenominator = lengthOfOneBeat == LengthOfOneBeat::dependsOnTimeSignature;
546
547 for (size_t i = 0; i < beatsWithChanges.size(); ++i)
548 {
549 const auto currentBeat = beatsWithChanges[i];
550 assert (std::abs ((currentBeat - beatNum).inBeats()) < 0.001);
551
552 while (tempoIdx < tempos.size() && tempos[tempoIdx].startBeat == currentBeat)
553 currTempo = tempos[tempoIdx++];
554
555 if (timeSigIdx < timeSigs.size() && timeSigs[timeSigIdx].startBeat == currentBeat)
556 currTimeSig = timeSigs[timeSigIdx++];
557
558 if (keyIdx < keys.size() && keys[keyIdx].startBeat == currentBeat)
559 currKey = keys[keyIdx++];
560
561 const bool nextTempoValid = tempoIdx < tempos.size();
562 double bpm = nextTempoValid ? calcCurveBpm (currTempo.startBeat.inBeats(), currTempo, tempos[tempoIdx])
563 : currTempo.bpm;
564
565 int numSubdivisions = 1;
566
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));
569
570 const auto numBeats = BeatDuration::fromBeats ((i < beatsWithChanges.size() - 1)
571 ? ((beatsWithChanges[i + 1] - currentBeat).inBeats() / (double) numSubdivisions)
572 : 1.0e6);
573
574 for (int k = 0; k < numSubdivisions; ++k)
575 {
576 Section it;
577
578 it.bpm = bpm;
579 it.numerator = currTimeSig.numerator;
580 it.prevNumerator = it.numerator;
581 it.denominator = currTimeSig.denominator;
582 it.triplets = currTimeSig.triplets;
583 it.startTime = time;
584 it.startBeat = beatNum;
585
586 it.secondsPerBeat = SecondsPerBeat { useDenominator ? (240.0 / (bpm * it.denominator))
587 : (60.0 / bpm) };
588 it.beatsPerSecond = 1.0 / it.secondsPerBeat;
589
590 it.ppqAtStart = ppq;
591 ppq += 4 * numBeats.inBeats() / it.denominator;
592
593 it.key = currKey.key;
594
595 if (sections.empty())
596 {
597 it.barNumberOfFirstBar = 0;
598 it.beatsUntilFirstBar = {};
599 it.timeOfFirstBar = {};
600 }
601 else
602 {
603 const auto& prevSection = sections[sections.size() - 1];
604
605 const auto beatsSincePreviousBarUntilStart = (time - prevSection.timeOfFirstBar) * prevSection.beatsPerSecond;
606 const auto barsSincePrevBar = (int) std::ceil (beatsSincePreviousBarUntilStart.inBeats() / prevSection.numerator - 1.0e-5);
607
608 it.barNumberOfFirstBar = prevSection.barNumberOfFirstBar + barsSincePrevBar;
609
610 const auto beatNumInEditOfNextBar = BeatPosition::fromBeats ((int) std::lround ((prevSection.startBeat + prevSection.beatsUntilFirstBar).inBeats())
611 + (barsSincePrevBar * prevSection.numerator));
612
613 it.beatsUntilFirstBar = beatNumInEditOfNextBar - it.startBeat;
614 it.timeOfFirstBar = time + it.beatsUntilFirstBar * it.secondsPerBeat;
615
616 for (int j = (int) sections.size(); --j >= 0;)
617 {
618 auto& tempo = sections[(size_t) j];
619
620 if (tempo.barNumberOfFirstBar < it.barNumberOfFirstBar)
621 {
622 it.prevNumerator = tempo.numerator;
623 break;
624 }
625 }
626 }
627
628 sections.push_back (it);
629
630 time = time + numBeats * it.secondsPerBeat;
631 beatNum = beatNum + numBeats;
632
633 bpm = nextTempoValid ? calcCurveBpm (beatNum.inBeats(), currTempo, tempos[tempoIdx])
634 : currTempo.bpm;
635 }
636 }
637}
638
640 : sections (o.sections), hashCode (o.hashCode)
641{
642 assert (sections.size() > 0);
643}
644
646 : sections (std::move (o.sections)), hashCode (o.hashCode)
647{
648 assert (sections.size() > 0);
649}
650
652{
653 sections = o.sections;
654 assert (sections.size() > 0);
655 hashCode = o.hashCode;
656 return *this;
657}
658
660{
661 sections = std::move (o.sections);
662 assert (sections.size() > 0);
663 hashCode = o.hashCode;
664 return *this;
665}
666
667//==============================================================================
669{
670 return details::toBeats (sections, time);
671}
672
673inline BeatPosition Sequence::toBeats (BarsAndBeats barsAndBeats) const
674{
675 return toBeats (details::toTime (sections, barsAndBeats));
676}
677
679{
680 return details::toTime (sections, beats);
681}
682
683inline TimePosition Sequence::toTime (BarsAndBeats barsAndBeats) const
684{
685 return details::toTime (sections, barsAndBeats);
686}
687
689{
690 return details::toBarsAndBeats (sections, t);
691}
692
693//==============================================================================
694inline double Sequence::getBpmAt (TimePosition t) const
695{
696 for (int i = (int) sections.size(); --i >= 0;)
697 {
698 auto& it = sections[(size_t) i];
699
700 if (it.startTime <= t || i == 0)
701 return it.bpm;
702 }
703
704 assert(false);
705 return 120.0;
706}
707
709{
710 for (int i = (int) sections.size(); --i >= 0;)
711 {
712 auto& it = sections[(size_t) i];
713
714 if (it.startTime <= t || i == 0)
715 return it.key;
716 }
717
718 assert(false);
719 return {};
720}
721
723{
724 for (int i = (int) sections.size(); --i >= 0;)
725 {
726 auto& it = sections[(size_t) i];
727
728 if (it.startTime <= t || i == 0)
729 return { .numerator = it.numerator, .denominator = it.denominator };
730 }
731
732 assert(false);
733 return {};
734}
735
737{
738 for (int i = (int) sections.size(); --i >= 0;)
739 {
740 auto& it = sections[(size_t) i];
741
742 if (it.startTime <= t || i == 0)
743 return it.beatsPerSecond;
744 }
745
746 assert(false);
747 return { 2.0 };
748}
749
750inline size_t Sequence::hash() const
751{
752 return hashCode;
753}
754
755
756//==============================================================================
757//==============================================================================
758inline Sequence::Position::Position (const Sequence& sequenceToUse)
759 : sequence (sequenceToUse)
760{
761 assert (sequence.sections.size() > 0);
762}
763
765 : sequence (o.sequence)
766{
767 assert (sequence.sections.size() > 0);
768}
769
771{
772 const auto& it = sequence.sections[index];
773 return it.startBeat + (time - it.startTime) * it.beatsPerSecond;
774}
775
777{
778 const auto& it = sequence.sections[index];
779 const auto beatsSinceFirstBar = ((time - it.timeOfFirstBar) * it.beatsPerSecond).inBeats();
780
781 if (beatsSinceFirstBar < 0)
782 return { it.barNumberOfFirstBar + (int) std::floor (beatsSinceFirstBar / it.numerator),
783 BeatDuration::fromBeats (std::fmod (std::fmod (beatsSinceFirstBar, it.numerator) + it.numerator, it.numerator)),
784 it.numerator };
785
786 return { it.barNumberOfFirstBar + (int) std::floor (beatsSinceFirstBar / it.numerator),
787 BeatDuration::fromBeats (std::fmod (beatsSinceFirstBar, it.numerator)),
788 it.numerator };
789}
790
791inline double Sequence::Position::getTempo() const
792{
793 return sequence.sections[index].bpm;
794}
795
797{
798 auto& it = sequence.sections[index];
799 return { it.numerator, it.denominator };
800}
801
803{
804 auto& it = sequence.sections[index];
805 return it.key;
806}
807
808//==============================================================================
810{
811 const auto maxIndex = sequence.sections.size() - 1;
812
813 if (index > maxIndex)
814 {
815 index = maxIndex;
816 time = sequence.sections[index].startTime;
817 }
818
819 if (t >= time)
820 {
821 while (index < maxIndex && sequence.sections[index + 1].startTime <= t)
822 ++index;
823 }
824 else
825 {
826 while (index > 0 && sequence.sections[index].startTime > t)
827 --index;
828 }
829
830 time = t;
831}
832
834{
835 set (details::toTime (sequence.sections, t));
836 return time;
837}
838
840{
841 set (details::toTime (sequence.sections, t));
842 return time;
843}
844
846{
847 if (bars > 0)
848 {
849 while (--bars >= 0)
850 add (BeatDuration::fromBeats (sequence.sections[index].numerator));
851 }
852 else
853 {
854 while (++bars <= 0)
855 add (BeatDuration::fromBeats (-sequence.sections[index].numerator));
856 }
857
858 return time;
859}
860
862{
863 const auto maxIndex = sequence.sections.size() - 1;
864
865 if (index == maxIndex)
866 return false;
867
868 time = sequence.sections[++index].startTime;
869 return true;
870}
871
873{
874 if (beats > BeatDuration())
875 {
876 for (;;)
877 {
878 auto maxIndex = sequence.sections.size() - 1;
879 const auto& it = sequence.sections[index];
880 auto beatTime = it.secondsPerBeat * beats;
881
882 if (index >= maxIndex
883 || sequence.sections[index + 1].startTime > (time + beatTime))
884 {
885 time = time + beatTime;
886 break;
887 }
888
889 ++index;
890 auto nextStart = sequence.sections[index].startTime;
891 beats = beats - (nextStart - time) * it.beatsPerSecond;
892 time = nextStart;
893 }
894 }
895 else
896 {
897 for (;;)
898 {
899 const auto& it = sequence.sections[index];
900 const auto beatTime = it.secondsPerBeat * beats;
901
902 if (index <= 0
903 || sequence.sections[index].startTime <= (time + beatTime))
904 {
905 time = time + beatTime;
906 break;
907 }
908
909 beats = beats + (time - it.startTime) * it.beatsPerSecond;
910 time = sequence.sections[index].startTime;
911 --index;
912 }
913 }
914
915 return time;
916}
917
919{
920 set (time + d);
921 return time;
922}
923
924//==============================================================================
926{
927 assert (sequence.sections.size() > 0);
928 const auto currentSectionTime = sequence.sections[index].startTime;
929
930 for (auto i = index + 1; i < sequence.sections.size(); ++i)
931 if (sequence.sections[i].startTime > currentSectionTime)
932 return sequence.sections[i].startTime;
933
934 return currentSectionTime;
935}
936
938{
939 assert (sequence.sections.size() > 0);
940 const auto currentSectionBeat = sequence.sections[index].startBeat;
941
942 for (auto i = index + 1; i < sequence.sections.size(); ++i)
943 if (sequence.sections[i].startBeat > currentSectionBeat)
944 return sequence.sections[i].startBeat;
945
946 return currentSectionBeat;
947}
948
949//==============================================================================
950inline void Sequence::Position::setPPQTime (double ppq)
951{
952 for (int i = (int) sequence.sections.size(); --i >= 0;)
953 {
954 index = (size_t) i;
955
956 if (sequence.sections[index].ppqAtStart <= ppq)
957 break;
958 }
959
960 const auto& it = sequence.sections[index];
961 const auto beatsSinceStart = BeatPosition::fromBeats (((ppq - it.ppqAtStart) * it.denominator) / 4.0);
962 time = (beatsSinceStart * it.secondsPerBeat) + toDuration (it.startTime);
963}
964
965inline double Sequence::Position::getPPQTime() const noexcept
966{
967 const auto& it = sequence.sections[index];
968 const auto beatsSinceStart = (time - it.startTime) * it.beatsPerSecond;
969
970 return it.ppqAtStart + 4.0 * beatsSinceStart.inBeats() / it.denominator;
971}
972
973inline double Sequence::Position::getPPQTimeOfBarStart() const noexcept
974{
975 for (int i = int (index) + 1; --i >= 0;)
976 {
977 const auto& it = sequence.sections[(size_t) i];
978 const auto beatsSinceFirstBar = ((time - it.timeOfFirstBar) * it.beatsPerSecond).inBeats();
979
980 if (beatsSinceFirstBar >= -it.beatsUntilFirstBar.inBeats() || i == 0)
981 {
982 const double beatNumberOfLastBarSinceFirstBar = it.numerator * std::floor (beatsSinceFirstBar / it.numerator);
983
984 return it.ppqAtStart + 4.0 * (it.beatsUntilFirstBar.inBeats() + beatNumberOfLastBarSinceFirstBar) / it.denominator;
985 }
986 }
987
988 return 0.0;
989}
990
991} // namespace Tempo
992
993}} // namespace tracktion
assert
T begin(T... args)
T ceil(T... args)
T clamp(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T floor(T... args)
T fmod(T... args)
typedef int
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.
T push_back(T... args)
T reserve(T... args)
T lround(T... args)
T size(T... args)
T sort(T... args)
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.
typedef size_t
time
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,...
T unique(T... args)