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.
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "evoral/types.hpp"
30 #include "ardour/debug.h"
31 #include "ardour/tempo.h"
32 #include "ardour/utils.h"
38 using namespace ARDOUR;
41 using Timecode::BBT_Time;
43 /* _default tempo is 4/4 qtr=120 */
45 Meter TempoMap::_default_meter (4.0, 4.0);
46 Tempo TempoMap::_default_tempo (120.0);
49 Tempo::frames_per_beat (framecnt_t sr) const
51 return (60.0 * sr) / _beats_per_minute;
54 /***********************************************************************/
57 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
59 /* This is tempo- and meter-sensitive. The number it returns
60 is based on the interval between any two lines in the
61 grid that is constructed from tempo and meter sections.
63 The return value IS NOT interpretable in terms of "beats".
66 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
70 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
72 return frames_per_grid (tempo, sr) * _divisions_per_bar;
75 /***********************************************************************/
77 const string TempoSection::xml_state_node_name = "Tempo";
79 TempoSection::TempoSection (const XMLNode& node)
80 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
82 const XMLProperty *prop;
84 LocaleGuard lg (X_("POSIX"));
86 if ((prop = node.property ("start")) == 0) {
87 error << _("TempoSection XML node has no \"start\" property") << endmsg;
88 throw failed_constructor();
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
96 throw failed_constructor();
101 if ((prop = node.property ("beats-per-minute")) == 0) {
102 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
103 throw failed_constructor();
106 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
107 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
108 throw failed_constructor();
111 if ((prop = node.property ("note-type")) == 0) {
112 /* older session, make note type be quarter by default */
115 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
116 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
117 throw failed_constructor();
121 if ((prop = node.property ("movable")) == 0) {
122 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
123 throw failed_constructor();
126 set_movable (string_is_affirmative (prop->value()));
128 if ((prop = node.property ("bar-offset")) == 0) {
131 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
132 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
133 throw failed_constructor();
139 TempoSection::get_state() const
141 XMLNode *root = new XMLNode (xml_state_node_name);
143 LocaleGuard lg (X_("POSIX"));
145 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
149 root->add_property ("start", buf);
150 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
151 root->add_property ("beats-per-minute", buf);
152 snprintf (buf, sizeof (buf), "%f", _note_type);
153 root->add_property ("note-type", buf);
154 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
155 // root->add_property ("bar-offset", buf);
156 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
157 root->add_property ("movable", buf);
164 TempoSection::update_bar_offset_from_bbt (const Meter& m)
166 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
167 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
169 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
173 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
177 if (_bar_offset < 0.0) {
182 new_start.bars = start().bars;
184 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
185 new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
186 new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
188 /* remember the 1-based counting properties of beats */
189 new_start.beats += 1;
191 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
192 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
194 set_start (new_start);
197 /***********************************************************************/
199 const string MeterSection::xml_state_node_name = "Meter";
201 MeterSection::MeterSection (const XMLNode& node)
202 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
204 const XMLProperty *prop;
206 LocaleGuard lg (X_("POSIX"));
208 if ((prop = node.property ("start")) == 0) {
209 error << _("MeterSection XML node has no \"start\" property") << endmsg;
210 throw failed_constructor();
213 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
217 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
218 throw failed_constructor();
223 /* beats-per-bar is old; divisions-per-bar is new */
225 if ((prop = node.property ("divisions-per-bar")) == 0) {
226 if ((prop = node.property ("beats-per-bar")) == 0) {
227 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
228 throw failed_constructor();
232 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
233 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
234 throw failed_constructor();
237 if ((prop = node.property ("note-type")) == 0) {
238 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
239 throw failed_constructor();
242 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
243 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
244 throw failed_constructor();
247 if ((prop = node.property ("movable")) == 0) {
248 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
249 throw failed_constructor();
252 set_movable (string_is_affirmative (prop->value()));
256 MeterSection::get_state() const
258 XMLNode *root = new XMLNode (xml_state_node_name);
260 LocaleGuard lg (X_("POSIX"));
262 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
266 root->add_property ("start", buf);
267 snprintf (buf, sizeof (buf), "%f", _note_type);
268 root->add_property ("note-type", buf);
269 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
270 root->add_property ("divisions-per-bar", buf);
271 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
272 root->add_property ("movable", buf);
277 /***********************************************************************/
279 struct MetricSectionSorter {
280 bool operator() (const MetricSection* a, const MetricSection* b) {
281 return a->start() < b->start();
285 TempoMap::TempoMap (framecnt_t fr)
294 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
295 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
297 t->set_movable (false);
298 m->set_movable (false);
300 /* note: frame time is correct (zero) for both of these */
302 metrics.push_back (t);
303 metrics.push_back (m);
306 TempoMap::~TempoMap ()
311 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
313 bool removed = false;
316 Glib::RWLock::WriterLock lm (lock);
319 for (i = metrics.begin(); i != metrics.end(); ++i) {
320 if (dynamic_cast<TempoSection*> (*i) != 0) {
321 if (tempo.frame() == (*i)->frame()) {
322 if ((*i)->movable()) {
331 if (removed && complete_operation) {
332 recompute_map (false);
336 if (removed && complete_operation) {
337 PropertyChanged (PropertyChange ());
342 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
344 bool removed = false;
347 Glib::RWLock::WriterLock lm (lock);
350 for (i = metrics.begin(); i != metrics.end(); ++i) {
351 if (dynamic_cast<MeterSection*> (*i) != 0) {
352 if (tempo.frame() == (*i)->frame()) {
353 if ((*i)->movable()) {
362 if (removed && complete_operation) {
363 recompute_map (true);
367 if (removed && complete_operation) {
368 PropertyChanged (PropertyChange ());
373 TempoMap::do_insert (MetricSection* section)
375 bool need_add = true;
377 assert (section->start().ticks == 0);
379 /* we only allow new meters to be inserted on beat 1 of an existing
383 if (dynamic_cast<MeterSection*>(section)) {
385 /* we need to (potentially) update the BBT times of tempo
386 sections based on this new meter.
389 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
391 BBT_Time corrected = section->start();
395 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
396 section->start(), corrected) << endmsg;
398 section->set_start (corrected);
404 /* Look for any existing MetricSection that is of the same type and
405 in the same bar as the new one, and remove it before adding
406 the new one. Note that this means that if we find a matching,
407 existing section, we can break out of the loop since we're
408 guaranteed that there is only one such match.
411 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
413 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
414 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
416 if (iter_is_tempo && insert_is_tempo) {
420 if ((*i)->start().bars == section->start().bars &&
421 (*i)->start().beats == section->start().beats) {
423 if (!(*i)->movable()) {
425 /* can't (re)move this section, so overwrite
426 * its data content (but not its properties as
430 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
438 } else if (!iter_is_tempo && !insert_is_tempo) {
442 if ((*i)->start().bars == section->start().bars) {
444 if (!(*i)->movable()) {
446 /* can't (re)move this section, so overwrite
447 * its data content (but not its properties as
451 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
461 /* non-matching types, so we don't care */
465 /* Add the given MetricSection, if we didn't just reset an existing
473 for (i = metrics.begin(); i != metrics.end(); ++i) {
474 if ((*i)->start() > section->start()) {
479 metrics.insert (i, section);
484 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
486 const TempoSection& first (first_tempo());
488 if (ts.start() != first.start()) {
489 remove_tempo (ts, false);
490 add_tempo (tempo, where);
493 Glib::RWLock::WriterLock lm (lock);
494 /* cannot move the first tempo section */
495 *((Tempo*)&first) = tempo;
496 recompute_map (false);
500 PropertyChanged (PropertyChange ());
504 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
507 Glib::RWLock::WriterLock lm (lock);
509 /* new tempos always start on a beat */
512 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
514 /* find the meter to use to set the bar offset of this
518 const Meter* meter = &first_meter();
520 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
521 at something, because we insert the default tempo and meter during
522 TempoMap construction.
524 now see if we can find better candidates.
527 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
529 const MeterSection* m;
531 if (where < (*i)->start()) {
535 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
540 ts->update_bar_offset_from_bbt (*meter);
546 recompute_map (false);
550 PropertyChanged (PropertyChange ());
554 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
556 const MeterSection& first (first_meter());
558 if (ms.start() != first.start()) {
559 remove_meter (ms, false);
560 add_meter (meter, where);
563 Glib::RWLock::WriterLock lm (lock);
564 /* cannot move the first meter section */
565 *((Meter*)&first) = meter;
566 recompute_map (true);
570 PropertyChanged (PropertyChange ());
574 TempoMap::add_meter (const Meter& meter, BBT_Time where)
577 Glib::RWLock::WriterLock lm (lock);
579 /* a new meter always starts a new bar on the first beat. so
580 round the start time appropriately. remember that
581 `where' is based on the existing tempo map, not
582 the result after we insert the new meter.
586 if (where.beats != 1) {
591 /* new meters *always* start on a beat. */
594 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
595 recompute_map (true);
600 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
605 PropertyChanged (PropertyChange ());
609 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
611 Tempo newtempo (beats_per_minute, note_type);
614 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
615 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
617 Glib::RWLock::WriterLock lm (lock);
618 *((Tempo*) t) = newtempo;
619 recompute_map (false);
621 PropertyChanged (PropertyChange ());
628 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
630 Tempo newtempo (beats_per_minute, note_type);
636 /* find the TempoSection immediately preceding "where"
639 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
641 if ((*i)->frame() > where) {
647 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
657 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
667 Glib::RWLock::WriterLock lm (lock);
668 /* cannot move the first tempo section */
669 *((Tempo*)prev) = newtempo;
670 recompute_map (false);
673 PropertyChanged (PropertyChange ());
677 TempoMap::first_meter () const
679 const MeterSection *m = 0;
681 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
682 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
687 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
693 TempoMap::first_tempo () const
695 const TempoSection *t = 0;
697 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
698 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
703 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
709 TempoMap::require_map_to (framepos_t pos)
711 Glib::RWLock::WriterLock lm (lock);
713 if (_map.empty() || _map.back().frame < pos) {
719 TempoMap::require_map_to (const BBT_Time& bbt)
721 Glib::RWLock::WriterLock lm (lock);
723 /* since we have no idea where BBT is if its off the map, see the last
724 * point in the map is past BBT, and if not add an arbitrary amount of
728 int additional_minutes = 1;
731 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
734 /* add some more distance, using bigger steps each time */
735 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
736 additional_minutes *= 2;
741 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
743 /* CALLER MUST HOLD WRITE LOCK */
745 MeterSection* meter = 0;
746 TempoSection* tempo = 0;
747 double current_frame;
749 Metrics::iterator next_metric;
754 /* compute 1 mins worth */
755 end = _frame_rate * 60;
757 end = _map.back().frame;
760 if (!_map.empty ()) {
761 /* never allow the map to be shortened */
762 end = max (end, _map.back().frame);
766 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
768 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
771 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
777 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
780 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
786 /* assumes that the first meter & tempo are at frame zero */
788 meter->set_frame (0);
789 tempo->set_frame (0);
791 /* assumes that the first meter & tempo are at 1|1|0 */
796 if (reassign_tempo_bbt) {
798 MeterSection* rmeter = meter;
800 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
802 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
807 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
809 /* reassign the BBT time of this tempo section
810 * based on its bar offset position.
813 ts->update_bbt_time_from_bar_offset (*rmeter);
815 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
818 fatal << _("programming error: unhandled MetricSection type") << endmsg;
824 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
826 next_metric = metrics.begin();
827 ++next_metric; // skip meter (or tempo)
828 ++next_metric; // skip tempo (or meter)
832 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
833 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
836 /* silly call from Session::process() during startup
841 _extend_map (tempo, meter, next_metric, current, current_frame, end);
845 TempoMap::extend_map (framepos_t end)
847 /* CALLER MUST HOLD WRITE LOCK */
850 recompute_map (false, end);
854 BBTPointList::const_iterator i = _map.end();
855 Metrics::iterator next_metric;
859 BBT_Time last_metric_start;
861 if ((*i).tempo->frame() > (*i).meter->frame()) {
862 last_metric_start = (*i).tempo->start();
864 last_metric_start = (*i).meter->start();
867 /* find the metric immediately after the tempo + meter sections for the
868 * last point in the map
871 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
872 if ((*next_metric)->start() > last_metric_start) {
877 /* we cast away const here because this is the one place where we need
878 * to actually modify the frame time of each metric section.
881 _extend_map (const_cast<TempoSection*> ((*i).tempo),
882 const_cast<MeterSection*> ((*i).meter),
883 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
887 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
888 Metrics::iterator next_metric,
889 BBT_Time current, framepos_t current_frame, framepos_t end)
891 /* CALLER MUST HOLD WRITE LOCK */
895 double divisions_per_bar;
897 framepos_t bar_start_frame;
899 if (current.beats == 1) {
900 bar_start_frame = current_frame;
905 divisions_per_bar = meter->divisions_per_bar ();
906 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
908 while (current_frame < end) {
911 current_frame += beat_frames;
913 if (current.beats > meter->divisions_per_bar()) {
918 if (next_metric != metrics.end()) {
920 /* no operator >= so invert operator < */
922 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
924 if (!(current < (*next_metric)->start())) {
927 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
931 /* new tempo section: if its on a beat,
932 * we don't have to do anything other
933 * than recompute various distances,
934 * done further below as we transition
935 * the next metric section.
937 * if its not on the beat, we have to
938 * compute the duration of the beat it
939 * is within, which will be different
940 * from the preceding following ones
941 * since it takes part of its duration
942 * from the preceding tempo and part
943 * from this new tempo.
946 if (tempo->start().ticks != 0) {
948 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
950 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
951 tempo->start(), current_frame, tempo->bar_offset()));
953 /* back up to previous beat */
954 current_frame -= beat_frames;
956 /* set tempo section location
957 * based on offset from last
960 tempo->set_frame (bar_start_frame +
961 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
963 /* advance to the location of
964 * the new (adjusted) beat. do
965 * this by figuring out the
966 * offset within the beat that
967 * would have been there
969 * change. then stretch the
973 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
975 current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
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 divisions_per_bar = meter->divisions_per_bar ();
1007 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1009 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1010 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
1014 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1015 /* 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,(framepos_t) llrint(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, (framepos_t) llrint(current_frame), current.bars, current.beats));
1034 TempoMap::metric_at (framepos_t frame) const
1036 Glib::RWLock::ReaderLock lm (lock);
1037 TempoMetric m (first_meter(), first_tempo());
1041 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1042 at something, because we insert the default tempo and meter during
1043 TempoMap construction.
1045 now see if we can find better candidates.
1048 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1050 if ((*i)->frame() > frame) {
1054 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1055 m.set_tempo (*tempo);
1056 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1057 m.set_meter (*meter);
1060 m.set_frame ((*i)->frame ());
1061 m.set_start ((*i)->start ());
1068 TempoMap::metric_at (BBT_Time bbt) const
1070 Glib::RWLock::ReaderLock lm (lock);
1071 TempoMetric m (first_meter(), first_tempo());
1075 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1076 at something, because we insert the default tempo and meter during
1077 TempoMap construction.
1079 now see if we can find better candidates.
1082 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1084 BBT_Time section_start ((*i)->start());
1086 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1090 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1091 m.set_tempo (*tempo);
1092 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1093 m.set_meter (*meter);
1096 m.set_frame ((*i)->frame ());
1097 m.set_start (section_start);
1104 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1106 require_map_to (frame);
1108 Glib::RWLock::ReaderLock lm (lock);
1114 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1118 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1122 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1124 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1127 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1130 if (_map.empty() || _map.back().frame < frame) {
1131 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1134 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1138 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1140 /* CALLER MUST HOLD READ LOCK */
1142 bbt.bars = (*i).bar;
1143 bbt.beats = (*i).beat;
1145 if ((*i).frame == frame) {
1148 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1149 BBT_Time::ticks_per_beat);
1154 TempoMap::frame_time (const BBT_Time& bbt)
1157 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1161 if (bbt.beats < 1) {
1162 throw std::logic_error ("beats are counted from one");
1165 require_map_to (bbt);
1167 Glib::RWLock::ReaderLock lm (lock);
1169 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1170 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1172 if (bbt.ticks != 0) {
1173 return ((*e).frame - (*s).frame) +
1174 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1176 return ((*e).frame - (*s).frame);
1181 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1183 Glib::RWLock::ReaderLock lm (lock);
1184 framecnt_t frames = 0;
1187 bbt_time (pos, when);
1188 frames = bbt_duration_at_unlocked (when, bbt,dir);
1194 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1196 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1200 /* round back to the previous precise beat */
1201 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1202 BBTPointList::const_iterator start (wi);
1203 double tick_frames = 0;
1205 assert (wi != _map.end());
1207 /* compute how much rounding we did because of non-zero ticks */
1209 if (when.ticks != 0) {
1210 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1216 while (wi != _map.end() && bars < bbt.bars) {
1218 if ((*wi).is_bar()) {
1222 assert (wi != _map.end());
1224 while (wi != _map.end() && beats < bbt.beats) {
1228 assert (wi != _map.end());
1230 /* add any additional frames related to ticks in the added value */
1232 if (bbt.ticks != 0) {
1233 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1236 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1240 TempoMap::round_to_bar (framepos_t fr, int dir)
1242 return round_to_type (fr, dir, Bar);
1246 TempoMap::round_to_beat (framepos_t fr, int dir)
1248 return round_to_type (fr, dir, Beat);
1252 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1254 require_map_to (fr);
1256 Glib::RWLock::ReaderLock lm (lock);
1257 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1259 uint32_t ticks_one_subdivisions_worth;
1260 uint32_t difference;
1262 bbt_time (fr, the_beat, i);
1264 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1265 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1267 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1271 /* round to next (even if we're on a subdivision */
1273 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1276 /* right on the subdivision, so the difference is just the subdivision ticks */
1277 the_beat.ticks += ticks_one_subdivisions_worth;
1280 /* not on subdivision, compute distance to next subdivision */
1282 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1285 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1286 assert (i != _map.end());
1288 assert (i != _map.end());
1289 the_beat.ticks -= BBT_Time::ticks_per_beat;
1293 } else if (dir < 0) {
1295 /* round to previous (even if we're on a subdivision) */
1297 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1300 /* right on the subdivision, so the difference is just the subdivision ticks */
1301 difference = ticks_one_subdivisions_worth;
1303 /* not on subdivision, compute distance to previous subdivision, which
1304 is just the modulus.
1310 if (the_beat.ticks < difference) {
1311 if (i == _map.begin()) {
1312 /* can't go backwards from wherever pos is, so just return it */
1316 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1318 the_beat.ticks -= difference;
1322 /* round to nearest */
1326 /* compute the distance to the previous and next subdivision */
1328 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1330 /* closer to the next subdivision, so shift forward */
1332 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1334 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1336 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1337 assert (i != _map.end());
1339 assert (i != _map.end());
1340 the_beat.ticks -= BBT_Time::ticks_per_beat;
1341 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1344 } else if (rem > 0) {
1346 /* closer to previous subdivision, so shift backward */
1348 if (rem > the_beat.ticks) {
1349 if (i == _map.begin()) {
1350 /* can't go backwards past zero, so ... */
1353 /* step back to previous beat */
1355 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1356 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1358 the_beat.ticks = lrint (the_beat.ticks - rem);
1359 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1362 /* on the subdivision, do nothing */
1366 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1367 (*i).tempo->frames_per_beat (_frame_rate);
1371 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1373 require_map_to (frame);
1375 Glib::RWLock::ReaderLock lm (lock);
1376 BBTPointList::const_iterator fi;
1379 fi = bbt_after_or_at (frame);
1381 fi = bbt_before_or_at (frame);
1384 assert (fi != _map.end());
1386 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,
1387 (type == Bar ? "bar" : "beat")));
1392 /* find bar previous to 'frame' */
1394 if (fi == _map.begin()) {
1398 if ((*fi).is_bar() && (*fi).frame == frame) {
1402 while (!(*fi).is_bar()) {
1403 if (fi == _map.begin()) {
1408 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1409 (*fi).bar, (*fi).beat, (*fi).frame));
1412 } else if (dir > 0) {
1414 /* find bar following 'frame' */
1416 if ((*fi).is_bar() && (*fi).frame == frame) {
1420 while (!(*fi).is_bar()) {
1422 if (fi == _map.end()) {
1428 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1429 (*fi).bar, (*fi).beat, (*fi).frame));
1434 /* true rounding: find nearest bar */
1436 BBTPointList::const_iterator prev = fi;
1437 BBTPointList::const_iterator next = fi;
1439 if ((*fi).frame == frame) {
1443 while ((*prev).beat != 1) {
1444 if (prev == _map.begin()) {
1450 while ((next != _map.end()) && (*next).beat != 1) {
1454 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1455 return (*prev).frame;
1457 return (*next).frame;
1467 if (fi == _map.begin()) {
1471 if ((*fi).frame >= frame) {
1472 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1475 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1476 (*fi).bar, (*fi).beat, (*fi).frame));
1478 } else if (dir > 0) {
1479 if ((*fi).frame <= frame) {
1480 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1483 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1484 (*fi).bar, (*fi).beat, (*fi).frame));
1487 /* find beat nearest to frame */
1488 if ((*fi).frame == frame) {
1492 BBTPointList::const_iterator prev = fi;
1493 BBTPointList::const_iterator next = fi;
1495 /* fi is already the beat before_or_at frame, and
1496 we've just established that its not at frame, so its
1497 the beat before frame.
1501 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1502 return (*prev).frame;
1504 return (*next).frame;
1516 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1517 TempoMap::BBTPointList::const_iterator& end,
1518 framepos_t lower, framepos_t upper)
1521 Glib::RWLock::WriterLock lm (lock);
1522 if (_map.empty() || (_map.back().frame < upper)) {
1523 recompute_map (false, upper);
1527 begin = lower_bound (_map.begin(), _map.end(), lower);
1528 end = upper_bound (_map.begin(), _map.end(), upper);
1532 TempoMap::tempo_section_at (framepos_t frame) const
1534 Glib::RWLock::ReaderLock lm (lock);
1535 Metrics::const_iterator i;
1536 TempoSection* prev = 0;
1538 for (i = metrics.begin(); i != metrics.end(); ++i) {
1541 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1543 if ((*i)->frame() > frame) {
1559 TempoMap::tempo_at (framepos_t frame) const
1561 TempoMetric m (metric_at (frame));
1567 TempoMap::meter_at (framepos_t frame) const
1569 TempoMetric m (metric_at (frame));
1574 TempoMap::get_state ()
1576 Metrics::const_iterator i;
1577 XMLNode *root = new XMLNode ("TempoMap");
1580 Glib::RWLock::ReaderLock lm (lock);
1581 for (i = metrics.begin(); i != metrics.end(); ++i) {
1582 root->add_child_nocopy ((*i)->get_state());
1590 TempoMap::set_state (const XMLNode& node, int /*version*/)
1593 Glib::RWLock::WriterLock lm (lock);
1596 XMLNodeConstIterator niter;
1597 Metrics old_metrics (metrics);
1598 MeterSection* last_meter = 0;
1602 nlist = node.children();
1604 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1605 XMLNode* child = *niter;
1607 if (child->name() == TempoSection::xml_state_node_name) {
1610 TempoSection* ts = new TempoSection (*child);
1611 metrics.push_back (ts);
1613 if (ts->bar_offset() < 0.0) {
1615 ts->update_bar_offset_from_bbt (*last_meter);
1620 catch (failed_constructor& err){
1621 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1622 metrics = old_metrics;
1626 } else if (child->name() == MeterSection::xml_state_node_name) {
1629 MeterSection* ms = new MeterSection (*child);
1630 metrics.push_back (ms);
1634 catch (failed_constructor& err) {
1635 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1636 metrics = old_metrics;
1642 if (niter == nlist.end()) {
1643 MetricSectionSorter cmp;
1647 recompute_map (true);
1650 PropertyChanged (PropertyChange ());
1656 TempoMap::dump (std::ostream& o) const
1658 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1659 const MeterSection* m;
1660 const TempoSection* t;
1662 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1664 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1665 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? "
1666 << t->movable() << ')' << endl;
1667 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1668 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1669 << " (movable? " << m->movable() << ')' << endl;
1675 TempoMap::n_tempos() const
1677 Glib::RWLock::ReaderLock lm (lock);
1680 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1681 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1690 TempoMap::n_meters() const
1692 Glib::RWLock::ReaderLock lm (lock);
1695 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1696 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1705 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1708 Glib::RWLock::WriterLock lm (lock);
1709 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1710 if ((*i)->frame() >= where && (*i)->movable ()) {
1711 (*i)->set_frame ((*i)->frame() + amount);
1715 /* now reset the BBT time of all metrics, based on their new
1716 * audio time. This is the only place where we do this reverse
1720 Metrics::iterator i;
1721 const MeterSection* meter;
1722 const TempoSection* tempo;
1726 meter = &first_meter ();
1727 tempo = &first_tempo ();
1732 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1735 MetricSection* prev = 0;
1737 for (i = metrics.begin(); i != metrics.end(); ++i) {
1740 TempoMetric metric (*meter, *tempo);
1743 metric.set_start (prev->start());
1744 metric.set_frame (prev->frame());
1746 // metric will be at frames=0 bbt=1|1|0 by default
1747 // which is correct for our purpose
1750 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1751 bbt_time ((*i)->frame(), bbt, bi);
1753 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1759 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1760 /* round up to next beat */
1766 if (bbt.beats != 1) {
1767 /* round up to next bar */
1773 // cerr << bbt << endl;
1775 (*i)->set_start (bbt);
1777 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1779 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1780 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1782 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1784 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1791 recompute_map (true);
1795 PropertyChanged (PropertyChange ());
1798 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1799 * pos can be -ve, if required.
1802 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1804 Glib::RWLock::ReaderLock lm (lock);
1805 Metrics::const_iterator next_tempo;
1806 const TempoSection* tempo;
1808 /* Find the starting tempo metric */
1810 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1812 const TempoSection* t;
1814 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1816 /* This is a bit of a hack, but pos could be -ve, and if it is,
1817 we consider the initial metric changes (at time 0) to actually
1818 be in effect at pos.
1821 framepos_t f = (*next_tempo)->frame ();
1823 if (pos < 0 && f == 0) {
1837 tempo -> the Tempo for "pos"
1838 next_tempo -> first tempo after "pos", possibly metrics.end()
1841 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1842 pos, beats, *((Tempo*)tempo), tempo->frame()));
1846 /* Distance to the end of this section in frames */
1847 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1849 /* Distance to the end in beats */
1850 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1852 /* Amount to subtract this time */
1853 double const delta = min (distance_beats, beats);
1855 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1856 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1857 distance_frames, distance_beats));
1861 pos += delta * tempo->frames_per_beat (_frame_rate);
1863 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1865 /* step forwards to next tempo section */
1867 if (next_tempo != metrics.end()) {
1869 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1871 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1872 *((Tempo*)tempo), tempo->frame(),
1873 tempo->frames_per_beat (_frame_rate)));
1875 while (next_tempo != metrics.end ()) {
1879 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1889 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1891 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1893 Glib::RWLock::ReaderLock lm (lock);
1894 Metrics::const_reverse_iterator prev_tempo;
1895 const TempoSection* tempo = 0;
1897 /* Find the starting tempo metric */
1899 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1901 const TempoSection* t;
1903 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1905 /* This is a bit of a hack, but pos could be -ve, and if it is,
1906 we consider the initial metric changes (at time 0) to actually
1907 be in effect at pos.
1910 framepos_t f = (*prev_tempo)->frame ();
1912 if (pos < 0 && f == 0) {
1916 /* this is slightly more complex than the forward case
1917 because we reach the tempo in effect at pos after
1918 passing through pos (rather before, as in the
1919 forward case). having done that, we then need to
1920 keep going to get the previous tempo (or
1926 /* first tempo with position at or
1930 } else if (f < pos) {
1931 /* some other tempo section that
1932 is even earlier than 'tempo'
1940 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1941 pos, beats, *((Tempo*)tempo), tempo->frame(),
1942 prev_tempo == metrics.rend()));
1946 tempo -> the Tempo for "pos"
1947 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1952 /* Distance to the start of this section in frames */
1953 framecnt_t distance_frames = (pos - tempo->frame());
1955 /* Distance to the start in beats */
1956 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1958 /* Amount to subtract this time */
1959 double const sub = min (distance_beats, beats);
1961 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1962 tempo->frame(), distance_frames, distance_beats));
1966 pos -= sub * tempo->frames_per_beat (_frame_rate);
1968 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1969 (prev_tempo == metrics.rend())));
1971 /* step backwards to prior TempoSection */
1973 if (prev_tempo != metrics.rend()) {
1975 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1977 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1978 *((Tempo*)tempo), tempo->frame(),
1979 tempo->frames_per_beat (_frame_rate)));
1981 while (prev_tempo != metrics.rend ()) {
1985 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1990 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
1998 /** Add the BBT interval op to pos and return the result */
2000 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2002 Glib::RWLock::ReaderLock lm (lock);
2003 Metrics::const_iterator i;
2004 const MeterSection* meter;
2005 const MeterSection* m;
2006 const TempoSection* tempo;
2007 const TempoSection* t;
2008 double frames_per_beat;
2010 meter = &first_meter ();
2011 tempo = &first_tempo ();
2016 /* find the starting metrics for tempo & meter */
2018 for (i = metrics.begin(); i != metrics.end(); ++i) {
2020 if ((*i)->frame() > pos) {
2024 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2026 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2033 meter -> the Meter for "pos"
2034 tempo -> the Tempo for "pos"
2035 i -> for first new metric after "pos", possibly metrics.end()
2038 /* now comes the complicated part. we have to add one beat a time,
2039 checking for a new metric on every beat.
2042 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2051 /* check if we need to use a new metric section: has adding frames moved us
2052 to or after the start of the next metric section? in which case, use it.
2055 if (i != metrics.end()) {
2056 if ((*i)->frame() <= pos) {
2058 /* about to change tempo or meter, so add the
2059 * number of frames for the bars we've just
2060 * traversed before we change the
2061 * frames_per_beat value.
2064 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2067 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2069 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2073 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2080 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2086 /* given the current meter, have we gone past the end of the bar ? */
2091 /* check if we need to use a new metric section: has adding frames moved us
2092 to or after the start of the next metric section? in which case, use it.
2095 if (i != metrics.end()) {
2096 if ((*i)->frame() <= pos) {
2098 /* about to change tempo or meter, so add the
2099 * number of frames for the beats we've just
2100 * traversed before we change the
2101 * frames_per_beat value.
2104 pos += llrint (beats * frames_per_beat);
2107 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2109 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2113 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2118 pos += llrint (beats * frames_per_beat);
2121 if (op.ticks >= BBT_Time::ticks_per_beat) {
2122 pos += llrint (frames_per_beat + /* extra beat */
2123 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2124 (double) BBT_Time::ticks_per_beat)));
2126 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2133 /** Count the number of beats that are equivalent to distance when going forward,
2137 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2139 Glib::RWLock::ReaderLock lm (lock);
2140 Metrics::const_iterator next_tempo;
2141 const TempoSection* tempo;
2143 /* Find the relevant initial tempo metric */
2145 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2147 const TempoSection* t;
2149 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2151 if ((*next_tempo)->frame() > pos) {
2161 tempo -> the Tempo for "pos"
2162 next_tempo -> the next tempo after "pos", possibly metrics.end()
2165 Evoral::MusicalTime beats = 0;
2169 /* End of this section */
2170 framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
2172 /* Distance to the end in frames */
2173 framecnt_t const distance_to_end = end - pos;
2175 /* Amount to subtract this time */
2176 double const sub = min (distance, distance_to_end);
2181 beats += sub / tempo->frames_per_beat (_frame_rate);
2183 /* Move on if there's anything to move to */
2185 if (next_tempo != metrics.end()) {
2187 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2189 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2190 *((Tempo*)tempo), tempo->frame(),
2191 tempo->frames_per_beat (_frame_rate)));
2193 while (next_tempo != metrics.end ()) {
2197 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2207 TempoMap::BBTPointList::const_iterator
2208 TempoMap::bbt_before_or_at (framepos_t pos)
2210 /* CALLER MUST HOLD READ LOCK */
2212 BBTPointList::const_iterator i;
2215 /* not really correct, but we should catch pos < 0 at a higher
2218 return _map.begin();
2221 i = lower_bound (_map.begin(), _map.end(), pos);
2222 assert (i != _map.end());
2223 if ((*i).frame > pos) {
2224 assert (i != _map.begin());
2231 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2236 TempoMap::BBTPointList::const_iterator
2237 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2239 BBTPointList::const_iterator i;
2242 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2243 assert (i != _map.end());
2244 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2245 assert (i != _map.begin());
2251 TempoMap::BBTPointList::const_iterator
2252 TempoMap::bbt_after_or_at (framepos_t pos)
2254 /* CALLER MUST HOLD READ LOCK */
2256 BBTPointList::const_iterator i;
2258 if (_map.back().frame == pos) {
2260 assert (i != _map.begin());
2265 i = upper_bound (_map.begin(), _map.end(), pos);
2266 assert (i != _map.end());
2271 operator<< (std::ostream& o, const Meter& m) {
2272 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2276 operator<< (std::ostream& o, const Tempo& t) {
2277 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2281 operator<< (std::ostream& o, const MetricSection& section) {
2283 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2285 const TempoSection* ts;
2286 const MeterSection* ms;
2288 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2289 o << *((Tempo*) ts);
2290 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2291 o << *((Meter*) ms);