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 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 *static_cast<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 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 *static_cast<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_meter ()
689 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
690 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
695 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
696 abort(); /*NOTREACHED*/
701 TempoMap::first_tempo () const
703 const TempoSection *t = 0;
705 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
706 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
711 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
712 abort(); /*NOTREACHED*/
717 TempoMap::first_tempo ()
721 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
722 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
727 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
728 abort(); /*NOTREACHED*/
733 TempoMap::require_map_to (framepos_t pos)
735 Glib::Threads::RWLock::WriterLock lm (lock);
737 if (_map.empty() || _map.back().frame < pos) {
743 TempoMap::require_map_to (const BBT_Time& bbt)
745 Glib::Threads::RWLock::WriterLock lm (lock);
747 /* since we have no idea where BBT is if its off the map, see the last
748 * point in the map is past BBT, and if not add an arbitrary amount of
752 int additional_minutes = 1;
755 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
758 /* add some more distance, using bigger steps each time */
759 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
760 additional_minutes *= 2;
765 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
767 /* CALLER MUST HOLD WRITE LOCK */
769 MeterSection* meter = 0;
770 TempoSection* tempo = 0;
771 double current_frame;
773 Metrics::iterator next_metric;
777 /* we will actually stop once we hit
783 if (!_map.empty ()) {
784 /* never allow the map to be shortened */
785 end = max (end, _map.back().frame);
789 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
791 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
794 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
802 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
805 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
813 /* assumes that the first meter & tempo are at frame zero */
815 meter->set_frame (0);
816 tempo->set_frame (0);
818 /* assumes that the first meter & tempo are at 1|1|0 */
823 if (reassign_tempo_bbt) {
825 MeterSection* rmeter = meter;
827 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
829 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
834 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
836 /* reassign the BBT time of this tempo section
837 * based on its bar offset position.
840 ts->update_bbt_time_from_bar_offset (*rmeter);
842 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
845 fatal << _("programming error: unhandled MetricSection type") << endmsg;
846 abort(); /*NOTREACHED*/
851 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
853 next_metric = metrics.begin();
854 ++next_metric; // skip meter (or tempo)
855 ++next_metric; // skip tempo (or meter)
859 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
860 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
863 /* silly call from Session::process() during startup
868 _extend_map (tempo, meter, next_metric, current, current_frame, end);
872 TempoMap::extend_map (framepos_t end)
874 /* CALLER MUST HOLD WRITE LOCK */
877 recompute_map (false, end);
881 BBTPointList::const_iterator i = _map.end();
882 Metrics::iterator next_metric;
886 BBT_Time last_metric_start;
888 if ((*i).tempo->frame() > (*i).meter->frame()) {
889 last_metric_start = (*i).tempo->start();
891 last_metric_start = (*i).meter->start();
894 /* find the metric immediately after the tempo + meter sections for the
895 * last point in the map
898 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
899 if ((*next_metric)->start() > last_metric_start) {
904 /* we cast away const here because this is the one place where we need
905 * to actually modify the frame time of each metric section.
908 _extend_map (const_cast<TempoSection*> ((*i).tempo),
909 const_cast<MeterSection*> ((*i).meter),
910 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
914 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
915 Metrics::iterator next_metric,
916 BBT_Time current, framepos_t current_frame, framepos_t end)
918 /* CALLER MUST HOLD WRITE LOCK */
923 double current_frame_exact;
924 framepos_t bar_start_frame;
926 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
928 if (current.beats == 1) {
929 bar_start_frame = current_frame;
934 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
935 current_frame_exact = current_frame;
937 while (current_frame < end) {
940 current_frame_exact += beat_frames;
941 current_frame = llrint(current_frame_exact);
943 if (current.beats > meter->divisions_per_bar()) {
948 if (next_metric != metrics.end()) {
950 /* no operator >= so invert operator < */
952 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
954 if (!(current < (*next_metric)->start())) {
957 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
961 /* new tempo section: if its on a beat,
962 * we don't have to do anything other
963 * than recompute various distances,
964 * done further below as we transition
965 * the next metric section.
967 * if its not on the beat, we have to
968 * compute the duration of the beat it
969 * is within, which will be different
970 * from the preceding following ones
971 * since it takes part of its duration
972 * from the preceding tempo and part
973 * from this new tempo.
976 if (tempo->start().ticks != 0) {
978 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
980 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
981 tempo->start(), current_frame, tempo->bar_offset()));
983 /* back up to previous beat */
984 current_frame_exact -= beat_frames;
985 current_frame = llrint(current_frame_exact);
987 /* set tempo section location
988 * based on offset from last
991 tempo->set_frame (bar_start_frame +
992 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
994 /* advance to the location of
995 * the new (adjusted) beat. do
996 * this by figuring out the
997 * offset within the beat that
998 * would have been there
1000 * change. then stretch the
1004 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
1006 current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
1007 current_frame = llrint(current_frame_exact);
1009 /* next metric doesn't have to
1010 * match this precisely to
1011 * merit a reloop ...
1013 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
1017 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
1018 tempo->start(), current_frame));
1019 tempo->set_frame (current_frame);
1022 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
1026 /* new meter section: always defines the
1030 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
1031 meter->start(), current, current_frame));
1033 assert (current.beats == 1);
1035 meter->set_frame (current_frame);
1038 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1040 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1041 beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
1045 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1046 /* same position so go back and set this one up before advancing
1054 if (current.beats == 1) {
1055 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1056 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
1057 bar_start_frame = current_frame;
1059 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1060 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
1063 if (next_metric == metrics.end()) {
1064 /* no more metrics - we've timestamped them all, stop here */
1065 if (end == max_framepos) {
1066 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
1067 current.bars, current.beats, current_frame));
1075 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1077 Glib::Threads::RWLock::ReaderLock lm (lock);
1078 TempoMetric m (first_meter(), first_tempo());
1080 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1081 at something, because we insert the default tempo and meter during
1082 TempoMap construction.
1084 now see if we can find better candidates.
1087 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1089 if ((*i)->frame() > frame) {
1104 TempoMap::metric_at (BBT_Time bbt) const
1106 Glib::Threads::RWLock::ReaderLock lm (lock);
1107 TempoMetric m (first_meter(), first_tempo());
1109 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1110 at something, because we insert the default tempo and meter during
1111 TempoMap construction.
1113 now see if we can find better candidates.
1116 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1118 BBT_Time section_start ((*i)->start());
1120 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1131 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1133 require_map_to (frame);
1135 Glib::Threads::RWLock::ReaderLock lm (lock);
1141 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1145 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1149 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1151 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1154 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1157 if (_map.empty() || _map.back().frame < frame) {
1158 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1161 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1165 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1167 /* CALLER MUST HOLD READ LOCK */
1169 bbt.bars = (*i).bar;
1170 bbt.beats = (*i).beat;
1172 if ((*i).frame == frame) {
1175 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1176 BBT_Time::ticks_per_beat);
1181 TempoMap::frame_time (const BBT_Time& bbt)
1184 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1188 if (bbt.beats < 1) {
1189 throw std::logic_error ("beats are counted from one");
1192 require_map_to (bbt);
1194 Glib::Threads::RWLock::ReaderLock lm (lock);
1196 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1197 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1199 if (bbt.ticks != 0) {
1200 return ((*e).frame - (*s).frame) +
1201 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1203 return ((*e).frame - (*s).frame);
1208 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1211 bbt_time (pos, when);
1213 Glib::Threads::RWLock::ReaderLock lm (lock);
1214 return bbt_duration_at_unlocked (when, bbt, dir);
1218 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
1220 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1224 /* round back to the previous precise beat */
1225 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1226 BBTPointList::const_iterator start (wi);
1228 assert (wi != _map.end());
1233 while (wi != _map.end() && bars < bbt.bars) {
1235 if ((*wi).is_bar()) {
1239 assert (wi != _map.end());
1241 while (wi != _map.end() && beats < bbt.beats) {
1245 assert (wi != _map.end());
1247 /* add any additional frames related to ticks in the added value */
1249 if (bbt.ticks != 0) {
1250 return ((*wi).frame - (*start).frame) +
1251 (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1253 return ((*wi).frame - (*start).frame);
1258 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1260 return round_to_type (fr, dir, Bar);
1264 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1266 return round_to_type (fr, dir, Beat);
1270 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1272 require_map_to (fr);
1274 Glib::Threads::RWLock::ReaderLock lm (lock);
1275 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1277 uint32_t ticks_one_subdivisions_worth;
1279 bbt_time (fr, the_beat, i);
1281 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1282 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1284 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1288 /* round to next (or same iff dir == RoundUpMaybe) */
1290 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1292 if (mod == 0 && dir == RoundUpMaybe) {
1293 /* right on the subdivision, which is fine, so do nothing */
1295 } else if (mod == 0) {
1296 /* right on the subdivision, so the difference is just the subdivision ticks */
1297 the_beat.ticks += ticks_one_subdivisions_worth;
1300 /* not on subdivision, compute distance to next subdivision */
1302 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1305 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1306 assert (i != _map.end());
1308 assert (i != _map.end());
1309 the_beat.ticks -= BBT_Time::ticks_per_beat;
1313 } else if (dir < 0) {
1315 /* round to previous (or same iff dir == RoundDownMaybe) */
1317 uint32_t difference = the_beat.ticks % ticks_one_subdivisions_worth;
1319 if (difference == 0 && dir == RoundDownAlways) {
1320 /* right on the subdivision, but force-rounding down,
1321 so the difference is just the subdivision ticks */
1322 difference = ticks_one_subdivisions_worth;
1325 if (the_beat.ticks < difference) {
1326 if (i == _map.begin()) {
1327 /* can't go backwards from wherever pos is, so just return it */
1331 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1333 the_beat.ticks -= difference;
1337 /* round to nearest */
1341 /* compute the distance to the previous and next subdivision */
1343 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1345 /* closer to the next subdivision, so shift forward */
1347 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1349 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1351 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1352 assert (i != _map.end());
1354 assert (i != _map.end());
1355 the_beat.ticks -= BBT_Time::ticks_per_beat;
1356 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1359 } else if (rem > 0) {
1361 /* closer to previous subdivision, so shift backward */
1363 if (rem > the_beat.ticks) {
1364 if (i == _map.begin()) {
1365 /* can't go backwards past zero, so ... */
1368 /* step back to previous beat */
1370 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1371 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1373 the_beat.ticks = lrint (the_beat.ticks - rem);
1374 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1377 /* on the subdivision, do nothing */
1381 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1382 (*i).tempo->frames_per_beat (_frame_rate);
1386 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1388 require_map_to (frame);
1390 Glib::Threads::RWLock::ReaderLock lm (lock);
1391 BBTPointList::const_iterator fi;
1394 fi = bbt_after_or_at (frame);
1396 fi = bbt_before_or_at (frame);
1399 assert (fi != _map.end());
1401 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,
1402 (type == Bar ? "bar" : "beat")));
1407 /* find bar previous to 'frame' */
1409 if (fi == _map.begin()) {
1413 if ((*fi).is_bar() && (*fi).frame == frame) {
1414 if (dir == RoundDownMaybe) {
1420 while (!(*fi).is_bar()) {
1421 if (fi == _map.begin()) {
1426 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1427 (*fi).bar, (*fi).beat, (*fi).frame));
1430 } else if (dir > 0) {
1432 /* find bar following 'frame' */
1434 if ((*fi).is_bar() && (*fi).frame == frame) {
1435 if (dir == RoundUpMaybe) {
1441 while (!(*fi).is_bar()) {
1443 if (fi == _map.end()) {
1449 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1450 (*fi).bar, (*fi).beat, (*fi).frame));
1455 /* true rounding: find nearest bar */
1457 BBTPointList::const_iterator prev = fi;
1458 BBTPointList::const_iterator next = fi;
1460 if ((*fi).frame == frame) {
1464 while ((*prev).beat != 1) {
1465 if (prev == _map.begin()) {
1471 while ((next != _map.end()) && (*next).beat != 1) {
1475 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1476 return (*prev).frame;
1478 return (*next).frame;
1488 if (fi == _map.begin()) {
1492 if ((*fi).frame > frame || ((*fi).frame == frame && dir == RoundDownAlways)) {
1493 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1496 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1497 (*fi).bar, (*fi).beat, (*fi).frame));
1499 } else if (dir > 0) {
1500 if ((*fi).frame < frame || ((*fi).frame == frame && dir == RoundUpAlways)) {
1501 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1504 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1505 (*fi).bar, (*fi).beat, (*fi).frame));
1508 /* find beat nearest to frame */
1509 if ((*fi).frame == frame) {
1513 BBTPointList::const_iterator prev = fi;
1514 BBTPointList::const_iterator next = fi;
1516 /* fi is already the beat before_or_at frame, and
1517 we've just established that its not at frame, so its
1518 the beat before frame.
1522 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1523 return (*prev).frame;
1525 return (*next).frame;
1531 abort(); /* NOTREACHED */
1536 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1537 TempoMap::BBTPointList::const_iterator& end,
1538 framepos_t lower, framepos_t upper)
1541 Glib::Threads::RWLock::WriterLock lm (lock);
1542 if (_map.empty() || (_map.back().frame < upper)) {
1543 recompute_map (false, upper);
1547 begin = lower_bound (_map.begin(), _map.end(), lower);
1548 end = upper_bound (_map.begin(), _map.end(), upper);
1552 TempoMap::tempo_section_at (framepos_t frame) const
1554 Glib::Threads::RWLock::ReaderLock lm (lock);
1555 Metrics::const_iterator i;
1556 TempoSection* prev = 0;
1558 for (i = metrics.begin(); i != metrics.end(); ++i) {
1561 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1563 if ((*i)->frame() > frame) {
1573 abort(); /*NOTREACHED*/
1580 TempoMap::tempo_at (framepos_t frame) const
1582 TempoMetric m (metric_at (frame));
1588 TempoMap::meter_at (framepos_t frame) const
1590 TempoMetric m (metric_at (frame));
1595 TempoMap::get_state ()
1597 Metrics::const_iterator i;
1598 XMLNode *root = new XMLNode ("TempoMap");
1601 Glib::Threads::RWLock::ReaderLock lm (lock);
1602 for (i = metrics.begin(); i != metrics.end(); ++i) {
1603 root->add_child_nocopy ((*i)->get_state());
1611 TempoMap::set_state (const XMLNode& node, int /*version*/)
1614 Glib::Threads::RWLock::WriterLock lm (lock);
1617 XMLNodeConstIterator niter;
1618 Metrics old_metrics (metrics);
1619 MeterSection* last_meter = 0;
1622 nlist = node.children();
1624 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1625 XMLNode* child = *niter;
1627 if (child->name() == TempoSection::xml_state_node_name) {
1630 TempoSection* ts = new TempoSection (*child);
1631 metrics.push_back (ts);
1633 if (ts->bar_offset() < 0.0) {
1635 ts->update_bar_offset_from_bbt (*last_meter);
1640 catch (failed_constructor& err){
1641 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1642 metrics = old_metrics;
1646 } else if (child->name() == MeterSection::xml_state_node_name) {
1649 MeterSection* ms = new MeterSection (*child);
1650 metrics.push_back (ms);
1654 catch (failed_constructor& err) {
1655 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1656 metrics = old_metrics;
1662 if (niter == nlist.end()) {
1663 MetricSectionSorter cmp;
1667 /* check for multiple tempo/meters at the same location, which
1668 ardour2 somehow allowed.
1671 Metrics::iterator prev = metrics.end();
1672 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1673 if (prev != metrics.end()) {
1674 if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
1675 if ((*prev)->start() == (*i)->start()) {
1676 error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1679 } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
1680 if ((*prev)->start() == (*i)->start()) {
1681 error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1689 recompute_map (true, -1);
1692 PropertyChanged (PropertyChange ());
1698 TempoMap::dump (std::ostream& o) const
1700 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1701 const MeterSection* m;
1702 const TempoSection* t;
1704 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1706 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1707 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? "
1708 << t->movable() << ')' << endl;
1709 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1710 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1711 << " (movable? " << m->movable() << ')' << endl;
1717 TempoMap::n_tempos() const
1719 Glib::Threads::RWLock::ReaderLock lm (lock);
1722 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1723 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1732 TempoMap::n_meters() const
1734 Glib::Threads::RWLock::ReaderLock lm (lock);
1737 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1738 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1747 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1750 Glib::Threads::RWLock::WriterLock lm (lock);
1751 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1752 if ((*i)->frame() >= where && (*i)->movable ()) {
1753 (*i)->set_frame ((*i)->frame() + amount);
1757 /* now reset the BBT time of all metrics, based on their new
1758 * audio time. This is the only place where we do this reverse
1762 Metrics::iterator i;
1763 const MeterSection* meter;
1764 const TempoSection* tempo;
1768 meter = &first_meter ();
1769 tempo = &first_tempo ();
1774 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1777 MetricSection* prev = 0;
1779 for (i = metrics.begin(); i != metrics.end(); ++i) {
1782 TempoMetric metric (*meter, *tempo);
1785 metric.set_start (prev->start());
1786 metric.set_frame (prev->frame());
1788 // metric will be at frames=0 bbt=1|1|0 by default
1789 // which is correct for our purpose
1792 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1793 bbt_time ((*i)->frame(), bbt, bi);
1795 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1801 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1802 /* round up to next beat */
1808 if (bbt.beats != 1) {
1809 /* round up to next bar */
1815 // cerr << bbt << endl;
1817 (*i)->set_start (bbt);
1819 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1821 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1822 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1824 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1826 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1827 abort(); /*NOTREACHED*/
1833 recompute_map (true);
1837 PropertyChanged (PropertyChange ());
1840 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1841 * pos can be -ve, if required.
1844 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1846 Glib::Threads::RWLock::ReaderLock lm (lock);
1847 Metrics::const_iterator next_tempo;
1848 const TempoSection* tempo = 0;
1850 /* Find the starting tempo metric */
1852 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1854 const TempoSection* t;
1856 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1858 /* This is a bit of a hack, but pos could be -ve, and if it is,
1859 we consider the initial metric changes (at time 0) to actually
1860 be in effect at pos.
1863 framepos_t f = (*next_tempo)->frame ();
1865 if (pos < 0 && f == 0) {
1879 tempo -> the Tempo for "pos"
1880 next_tempo -> first tempo after "pos", possibly metrics.end()
1884 DEBUG_TRACE (DEBUG::TempoMath,
1885 string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1886 pos, beats, *((const Tempo*)tempo), tempo->frame()));
1890 /* Distance to the end of this section in frames */
1891 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1893 /* Distance to the end in beats */
1894 Evoral::MusicalTime distance_beats = Evoral::MusicalTime::ticks_at_rate(
1895 distance_frames, tempo->frames_per_beat (_frame_rate));
1897 /* Amount to subtract this time */
1898 Evoral::MusicalTime const delta = min (distance_beats, beats);
1900 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1901 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1902 distance_frames, distance_beats));
1906 pos += delta.to_ticks(tempo->frames_per_beat (_frame_rate));
1908 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1910 /* step forwards to next tempo section */
1912 if (next_tempo != metrics.end()) {
1914 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1916 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1917 *((const Tempo*)tempo), tempo->frame(),
1918 tempo->frames_per_beat (_frame_rate)));
1920 while (next_tempo != metrics.end ()) {
1924 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1934 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
1936 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1938 Glib::Threads::RWLock::ReaderLock lm (lock);
1939 Metrics::const_reverse_iterator prev_tempo;
1940 const TempoSection* tempo = 0;
1942 /* Find the starting tempo metric */
1944 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1946 const TempoSection* t;
1948 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1950 /* This is a bit of a hack, but pos could be -ve, and if it is,
1951 we consider the initial metric changes (at time 0) to actually
1952 be in effect at pos.
1955 framepos_t f = (*prev_tempo)->frame ();
1957 if (pos < 0 && f == 0) {
1961 /* this is slightly more complex than the forward case
1962 because we reach the tempo in effect at pos after
1963 passing through pos (rather before, as in the
1964 forward case). having done that, we then need to
1965 keep going to get the previous tempo (or
1971 /* first tempo with position at or
1975 } else if (f < pos) {
1976 /* some other tempo section that
1977 is even earlier than 'tempo'
1986 DEBUG_TRACE (DEBUG::TempoMath,
1987 string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1988 pos, beats, *((const Tempo*)tempo), tempo->frame(),
1989 prev_tempo == metrics.rend()));
1993 tempo -> the Tempo for "pos"
1994 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1999 /* Distance to the start of this section in frames */
2000 framecnt_t distance_frames = (pos - tempo->frame());
2002 /* Distance to the start in beats */
2003 Evoral::MusicalTime distance_beats = Evoral::MusicalTime::ticks_at_rate(
2004 distance_frames, tempo->frames_per_beat (_frame_rate));
2006 /* Amount to subtract this time */
2007 Evoral::MusicalTime const sub = min (distance_beats, beats);
2009 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
2010 tempo->frame(), distance_frames, distance_beats));
2014 pos -= sub * tempo->frames_per_beat (_frame_rate);
2016 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
2017 (prev_tempo == metrics.rend())));
2019 /* step backwards to prior TempoSection */
2021 if (prev_tempo != metrics.rend()) {
2023 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
2025 DEBUG_TRACE (DEBUG::TempoMath,
2026 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2027 *((const Tempo*)tempo), tempo->frame(),
2028 tempo->frames_per_beat (_frame_rate)));
2030 while (prev_tempo != metrics.rend ()) {
2034 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
2039 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
2040 beats = Evoral::MusicalTime();
2047 /** Add the BBT interval op to pos and return the result */
2049 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2051 Glib::Threads::RWLock::ReaderLock lm (lock);
2052 Metrics::const_iterator i;
2053 const MeterSection* meter;
2054 const MeterSection* m;
2055 const TempoSection* tempo;
2056 const TempoSection* t;
2057 double frames_per_beat;
2058 framepos_t effective_pos = max (pos, (framepos_t) 0);
2060 meter = &first_meter ();
2061 tempo = &first_tempo ();
2066 /* find the starting metrics for tempo & meter */
2068 for (i = metrics.begin(); i != metrics.end(); ++i) {
2070 if ((*i)->frame() > effective_pos) {
2074 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2076 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2083 meter -> the Meter for "pos"
2084 tempo -> the Tempo for "pos"
2085 i -> for first new metric after "pos", possibly metrics.end()
2088 /* now comes the complicated part. we have to add one beat a time,
2089 checking for a new metric on every beat.
2092 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2101 /* check if we need to use a new metric section: has adding frames moved us
2102 to or after the start of the next metric section? in which case, use it.
2105 if (i != metrics.end()) {
2106 if ((*i)->frame() <= pos) {
2108 /* about to change tempo or meter, so add the
2109 * number of frames for the bars we've just
2110 * traversed before we change the
2111 * frames_per_beat value.
2114 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2117 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2119 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2123 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2130 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2136 /* given the current meter, have we gone past the end of the bar ? */
2141 /* check if we need to use a new metric section: has adding frames moved us
2142 to or after the start of the next metric section? in which case, use it.
2145 if (i != metrics.end()) {
2146 if ((*i)->frame() <= pos) {
2148 /* about to change tempo or meter, so add the
2149 * number of frames for the beats we've just
2150 * traversed before we change the
2151 * frames_per_beat value.
2154 pos += llrint (beats * frames_per_beat);
2157 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2159 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2163 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2168 pos += llrint (beats * frames_per_beat);
2171 if (op.ticks >= BBT_Time::ticks_per_beat) {
2172 pos += llrint (frames_per_beat + /* extra beat */
2173 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2174 (double) BBT_Time::ticks_per_beat)));
2176 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2183 /** Count the number of beats that are equivalent to distance when going forward,
2187 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2189 Glib::Threads::RWLock::ReaderLock lm (lock);
2190 Metrics::const_iterator next_tempo;
2191 const TempoSection* tempo = 0;
2192 framepos_t effective_pos = max (pos, (framepos_t) 0);
2194 /* Find the relevant initial tempo metric */
2196 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2198 const TempoSection* t;
2200 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2202 if ((*next_tempo)->frame() > effective_pos) {
2212 tempo -> the Tempo for "pos"
2213 next_tempo -> the next tempo after "pos", possibly metrics.end()
2217 DEBUG_TRACE (DEBUG::TempoMath,
2218 string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
2219 pos, distance, *((const Tempo*)tempo), tempo->frame()));
2221 Evoral::MusicalTime beats = Evoral::MusicalTime();
2225 /* End of this section */
2227 /* Distance to `end' in frames */
2228 framepos_t distance_to_end;
2230 if (next_tempo == metrics.end ()) {
2231 /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
2233 distance_to_end = max_framepos;
2235 end = (*next_tempo)->frame ();
2236 distance_to_end = end - pos;
2239 /* Amount to subtract this time in frames */
2240 framecnt_t const sub = min (distance, distance_to_end);
2242 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
2243 distance_to_end, sub));
2249 beats += Evoral::MusicalTime::ticks_at_rate(sub, tempo->frames_per_beat (_frame_rate));
2251 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
2252 pos, beats, distance));
2254 /* Move on if there's anything to move to */
2256 if (next_tempo != metrics.end()) {
2258 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2260 DEBUG_TRACE (DEBUG::TempoMath,
2261 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2262 *((const Tempo*)tempo), tempo->frame(),
2263 tempo->frames_per_beat (_frame_rate)));
2265 while (next_tempo != metrics.end ()) {
2269 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2274 if (next_tempo == metrics.end()) {
2275 DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
2277 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
2278 **next_tempo, (*next_tempo)->frame()));
2288 TempoMap::BBTPointList::const_iterator
2289 TempoMap::bbt_before_or_at (framepos_t pos)
2291 /* CALLER MUST HOLD READ LOCK */
2293 BBTPointList::const_iterator i;
2296 /* not really correct, but we should catch pos < 0 at a higher
2299 return _map.begin();
2302 i = lower_bound (_map.begin(), _map.end(), pos);
2303 assert (i != _map.end());
2304 if ((*i).frame > pos) {
2305 assert (i != _map.begin());
2312 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2317 TempoMap::BBTPointList::const_iterator
2318 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2320 BBTPointList::const_iterator i;
2323 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2324 assert (i != _map.end());
2325 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2326 assert (i != _map.begin());
2332 TempoMap::BBTPointList::const_iterator
2333 TempoMap::bbt_after_or_at (framepos_t pos)
2335 /* CALLER MUST HOLD READ LOCK */
2337 BBTPointList::const_iterator i;
2339 if (_map.back().frame == pos) {
2341 assert (i != _map.begin());
2346 i = upper_bound (_map.begin(), _map.end(), pos);
2347 assert (i != _map.end());
2352 operator<< (std::ostream& o, const Meter& m) {
2353 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2357 operator<< (std::ostream& o, const Tempo& t) {
2358 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2362 operator<< (std::ostream& o, const MetricSection& section) {
2364 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2366 const TempoSection* ts;
2367 const MeterSection* ms;
2369 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2370 o << *((const Tempo*) ts);
2371 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2372 o << *((const Meter*) ms);