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 = (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 at the same time as the new one, and remove it before adding
409 Metrics::iterator to_remove = metrics.end ();
411 for (i = metrics.begin(); i != metrics.end(); ++i) {
413 int const c = (*i)->compare (*section);
416 /* this section is before the one to be added; go back round */
419 /* this section is after the one to be added; there can't be any at the same time */
423 /* hacky comparison of type */
424 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
425 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
427 if (iter_is_tempo == insert_is_tempo) {
429 if (!(*i)->movable()) {
431 /* can't (re)move this section, so overwrite
432 * its data content (but not its properties as
436 if (!iter_is_tempo) {
437 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
439 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
450 if (to_remove != metrics.end()) {
451 /* remove the MetricSection at the same time as the one we are about to add */
452 metrics.erase (to_remove);
455 /* Add the given MetricSection */
458 for (i = metrics.begin(); i != metrics.end(); ++i) {
460 if ((*i)->compare (*section) < 0) {
464 metrics.insert (i, section);
468 if (i == metrics.end()) {
469 metrics.insert (metrics.end(), section);
475 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
477 const TempoSection& first (first_tempo());
480 remove_tempo (ts, false);
481 add_tempo (tempo, where);
484 Glib::RWLock::WriterLock lm (lock);
485 /* cannot move the first tempo section */
486 *((Tempo*)&first) = tempo;
487 recompute_map (false);
491 PropertyChanged (PropertyChange ());
495 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
498 Glib::RWLock::WriterLock lm (lock);
500 /* new tempos always start on a beat */
503 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
505 /* find the meter to use to set the bar offset of this
509 const Meter* meter = &first_meter();
511 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
512 at something, because we insert the default tempo and meter during
513 TempoMap construction.
515 now see if we can find better candidates.
518 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
520 const MeterSection* m;
522 if (where < (*i)->start()) {
526 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
531 ts->update_bar_offset_from_bbt (*meter);
537 recompute_map (false);
541 PropertyChanged (PropertyChange ());
545 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
547 const MeterSection& first (first_meter());
550 remove_meter (ms, false);
551 add_meter (meter, where);
554 Glib::RWLock::WriterLock lm (lock);
555 /* cannot move the first meter section */
556 *((Meter*)&first) = meter;
557 recompute_map (true);
561 PropertyChanged (PropertyChange ());
565 TempoMap::add_meter (const Meter& meter, BBT_Time where)
568 Glib::RWLock::WriterLock lm (lock);
570 /* a new meter always starts a new bar on the first beat. so
571 round the start time appropriately. remember that
572 `where' is based on the existing tempo map, not
573 the result after we insert the new meter.
577 if (where.beats != 1) {
582 /* new meters *always* start on a beat. */
585 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
586 recompute_map (true);
591 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
596 PropertyChanged (PropertyChange ());
600 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
602 Tempo newtempo (beats_per_minute, note_type);
605 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
606 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
608 Glib::RWLock::WriterLock lm (lock);
609 *((Tempo*) t) = newtempo;
610 recompute_map (false);
612 PropertyChanged (PropertyChange ());
619 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
621 Tempo newtempo (beats_per_minute, note_type);
627 /* find the TempoSection immediately preceding "where"
630 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
632 if ((*i)->frame() > where) {
638 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
648 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
658 Glib::RWLock::WriterLock lm (lock);
659 /* cannot move the first tempo section */
660 *((Tempo*)prev) = newtempo;
661 recompute_map (false);
664 PropertyChanged (PropertyChange ());
668 TempoMap::first_meter () const
670 const MeterSection *m = 0;
672 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
673 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
678 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
684 TempoMap::first_tempo () const
686 const TempoSection *t = 0;
688 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
689 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
694 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
700 TempoMap::require_map_to (framepos_t pos)
702 Glib::RWLock::WriterLock lm (lock);
704 if (_map.empty() || _map.back().frame < pos) {
710 TempoMap::require_map_to (const BBT_Time& bbt)
712 Glib::RWLock::WriterLock lm (lock);
714 /* since we have no idea where BBT is if its off the map, see the last
715 * point in the map is past BBT, and if not add an arbitrary amount of
719 int additional_minutes = 1;
722 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
725 /* add some more distance, using bigger steps each time */
726 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
727 additional_minutes *= 2;
732 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
734 /* CALLER MUST HOLD WRITE LOCK */
736 MeterSection* meter = 0;
737 TempoSection* tempo = 0;
738 double current_frame;
740 Metrics::iterator next_metric;
745 /* compute 1 mins worth */
746 end = _frame_rate * 60;
748 end = _map.back().frame;
751 if (!_map.empty ()) {
752 /* never allow the map to be shortened */
753 end = max (end, _map.back().frame);
757 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
759 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
762 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
768 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
771 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
777 /* assumes that the first meter & tempo are at frame zero */
779 meter->set_frame (0);
780 tempo->set_frame (0);
782 /* assumes that the first meter & tempo are at 1|1|0 */
787 if (reassign_tempo_bbt) {
789 MeterSection* rmeter = meter;
791 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
793 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
798 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
800 /* reassign the BBT time of this tempo section
801 * based on its bar offset position.
804 ts->update_bbt_time_from_bar_offset (*rmeter);
806 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
809 fatal << _("programming error: unhandled MetricSection type") << endmsg;
815 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
817 next_metric = metrics.begin();
818 ++next_metric; // skip meter (or tempo)
819 ++next_metric; // skip tempo (or meter)
823 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
824 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
827 /* silly call from Session::process() during startup
832 _extend_map (tempo, meter, next_metric, current, current_frame, end);
836 TempoMap::extend_map (framepos_t end)
838 /* CALLER MUST HOLD WRITE LOCK */
841 recompute_map (false, end);
845 BBTPointList::const_iterator i = _map.end();
846 Metrics::iterator next_metric;
850 BBT_Time last_metric_start;
852 if ((*i).tempo->frame() > (*i).meter->frame()) {
853 last_metric_start = (*i).tempo->start();
855 last_metric_start = (*i).meter->start();
858 /* find the metric immediately after the tempo + meter sections for the
859 * last point in the map
862 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
863 if ((*next_metric)->start() > last_metric_start) {
868 /* we cast away const here because this is the one place where we need
869 * to actually modify the frame time of each metric section.
872 _extend_map (const_cast<TempoSection*> ((*i).tempo),
873 const_cast<MeterSection*> ((*i).meter),
874 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
878 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
879 Metrics::iterator next_metric,
880 BBT_Time current, framepos_t current_frame, framepos_t end)
882 /* CALLER MUST HOLD WRITE LOCK */
886 double divisions_per_bar;
888 framepos_t bar_start_frame;
890 if (current.beats == 1) {
891 bar_start_frame = current_frame;
896 divisions_per_bar = meter->divisions_per_bar ();
897 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
899 while (current_frame < end) {
902 current_frame += beat_frames;
904 if (current.beats > meter->divisions_per_bar()) {
909 if (next_metric != metrics.end()) {
911 /* no operator >= so invert operator < */
913 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
915 if (!(current < (*next_metric)->start())) {
918 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
922 /* new tempo section: if its on a beat,
923 * we don't have to do anything other
924 * than recompute various distances,
925 * done further below as we transition
926 * the next metric section.
928 * if its not on the beat, we have to
929 * compute the duration of the beat it
930 * is within, which will be different
931 * from the preceding following ones
932 * since it takes part of its duration
933 * from the preceding tempo and part
934 * from this new tempo.
937 if (tempo->start().ticks != 0) {
939 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
941 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
942 tempo->start(), current_frame, tempo->bar_offset()));
944 /* back up to previous beat */
945 current_frame -= beat_frames;
947 /* set tempo section location
948 * based on offset from last
951 tempo->set_frame (bar_start_frame +
952 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
954 /* advance to the location of
955 * the new (adjusted) beat. do
956 * this by figuring out the
957 * offset within the beat that
958 * would have been there
960 * change. then stretch the
964 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
966 current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
968 /* next metric doesn't have to
969 * match this precisely to
972 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
976 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
977 tempo->start(), current_frame));
978 tempo->set_frame (current_frame);
981 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
985 /* new meter section: always defines the
989 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
990 meter->start(), current, current_frame));
992 assert (current.beats == 1);
994 meter->set_frame (current_frame);
997 divisions_per_bar = meter->divisions_per_bar ();
998 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1000 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1001 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
1005 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1006 /* same position so go back and set this one up before advancing
1013 if (current.beats == 1) {
1014 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1015 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
1016 bar_start_frame = current_frame;
1018 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1019 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
1025 TempoMap::metric_at (framepos_t frame) const
1027 Glib::RWLock::ReaderLock lm (lock);
1028 TempoMetric m (first_meter(), first_tempo());
1032 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1033 at something, because we insert the default tempo and meter during
1034 TempoMap construction.
1036 now see if we can find better candidates.
1039 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1041 // cerr << "Looking at a metric section " << **i << endl;
1043 if ((*i)->frame() > frame) {
1047 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1048 m.set_tempo (*tempo);
1049 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1050 m.set_meter (*meter);
1053 m.set_frame ((*i)->frame ());
1054 m.set_start ((*i)->start ());
1057 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1062 TempoMap::metric_at (BBT_Time bbt) const
1064 Glib::RWLock::ReaderLock lm (lock);
1065 TempoMetric m (first_meter(), first_tempo());
1069 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1070 at something, because we insert the default tempo and meter during
1071 TempoMap construction.
1073 now see if we can find better candidates.
1076 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1078 BBT_Time section_start ((*i)->start());
1080 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1084 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1085 m.set_tempo (*tempo);
1086 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1087 m.set_meter (*meter);
1090 m.set_frame ((*i)->frame ());
1091 m.set_start (section_start);
1098 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1100 require_map_to (frame);
1102 Glib::RWLock::ReaderLock lm (lock);
1103 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1107 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1109 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1112 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1115 if (_map.empty() || _map.back().frame < frame) {
1116 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1119 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1123 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1125 /* CALLER MUST HOLD READ LOCK */
1127 bbt.bars = (*i).bar;
1128 bbt.beats = (*i).beat;
1130 if ((*i).frame == frame) {
1133 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1134 BBT_Time::ticks_per_beat);
1139 TempoMap::frame_time (const BBT_Time& bbt)
1141 require_map_to (bbt);
1143 Glib::RWLock::ReaderLock lm (lock);
1145 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1146 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1148 if (bbt.ticks != 0) {
1149 return ((*e).frame - (*s).frame) +
1150 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1152 return ((*e).frame - (*s).frame);
1157 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1159 Glib::RWLock::ReaderLock lm (lock);
1160 framecnt_t frames = 0;
1163 bbt_time (pos, when);
1164 frames = bbt_duration_at_unlocked (when, bbt,dir);
1170 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1172 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1176 /* round back to the previous precise beat */
1177 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1178 BBTPointList::const_iterator start (wi);
1179 double tick_frames = 0;
1181 assert (wi != _map.end());
1183 /* compute how much rounding we did because of non-zero ticks */
1185 if (when.ticks != 0) {
1186 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1192 while (wi != _map.end() && bars < bbt.bars) {
1194 if ((*wi).is_bar()) {
1198 assert (wi != _map.end());
1200 while (wi != _map.end() && beats < bbt.beats) {
1204 assert (wi != _map.end());
1206 /* add any additional frames related to ticks in the added value */
1208 if (bbt.ticks != 0) {
1209 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1212 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1216 TempoMap::round_to_bar (framepos_t fr, int dir)
1218 return round_to_type (fr, dir, Bar);
1222 TempoMap::round_to_beat (framepos_t fr, int dir)
1224 return round_to_type (fr, dir, Beat);
1228 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1230 require_map_to (fr);
1232 Glib::RWLock::ReaderLock lm (lock);
1233 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1235 uint32_t ticks_one_subdivisions_worth;
1236 uint32_t difference;
1238 bbt_time (fr, the_beat, i);
1240 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1241 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1243 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1247 /* round to next (even if we're on a subdivision */
1249 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1252 /* right on the subdivision, so the difference is just the subdivision ticks */
1253 the_beat.ticks += ticks_one_subdivisions_worth;
1256 /* not on subdivision, compute distance to next subdivision */
1258 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1261 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1262 assert (i != _map.end());
1264 assert (i != _map.end());
1265 the_beat.ticks -= BBT_Time::ticks_per_beat;
1269 } else if (dir < 0) {
1271 /* round to previous (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 difference = ticks_one_subdivisions_worth;
1279 /* not on subdivision, compute distance to previous subdivision, which
1280 is just the modulus.
1286 if (the_beat.ticks < difference) {
1287 if (i == _map.begin()) {
1288 /* can't go backwards from wherever pos is, so just return it */
1292 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1294 the_beat.ticks -= difference;
1298 /* round to nearest */
1302 /* compute the distance to the previous and next subdivision */
1304 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1306 /* closer to the next subdivision, so shift forward */
1308 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1310 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1312 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1313 assert (i != _map.end());
1315 assert (i != _map.end());
1316 the_beat.ticks -= BBT_Time::ticks_per_beat;
1317 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1320 } else if (rem > 0) {
1322 /* closer to previous subdivision, so shift backward */
1324 if (rem > the_beat.ticks) {
1325 if (i == _map.begin()) {
1326 /* can't go backwards past zero, so ... */
1329 /* step back to previous beat */
1331 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1332 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1334 the_beat.ticks = lrint (the_beat.ticks - rem);
1335 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1338 /* on the subdivision, do nothing */
1342 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1343 (*i).tempo->frames_per_beat (_frame_rate);
1347 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1349 require_map_to (frame);
1351 Glib::RWLock::ReaderLock lm (lock);
1352 BBTPointList::const_iterator fi;
1355 fi = bbt_after_or_at (frame);
1357 fi = bbt_before_or_at (frame);
1360 assert (fi != _map.end());
1362 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to bars in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame));
1367 /* find bar previous to 'frame' */
1369 if (fi == _map.begin()) {
1373 if ((*fi).is_bar() && (*fi).frame == frame) {
1377 while (!(*fi).is_bar()) {
1378 if (fi == _map.begin()) {
1383 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1384 (*fi).bar, (*fi).beat, (*fi).frame));
1387 } else if (dir > 0) {
1389 /* find bar following 'frame' */
1391 if ((*fi).is_bar() && (*fi).frame == frame) {
1395 while (!(*fi).is_bar()) {
1397 if (fi == _map.end()) {
1403 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1404 (*fi).bar, (*fi).beat, (*fi).frame));
1409 /* true rounding: find nearest bar */
1411 BBTPointList::const_iterator prev = fi;
1412 BBTPointList::const_iterator next = fi;
1414 if ((*fi).frame == frame) {
1418 while ((*prev).beat != 1) {
1419 if (prev == _map.begin()) {
1425 while ((next != _map.end()) && (*next).beat != 1) {
1429 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1430 return (*prev).frame;
1432 return (*next).frame;
1442 if (fi == _map.begin()) {
1446 if ((*fi).frame >= frame) {
1447 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1450 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1451 (*fi).bar, (*fi).beat, (*fi).frame));
1453 } else if (dir > 0) {
1454 if ((*fi).frame <= frame) {
1455 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1458 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1459 (*fi).bar, (*fi).beat, (*fi).frame));
1462 /* find beat nearest to frame */
1463 if ((*fi).frame == frame) {
1467 BBTPointList::const_iterator prev = fi;
1468 BBTPointList::const_iterator next = fi;
1469 if (prev != _map.begin()) {
1474 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1475 return (*prev).frame;
1477 return (*next).frame;
1489 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1490 TempoMap::BBTPointList::const_iterator& end,
1491 framepos_t lower, framepos_t upper)
1494 Glib::RWLock::WriterLock lm (lock);
1495 if (_map.empty() || (_map.back().frame < upper)) {
1496 recompute_map (false, upper);
1500 begin = lower_bound (_map.begin(), _map.end(), lower);
1501 end = upper_bound (_map.begin(), _map.end(), upper);
1505 TempoMap::tempo_section_at (framepos_t frame) const
1507 Glib::RWLock::ReaderLock lm (lock);
1508 Metrics::const_iterator i;
1509 TempoSection* prev = 0;
1511 for (i = metrics.begin(); i != metrics.end(); ++i) {
1514 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1516 if ((*i)->frame() > frame) {
1532 TempoMap::tempo_at (framepos_t frame) const
1534 TempoMetric m (metric_at (frame));
1540 TempoMap::meter_at (framepos_t frame) const
1542 TempoMetric m (metric_at (frame));
1547 TempoMap::get_state ()
1549 Metrics::const_iterator i;
1550 XMLNode *root = new XMLNode ("TempoMap");
1553 Glib::RWLock::ReaderLock lm (lock);
1554 for (i = metrics.begin(); i != metrics.end(); ++i) {
1555 root->add_child_nocopy ((*i)->get_state());
1563 TempoMap::set_state (const XMLNode& node, int /*version*/)
1566 Glib::RWLock::WriterLock lm (lock);
1569 XMLNodeConstIterator niter;
1570 Metrics old_metrics (metrics);
1571 MeterSection* last_meter = 0;
1575 nlist = node.children();
1577 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1578 XMLNode* child = *niter;
1580 if (child->name() == TempoSection::xml_state_node_name) {
1583 TempoSection* ts = new TempoSection (*child);
1584 metrics.push_back (ts);
1586 if (ts->bar_offset() < 0.0) {
1588 ts->update_bar_offset_from_bbt (*last_meter);
1593 catch (failed_constructor& err){
1594 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1595 metrics = old_metrics;
1599 } else if (child->name() == MeterSection::xml_state_node_name) {
1602 MeterSection* ms = new MeterSection (*child);
1603 metrics.push_back (ms);
1607 catch (failed_constructor& err) {
1608 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1609 metrics = old_metrics;
1615 if (niter == nlist.end()) {
1616 MetricSectionSorter cmp;
1620 recompute_map (true);
1623 PropertyChanged (PropertyChange ());
1629 TempoMap::dump (std::ostream& o) const
1631 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1632 const MeterSection* m;
1633 const TempoSection* t;
1635 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1637 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1638 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? "
1639 << t->movable() << ')' << endl;
1640 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1641 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1642 << " (movable? " << m->movable() << ')' << endl;
1648 TempoMap::n_tempos() const
1650 Glib::RWLock::ReaderLock lm (lock);
1653 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1654 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1663 TempoMap::n_meters() const
1665 Glib::RWLock::ReaderLock lm (lock);
1668 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1669 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1678 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1681 Glib::RWLock::WriterLock lm (lock);
1682 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1683 if ((*i)->frame() >= where && (*i)->movable ()) {
1684 (*i)->set_frame ((*i)->frame() + amount);
1688 /* now reset the BBT time of all metrics, based on their new
1689 * audio time. This is the only place where we do this reverse
1693 Metrics::iterator i;
1694 const MeterSection* meter;
1695 const TempoSection* tempo;
1699 meter = &first_meter ();
1700 tempo = &first_tempo ();
1705 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1708 MetricSection* prev = 0;
1710 for (i = metrics.begin(); i != metrics.end(); ++i) {
1713 TempoMetric metric (*meter, *tempo);
1716 metric.set_start (prev->start());
1717 metric.set_frame (prev->frame());
1719 // metric will be at frames=0 bbt=1|1|0 by default
1720 // which is correct for our purpose
1723 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1724 bbt_time ((*i)->frame(), bbt, bi);
1726 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1732 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1733 /* round up to next beat */
1739 if (bbt.beats != 1) {
1740 /* round up to next bar */
1746 // cerr << bbt << endl;
1748 (*i)->set_start (bbt);
1750 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1752 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1753 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1755 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1757 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1764 recompute_map (true);
1768 PropertyChanged (PropertyChange ());
1771 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1772 * pos can be -ve, if required.
1775 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1777 Glib::RWLock::ReaderLock lm (lock);
1778 Metrics::const_iterator next_tempo;
1779 const TempoSection* tempo;
1781 /* Find the starting tempo metric */
1783 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1785 const TempoSection* t;
1787 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1789 /* This is a bit of a hack, but pos could be -ve, and if it is,
1790 we consider the initial metric changes (at time 0) to actually
1791 be in effect at pos.
1794 framepos_t f = (*next_tempo)->frame ();
1796 if (pos < 0 && f == 0) {
1810 tempo -> the Tempo for "pos"
1811 next_tempo -> first tempo after "pos", possibly metrics.end()
1814 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1815 pos, beats, *((Tempo*)tempo), tempo->frame()));
1819 /* Distance to the end of this section in frames */
1820 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1822 /* Distance to the end in beats */
1823 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1825 /* Amount to subtract this time */
1826 double const delta = min (distance_beats, beats);
1828 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1829 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1830 distance_frames, distance_beats));
1834 pos += delta * tempo->frames_per_beat (_frame_rate);
1836 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1838 /* step forwards to next tempo section */
1840 if (next_tempo != metrics.end()) {
1842 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1844 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1845 *((Tempo*)tempo), tempo->frame(),
1846 tempo->frames_per_beat (_frame_rate)));
1848 while (next_tempo != metrics.end ()) {
1852 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1862 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1864 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1866 Glib::RWLock::ReaderLock lm (lock);
1867 Metrics::const_reverse_iterator prev_tempo;
1868 const TempoSection* tempo = 0;
1870 /* Find the starting tempo metric */
1872 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1874 const TempoSection* t;
1876 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1878 /* This is a bit of a hack, but pos could be -ve, and if it is,
1879 we consider the initial metric changes (at time 0) to actually
1880 be in effect at pos.
1883 framepos_t f = (*prev_tempo)->frame ();
1885 if (pos < 0 && f == 0) {
1889 /* this is slightly more complex than the forward case
1890 because we reach the tempo in effect at pos after
1891 passing through pos (rather before, as in the
1892 forward case). having done that, we then need to
1893 keep going to get the previous tempo (or
1899 /* first tempo with position at or
1903 } else if (f < pos) {
1904 /* some other tempo section that
1905 is even earlier than 'tempo'
1913 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1914 pos, beats, *((Tempo*)tempo), tempo->frame(),
1915 prev_tempo == metrics.rend()));
1919 tempo -> the Tempo for "pos"
1920 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1925 /* Distance to the start of this section in frames */
1926 framecnt_t distance_frames = (pos - tempo->frame());
1928 /* Distance to the start in beats */
1929 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1931 /* Amount to subtract this time */
1932 double const sub = min (distance_beats, beats);
1934 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1935 tempo->frame(), distance_frames, distance_beats));
1939 pos -= sub * tempo->frames_per_beat (_frame_rate);
1941 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1942 (prev_tempo == metrics.rend())));
1944 /* step backwards to prior TempoSection */
1946 if (prev_tempo != metrics.rend()) {
1948 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1950 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1951 *((Tempo*)tempo), tempo->frame(),
1952 tempo->frames_per_beat (_frame_rate)));
1954 while (prev_tempo != metrics.rend ()) {
1958 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1963 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
1971 /** Add the BBT interval op to pos and return the result */
1973 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
1975 Glib::RWLock::ReaderLock lm (lock);
1976 Metrics::const_iterator i;
1977 const MeterSection* meter;
1978 const MeterSection* m;
1979 const TempoSection* tempo;
1980 const TempoSection* t;
1981 double frames_per_beat;
1983 meter = &first_meter ();
1984 tempo = &first_tempo ();
1989 /* find the starting metrics for tempo & meter */
1991 for (i = metrics.begin(); i != metrics.end(); ++i) {
1993 if ((*i)->frame() > pos) {
1997 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1999 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2006 meter -> the Meter for "pos"
2007 tempo -> the Tempo for "pos"
2008 i -> for first new metric after "pos", possibly metrics.end()
2011 /* now comes the complicated part. we have to add one beat a time,
2012 checking for a new metric on every beat.
2015 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2024 /* check if we need to use a new metric section: has adding frames moved us
2025 to or after the start of the next metric section? in which case, use it.
2028 if (i != metrics.end()) {
2029 if ((*i)->frame() <= pos) {
2031 /* about to change tempo or meter, so add the
2032 * number of frames for the bars we've just
2033 * traversed before we change the
2034 * frames_per_beat value.
2037 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2040 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2042 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2046 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2053 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2059 /* given the current meter, have we gone past the end of the bar ? */
2064 /* check if we need to use a new metric section: has adding frames moved us
2065 to or after the start of the next metric section? in which case, use it.
2068 if (i != metrics.end()) {
2069 if ((*i)->frame() <= pos) {
2071 /* about to change tempo or meter, so add the
2072 * number of frames for the beats we've just
2073 * traversed before we change the
2074 * frames_per_beat value.
2077 pos += llrint (beats * frames_per_beat);
2080 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2082 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2086 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2091 pos += llrint (beats * frames_per_beat);
2094 if (op.ticks >= BBT_Time::ticks_per_beat) {
2095 pos += llrint (frames_per_beat + /* extra beat */
2096 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2097 (double) BBT_Time::ticks_per_beat)));
2099 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2106 /** Count the number of beats that are equivalent to distance when going forward,
2110 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2112 Glib::RWLock::ReaderLock lm (lock);
2113 Metrics::const_iterator next_tempo;
2114 const TempoSection* tempo;
2116 /* Find the relevant initial tempo metric */
2118 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2120 const TempoSection* t;
2122 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2124 if ((*next_tempo)->frame() > pos) {
2134 tempo -> the Tempo for "pos"
2135 next_tempo -> the next tempo after "pos", possibly metrics.end()
2138 Evoral::MusicalTime beats = 0;
2142 /* End of this section */
2143 framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
2145 /* Distance to the end in frames */
2146 framecnt_t const distance_to_end = end - pos;
2148 /* Amount to subtract this time */
2149 double const sub = min (distance, distance_to_end);
2154 beats += sub / tempo->frames_per_beat (_frame_rate);
2156 /* Move on if there's anything to move to */
2158 if (next_tempo != metrics.end()) {
2160 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2162 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2163 *((Tempo*)tempo), tempo->frame(),
2164 tempo->frames_per_beat (_frame_rate)));
2166 while (next_tempo != metrics.end ()) {
2170 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2180 TempoMap::BBTPointList::const_iterator
2181 TempoMap::bbt_before_or_at (framepos_t pos)
2183 /* CALLER MUST HOLD READ LOCK */
2185 BBTPointList::const_iterator i;
2187 i = lower_bound (_map.begin(), _map.end(), pos);
2188 assert (i != _map.end());
2189 if ((*i).frame > pos) {
2190 assert (i != _map.begin());
2197 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2202 TempoMap::BBTPointList::const_iterator
2203 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2205 BBTPointList::const_iterator i;
2208 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2209 assert (i != _map.end());
2210 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2211 assert (i != _map.begin());
2217 TempoMap::BBTPointList::const_iterator
2218 TempoMap::bbt_after_or_at (framepos_t pos)
2220 /* CALLER MUST HOLD READ LOCK */
2222 BBTPointList::const_iterator i;
2224 if (_map.back().frame == pos) {
2226 assert (i != _map.begin());
2231 i = upper_bound (_map.begin(), _map.end(), pos);
2232 assert (i != _map.end());
2236 /** Compare the time of this with that of another MetricSection.
2237 * @param with_bbt True to compare using start(), false to use frame().
2238 * @return -1 for less than, 0 for equal, 1 for greater than.
2242 MetricSection::compare (const MetricSection& other) const
2244 if (start() == other.start()) {
2246 } else if (start() < other.start()) {
2257 MetricSection::operator== (const MetricSection& other) const
2259 return compare (other) == 0;
2263 MetricSection::operator!= (const MetricSection& other) const
2265 return compare (other) != 0;
2269 operator<< (std::ostream& o, const Meter& m) {
2270 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2274 operator<< (std::ostream& o, const Tempo& t) {
2275 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2279 operator<< (std::ostream& o, const MetricSection& section) {
2281 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2283 const TempoSection* ts;
2284 const MeterSection* ms;
2286 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2287 o << *((Tempo*) ts);
2288 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2289 o << *((Meter*) ms);