2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
28 #include "evoral/types.hpp"
29 #include "ardour/debug.h"
30 #include "ardour/tempo.h"
36 using namespace ARDOUR;
39 using Timecode::BBT_Time;
41 /* _default tempo is 4/4 qtr=120 */
43 Meter TempoMap::_default_meter (4.0, 4.0);
44 Tempo TempoMap::_default_tempo (120.0);
46 /***********************************************************************/
49 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
51 /* This is tempo- and meter-sensitive. The number it returns
52 is based on the interval between any two lines in the
53 grid that is constructed from tempo and meter sections.
55 The return value IS NOT interpretable in terms of "beats".
58 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
62 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
64 return frames_per_grid (tempo, sr) * _divisions_per_bar;
67 /***********************************************************************/
69 const string TempoSection::xml_state_node_name = "Tempo";
71 TempoSection::TempoSection (const XMLNode& node)
72 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
74 const XMLProperty *prop;
76 LocaleGuard lg (X_("POSIX"));
78 if ((prop = node.property ("start")) == 0) {
79 error << _("TempoSection XML node has no \"start\" property") << endmsg;
80 throw failed_constructor();
83 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
87 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
88 throw failed_constructor();
93 if ((prop = node.property ("beats-per-minute")) == 0) {
94 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
95 throw failed_constructor();
98 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
99 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
100 throw failed_constructor();
103 if ((prop = node.property ("note-type")) == 0) {
104 /* older session, make note type be quarter by default */
107 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
108 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
109 throw failed_constructor();
113 if ((prop = node.property ("movable")) == 0) {
114 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
115 throw failed_constructor();
118 set_movable (string_is_affirmative (prop->value()));
120 if ((prop = node.property ("bar-offset")) == 0) {
123 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
124 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
125 throw failed_constructor();
131 TempoSection::get_state() const
133 XMLNode *root = new XMLNode (xml_state_node_name);
135 LocaleGuard lg (X_("POSIX"));
137 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
141 root->add_property ("start", buf);
142 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
143 root->add_property ("beats-per-minute", buf);
144 snprintf (buf, sizeof (buf), "%f", _note_type);
145 root->add_property ("note-type", buf);
146 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
147 // root->add_property ("bar-offset", buf);
148 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
149 root->add_property ("movable", buf);
156 TempoSection::update_bar_offset_from_bbt (const Meter& m)
158 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
159 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
161 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
165 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
169 if (_bar_offset < 0.0) {
174 new_start.bars = start().bars;
176 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
177 new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
178 new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
180 /* remember the 1-based counting properties of beats */
181 new_start.beats += 1;
183 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
184 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
186 set_start (new_start);
189 /***********************************************************************/
191 const string MeterSection::xml_state_node_name = "Meter";
193 MeterSection::MeterSection (const XMLNode& node)
194 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
196 const XMLProperty *prop;
198 LocaleGuard lg (X_("POSIX"));
200 if ((prop = node.property ("start")) == 0) {
201 error << _("MeterSection XML node has no \"start\" property") << endmsg;
202 throw failed_constructor();
205 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
209 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
210 throw failed_constructor();
215 /* beats-per-bar is old; divisions-per-bar is new */
217 if ((prop = node.property ("divisions-per-bar")) == 0) {
218 if ((prop = node.property ("beats-per-bar")) == 0) {
219 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
220 throw failed_constructor();
224 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
225 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
226 throw failed_constructor();
229 if ((prop = node.property ("note-type")) == 0) {
230 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
231 throw failed_constructor();
234 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
235 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
236 throw failed_constructor();
239 if ((prop = node.property ("movable")) == 0) {
240 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
241 throw failed_constructor();
244 set_movable (string_is_affirmative (prop->value()));
248 MeterSection::get_state() const
250 XMLNode *root = new XMLNode (xml_state_node_name);
252 LocaleGuard lg (X_("POSIX"));
254 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
258 root->add_property ("start", buf);
259 snprintf (buf, sizeof (buf), "%f", _note_type);
260 root->add_property ("note-type", buf);
261 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
262 root->add_property ("divisions-per-bar", buf);
263 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
264 root->add_property ("movable", buf);
269 /***********************************************************************/
271 struct MetricSectionSorter {
272 bool operator() (const MetricSection* a, const MetricSection* b) {
273 return a->start() < b->start();
277 TempoMap::TempoMap (framecnt_t fr)
286 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
287 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
289 t->set_movable (false);
290 m->set_movable (false);
292 /* note: frame time is correct (zero) for both of these */
294 metrics.push_back (t);
295 metrics.push_back (m);
298 TempoMap::~TempoMap ()
303 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
305 bool removed = false;
308 Glib::Threads::RWLock::WriterLock lm (lock);
311 for (i = metrics.begin(); i != metrics.end(); ++i) {
312 if (dynamic_cast<TempoSection*> (*i) != 0) {
313 if (tempo.frame() == (*i)->frame()) {
314 if ((*i)->movable()) {
323 if (removed && complete_operation) {
324 recompute_map (false);
328 if (removed && complete_operation) {
329 PropertyChanged (PropertyChange ());
334 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
336 bool removed = false;
339 Glib::Threads::RWLock::WriterLock lm (lock);
342 for (i = metrics.begin(); i != metrics.end(); ++i) {
343 if (dynamic_cast<MeterSection*> (*i) != 0) {
344 if (tempo.frame() == (*i)->frame()) {
345 if ((*i)->movable()) {
354 if (removed && complete_operation) {
355 recompute_map (true);
359 if (removed && complete_operation) {
360 PropertyChanged (PropertyChange ());
365 TempoMap::do_insert (MetricSection* section)
367 bool need_add = true;
369 assert (section->start().ticks == 0);
371 /* we only allow new meters to be inserted on beat 1 of an existing
375 if (dynamic_cast<MeterSection*>(section)) {
377 /* we need to (potentially) update the BBT times of tempo
378 sections based on this new meter.
381 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
383 BBT_Time corrected = section->start();
387 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
388 section->start(), corrected) << endmsg;
390 section->set_start (corrected);
396 /* Look for any existing MetricSection that is of the same type and
397 in the same bar as the new one, and remove it before adding
398 the new one. Note that this means that if we find a matching,
399 existing section, we can break out of the loop since we're
400 guaranteed that there is only one such match.
403 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
405 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
406 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
408 if (iter_is_tempo && insert_is_tempo) {
412 if ((*i)->start().bars == section->start().bars &&
413 (*i)->start().beats == section->start().beats) {
415 if (!(*i)->movable()) {
417 /* can't (re)move this section, so overwrite
418 * its data content (but not its properties as
422 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
430 } else if (!iter_is_tempo && !insert_is_tempo) {
434 if ((*i)->start().bars == section->start().bars) {
436 if (!(*i)->movable()) {
438 /* can't (re)move this section, so overwrite
439 * its data content (but not its properties as
443 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
453 /* non-matching types, so we don't care */
457 /* Add the given MetricSection, if we didn't just reset an existing
465 for (i = metrics.begin(); i != metrics.end(); ++i) {
466 if ((*i)->start() > section->start()) {
471 metrics.insert (i, section);
476 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
478 const TempoSection& first (first_tempo());
480 if (ts.start() != first.start()) {
481 remove_tempo (ts, false);
482 add_tempo (tempo, where);
485 Glib::Threads::RWLock::WriterLock lm (lock);
486 /* cannot move the first tempo section */
487 *((Tempo*)&first) = tempo;
488 recompute_map (false);
492 PropertyChanged (PropertyChange ());
496 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
499 Glib::Threads::RWLock::WriterLock lm (lock);
501 /* new tempos always start on a beat */
504 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
506 /* find the meter to use to set the bar offset of this
510 const Meter* meter = &first_meter();
512 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
513 at something, because we insert the default tempo and meter during
514 TempoMap construction.
516 now see if we can find better candidates.
519 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
521 const MeterSection* m;
523 if (where < (*i)->start()) {
527 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
532 ts->update_bar_offset_from_bbt (*meter);
538 recompute_map (false);
542 PropertyChanged (PropertyChange ());
546 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
548 const MeterSection& first (first_meter());
550 if (ms.start() != first.start()) {
551 remove_meter (ms, false);
552 add_meter (meter, where);
555 Glib::Threads::RWLock::WriterLock lm (lock);
556 /* cannot move the first meter section */
557 *((Meter*)&first) = meter;
558 recompute_map (true);
562 PropertyChanged (PropertyChange ());
566 TempoMap::add_meter (const Meter& meter, BBT_Time where)
569 Glib::Threads::RWLock::WriterLock lm (lock);
571 /* a new meter always starts a new bar on the first beat. so
572 round the start time appropriately. remember that
573 `where' is based on the existing tempo map, not
574 the result after we insert the new meter.
578 if (where.beats != 1) {
583 /* new meters *always* start on a beat. */
586 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
587 recompute_map (true);
592 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
597 PropertyChanged (PropertyChange ());
601 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
603 Tempo newtempo (beats_per_minute, note_type);
606 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
607 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
609 Glib::Threads::RWLock::WriterLock lm (lock);
610 *((Tempo*) t) = newtempo;
611 recompute_map (false);
613 PropertyChanged (PropertyChange ());
620 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
622 Tempo newtempo (beats_per_minute, note_type);
628 /* find the TempoSection immediately preceding "where"
631 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
633 if ((*i)->frame() > where) {
639 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
649 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
659 Glib::Threads::RWLock::WriterLock lm (lock);
660 /* cannot move the first tempo section */
661 *((Tempo*)prev) = newtempo;
662 recompute_map (false);
665 PropertyChanged (PropertyChange ());
669 TempoMap::first_meter () const
671 const MeterSection *m = 0;
673 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
674 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
679 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
680 abort(); /*NOTREACHED*/
685 TempoMap::first_tempo () const
687 const TempoSection *t = 0;
689 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
690 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
695 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
696 abort(); /*NOTREACHED*/
701 TempoMap::require_map_to (framepos_t pos)
703 Glib::Threads::RWLock::WriterLock lm (lock);
705 if (_map.empty() || _map.back().frame < pos) {
711 TempoMap::require_map_to (const BBT_Time& bbt)
713 Glib::Threads::RWLock::WriterLock lm (lock);
715 /* since we have no idea where BBT is if its off the map, see the last
716 * point in the map is past BBT, and if not add an arbitrary amount of
720 int additional_minutes = 1;
723 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
726 /* add some more distance, using bigger steps each time */
727 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
728 additional_minutes *= 2;
733 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
735 /* CALLER MUST HOLD WRITE LOCK */
737 MeterSection* meter = 0;
738 TempoSection* tempo = 0;
739 double current_frame;
741 Metrics::iterator next_metric;
745 /* we will actually stop once we hit
751 if (!_map.empty ()) {
752 /* never allow the map to be shortened */
753 end = max (end, _map.back().frame);
757 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
759 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
762 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
770 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
773 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
781 /* assumes that the first meter & tempo are at frame zero */
783 meter->set_frame (0);
784 tempo->set_frame (0);
786 /* assumes that the first meter & tempo are at 1|1|0 */
791 if (reassign_tempo_bbt) {
793 MeterSection* rmeter = meter;
795 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
797 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
802 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
804 /* reassign the BBT time of this tempo section
805 * based on its bar offset position.
808 ts->update_bbt_time_from_bar_offset (*rmeter);
810 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
813 fatal << _("programming error: unhandled MetricSection type") << endmsg;
814 abort(); /*NOTREACHED*/
819 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
821 next_metric = metrics.begin();
822 ++next_metric; // skip meter (or tempo)
823 ++next_metric; // skip tempo (or meter)
827 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
828 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
831 /* silly call from Session::process() during startup
836 _extend_map (tempo, meter, next_metric, current, current_frame, end);
840 TempoMap::extend_map (framepos_t end)
842 /* CALLER MUST HOLD WRITE LOCK */
845 recompute_map (false, end);
849 BBTPointList::const_iterator i = _map.end();
850 Metrics::iterator next_metric;
854 BBT_Time last_metric_start;
856 if ((*i).tempo->frame() > (*i).meter->frame()) {
857 last_metric_start = (*i).tempo->start();
859 last_metric_start = (*i).meter->start();
862 /* find the metric immediately after the tempo + meter sections for the
863 * last point in the map
866 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
867 if ((*next_metric)->start() > last_metric_start) {
872 /* we cast away const here because this is the one place where we need
873 * to actually modify the frame time of each metric section.
876 _extend_map (const_cast<TempoSection*> ((*i).tempo),
877 const_cast<MeterSection*> ((*i).meter),
878 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
882 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
883 Metrics::iterator next_metric,
884 BBT_Time current, framepos_t current_frame, framepos_t end)
886 /* CALLER MUST HOLD WRITE LOCK */
891 double current_frame_exact;
892 framepos_t bar_start_frame;
894 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
896 if (current.beats == 1) {
897 bar_start_frame = current_frame;
902 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
903 current_frame_exact = current_frame;
905 while (current_frame < end) {
908 current_frame_exact += beat_frames;
909 current_frame = llrint(current_frame_exact);
911 if (current.beats > meter->divisions_per_bar()) {
916 if (next_metric != metrics.end()) {
918 /* no operator >= so invert operator < */
920 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
922 if (!(current < (*next_metric)->start())) {
925 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
929 /* new tempo section: if its on a beat,
930 * we don't have to do anything other
931 * than recompute various distances,
932 * done further below as we transition
933 * the next metric section.
935 * if its not on the beat, we have to
936 * compute the duration of the beat it
937 * is within, which will be different
938 * from the preceding following ones
939 * since it takes part of its duration
940 * from the preceding tempo and part
941 * from this new tempo.
944 if (tempo->start().ticks != 0) {
946 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
948 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
949 tempo->start(), current_frame, tempo->bar_offset()));
951 /* back up to previous beat */
952 current_frame_exact -= beat_frames;
953 current_frame = llrint(current_frame_exact);
955 /* set tempo section location
956 * based on offset from last
959 tempo->set_frame (bar_start_frame +
960 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
962 /* advance to the location of
963 * the new (adjusted) beat. do
964 * this by figuring out the
965 * offset within the beat that
966 * would have been there
968 * change. then stretch the
972 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
974 current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
975 current_frame = llrint(current_frame_exact);
977 /* next metric doesn't have to
978 * match this precisely to
981 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
985 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
986 tempo->start(), current_frame));
987 tempo->set_frame (current_frame);
990 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
994 /* new meter section: always defines the
998 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
999 meter->start(), current, current_frame));
1001 assert (current.beats == 1);
1003 meter->set_frame (current_frame);
1006 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1008 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1009 beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
1013 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1014 /* same position so go back and set this one up before advancing
1022 if (current.beats == 1) {
1023 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1024 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
1025 bar_start_frame = current_frame;
1027 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1028 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
1031 if (next_metric == metrics.end()) {
1032 /* no more metrics - we've timestamped them all, stop here */
1033 if (end == max_framepos) {
1034 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
1035 current.bars, current.beats, current_frame));
1043 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1045 Glib::Threads::RWLock::ReaderLock lm (lock);
1046 TempoMetric m (first_meter(), first_tempo());
1048 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1049 at something, because we insert the default tempo and meter during
1050 TempoMap construction.
1052 now see if we can find better candidates.
1055 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1057 if ((*i)->frame() > frame) {
1072 TempoMap::metric_at (BBT_Time bbt) const
1074 Glib::Threads::RWLock::ReaderLock lm (lock);
1075 TempoMetric m (first_meter(), first_tempo());
1077 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1078 at something, because we insert the default tempo and meter during
1079 TempoMap construction.
1081 now see if we can find better candidates.
1084 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1086 BBT_Time section_start ((*i)->start());
1088 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1099 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1101 require_map_to (frame);
1103 Glib::Threads::RWLock::ReaderLock lm (lock);
1109 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1113 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1117 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1119 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1122 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1125 if (_map.empty() || _map.back().frame < frame) {
1126 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1129 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1133 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1135 /* CALLER MUST HOLD READ LOCK */
1137 bbt.bars = (*i).bar;
1138 bbt.beats = (*i).beat;
1140 if ((*i).frame == frame) {
1143 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1144 BBT_Time::ticks_per_beat);
1149 TempoMap::frame_time (const BBT_Time& bbt)
1152 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1156 if (bbt.beats < 1) {
1157 throw std::logic_error ("beats are counted from one");
1160 require_map_to (bbt);
1162 Glib::Threads::RWLock::ReaderLock lm (lock);
1164 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1165 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1167 if (bbt.ticks != 0) {
1168 return ((*e).frame - (*s).frame) +
1169 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1171 return ((*e).frame - (*s).frame);
1176 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1179 bbt_time (pos, when);
1181 Glib::Threads::RWLock::ReaderLock lm (lock);
1182 return bbt_duration_at_unlocked (when, bbt, dir);
1186 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
1188 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1192 /* round back to the previous precise beat */
1193 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1194 BBTPointList::const_iterator start (wi);
1196 assert (wi != _map.end());
1201 while (wi != _map.end() && bars < bbt.bars) {
1203 if ((*wi).is_bar()) {
1207 assert (wi != _map.end());
1209 while (wi != _map.end() && beats < bbt.beats) {
1213 assert (wi != _map.end());
1215 /* add any additional frames related to ticks in the added value */
1217 if (bbt.ticks != 0) {
1218 return ((*wi).frame - (*start).frame) +
1219 (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1221 return ((*wi).frame - (*start).frame);
1226 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1228 return round_to_type (fr, dir, Bar);
1232 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1234 return round_to_type (fr, dir, Beat);
1238 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1240 require_map_to (fr);
1242 Glib::Threads::RWLock::ReaderLock lm (lock);
1243 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1245 uint32_t ticks_one_subdivisions_worth;
1246 uint32_t difference;
1248 bbt_time (fr, the_beat, i);
1250 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1251 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1253 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1257 /* round to next (even if we're on a subdivision */
1259 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1262 /* right on the subdivision, so the difference is just the subdivision ticks */
1263 the_beat.ticks += ticks_one_subdivisions_worth;
1266 /* not on subdivision, compute distance to next subdivision */
1268 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1271 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1272 assert (i != _map.end());
1274 assert (i != _map.end());
1275 the_beat.ticks -= BBT_Time::ticks_per_beat;
1279 } else if (dir < 0) {
1281 /* round to previous (even if we're on a subdivision) */
1283 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1286 /* right on the subdivision, so the difference is just the subdivision ticks */
1287 difference = ticks_one_subdivisions_worth;
1289 /* not on subdivision, compute distance to previous subdivision, which
1290 is just the modulus.
1296 if (the_beat.ticks < difference) {
1297 if (i == _map.begin()) {
1298 /* can't go backwards from wherever pos is, so just return it */
1302 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1304 the_beat.ticks -= difference;
1308 /* round to nearest */
1312 /* compute the distance to the previous and next subdivision */
1314 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1316 /* closer to the next subdivision, so shift forward */
1318 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1320 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1322 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1323 assert (i != _map.end());
1325 assert (i != _map.end());
1326 the_beat.ticks -= BBT_Time::ticks_per_beat;
1327 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1330 } else if (rem > 0) {
1332 /* closer to previous subdivision, so shift backward */
1334 if (rem > the_beat.ticks) {
1335 if (i == _map.begin()) {
1336 /* can't go backwards past zero, so ... */
1339 /* step back to previous beat */
1341 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1342 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1344 the_beat.ticks = lrint (the_beat.ticks - rem);
1345 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1348 /* on the subdivision, do nothing */
1352 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1353 (*i).tempo->frames_per_beat (_frame_rate);
1357 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1359 require_map_to (frame);
1361 Glib::Threads::RWLock::ReaderLock lm (lock);
1362 BBTPointList::const_iterator fi;
1365 fi = bbt_after_or_at (frame);
1367 fi = bbt_before_or_at (frame);
1370 assert (fi != _map.end());
1372 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to %6 in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame,
1373 (type == Bar ? "bar" : "beat")));
1378 /* find bar previous to 'frame' */
1380 if (fi == _map.begin()) {
1384 if ((*fi).is_bar() && (*fi).frame == frame) {
1388 while (!(*fi).is_bar()) {
1389 if (fi == _map.begin()) {
1394 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1395 (*fi).bar, (*fi).beat, (*fi).frame));
1398 } else if (dir > 0) {
1400 /* find bar following 'frame' */
1402 if ((*fi).is_bar() && (*fi).frame == frame) {
1406 while (!(*fi).is_bar()) {
1408 if (fi == _map.end()) {
1414 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1415 (*fi).bar, (*fi).beat, (*fi).frame));
1420 /* true rounding: find nearest bar */
1422 BBTPointList::const_iterator prev = fi;
1423 BBTPointList::const_iterator next = fi;
1425 if ((*fi).frame == frame) {
1429 while ((*prev).beat != 1) {
1430 if (prev == _map.begin()) {
1436 while ((next != _map.end()) && (*next).beat != 1) {
1440 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1441 return (*prev).frame;
1443 return (*next).frame;
1453 if (fi == _map.begin()) {
1457 if ((*fi).frame >= frame) {
1458 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1461 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1462 (*fi).bar, (*fi).beat, (*fi).frame));
1464 } else if (dir > 0) {
1465 if ((*fi).frame <= frame) {
1466 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1469 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1470 (*fi).bar, (*fi).beat, (*fi).frame));
1473 /* find beat nearest to frame */
1474 if ((*fi).frame == frame) {
1478 BBTPointList::const_iterator prev = fi;
1479 BBTPointList::const_iterator next = fi;
1481 /* fi is already the beat before_or_at frame, and
1482 we've just established that its not at frame, so its
1483 the beat before frame.
1487 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1488 return (*prev).frame;
1490 return (*next).frame;
1496 abort(); /* NOTREACHED */
1501 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1502 TempoMap::BBTPointList::const_iterator& end,
1503 framepos_t lower, framepos_t upper)
1506 Glib::Threads::RWLock::WriterLock lm (lock);
1507 if (_map.empty() || (_map.back().frame < upper)) {
1508 recompute_map (false, upper);
1512 begin = lower_bound (_map.begin(), _map.end(), lower);
1513 end = upper_bound (_map.begin(), _map.end(), upper);
1517 TempoMap::tempo_section_at (framepos_t frame) const
1519 Glib::Threads::RWLock::ReaderLock lm (lock);
1520 Metrics::const_iterator i;
1521 TempoSection* prev = 0;
1523 for (i = metrics.begin(); i != metrics.end(); ++i) {
1526 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1528 if ((*i)->frame() > frame) {
1538 abort(); /*NOTREACHED*/
1545 TempoMap::tempo_at (framepos_t frame) const
1547 TempoMetric m (metric_at (frame));
1553 TempoMap::meter_at (framepos_t frame) const
1555 TempoMetric m (metric_at (frame));
1560 TempoMap::get_state ()
1562 Metrics::const_iterator i;
1563 XMLNode *root = new XMLNode ("TempoMap");
1566 Glib::Threads::RWLock::ReaderLock lm (lock);
1567 for (i = metrics.begin(); i != metrics.end(); ++i) {
1568 root->add_child_nocopy ((*i)->get_state());
1576 TempoMap::set_state (const XMLNode& node, int /*version*/)
1579 Glib::Threads::RWLock::WriterLock lm (lock);
1582 XMLNodeConstIterator niter;
1583 Metrics old_metrics (metrics);
1584 MeterSection* last_meter = 0;
1587 nlist = node.children();
1589 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1590 XMLNode* child = *niter;
1592 if (child->name() == TempoSection::xml_state_node_name) {
1595 TempoSection* ts = new TempoSection (*child);
1596 metrics.push_back (ts);
1598 if (ts->bar_offset() < 0.0) {
1600 ts->update_bar_offset_from_bbt (*last_meter);
1605 catch (failed_constructor& err){
1606 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1607 metrics = old_metrics;
1611 } else if (child->name() == MeterSection::xml_state_node_name) {
1614 MeterSection* ms = new MeterSection (*child);
1615 metrics.push_back (ms);
1619 catch (failed_constructor& err) {
1620 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1621 metrics = old_metrics;
1627 if (niter == nlist.end()) {
1628 MetricSectionSorter cmp;
1632 /* check for multiple tempo/meters at the same location, which
1633 ardour2 somehow allowed.
1636 Metrics::iterator prev = metrics.end();
1637 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1638 if (prev != metrics.end()) {
1639 if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
1640 if ((*prev)->start() == (*i)->start()) {
1641 error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1644 } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
1645 if ((*prev)->start() == (*i)->start()) {
1646 error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1654 recompute_map (true, -1);
1657 PropertyChanged (PropertyChange ());
1663 TempoMap::dump (std::ostream& o) const
1665 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1666 const MeterSection* m;
1667 const TempoSection* t;
1669 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1671 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1672 o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
1673 << t->movable() << ')' << endl;
1674 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1675 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1676 << " (movable? " << m->movable() << ')' << endl;
1682 TempoMap::n_tempos() const
1684 Glib::Threads::RWLock::ReaderLock lm (lock);
1687 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1688 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1697 TempoMap::n_meters() const
1699 Glib::Threads::RWLock::ReaderLock lm (lock);
1702 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1703 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1712 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1715 Glib::Threads::RWLock::WriterLock lm (lock);
1716 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1717 if ((*i)->frame() >= where && (*i)->movable ()) {
1718 (*i)->set_frame ((*i)->frame() + amount);
1722 /* now reset the BBT time of all metrics, based on their new
1723 * audio time. This is the only place where we do this reverse
1727 Metrics::iterator i;
1728 const MeterSection* meter;
1729 const TempoSection* tempo;
1733 meter = &first_meter ();
1734 tempo = &first_tempo ();
1739 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1742 MetricSection* prev = 0;
1744 for (i = metrics.begin(); i != metrics.end(); ++i) {
1747 TempoMetric metric (*meter, *tempo);
1750 metric.set_start (prev->start());
1751 metric.set_frame (prev->frame());
1753 // metric will be at frames=0 bbt=1|1|0 by default
1754 // which is correct for our purpose
1757 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1758 bbt_time ((*i)->frame(), bbt, bi);
1760 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1766 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1767 /* round up to next beat */
1773 if (bbt.beats != 1) {
1774 /* round up to next bar */
1780 // cerr << bbt << endl;
1782 (*i)->set_start (bbt);
1784 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1786 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1787 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1789 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1791 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1792 abort(); /*NOTREACHED*/
1798 recompute_map (true);
1802 PropertyChanged (PropertyChange ());
1805 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1806 * pos can be -ve, if required.
1809 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1811 Glib::Threads::RWLock::ReaderLock lm (lock);
1812 Metrics::const_iterator next_tempo;
1813 const TempoSection* tempo = 0;
1815 /* Find the starting tempo metric */
1817 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1819 const TempoSection* t;
1821 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1823 /* This is a bit of a hack, but pos could be -ve, and if it is,
1824 we consider the initial metric changes (at time 0) to actually
1825 be in effect at pos.
1828 framepos_t f = (*next_tempo)->frame ();
1830 if (pos < 0 && f == 0) {
1844 tempo -> the Tempo for "pos"
1845 next_tempo -> first tempo after "pos", possibly metrics.end()
1849 DEBUG_TRACE (DEBUG::TempoMath,
1850 string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1851 pos, beats, *((const Tempo*)tempo), tempo->frame()));
1855 /* Distance to the end of this section in frames */
1856 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1858 /* Distance to the end in beats */
1859 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1861 /* Amount to subtract this time */
1862 double const delta = min (distance_beats, beats);
1864 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1865 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1866 distance_frames, distance_beats));
1870 pos += delta * tempo->frames_per_beat (_frame_rate);
1872 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1874 /* step forwards to next tempo section */
1876 if (next_tempo != metrics.end()) {
1878 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1880 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1881 *((const Tempo*)tempo), tempo->frame(),
1882 tempo->frames_per_beat (_frame_rate)));
1884 while (next_tempo != metrics.end ()) {
1888 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1898 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
1900 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1902 Glib::Threads::RWLock::ReaderLock lm (lock);
1903 Metrics::const_reverse_iterator prev_tempo;
1904 const TempoSection* tempo = 0;
1906 /* Find the starting tempo metric */
1908 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1910 const TempoSection* t;
1912 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1914 /* This is a bit of a hack, but pos could be -ve, and if it is,
1915 we consider the initial metric changes (at time 0) to actually
1916 be in effect at pos.
1919 framepos_t f = (*prev_tempo)->frame ();
1921 if (pos < 0 && f == 0) {
1925 /* this is slightly more complex than the forward case
1926 because we reach the tempo in effect at pos after
1927 passing through pos (rather before, as in the
1928 forward case). having done that, we then need to
1929 keep going to get the previous tempo (or
1935 /* first tempo with position at or
1939 } else if (f < pos) {
1940 /* some other tempo section that
1941 is even earlier than 'tempo'
1950 DEBUG_TRACE (DEBUG::TempoMath,
1951 string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1952 pos, beats, *((const Tempo*)tempo), tempo->frame(),
1953 prev_tempo == metrics.rend()));
1957 tempo -> the Tempo for "pos"
1958 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1963 /* Distance to the start of this section in frames */
1964 framecnt_t distance_frames = (pos - tempo->frame());
1966 /* Distance to the start in beats */
1967 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1969 /* Amount to subtract this time */
1970 double const sub = min (distance_beats, beats);
1972 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1973 tempo->frame(), distance_frames, distance_beats));
1977 pos -= sub * tempo->frames_per_beat (_frame_rate);
1979 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1980 (prev_tempo == metrics.rend())));
1982 /* step backwards to prior TempoSection */
1984 if (prev_tempo != metrics.rend()) {
1986 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1988 DEBUG_TRACE (DEBUG::TempoMath,
1989 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1990 *((const Tempo*)tempo), tempo->frame(),
1991 tempo->frames_per_beat (_frame_rate)));
1993 while (prev_tempo != metrics.rend ()) {
1997 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
2002 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
2010 /** Add the BBT interval op to pos and return the result */
2012 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2014 Glib::Threads::RWLock::ReaderLock lm (lock);
2015 Metrics::const_iterator i;
2016 const MeterSection* meter;
2017 const MeterSection* m;
2018 const TempoSection* tempo;
2019 const TempoSection* t;
2020 double frames_per_beat;
2021 framepos_t effective_pos = max (pos, (framepos_t) 0);
2023 meter = &first_meter ();
2024 tempo = &first_tempo ();
2029 /* find the starting metrics for tempo & meter */
2031 for (i = metrics.begin(); i != metrics.end(); ++i) {
2033 if ((*i)->frame() > effective_pos) {
2037 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2039 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2046 meter -> the Meter for "pos"
2047 tempo -> the Tempo for "pos"
2048 i -> for first new metric after "pos", possibly metrics.end()
2051 /* now comes the complicated part. we have to add one beat a time,
2052 checking for a new metric on every beat.
2055 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2064 /* check if we need to use a new metric section: has adding frames moved us
2065 to or after the start of the next metric section? in which case, use it.
2068 if (i != metrics.end()) {
2069 if ((*i)->frame() <= pos) {
2071 /* about to change tempo or meter, so add the
2072 * number of frames for the bars we've just
2073 * traversed before we change the
2074 * frames_per_beat value.
2077 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2080 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2082 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2086 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2093 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2099 /* given the current meter, have we gone past the end of the bar ? */
2104 /* check if we need to use a new metric section: has adding frames moved us
2105 to or after the start of the next metric section? in which case, use it.
2108 if (i != metrics.end()) {
2109 if ((*i)->frame() <= pos) {
2111 /* about to change tempo or meter, so add the
2112 * number of frames for the beats we've just
2113 * traversed before we change the
2114 * frames_per_beat value.
2117 pos += llrint (beats * frames_per_beat);
2120 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2122 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2126 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2131 pos += llrint (beats * frames_per_beat);
2134 if (op.ticks >= BBT_Time::ticks_per_beat) {
2135 pos += llrint (frames_per_beat + /* extra beat */
2136 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2137 (double) BBT_Time::ticks_per_beat)));
2139 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2146 /** Count the number of beats that are equivalent to distance when going forward,
2150 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2152 Glib::Threads::RWLock::ReaderLock lm (lock);
2153 Metrics::const_iterator next_tempo;
2154 const TempoSection* tempo = 0;
2155 framepos_t effective_pos = max (pos, (framepos_t) 0);
2157 /* Find the relevant initial tempo metric */
2159 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2161 const TempoSection* t;
2163 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2165 if ((*next_tempo)->frame() > effective_pos) {
2175 tempo -> the Tempo for "pos"
2176 next_tempo -> the next tempo after "pos", possibly metrics.end()
2180 DEBUG_TRACE (DEBUG::TempoMath,
2181 string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
2182 pos, distance, *((const Tempo*)tempo), tempo->frame()));
2184 Evoral::MusicalTime beats = 0;
2188 /* End of this section */
2190 /* Distance to `end' in frames */
2191 framepos_t distance_to_end;
2193 if (next_tempo == metrics.end ()) {
2194 /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
2196 distance_to_end = max_framepos;
2198 end = (*next_tempo)->frame ();
2199 distance_to_end = end - pos;
2202 /* Amount to subtract this time */
2203 double const sub = min (distance, distance_to_end);
2205 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
2206 distance_to_end, sub));
2212 beats += sub / tempo->frames_per_beat (_frame_rate);
2214 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
2215 pos, beats, distance));
2217 /* Move on if there's anything to move to */
2219 if (next_tempo != metrics.end()) {
2221 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2223 DEBUG_TRACE (DEBUG::TempoMath,
2224 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2225 *((const Tempo*)tempo), tempo->frame(),
2226 tempo->frames_per_beat (_frame_rate)));
2228 while (next_tempo != metrics.end ()) {
2232 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2237 if (next_tempo == metrics.end()) {
2238 DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
2240 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
2241 **next_tempo, (*next_tempo)->frame()));
2251 TempoMap::BBTPointList::const_iterator
2252 TempoMap::bbt_before_or_at (framepos_t pos)
2254 /* CALLER MUST HOLD READ LOCK */
2256 BBTPointList::const_iterator i;
2259 /* not really correct, but we should catch pos < 0 at a higher
2262 return _map.begin();
2265 i = lower_bound (_map.begin(), _map.end(), pos);
2266 assert (i != _map.end());
2267 if ((*i).frame > pos) {
2268 assert (i != _map.begin());
2275 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2280 TempoMap::BBTPointList::const_iterator
2281 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2283 BBTPointList::const_iterator i;
2286 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2287 assert (i != _map.end());
2288 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2289 assert (i != _map.begin());
2295 TempoMap::BBTPointList::const_iterator
2296 TempoMap::bbt_after_or_at (framepos_t pos)
2298 /* CALLER MUST HOLD READ LOCK */
2300 BBTPointList::const_iterator i;
2302 if (_map.back().frame == pos) {
2304 assert (i != _map.begin());
2309 i = upper_bound (_map.begin(), _map.end(), pos);
2310 assert (i != _map.end());
2315 operator<< (std::ostream& o, const Meter& m) {
2316 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2320 operator<< (std::ostream& o, const Tempo& t) {
2321 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2325 operator<< (std::ostream& o, const MetricSection& section) {
2327 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2329 const TempoSection* ts;
2330 const MeterSection* ms;
2332 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2333 o << *((const Tempo*) ts);
2334 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2335 o << *((const Meter*) ms);