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);
838 TempoMap::extend_map (framepos_t end)
840 /* CALLER MUST HOLD WRITE LOCK */
843 recompute_map (false, end);
847 BBTPointList::const_iterator i = _map.end();
848 Metrics::iterator next_metric;
852 BBT_Time last_metric_start;
854 if ((*i).tempo->frame() > (*i).meter->frame()) {
855 last_metric_start = (*i).tempo->start();
857 last_metric_start = (*i).meter->start();
860 /* find the metric immediately after the tempo + meter sections for the
861 * last point in the map
864 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
865 if ((*next_metric)->start() > last_metric_start) {
870 /* we cast away const here because this is the one place where we need
871 * to actually modify the frame time of each metric section.
874 _extend_map (const_cast<TempoSection*> ((*i).tempo),
875 const_cast<MeterSection*> ((*i).meter),
876 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
880 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
881 Metrics::iterator next_metric,
882 BBT_Time current, framepos_t current_frame, framepos_t end)
884 /* CALLER MUST HOLD WRITE LOCK */
888 double divisions_per_bar;
891 divisions_per_bar = meter->divisions_per_bar ();
892 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
894 while (current_frame < end) {
897 current_frame += beat_frames;
899 if (current.beats > meter->divisions_per_bar()) {
904 if (next_metric != metrics.end()) {
906 /* no operator >= so invert operator < */
908 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
910 if (!(current < (*next_metric)->start())) {
913 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
917 /* new tempo section: if its on a beat,
918 * we don't have to do anything other
919 * than recompute various distances,
920 * done further below as we transition
921 * the next metric section.
923 * if its not on the beat, we have to
924 * compute the duration of the beat it
925 * is within, which will be different
926 * from the preceding following ones
927 * since it takes part of its duration
928 * from the preceding tempo and part
929 * from this new tempo.
932 if (tempo->start().ticks != 0) {
934 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
936 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
937 tempo->start(), current_frame, tempo->bar_offset()));
939 /* back up to previous beat */
940 current_frame -= beat_frames;
942 /* set tempo section location based on offset from last beat */
944 double bar_offset_in_beats = 1 + (ts->bar_offset() * meter->divisions_per_bar());
946 /* we've already advanced
947 * current.beats, but we want
948 * the previous beat's value
951 bar_offset_in_beats -= current.beats - 1;
953 tempo->set_frame (current_frame + (bar_offset_in_beats * beat_frames));
955 /* advance to the location of the new (adjusted) beat */
956 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
957 /* next metric doesn't have to
958 * match this precisely to
961 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
965 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
966 tempo->start(), current_frame));
967 tempo->set_frame (current_frame);
970 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
974 /* new meter section: always defines the
978 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
979 meter->start(), current, current_frame));
981 assert (current.beats == 1);
983 meter->set_frame (current_frame);
986 divisions_per_bar = meter->divisions_per_bar ();
987 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
989 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
990 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
994 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
995 /* same position so go back and set this one up before advancing
1002 if (current.beats == 1) {
1003 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1004 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
1006 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1007 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
1013 TempoMap::metric_at (framepos_t frame) const
1015 Glib::RWLock::ReaderLock lm (lock);
1016 TempoMetric m (first_meter(), first_tempo());
1020 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1021 at something, because we insert the default tempo and meter during
1022 TempoMap construction.
1024 now see if we can find better candidates.
1027 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1029 // cerr << "Looking at a metric section " << **i << endl;
1031 if ((*i)->frame() > frame) {
1035 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1036 m.set_tempo (*tempo);
1037 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1038 m.set_meter (*meter);
1041 m.set_frame ((*i)->frame ());
1042 m.set_start ((*i)->start ());
1045 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1050 TempoMap::metric_at (BBT_Time bbt) const
1052 Glib::RWLock::ReaderLock lm (lock);
1053 TempoMetric m (first_meter(), first_tempo());
1057 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1058 at something, because we insert the default tempo and meter during
1059 TempoMap construction.
1061 now see if we can find better candidates.
1064 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1066 BBT_Time section_start ((*i)->start());
1068 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1072 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1073 m.set_tempo (*tempo);
1074 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1075 m.set_meter (*meter);
1078 m.set_frame ((*i)->frame ());
1079 m.set_start (section_start);
1086 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1088 require_map_to (frame);
1090 Glib::RWLock::ReaderLock lm (lock);
1091 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1095 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1097 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1100 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1103 if (_map.empty() || _map.back().frame < frame) {
1104 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1107 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1111 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1113 /* CALLER MUST HOLD READ LOCK */
1115 bbt.bars = (*i).bar;
1116 bbt.beats = (*i).beat;
1118 if ((*i).frame == frame) {
1121 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1122 BBT_Time::ticks_per_beat);
1127 TempoMap::frame_time (const BBT_Time& bbt)
1129 require_map_to (bbt);
1131 Glib::RWLock::ReaderLock lm (lock);
1133 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1134 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1136 if (bbt.ticks != 0) {
1137 return ((*e).frame - (*s).frame) +
1138 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1140 return ((*e).frame - (*s).frame);
1145 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1147 Glib::RWLock::ReaderLock lm (lock);
1148 framecnt_t frames = 0;
1151 bbt_time (pos, when);
1152 frames = bbt_duration_at_unlocked (when, bbt,dir);
1158 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1160 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1164 /* round back to the previous precise beat */
1165 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1166 BBTPointList::const_iterator start (wi);
1167 double tick_frames = 0;
1169 assert (wi != _map.end());
1171 /* compute how much rounding we did because of non-zero ticks */
1173 if (when.ticks != 0) {
1174 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1180 while (wi != _map.end() && bars < bbt.bars) {
1182 if ((*wi).is_bar()) {
1186 assert (wi != _map.end());
1188 while (wi != _map.end() && beats < bbt.beats) {
1192 assert (wi != _map.end());
1194 /* add any additional frames related to ticks in the added value */
1196 if (bbt.ticks != 0) {
1197 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1200 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1204 TempoMap::round_to_bar (framepos_t fr, int dir)
1206 return round_to_type (fr, dir, Bar);
1210 TempoMap::round_to_beat (framepos_t fr, int dir)
1212 return round_to_type (fr, dir, Beat);
1216 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1218 require_map_to (fr);
1220 Glib::RWLock::ReaderLock lm (lock);
1221 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1223 uint32_t ticks_one_subdivisions_worth;
1224 uint32_t difference;
1226 bbt_time (fr, the_beat, i);
1228 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1229 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1231 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1235 /* round to next (even if we're on a subdivision */
1237 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1240 /* right on the subdivision, so the difference is just the subdivision ticks */
1241 the_beat.ticks += ticks_one_subdivisions_worth;
1244 /* not on subdivision, compute distance to next subdivision */
1246 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1249 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1250 assert (i != _map.end());
1252 assert (i != _map.end());
1253 the_beat.ticks -= BBT_Time::ticks_per_beat;
1257 } else if (dir < 0) {
1259 /* round to previous (even if we're on a subdivision) */
1261 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1264 /* right on the subdivision, so the difference is just the subdivision ticks */
1265 difference = ticks_one_subdivisions_worth;
1267 /* not on subdivision, compute distance to previous subdivision, which
1268 is just the modulus.
1274 if (the_beat.ticks < difference) {
1275 if (i == _map.begin()) {
1276 /* can't go backwards from wherever pos is, so just return it */
1280 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1282 the_beat.ticks -= difference;
1286 /* round to nearest */
1290 /* compute the distance to the previous and next subdivision */
1292 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1294 /* closer to the next subdivision, so shift forward */
1296 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1298 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1300 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1301 assert (i != _map.end());
1303 assert (i != _map.end());
1304 the_beat.ticks -= BBT_Time::ticks_per_beat;
1305 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1308 } else if (rem > 0) {
1310 /* closer to previous subdivision, so shift backward */
1312 if (rem > the_beat.ticks) {
1313 if (i == _map.begin()) {
1314 /* can't go backwards past zero, so ... */
1317 /* step back to previous beat */
1319 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1320 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1322 the_beat.ticks = lrint (the_beat.ticks - rem);
1323 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1326 /* on the subdivision, do nothing */
1330 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1331 (*i).tempo->frames_per_beat (_frame_rate);
1335 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1337 require_map_to (frame);
1339 Glib::RWLock::ReaderLock lm (lock);
1340 BBTPointList::const_iterator fi;
1343 fi = bbt_after_or_at (frame);
1345 fi = bbt_before_or_at (frame);
1348 assert (fi != _map.end());
1350 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));
1355 /* find bar previous to 'frame' */
1357 if (fi == _map.begin()) {
1361 if ((*fi).is_bar() && (*fi).frame == frame) {
1365 while (!(*fi).is_bar()) {
1366 if (fi == _map.begin()) {
1371 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1372 (*fi).bar, (*fi).beat, (*fi).frame));
1375 } else if (dir > 0) {
1377 /* find bar following 'frame' */
1379 if ((*fi).is_bar() && (*fi).frame == frame) {
1383 while (!(*fi).is_bar()) {
1385 if (fi == _map.end()) {
1391 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1392 (*fi).bar, (*fi).beat, (*fi).frame));
1397 /* true rounding: find nearest bar */
1399 BBTPointList::const_iterator prev = fi;
1400 BBTPointList::const_iterator next = fi;
1402 if ((*fi).frame == frame) {
1406 while ((*prev).beat != 1) {
1407 if (prev == _map.begin()) {
1413 while ((next != _map.end()) && (*next).beat != 1) {
1417 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1418 return (*prev).frame;
1420 return (*next).frame;
1430 if (fi == _map.begin()) {
1434 if ((*fi).frame >= frame) {
1435 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1438 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1439 (*fi).bar, (*fi).beat, (*fi).frame));
1441 } else if (dir > 0) {
1442 if ((*fi).frame <= frame) {
1443 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1446 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1447 (*fi).bar, (*fi).beat, (*fi).frame));
1450 /* find beat nearest to frame */
1451 if ((*fi).frame == frame) {
1455 BBTPointList::const_iterator prev = fi;
1456 BBTPointList::const_iterator next = fi;
1457 if (prev != _map.begin()) {
1462 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1463 return (*prev).frame;
1465 return (*next).frame;
1477 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1478 TempoMap::BBTPointList::const_iterator& end,
1479 framepos_t lower, framepos_t upper)
1482 Glib::RWLock::WriterLock lm (lock);
1483 if (_map.empty() || (_map.back().frame < upper)) {
1484 recompute_map (false, upper);
1488 begin = lower_bound (_map.begin(), _map.end(), lower);
1489 end = upper_bound (_map.begin(), _map.end(), upper);
1493 TempoMap::tempo_section_at (framepos_t frame) const
1495 Glib::RWLock::ReaderLock lm (lock);
1496 Metrics::const_iterator i;
1497 TempoSection* prev = 0;
1499 for (i = metrics.begin(); i != metrics.end(); ++i) {
1502 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1504 if ((*i)->frame() > frame) {
1520 TempoMap::tempo_at (framepos_t frame) const
1522 TempoMetric m (metric_at (frame));
1528 TempoMap::meter_at (framepos_t frame) const
1530 TempoMetric m (metric_at (frame));
1535 TempoMap::get_state ()
1537 Metrics::const_iterator i;
1538 XMLNode *root = new XMLNode ("TempoMap");
1541 Glib::RWLock::ReaderLock lm (lock);
1542 for (i = metrics.begin(); i != metrics.end(); ++i) {
1543 root->add_child_nocopy ((*i)->get_state());
1551 TempoMap::set_state (const XMLNode& node, int /*version*/)
1554 Glib::RWLock::WriterLock lm (lock);
1557 XMLNodeConstIterator niter;
1558 Metrics old_metrics (metrics);
1559 MeterSection* last_meter = 0;
1563 nlist = node.children();
1565 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1566 XMLNode* child = *niter;
1568 if (child->name() == TempoSection::xml_state_node_name) {
1571 TempoSection* ts = new TempoSection (*child);
1572 metrics.push_back (ts);
1574 if (ts->bar_offset() < 0.0) {
1576 ts->update_bar_offset_from_bbt (*last_meter);
1581 catch (failed_constructor& err){
1582 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1583 metrics = old_metrics;
1587 } else if (child->name() == MeterSection::xml_state_node_name) {
1590 MeterSection* ms = new MeterSection (*child);
1591 metrics.push_back (ms);
1595 catch (failed_constructor& err) {
1596 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1597 metrics = old_metrics;
1603 if (niter == nlist.end()) {
1604 MetricSectionSorter cmp;
1608 recompute_map (true);
1611 PropertyChanged (PropertyChange ());
1617 TempoMap::dump (std::ostream& o) const
1619 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1620 const MeterSection* m;
1621 const TempoSection* t;
1623 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1625 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1626 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? "
1627 << t->movable() << ')' << endl;
1628 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1629 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1630 << " (movable? " << m->movable() << ')' << endl;
1636 TempoMap::n_tempos() const
1638 Glib::RWLock::ReaderLock lm (lock);
1641 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1642 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1651 TempoMap::n_meters() const
1653 Glib::RWLock::ReaderLock lm (lock);
1656 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1657 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1666 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1669 Glib::RWLock::WriterLock lm (lock);
1670 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1671 if ((*i)->frame() >= where && (*i)->movable ()) {
1672 (*i)->set_frame ((*i)->frame() + amount);
1676 /* now reset the BBT time of all metrics, based on their new
1677 * audio time. This is the only place where we do this reverse
1681 Metrics::iterator i;
1682 const MeterSection* meter;
1683 const TempoSection* tempo;
1687 meter = &first_meter ();
1688 tempo = &first_tempo ();
1693 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1696 MetricSection* prev = 0;
1698 for (i = metrics.begin(); i != metrics.end(); ++i) {
1701 TempoMetric metric (*meter, *tempo);
1704 metric.set_start (prev->start());
1705 metric.set_frame (prev->frame());
1707 // metric will be at frames=0 bbt=1|1|0 by default
1708 // which is correct for our purpose
1711 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1712 bbt_time ((*i)->frame(), bbt, bi);
1714 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1720 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1721 /* round up to next beat */
1727 if (bbt.beats != 1) {
1728 /* round up to next bar */
1734 // cerr << bbt << endl;
1736 (*i)->set_start (bbt);
1738 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1740 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1741 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1743 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1745 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1752 recompute_map (true);
1756 PropertyChanged (PropertyChange ());
1759 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1760 * pos can be -ve, if required.
1763 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1765 Glib::RWLock::ReaderLock lm (lock);
1766 Metrics::const_iterator next_tempo;
1767 const TempoSection* tempo;
1769 /* Find the starting tempo metric */
1771 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1773 const TempoSection* t;
1775 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1777 /* This is a bit of a hack, but pos could be -ve, and if it is,
1778 we consider the initial metric changes (at time 0) to actually
1779 be in effect at pos.
1782 framepos_t f = (*next_tempo)->frame ();
1784 if (pos < 0 && f == 0) {
1798 tempo -> the Tempo for "pos"
1799 next_tempo -> first tempo after "pos", possibly metrics.end()
1802 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1803 pos, beats, *((Tempo*)tempo), tempo->frame()));
1807 /* Distance to the end of this section in frames */
1808 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1810 /* Distance to the end in beats */
1811 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1813 /* Amount to subtract this time */
1814 double const delta = min (distance_beats, beats);
1816 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1817 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1818 distance_frames, distance_beats));
1822 pos += delta * tempo->frames_per_beat (_frame_rate);
1824 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1826 /* step forwards to next tempo section */
1828 if (next_tempo != metrics.end()) {
1830 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1832 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1833 *((Tempo*)tempo), tempo->frame(),
1834 tempo->frames_per_beat (_frame_rate)));
1836 while (next_tempo != metrics.end ()) {
1840 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1850 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1852 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1854 Glib::RWLock::ReaderLock lm (lock);
1855 Metrics::const_reverse_iterator prev_tempo;
1856 const TempoSection* tempo = 0;
1858 /* Find the starting tempo metric */
1860 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1862 const TempoSection* t;
1864 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1866 /* This is a bit of a hack, but pos could be -ve, and if it is,
1867 we consider the initial metric changes (at time 0) to actually
1868 be in effect at pos.
1871 framepos_t f = (*prev_tempo)->frame ();
1873 if (pos < 0 && f == 0) {
1877 /* this is slightly more complex than the forward case
1878 because we reach the tempo in effect at pos after
1879 passing through pos (rather before, as in the
1880 forward case). having done that, we then need to
1881 keep going to get the previous tempo (or
1887 /* first tempo with position at or
1891 } else if (f < pos) {
1892 /* some other tempo section that
1893 is even earlier than 'tempo'
1901 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1902 pos, beats, *((Tempo*)tempo), tempo->frame(),
1903 prev_tempo == metrics.rend()));
1907 tempo -> the Tempo for "pos"
1908 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1913 /* Distance to the start of this section in frames */
1914 framecnt_t distance_frames = (pos - tempo->frame());
1916 /* Distance to the start in beats */
1917 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1919 /* Amount to subtract this time */
1920 double const sub = min (distance_beats, beats);
1922 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1923 tempo->frame(), distance_frames, distance_beats));
1927 pos -= sub * tempo->frames_per_beat (_frame_rate);
1929 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1930 (prev_tempo == metrics.rend())));
1932 /* step backwards to prior TempoSection */
1934 if (prev_tempo != metrics.rend()) {
1936 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1938 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1939 *((Tempo*)tempo), tempo->frame(),
1940 tempo->frames_per_beat (_frame_rate)));
1942 while (prev_tempo != metrics.rend ()) {
1946 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1951 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
1959 /** Add the BBT interval op to pos and return the result */
1961 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
1963 Glib::RWLock::ReaderLock lm (lock);
1964 Metrics::const_iterator i;
1965 const MeterSection* meter;
1966 const MeterSection* m;
1967 const TempoSection* tempo;
1968 const TempoSection* t;
1969 double frames_per_beat;
1971 meter = &first_meter ();
1972 tempo = &first_tempo ();
1977 /* find the starting metrics for tempo & meter */
1979 for (i = metrics.begin(); i != metrics.end(); ++i) {
1981 if ((*i)->frame() > pos) {
1985 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1987 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1994 meter -> the Meter for "pos"
1995 tempo -> the Tempo for "pos"
1996 i -> for first new metric after "pos", possibly metrics.end()
1999 /* now comes the complicated part. we have to add one beat a time,
2000 checking for a new metric on every beat.
2003 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2012 /* check if we need to use a new metric section: has adding frames moved us
2013 to or after the start of the next metric section? in which case, use it.
2016 if (i != metrics.end()) {
2017 if ((*i)->frame() <= pos) {
2019 /* about to change tempo or meter, so add the
2020 * number of frames for the bars we've just
2021 * traversed before we change the
2022 * frames_per_beat value.
2025 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2028 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2030 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2034 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2041 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2047 /* given the current meter, have we gone past the end of the bar ? */
2052 /* check if we need to use a new metric section: has adding frames moved us
2053 to or after the start of the next metric section? in which case, use it.
2056 if (i != metrics.end()) {
2057 if ((*i)->frame() <= pos) {
2059 /* about to change tempo or meter, so add the
2060 * number of frames for the beats we've just
2061 * traversed before we change the
2062 * frames_per_beat value.
2065 pos += llrint (beats * frames_per_beat);
2068 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2070 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2074 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2079 pos += llrint (beats * frames_per_beat);
2082 if (op.ticks >= BBT_Time::ticks_per_beat) {
2083 pos += llrint (frames_per_beat + /* extra beat */
2084 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2085 (double) BBT_Time::ticks_per_beat)));
2087 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2094 /** Count the number of beats that are equivalent to distance when going forward,
2098 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2100 Glib::RWLock::ReaderLock lm (lock);
2101 Metrics::const_iterator next_tempo;
2102 const TempoSection* tempo;
2104 /* Find the relevant initial tempo metric */
2106 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2108 const TempoSection* t;
2110 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2112 if ((*next_tempo)->frame() > pos) {
2122 tempo -> the Tempo for "pos"
2123 next_tempo -> the next tempo after "pos", possibly metrics.end()
2126 Evoral::MusicalTime beats = 0;
2130 /* End of this section */
2131 framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
2133 /* Distance to the end in frames */
2134 framecnt_t const distance_to_end = end - pos;
2136 /* Amount to subtract this time */
2137 double const sub = min (distance, distance_to_end);
2142 beats += sub / tempo->frames_per_beat (_frame_rate);
2144 /* Move on if there's anything to move to */
2146 if (next_tempo != metrics.end()) {
2148 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2150 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2151 *((Tempo*)tempo), tempo->frame(),
2152 tempo->frames_per_beat (_frame_rate)));
2154 while (next_tempo != metrics.end ()) {
2158 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2168 TempoMap::BBTPointList::const_iterator
2169 TempoMap::bbt_before_or_at (framepos_t pos)
2171 /* CALLER MUST HOLD READ LOCK */
2173 BBTPointList::const_iterator i;
2175 i = lower_bound (_map.begin(), _map.end(), pos);
2176 assert (i != _map.end());
2177 if ((*i).frame > pos) {
2178 assert (i != _map.begin());
2185 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2190 TempoMap::BBTPointList::const_iterator
2191 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2193 BBTPointList::const_iterator i;
2196 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2197 assert (i != _map.end());
2198 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2199 assert (i != _map.begin());
2205 TempoMap::BBTPointList::const_iterator
2206 TempoMap::bbt_after_or_at (framepos_t pos)
2208 /* CALLER MUST HOLD READ LOCK */
2210 BBTPointList::const_iterator i;
2212 if (_map.back().frame == pos) {
2214 assert (i != _map.begin());
2219 i = upper_bound (_map.begin(), _map.end(), pos);
2220 assert (i != _map.end());
2224 /** Compare the time of this with that of another MetricSection.
2225 * @param with_bbt True to compare using start(), false to use frame().
2226 * @return -1 for less than, 0 for equal, 1 for greater than.
2230 MetricSection::compare (const MetricSection& other) const
2232 if (start() == other.start()) {
2234 } else if (start() < other.start()) {
2245 MetricSection::operator== (const MetricSection& other) const
2247 return compare (other) == 0;
2251 MetricSection::operator!= (const MetricSection& other) const
2253 return compare (other) != 0;
2257 operator<< (std::ostream& o, const Meter& m) {
2258 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2262 operator<< (std::ostream& o, const Tempo& t) {
2263 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2267 operator<< (std::ostream& o, const MetricSection& section) {
2269 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2271 const TempoSection* ts;
2272 const MeterSection* ms;
2274 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2275 o << *((Tempo*) ts);
2276 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2277 o << *((Meter*) ms);