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 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);
1109 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1113 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1115 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1118 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1121 if (_map.empty() || _map.back().frame < frame) {
1122 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1125 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1129 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1131 /* CALLER MUST HOLD READ LOCK */
1133 bbt.bars = (*i).bar;
1134 bbt.beats = (*i).beat;
1136 if ((*i).frame == frame) {
1139 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1140 BBT_Time::ticks_per_beat);
1145 TempoMap::frame_time (const BBT_Time& bbt)
1147 require_map_to (bbt);
1149 Glib::RWLock::ReaderLock lm (lock);
1151 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1152 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1154 if (bbt.ticks != 0) {
1155 return ((*e).frame - (*s).frame) +
1156 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1158 return ((*e).frame - (*s).frame);
1163 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1165 Glib::RWLock::ReaderLock lm (lock);
1166 framecnt_t frames = 0;
1169 bbt_time (pos, when);
1170 frames = bbt_duration_at_unlocked (when, bbt,dir);
1176 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1178 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1182 /* round back to the previous precise beat */
1183 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1184 BBTPointList::const_iterator start (wi);
1185 double tick_frames = 0;
1187 assert (wi != _map.end());
1189 /* compute how much rounding we did because of non-zero ticks */
1191 if (when.ticks != 0) {
1192 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1198 while (wi != _map.end() && bars < bbt.bars) {
1200 if ((*wi).is_bar()) {
1204 assert (wi != _map.end());
1206 while (wi != _map.end() && beats < bbt.beats) {
1210 assert (wi != _map.end());
1212 /* add any additional frames related to ticks in the added value */
1214 if (bbt.ticks != 0) {
1215 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1218 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1222 TempoMap::round_to_bar (framepos_t fr, int dir)
1224 return round_to_type (fr, dir, Bar);
1228 TempoMap::round_to_beat (framepos_t fr, int dir)
1230 return round_to_type (fr, dir, Beat);
1234 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1236 require_map_to (fr);
1238 Glib::RWLock::ReaderLock lm (lock);
1239 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1241 uint32_t ticks_one_subdivisions_worth;
1242 uint32_t difference;
1244 bbt_time (fr, the_beat, i);
1246 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1247 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1249 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1253 /* round to next (even if we're on a subdivision */
1255 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1258 /* right on the subdivision, so the difference is just the subdivision ticks */
1259 the_beat.ticks += ticks_one_subdivisions_worth;
1262 /* not on subdivision, compute distance to next subdivision */
1264 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1267 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1268 assert (i != _map.end());
1270 assert (i != _map.end());
1271 the_beat.ticks -= BBT_Time::ticks_per_beat;
1275 } else if (dir < 0) {
1277 /* round to previous (even if we're on a subdivision) */
1279 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1282 /* right on the subdivision, so the difference is just the subdivision ticks */
1283 difference = ticks_one_subdivisions_worth;
1285 /* not on subdivision, compute distance to previous subdivision, which
1286 is just the modulus.
1292 if (the_beat.ticks < difference) {
1293 if (i == _map.begin()) {
1294 /* can't go backwards from wherever pos is, so just return it */
1298 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1300 the_beat.ticks -= difference;
1304 /* round to nearest */
1308 /* compute the distance to the previous and next subdivision */
1310 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1312 /* closer to the next subdivision, so shift forward */
1314 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1316 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1318 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1319 assert (i != _map.end());
1321 assert (i != _map.end());
1322 the_beat.ticks -= BBT_Time::ticks_per_beat;
1323 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1326 } else if (rem > 0) {
1328 /* closer to previous subdivision, so shift backward */
1330 if (rem > the_beat.ticks) {
1331 if (i == _map.begin()) {
1332 /* can't go backwards past zero, so ... */
1335 /* step back to previous beat */
1337 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1338 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1340 the_beat.ticks = lrint (the_beat.ticks - rem);
1341 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1344 /* on the subdivision, do nothing */
1348 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1349 (*i).tempo->frames_per_beat (_frame_rate);
1353 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1355 require_map_to (frame);
1357 Glib::RWLock::ReaderLock lm (lock);
1358 BBTPointList::const_iterator fi;
1361 fi = bbt_after_or_at (frame);
1363 fi = bbt_before_or_at (frame);
1366 assert (fi != _map.end());
1368 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,
1369 (type == Bar ? "bar" : "beat")));
1374 /* find bar previous to 'frame' */
1376 if (fi == _map.begin()) {
1380 if ((*fi).is_bar() && (*fi).frame == frame) {
1384 while (!(*fi).is_bar()) {
1385 if (fi == _map.begin()) {
1390 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1391 (*fi).bar, (*fi).beat, (*fi).frame));
1394 } else if (dir > 0) {
1396 /* find bar following 'frame' */
1398 if ((*fi).is_bar() && (*fi).frame == frame) {
1402 while (!(*fi).is_bar()) {
1404 if (fi == _map.end()) {
1410 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1411 (*fi).bar, (*fi).beat, (*fi).frame));
1416 /* true rounding: find nearest bar */
1418 BBTPointList::const_iterator prev = fi;
1419 BBTPointList::const_iterator next = fi;
1421 if ((*fi).frame == frame) {
1425 while ((*prev).beat != 1) {
1426 if (prev == _map.begin()) {
1432 while ((next != _map.end()) && (*next).beat != 1) {
1436 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1437 return (*prev).frame;
1439 return (*next).frame;
1449 if (fi == _map.begin()) {
1453 if ((*fi).frame >= frame) {
1454 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1457 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1458 (*fi).bar, (*fi).beat, (*fi).frame));
1460 } else if (dir > 0) {
1461 if ((*fi).frame <= frame) {
1462 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1465 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1466 (*fi).bar, (*fi).beat, (*fi).frame));
1469 /* find beat nearest to frame */
1470 if ((*fi).frame == frame) {
1474 BBTPointList::const_iterator prev = fi;
1475 BBTPointList::const_iterator next = fi;
1477 /* fi is already the beat before_or_at frame, and
1478 we've just established that its not at frame, so its
1479 the beat before frame.
1483 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1484 return (*prev).frame;
1486 return (*next).frame;
1498 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1499 TempoMap::BBTPointList::const_iterator& end,
1500 framepos_t lower, framepos_t upper)
1503 Glib::RWLock::WriterLock lm (lock);
1504 if (_map.empty() || (_map.back().frame < upper)) {
1505 recompute_map (false, upper);
1509 begin = lower_bound (_map.begin(), _map.end(), lower);
1510 end = upper_bound (_map.begin(), _map.end(), upper);
1514 TempoMap::tempo_section_at (framepos_t frame) const
1516 Glib::RWLock::ReaderLock lm (lock);
1517 Metrics::const_iterator i;
1518 TempoSection* prev = 0;
1520 for (i = metrics.begin(); i != metrics.end(); ++i) {
1523 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1525 if ((*i)->frame() > frame) {
1541 TempoMap::tempo_at (framepos_t frame) const
1543 TempoMetric m (metric_at (frame));
1549 TempoMap::meter_at (framepos_t frame) const
1551 TempoMetric m (metric_at (frame));
1556 TempoMap::get_state ()
1558 Metrics::const_iterator i;
1559 XMLNode *root = new XMLNode ("TempoMap");
1562 Glib::RWLock::ReaderLock lm (lock);
1563 for (i = metrics.begin(); i != metrics.end(); ++i) {
1564 root->add_child_nocopy ((*i)->get_state());
1572 TempoMap::set_state (const XMLNode& node, int /*version*/)
1575 Glib::RWLock::WriterLock lm (lock);
1578 XMLNodeConstIterator niter;
1579 Metrics old_metrics (metrics);
1580 MeterSection* last_meter = 0;
1584 nlist = node.children();
1586 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1587 XMLNode* child = *niter;
1589 if (child->name() == TempoSection::xml_state_node_name) {
1592 TempoSection* ts = new TempoSection (*child);
1593 metrics.push_back (ts);
1595 if (ts->bar_offset() < 0.0) {
1597 ts->update_bar_offset_from_bbt (*last_meter);
1602 catch (failed_constructor& err){
1603 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1604 metrics = old_metrics;
1608 } else if (child->name() == MeterSection::xml_state_node_name) {
1611 MeterSection* ms = new MeterSection (*child);
1612 metrics.push_back (ms);
1616 catch (failed_constructor& err) {
1617 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1618 metrics = old_metrics;
1624 if (niter == nlist.end()) {
1625 MetricSectionSorter cmp;
1629 recompute_map (true);
1632 PropertyChanged (PropertyChange ());
1638 TempoMap::dump (std::ostream& o) const
1640 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1641 const MeterSection* m;
1642 const TempoSection* t;
1644 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1646 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1647 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? "
1648 << t->movable() << ')' << endl;
1649 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1650 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1651 << " (movable? " << m->movable() << ')' << endl;
1657 TempoMap::n_tempos() const
1659 Glib::RWLock::ReaderLock lm (lock);
1662 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1663 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1672 TempoMap::n_meters() const
1674 Glib::RWLock::ReaderLock lm (lock);
1677 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1678 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1687 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1690 Glib::RWLock::WriterLock lm (lock);
1691 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1692 if ((*i)->frame() >= where && (*i)->movable ()) {
1693 (*i)->set_frame ((*i)->frame() + amount);
1697 /* now reset the BBT time of all metrics, based on their new
1698 * audio time. This is the only place where we do this reverse
1702 Metrics::iterator i;
1703 const MeterSection* meter;
1704 const TempoSection* tempo;
1708 meter = &first_meter ();
1709 tempo = &first_tempo ();
1714 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1717 MetricSection* prev = 0;
1719 for (i = metrics.begin(); i != metrics.end(); ++i) {
1722 TempoMetric metric (*meter, *tempo);
1725 metric.set_start (prev->start());
1726 metric.set_frame (prev->frame());
1728 // metric will be at frames=0 bbt=1|1|0 by default
1729 // which is correct for our purpose
1732 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1733 bbt_time ((*i)->frame(), bbt, bi);
1735 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1741 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1742 /* round up to next beat */
1748 if (bbt.beats != 1) {
1749 /* round up to next bar */
1755 // cerr << bbt << endl;
1757 (*i)->set_start (bbt);
1759 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1761 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1762 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1764 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1766 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1773 recompute_map (true);
1777 PropertyChanged (PropertyChange ());
1780 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1781 * pos can be -ve, if required.
1784 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1786 Glib::RWLock::ReaderLock lm (lock);
1787 Metrics::const_iterator next_tempo;
1788 const TempoSection* tempo;
1790 /* Find the starting tempo metric */
1792 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1794 const TempoSection* t;
1796 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1798 /* This is a bit of a hack, but pos could be -ve, and if it is,
1799 we consider the initial metric changes (at time 0) to actually
1800 be in effect at pos.
1803 framepos_t f = (*next_tempo)->frame ();
1805 if (pos < 0 && f == 0) {
1819 tempo -> the Tempo for "pos"
1820 next_tempo -> first tempo after "pos", possibly metrics.end()
1823 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1824 pos, beats, *((Tempo*)tempo), tempo->frame()));
1828 /* Distance to the end of this section in frames */
1829 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1831 /* Distance to the end in beats */
1832 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1834 /* Amount to subtract this time */
1835 double const delta = min (distance_beats, beats);
1837 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1838 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1839 distance_frames, distance_beats));
1843 pos += delta * tempo->frames_per_beat (_frame_rate);
1845 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1847 /* step forwards to next tempo section */
1849 if (next_tempo != metrics.end()) {
1851 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1853 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1854 *((Tempo*)tempo), tempo->frame(),
1855 tempo->frames_per_beat (_frame_rate)));
1857 while (next_tempo != metrics.end ()) {
1861 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1871 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1873 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1875 Glib::RWLock::ReaderLock lm (lock);
1876 Metrics::const_reverse_iterator prev_tempo;
1877 const TempoSection* tempo = 0;
1879 /* Find the starting tempo metric */
1881 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1883 const TempoSection* t;
1885 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1887 /* This is a bit of a hack, but pos could be -ve, and if it is,
1888 we consider the initial metric changes (at time 0) to actually
1889 be in effect at pos.
1892 framepos_t f = (*prev_tempo)->frame ();
1894 if (pos < 0 && f == 0) {
1898 /* this is slightly more complex than the forward case
1899 because we reach the tempo in effect at pos after
1900 passing through pos (rather before, as in the
1901 forward case). having done that, we then need to
1902 keep going to get the previous tempo (or
1908 /* first tempo with position at or
1912 } else if (f < pos) {
1913 /* some other tempo section that
1914 is even earlier than 'tempo'
1922 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1923 pos, beats, *((Tempo*)tempo), tempo->frame(),
1924 prev_tempo == metrics.rend()));
1928 tempo -> the Tempo for "pos"
1929 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1934 /* Distance to the start of this section in frames */
1935 framecnt_t distance_frames = (pos - tempo->frame());
1937 /* Distance to the start in beats */
1938 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1940 /* Amount to subtract this time */
1941 double const sub = min (distance_beats, beats);
1943 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1944 tempo->frame(), distance_frames, distance_beats));
1948 pos -= sub * tempo->frames_per_beat (_frame_rate);
1950 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1951 (prev_tempo == metrics.rend())));
1953 /* step backwards to prior TempoSection */
1955 if (prev_tempo != metrics.rend()) {
1957 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1959 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1960 *((Tempo*)tempo), tempo->frame(),
1961 tempo->frames_per_beat (_frame_rate)));
1963 while (prev_tempo != metrics.rend ()) {
1967 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1972 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
1980 /** Add the BBT interval op to pos and return the result */
1982 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
1984 Glib::RWLock::ReaderLock lm (lock);
1985 Metrics::const_iterator i;
1986 const MeterSection* meter;
1987 const MeterSection* m;
1988 const TempoSection* tempo;
1989 const TempoSection* t;
1990 double frames_per_beat;
1992 meter = &first_meter ();
1993 tempo = &first_tempo ();
1998 /* find the starting metrics for tempo & meter */
2000 for (i = metrics.begin(); i != metrics.end(); ++i) {
2002 if ((*i)->frame() > pos) {
2006 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2008 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2015 meter -> the Meter for "pos"
2016 tempo -> the Tempo for "pos"
2017 i -> for first new metric after "pos", possibly metrics.end()
2020 /* now comes the complicated part. we have to add one beat a time,
2021 checking for a new metric on every beat.
2024 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2033 /* check if we need to use a new metric section: has adding frames moved us
2034 to or after the start of the next metric section? in which case, use it.
2037 if (i != metrics.end()) {
2038 if ((*i)->frame() <= pos) {
2040 /* about to change tempo or meter, so add the
2041 * number of frames for the bars we've just
2042 * traversed before we change the
2043 * frames_per_beat value.
2046 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2049 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2051 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2055 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2062 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2068 /* given the current meter, have we gone past the end of the bar ? */
2073 /* check if we need to use a new metric section: has adding frames moved us
2074 to or after the start of the next metric section? in which case, use it.
2077 if (i != metrics.end()) {
2078 if ((*i)->frame() <= pos) {
2080 /* about to change tempo or meter, so add the
2081 * number of frames for the beats we've just
2082 * traversed before we change the
2083 * frames_per_beat value.
2086 pos += llrint (beats * frames_per_beat);
2089 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2091 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2095 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2100 pos += llrint (beats * frames_per_beat);
2103 if (op.ticks >= BBT_Time::ticks_per_beat) {
2104 pos += llrint (frames_per_beat + /* extra beat */
2105 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2106 (double) BBT_Time::ticks_per_beat)));
2108 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2115 /** Count the number of beats that are equivalent to distance when going forward,
2119 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2121 Glib::RWLock::ReaderLock lm (lock);
2122 Metrics::const_iterator next_tempo;
2123 const TempoSection* tempo;
2125 /* Find the relevant initial tempo metric */
2127 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2129 const TempoSection* t;
2131 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2133 if ((*next_tempo)->frame() > pos) {
2143 tempo -> the Tempo for "pos"
2144 next_tempo -> the next tempo after "pos", possibly metrics.end()
2147 Evoral::MusicalTime beats = 0;
2151 /* End of this section */
2152 framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
2154 /* Distance to the end in frames */
2155 framecnt_t const distance_to_end = end - pos;
2157 /* Amount to subtract this time */
2158 double const sub = min (distance, distance_to_end);
2163 beats += sub / tempo->frames_per_beat (_frame_rate);
2165 /* Move on if there's anything to move to */
2167 if (next_tempo != metrics.end()) {
2169 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2171 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2172 *((Tempo*)tempo), tempo->frame(),
2173 tempo->frames_per_beat (_frame_rate)));
2175 while (next_tempo != metrics.end ()) {
2179 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2189 TempoMap::BBTPointList::const_iterator
2190 TempoMap::bbt_before_or_at (framepos_t pos)
2192 /* CALLER MUST HOLD READ LOCK */
2194 BBTPointList::const_iterator i;
2196 i = lower_bound (_map.begin(), _map.end(), pos);
2197 assert (i != _map.end());
2198 if ((*i).frame > pos) {
2199 cerr << "lower bound was found at " << (*i).frame << " for " << pos;
2201 assert (i != _map.begin());
2208 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2213 TempoMap::BBTPointList::const_iterator
2214 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2216 BBTPointList::const_iterator i;
2219 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2220 assert (i != _map.end());
2221 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2222 assert (i != _map.begin());
2228 TempoMap::BBTPointList::const_iterator
2229 TempoMap::bbt_after_or_at (framepos_t pos)
2231 /* CALLER MUST HOLD READ LOCK */
2233 BBTPointList::const_iterator i;
2235 if (_map.back().frame == pos) {
2237 assert (i != _map.begin());
2242 i = upper_bound (_map.begin(), _map.end(), pos);
2243 assert (i != _map.end());
2248 operator<< (std::ostream& o, const Meter& m) {
2249 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2253 operator<< (std::ostream& o, const Tempo& t) {
2254 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2258 operator<< (std::ostream& o, const MetricSection& section) {
2260 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2262 const TempoSection* ts;
2263 const MeterSection* ms;
2265 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2266 o << *((Tempo*) ts);
2267 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2268 o << *((Meter*) ms);