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_division (const Tempo& tempo, framecnt_t sr) const
59 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
65 return frames_per_division (tempo, sr) * _divisions_per_bar;
68 /***********************************************************************/
70 const string TempoSection::xml_state_node_name = "Tempo";
72 TempoSection::TempoSection (const XMLNode& node)
73 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
75 const XMLProperty *prop;
77 LocaleGuard lg (X_("POSIX"));
79 if ((prop = node.property ("start")) == 0) {
80 error << _("TempoSection XML node has no \"start\" property") << endmsg;
81 throw failed_constructor();
84 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
88 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
89 throw failed_constructor();
94 if ((prop = node.property ("beats-per-minute")) == 0) {
95 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
96 throw failed_constructor();
99 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
100 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
101 throw failed_constructor();
104 if ((prop = node.property ("note-type")) == 0) {
105 /* older session, make note type be quarter by default */
108 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
109 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
110 throw failed_constructor();
114 if ((prop = node.property ("movable")) == 0) {
115 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
116 throw failed_constructor();
119 set_movable (string_is_affirmative (prop->value()));
121 if ((prop = node.property ("bar-offset")) == 0) {
124 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
125 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
126 throw failed_constructor();
132 TempoSection::get_state() const
134 XMLNode *root = new XMLNode (xml_state_node_name);
136 LocaleGuard lg (X_("POSIX"));
138 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
142 root->add_property ("start", buf);
143 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
144 root->add_property ("beats-per-minute", buf);
145 snprintf (buf, sizeof (buf), "%f", _note_type);
146 root->add_property ("note-type", buf);
147 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
148 // root->add_property ("bar-offset", buf);
149 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
150 root->add_property ("movable", buf);
157 TempoSection::update_bar_offset_from_bbt (const Meter& m)
159 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_bar_division + start().ticks) /
160 (m.divisions_per_bar() * BBT_Time::ticks_per_bar_division);
162 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
166 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
170 if (_bar_offset < 0.0) {
175 new_start.bars = start().bars;
177 double ticks = BBT_Time::ticks_per_bar_division * meter.divisions_per_bar() * _bar_offset;
178 new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_bar_division);
179 new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_bar_division);
181 /* remember the 1-based counting properties of beats */
182 new_start.beats += 1;
184 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
185 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
187 set_start (new_start);
190 /***********************************************************************/
192 const string MeterSection::xml_state_node_name = "Meter";
194 MeterSection::MeterSection (const XMLNode& node)
195 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
197 const XMLProperty *prop;
199 LocaleGuard lg (X_("POSIX"));
201 if ((prop = node.property ("start")) == 0) {
202 error << _("MeterSection XML node has no \"start\" property") << endmsg;
203 throw failed_constructor();
206 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
210 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
211 throw failed_constructor();
216 /* beats-per-bar is old; divisions-per-bar is new */
218 if ((prop = node.property ("divisions-per-bar")) == 0) {
219 if ((prop = node.property ("beats-per-bar")) == 0) {
220 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
221 throw failed_constructor();
225 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
226 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
227 throw failed_constructor();
230 if ((prop = node.property ("note-type")) == 0) {
231 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
232 throw failed_constructor();
235 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
236 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
237 throw failed_constructor();
240 if ((prop = node.property ("movable")) == 0) {
241 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
242 throw failed_constructor();
245 set_movable (string_is_affirmative (prop->value()));
249 MeterSection::get_state() const
251 XMLNode *root = new XMLNode (xml_state_node_name);
253 LocaleGuard lg (X_("POSIX"));
255 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
259 root->add_property ("start", buf);
260 snprintf (buf, sizeof (buf), "%f", _note_type);
261 root->add_property ("note-type", buf);
262 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
263 root->add_property ("divisions-per-bar", buf);
264 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
265 root->add_property ("movable", buf);
270 /***********************************************************************/
272 struct MetricSectionSorter {
273 bool operator() (const MetricSection* a, const MetricSection* b) {
274 return a->start() < b->start();
278 TempoMap::TempoMap (framecnt_t fr)
280 metrics = new Metrics;
282 last_bbt_valid = false;
289 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
290 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
292 t->set_movable (false);
293 m->set_movable (false);
295 /* note: frame time is correct (zero) for both of these */
297 metrics->push_back (t);
298 metrics->push_back (m);
301 TempoMap::~TempoMap ()
306 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
308 bool removed = false;
311 Glib::RWLock::WriterLock lm (lock);
314 for (i = metrics->begin(); i != metrics->end(); ++i) {
315 if (dynamic_cast<TempoSection*> (*i) != 0) {
316 if (tempo.frame() == (*i)->frame()) {
317 if ((*i)->movable()) {
326 if (removed && complete_operation) {
327 recompute_map (false);
331 if (removed && complete_operation) {
332 PropertyChanged (PropertyChange ());
337 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
339 bool removed = false;
342 Glib::RWLock::WriterLock lm (lock);
345 for (i = metrics->begin(); i != metrics->end(); ++i) {
346 if (dynamic_cast<MeterSection*> (*i) != 0) {
347 if (tempo.frame() == (*i)->frame()) {
348 if ((*i)->movable()) {
357 if (removed && complete_operation) {
358 recompute_map (true);
364 if (removed && complete_operation) {
365 PropertyChanged (PropertyChange ());
370 TempoMap::do_insert (MetricSection* section)
372 /* CALLER MUST HOLD WRITE LOCK */
374 bool reassign_tempo_bbt = false;
376 assert (section->start().ticks == 0);
378 /* we only allow new meters to be inserted on beat 1 of an existing
382 if (dynamic_cast<MeterSection*>(section)) {
384 /* we need to (potentially) update the BBT times of tempo
385 sections based on this new meter.
388 reassign_tempo_bbt = true;
390 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
392 BBT_Time corrected = section->start();
396 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
397 section->start(), corrected) << endmsg;
399 section->set_start (corrected);
405 /* Look for any existing MetricSection that is of the same type and
406 at the same time as the new one, and remove it before adding
410 Metrics::iterator to_remove = metrics->end ();
412 for (i = metrics->begin(); i != metrics->end(); ++i) {
414 int const c = (*i)->compare (*section);
417 /* this section is before the one to be added; go back round */
420 /* this section is after the one to be added; there can't be any at the same time */
424 /* hacky comparison of type */
425 bool const a = dynamic_cast<TempoSection*> (*i) != 0;
426 bool const b = dynamic_cast<TempoSection*> (section) != 0;
434 if (to_remove != metrics->end()) {
435 /* remove the MetricSection at the same time as the one we are about to add */
436 metrics->erase (to_remove);
439 /* Add the given MetricSection */
441 for (i = metrics->begin(); i != metrics->end(); ++i) {
443 if ((*i)->compare (*section) < 0) {
447 metrics->insert (i, section);
451 if (i == metrics->end()) {
452 metrics->insert (metrics->end(), section);
455 recompute_map (reassign_tempo_bbt);
459 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
461 const TempoSection& first (first_tempo());
464 remove_tempo (ts, false);
465 add_tempo (tempo, where);
467 Glib::RWLock::WriterLock lm (lock);
468 /* cannot move the first tempo section */
469 *((Tempo*)&first) = tempo;
470 recompute_map (false);
473 PropertyChanged (PropertyChange ());
477 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
480 Glib::RWLock::WriterLock lm (lock);
482 /* new tempos always start on a beat */
485 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
487 /* find the meter to use to set the bar offset of this
491 const Meter* meter = &first_meter();
493 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
494 at something, because we insert the default tempo and meter during
495 TempoMap construction.
497 now see if we can find better candidates.
500 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
502 const MeterSection* m;
504 if (where < (*i)->start()) {
508 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
513 ts->update_bar_offset_from_bbt (*meter);
520 PropertyChanged (PropertyChange ());
524 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
526 const MeterSection& first (first_meter());
529 remove_meter (ms, false);
530 add_meter (meter, where);
532 Glib::RWLock::WriterLock lm (lock);
533 /* cannot move the first meter section */
534 *((Meter*)&first) = meter;
535 recompute_map (true);
538 PropertyChanged (PropertyChange ());
542 TempoMap::add_meter (const Meter& meter, BBT_Time where)
545 Glib::RWLock::WriterLock lm (lock);
547 /* a new meter always starts a new bar on the first beat. so
548 round the start time appropriately. remember that
549 `where' is based on the existing tempo map, not
550 the result after we insert the new meter.
554 if (where.beats != 1) {
559 /* new meters *always* start on a beat. */
562 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
566 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
571 PropertyChanged (PropertyChange ());
575 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
577 Tempo newtempo (beats_per_minute, note_type);
580 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
581 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
583 Glib::RWLock::WriterLock lm (lock);
584 *((Tempo*) t) = newtempo;
585 recompute_map (false);
587 PropertyChanged (PropertyChange ());
594 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
596 Tempo newtempo (beats_per_minute, note_type);
602 /* find the TempoSection immediately preceding "where"
605 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
607 if ((*i)->frame() > where) {
613 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
623 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
633 Glib::RWLock::WriterLock lm (lock);
634 /* cannot move the first tempo section */
635 *((Tempo*)prev) = newtempo;
636 recompute_map (false);
639 PropertyChanged (PropertyChange ());
643 TempoMap::first_meter () const
645 const MeterSection *m = 0;
647 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
648 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
653 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
659 TempoMap::first_tempo () const
661 const TempoSection *t = 0;
663 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
664 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
669 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
675 TempoMap::timestamp_metrics_from_audio_time ()
678 const MeterSection* meter;
679 const TempoSection* tempo;
683 meter = &first_meter ();
684 tempo = &first_tempo ();
689 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
692 MetricSection* prev = 0;
694 for (i = metrics->begin(); i != metrics->end(); ++i) {
697 TempoMetric metric (*meter, *tempo);
700 metric.set_start (prev->start());
701 metric.set_frame (prev->frame());
703 // metric will be at frames=0 bbt=1|1|0 by default
704 // which is correct for our purpose
707 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
708 bbt_time_unlocked ((*i)->frame(), bbt, bi);
710 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
716 if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
717 /* round up to next beat */
723 if (bbt.beats != 1) {
724 /* round up to next bar */
730 // cerr << bbt << endl;
732 (*i)->set_start (bbt);
734 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
736 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
737 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
739 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
741 fatal << _("programming error: unhandled MetricSection type") << endmsg;
749 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
757 TempoMap::require_map_to (framepos_t pos)
759 if (_map.empty() || _map.back().frame < pos) {
760 recompute_map (false, pos);
765 TempoMap::require_map_to (const BBT_Time& bbt)
767 if (_map.empty() || _map.back().bbt() < bbt) {
768 recompute_map (false, 99);
773 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
779 double divisions_per_bar;
781 double current_frame;
783 Metrics::iterator next_metric;
787 /* compute 1 mins worth */
788 end = _frame_rate * 60;
790 end = _map.back().frame;
794 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
798 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
799 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
805 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
806 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
812 /* assumes that the first meter & tempo are at frame zero */
814 meter->set_frame (0);
815 tempo->set_frame (0);
817 /* assumes that the first meter & tempo are at 1|1|0 */
822 divisions_per_bar = meter->divisions_per_bar ();
823 beat_frames = meter->frames_per_division (*tempo,_frame_rate);
825 if (reassign_tempo_bbt) {
827 MeterSection* rmeter = meter;
829 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
831 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
833 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
835 /* reassign the BBT time of this tempo section
836 * based on its bar offset position.
839 ts->update_bbt_time_from_bar_offset (*rmeter);
841 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
844 fatal << _("programming error: unhandled MetricSection type") << endmsg;
850 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2 dpb %3 fpb %4\n",
851 *((Meter*)meter), *((Tempo*)tempo), divisions_per_bar, beat_frames));
853 next_metric = metrics->begin();
854 ++next_metric; // skip meter (or tempo)
855 ++next_metric; // skip tempo (or meter)
857 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
858 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
860 while (current_frame < end) {
863 current_frame += beat_frames;
865 if (current.beats > meter->divisions_per_bar()) {
870 if (next_metric != metrics->end()) {
872 /* no operator >= so invert operator < */
874 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
876 if (!(current < (*next_metric)->start())) {
879 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
883 /* new tempo section: if its on a beat,
884 * we don't have to do anything other
885 * than recompute various distances,
886 * done further below as we transition
887 * the next metric section.
889 * if its not on the beat, we have to
890 * compute the duration of the beat it
891 * is within, which will be different
892 * from the preceding following ones
893 * since it takes part of its duration
894 * from the preceding tempo and part
895 * from this new tempo.
898 if (tempo->start().ticks != 0) {
900 double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);
902 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
903 tempo->start(), current_frame, tempo->bar_offset()));
905 /* back up to previous beat */
906 current_frame -= beat_frames;
907 /* set tempo section location based on offset from last beat */
908 tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
909 /* advance to the location of the new (adjusted) beat */
910 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
911 /* next metric doesn't have to
912 * match this precisely to
915 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
919 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
920 tempo->start(), current_frame));
921 tempo->set_frame (current_frame);
924 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
928 /* new meter section: always defines the
932 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
933 meter->start(), current, current_frame));
935 assert (current.beats == 1);
937 meter->set_frame (current_frame);
940 divisions_per_bar = meter->divisions_per_bar ();
941 beat_frames = meter->frames_per_division (*tempo, _frame_rate);
943 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
944 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
948 if (next_metric != metrics->end() && ((*next_metric)->start() == current)) {
949 /* same position so go back and set this one up before advancing
956 if (current.beats == 1) {
957 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
958 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
960 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
961 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
967 TempoMap::metric_at (framepos_t frame) const
969 Glib::RWLock::ReaderLock lm (lock);
970 TempoMetric m (first_meter(), first_tempo());
974 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
975 at something, because we insert the default tempo and meter during
976 TempoMap construction.
978 now see if we can find better candidates.
981 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
983 // cerr << "Looking at a metric section " << **i << endl;
985 if ((*i)->frame() > frame) {
989 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
990 m.set_tempo (*tempo);
991 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
992 m.set_meter (*meter);
995 m.set_frame ((*i)->frame ());
996 m.set_start ((*i)->start ());
999 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1004 TempoMap::metric_at (BBT_Time bbt) const
1006 Glib::RWLock::ReaderLock lm (lock);
1007 TempoMetric m (first_meter(), first_tempo());
1011 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1012 at something, because we insert the default tempo and meter during
1013 TempoMap construction.
1015 now see if we can find better candidates.
1018 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1020 BBT_Time section_start ((*i)->start());
1022 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1026 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1027 m.set_tempo (*tempo);
1028 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1029 m.set_meter (*meter);
1032 m.set_frame ((*i)->frame ());
1033 m.set_start (section_start);
1040 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1043 Glib::RWLock::ReaderLock lm (lock);
1044 BBTPointList::const_iterator i = bbt_before_or_at (frame);
1045 bbt_time_unlocked (frame, bbt, i);
1050 TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1052 bbt.bars = (*i).bar;
1053 bbt.beats = (*i).beat;
1055 if ((*i).frame == frame) {
1058 bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
1059 BBT_Time::ticks_per_bar_division);
1064 TempoMap::frame_time (const BBT_Time& bbt)
1066 Glib::RWLock::ReaderLock lm (lock);
1068 BBTPointList::const_iterator s = bbt_point_for (BBT_Time (1, 1, 0));
1069 BBTPointList::const_iterator e = bbt_point_for (BBT_Time (bbt.bars, bbt.beats, 0));
1071 if (bbt.ticks != 0) {
1072 return ((*e).frame - (*s).frame) +
1073 llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
1075 return ((*e).frame - (*s).frame);
1080 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1082 Glib::RWLock::ReaderLock lm (lock);
1083 framecnt_t frames = 0;
1086 bbt_time (pos, when);
1087 frames = bbt_duration_at_unlocked (when, bbt,dir);
1093 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1095 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1099 /* round back to the previous precise beat */
1100 BBTPointList::const_iterator wi = bbt_point_for (BBT_Time (when.bars, when.beats, 0));
1101 BBTPointList::const_iterator start (wi);
1102 double tick_frames = 0;
1104 assert (wi != _map.end());
1106 /* compute how much rounding we did because of non-zero ticks */
1108 if (when.ticks != 0) {
1109 tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
1115 while (wi != _map.end() && bars < bbt.bars) {
1117 if ((*wi).beat == 1) {
1121 assert (wi != _map.end());
1123 while (wi != _map.end() && beats < bbt.beats) {
1127 assert (wi != _map.end());
1129 /* add any additional frames related to ticks in the added value */
1131 if (bbt.ticks != 0) {
1132 tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
1135 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1139 TempoMap::round_to_bar (framepos_t fr, int dir)
1142 Glib::RWLock::ReaderLock lm (lock);
1143 return round_to_type (fr, dir, Bar);
1148 TempoMap::round_to_beat (framepos_t fr, int dir)
1151 Glib::RWLock::ReaderLock lm (lock);
1152 return round_to_type (fr, dir, Beat);
1157 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1159 Glib::RWLock::ReaderLock lm (lock);
1160 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1162 uint32_t ticks_one_subdivisions_worth;
1163 uint32_t difference;
1165 bbt_time_unlocked (fr, the_beat, i);
1167 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1168 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1170 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
1174 /* round to next (even if we're on a subdivision */
1176 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1179 /* right on the subdivision, so the difference is just the subdivision ticks */
1180 the_beat.ticks += ticks_one_subdivisions_worth;
1183 /* not on subdivision, compute distance to next subdivision */
1185 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1188 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1189 assert (i != _map.end());
1191 assert (i != _map.end());
1192 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1196 } else if (dir < 0) {
1198 /* round to previous (even if we're on a subdivision) */
1200 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1203 /* right on the subdivision, so the difference is just the subdivision ticks */
1204 difference = ticks_one_subdivisions_worth;
1206 /* not on subdivision, compute distance to previous subdivision, which
1207 is just the modulus.
1213 if (the_beat.ticks < difference) {
1214 if (i == _map.begin()) {
1215 /* can't go backwards from wherever pos is, so just return it */
1219 the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
1221 the_beat.ticks -= difference;
1225 /* round to nearest */
1229 /* compute the distance to the previous and next subdivision */
1231 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1233 /* closer to the next subdivision, so shift forward */
1235 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1237 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1239 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1240 assert (i != _map.end());
1242 assert (i != _map.end());
1243 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1244 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1247 } else if (rem > 0) {
1249 /* closer to previous subdivision, so shift backward */
1251 if (rem > the_beat.ticks) {
1252 if (i == _map.begin()) {
1253 /* can't go backwards past zero, so ... */
1256 /* step back to previous beat */
1258 the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
1259 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1261 the_beat.ticks = lrint (the_beat.ticks - rem);
1262 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1265 /* on the subdivision, do nothing */
1269 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
1270 (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
1274 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1276 BBTPointList::const_iterator fi;
1279 fi = bbt_after_or_at (frame);
1281 fi = bbt_before_or_at (frame);
1284 assert (fi != _map.end());
1286 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));
1291 /* find bar previous to 'frame' */
1293 if ((*fi).beat == 1 && (*fi).frame == frame) {
1297 while ((*fi).beat > 1) {
1298 if (fi == _map.begin()) {
1303 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1304 (*fi).bar, (*fi).beat, (*fi).frame));
1307 } else if (dir > 0) {
1309 /* find bar following 'frame' */
1311 if ((*fi).beat == 1 && (*fi).frame == frame) {
1315 while ((*fi).beat != 1) {
1317 if (fi == _map.end()) {
1323 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1324 (*fi).bar, (*fi).beat, (*fi).frame));
1329 /* true rounding: find nearest bar */
1331 BBTPointList::const_iterator prev = fi;
1332 BBTPointList::const_iterator next = fi;
1334 if ((*fi).frame == frame) {
1338 while ((*prev).beat != 1) {
1339 if (prev == _map.begin()) {
1345 while ((*next).beat != 1) {
1347 if (next == _map.end()) {
1353 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1354 return (*prev).frame;
1356 return (*next).frame;
1365 if ((*fi).frame >= frame) {
1366 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1369 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1370 (*fi).bar, (*fi).beat, (*fi).frame));
1372 } else if (dir > 0) {
1373 if ((*fi).frame <= frame) {
1374 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1377 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1378 (*fi).bar, (*fi).beat, (*fi).frame));
1381 /* find beat nearest to frame */
1382 if ((*fi).frame == frame) {
1386 BBTPointList::const_iterator prev = fi;
1387 BBTPointList::const_iterator next = fi;
1391 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1392 return (*prev).frame;
1394 return (*next).frame;
1406 TempoMap::map (TempoMap::BBTPointList::const_iterator& begin,
1407 TempoMap::BBTPointList::const_iterator& end,
1408 framepos_t lower, framepos_t upper)
1410 if (_map.empty() || upper >= _map.back().frame) {
1411 recompute_map (false, upper);
1414 begin = lower_bound (_map.begin(), _map.end(), lower);
1415 end = upper_bound (_map.begin(), _map.end(), upper);
1419 TempoMap::tempo_section_at (framepos_t frame) const
1421 Glib::RWLock::ReaderLock lm (lock);
1422 Metrics::const_iterator i;
1423 TempoSection* prev = 0;
1425 for (i = metrics->begin(); i != metrics->end(); ++i) {
1428 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1430 if ((*i)->frame() > frame) {
1446 TempoMap::tempo_at (framepos_t frame) const
1448 TempoMetric m (metric_at (frame));
1454 TempoMap::meter_at (framepos_t frame) const
1456 TempoMetric m (metric_at (frame));
1461 TempoMap::get_state ()
1463 Metrics::const_iterator i;
1464 XMLNode *root = new XMLNode ("TempoMap");
1467 Glib::RWLock::ReaderLock lm (lock);
1468 for (i = metrics->begin(); i != metrics->end(); ++i) {
1469 root->add_child_nocopy ((*i)->get_state());
1477 TempoMap::set_state (const XMLNode& node, int /*version*/)
1480 Glib::RWLock::WriterLock lm (lock);
1483 XMLNodeConstIterator niter;
1484 Metrics old_metrics (*metrics);
1485 MeterSection* last_meter = 0;
1489 nlist = node.children();
1491 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1492 XMLNode* child = *niter;
1494 if (child->name() == TempoSection::xml_state_node_name) {
1497 TempoSection* ts = new TempoSection (*child);
1498 metrics->push_back (ts);
1500 if (ts->bar_offset() < 0.0) {
1502 ts->update_bar_offset_from_bbt (*last_meter);
1507 catch (failed_constructor& err){
1508 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1509 *metrics = old_metrics;
1513 } else if (child->name() == MeterSection::xml_state_node_name) {
1516 MeterSection* ms = new MeterSection (*child);
1517 metrics->push_back (ms);
1521 catch (failed_constructor& err) {
1522 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1523 *metrics = old_metrics;
1529 if (niter == nlist.end()) {
1531 MetricSectionSorter cmp;
1532 metrics->sort (cmp);
1533 recompute_map (true);
1537 PropertyChanged (PropertyChange ());
1543 TempoMap::dump (std::ostream& o) const
1545 const MeterSection* m;
1546 const TempoSection* t;
1548 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1550 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1551 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? "
1552 << t->movable() << ')' << endl;
1553 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1554 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1555 << " (movable? " << m->movable() << ')' << endl;
1561 TempoMap::n_tempos() const
1563 Glib::RWLock::ReaderLock lm (lock);
1566 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1567 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1576 TempoMap::n_meters() const
1578 Glib::RWLock::ReaderLock lm (lock);
1581 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1582 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1591 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1593 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1594 if ((*i)->frame() >= where && (*i)->movable ()) {
1595 (*i)->set_frame ((*i)->frame() + amount);
1599 timestamp_metrics_from_audio_time ();
1601 PropertyChanged (PropertyChange ());
1604 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1605 * pos can be -ve, if required.
1608 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
1610 Metrics::const_iterator i;
1611 const TempoSection* tempo;
1613 /* Find the starting tempo */
1615 for (i = metrics->begin(); i != metrics->end(); ++i) {
1617 /* This is a bit of a hack, but pos could be -ve, and if it is,
1618 we consider the initial metric changes (at time 0) to actually
1619 be in effect at pos.
1621 framepos_t f = (*i)->frame ();
1622 if (pos < 0 && f == 0) {
1630 const TempoSection* t;
1632 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1639 tempo -> the Tempo for "pos"
1640 i -> for first new metric after "pos", possibly metrics->end()
1645 /* Distance to the end of this section in frames */
1646 framecnt_t distance_frames = i == metrics->end() ? max_framepos : ((*i)->frame() - pos);
1648 /* Distance to the end in beats */
1649 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1651 /* Amount to subtract this time */
1652 double const sub = min (distance_beats, beats);
1656 pos += sub * tempo->frames_per_beat (_frame_rate);
1658 /* Move on if there's anything to move to */
1659 if (i != metrics->end ()) {
1660 const TempoSection* t;
1662 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1673 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1675 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
1677 Metrics::const_iterator i;
1678 const TempoSection* tempo = 0;
1679 const TempoSection* t;
1681 /* Find the starting tempo */
1683 for (i = metrics->begin(); i != metrics->end(); ++i) {
1685 /* This is a bit of a hack, but pos could be -ve, and if it is,
1686 we consider the initial metric changes (at time 0) to actually
1687 be in effect at pos.
1689 framepos_t f = (*i)->frame ();
1690 if (pos < 0 && f == 0) {
1694 if ((*i)->frame() > pos) {
1698 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1703 bool no_more_tempos = false;
1705 /* Move i back to the tempo before "pos" */
1706 if (i != metrics->begin ()) {
1707 while (i != metrics->begin ()) {
1709 t = dynamic_cast<TempoSection*> (*i);
1715 no_more_tempos = true;
1720 tempo -> the Tempo for "pos"
1721 i -> the first metric before "pos", unless no_more_tempos is true
1726 /* Distance to the end of this section in frames */
1727 framecnt_t distance_frames = no_more_tempos ? max_framepos : (pos - (*i)->frame());
1729 /* Distance to the end in beats */
1730 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1732 /* Amount to subtract this time */
1733 double const sub = min (distance_beats, beats);
1737 pos -= sub * tempo->frames_per_beat (_frame_rate);
1739 /* Move i and tempo back, if there's anything to move to */
1740 if (i != metrics->begin ()) {
1741 while (i != metrics->begin ()) {
1743 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1749 no_more_tempos = true;
1756 /** Add the BBT interval op to pos and return the result */
1758 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
1760 Metrics::const_iterator i;
1761 const MeterSection* meter;
1762 const MeterSection* m;
1763 const TempoSection* tempo;
1764 const TempoSection* t;
1765 double frames_per_beat;
1767 meter = &first_meter ();
1768 tempo = &first_tempo ();
1773 /* find the starting metrics for tempo & meter */
1775 for (i = metrics->begin(); i != metrics->end(); ++i) {
1777 if ((*i)->frame() > pos) {
1781 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1783 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1790 meter -> the Meter for "pos"
1791 tempo -> the Tempo for "pos"
1792 i -> for first new metric after "pos", possibly metrics->end()
1795 /* now comes the complicated part. we have to add one beat a time,
1796 checking for a new metric on every beat.
1799 frames_per_beat = tempo->frames_per_beat (_frame_rate);
1808 /* check if we need to use a new metric section: has adding frames moved us
1809 to or after the start of the next metric section? in which case, use it.
1812 if (i != metrics->end()) {
1813 if ((*i)->frame() <= pos) {
1815 /* about to change tempo or meter, so add the
1816 * number of frames for the bars we've just
1817 * traversed before we change the
1818 * frames_per_beat value.
1821 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
1824 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1826 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1830 frames_per_beat = tempo->frames_per_beat (_frame_rate);
1837 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
1843 /* given the current meter, have we gone past the end of the bar ? */
1848 /* check if we need to use a new metric section: has adding frames moved us
1849 to or after the start of the next metric section? in which case, use it.
1852 if (i != metrics->end()) {
1853 if ((*i)->frame() <= pos) {
1855 /* about to change tempo or meter, so add the
1856 * number of frames for the beats we've just
1857 * traversed before we change the
1858 * frames_per_beat value.
1861 pos += llrint (beats * frames_per_beat);
1864 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1866 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1870 frames_per_beat = tempo->frames_per_beat (_frame_rate);
1875 pos += llrint (beats * frames_per_beat);
1878 if (op.ticks >= BBT_Time::ticks_per_bar_division) {
1879 pos += llrint (frames_per_beat + /* extra beat */
1880 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_bar_division) /
1881 (double) BBT_Time::ticks_per_bar_division)));
1883 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_bar_division));
1890 /** Count the number of beats that are equivalent to distance when going forward,
1894 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
1896 BBTPointList::const_iterator i = bbt_after_or_at (pos);
1897 Evoral::MusicalTime beats = 0;
1898 framepos_t end = pos + distance;
1900 require_map_to (end);
1902 /* if our starting BBTPoint is after pos, add a fractional beat
1903 to represent that distance.
1906 if ((*i).frame != pos) {
1907 beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1910 while (i != _map.end() && (*i).frame < end) {
1914 assert (i != _map.end());
1916 /* if our ending BBTPoint is after the end, subtract a fractional beat
1917 to represent that distance.
1920 if ((*i).frame > end) {
1921 beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1927 TempoMap::BBTPointList::const_iterator
1928 TempoMap::bbt_before_or_at (framepos_t pos)
1930 require_map_to (pos);
1931 BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), pos);
1932 assert (i != _map.end());
1933 if ((*i).frame > pos) {
1934 assert (i != _map.begin());
1940 TempoMap::BBTPointList::const_iterator
1941 TempoMap::bbt_after_or_at (framepos_t pos)
1943 require_map_to (pos);
1944 BBTPointList::const_iterator i = upper_bound (_map.begin(), _map.end(), pos);
1945 assert (i != _map.end());
1950 bool operator() (const BBT_Time& a, const BBT_Time& b) {
1955 TempoMap::BBTPointList::const_iterator
1956 TempoMap::bbt_point_for (const BBT_Time& bbt)
1959 int additional_minutes = 1;
1961 while (_map.empty() || _map.back().bar < (bbt.bars + 1)) {
1962 /* add some more distance, using bigger steps each time */
1963 require_map_to (_map.back().frame + (_frame_rate * 60 * additional_minutes));
1964 additional_minutes *= 2;
1967 BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
1968 assert (i != _map.end());
1973 /** Compare the time of this with that of another MetricSection.
1974 * @param with_bbt True to compare using start(), false to use frame().
1975 * @return -1 for less than, 0 for equal, 1 for greater than.
1979 MetricSection::compare (const MetricSection& other) const
1981 if (start() == other.start()) {
1983 } else if (start() < other.start()) {
1994 MetricSection::operator== (const MetricSection& other) const
1996 return compare (other) == 0;
2000 MetricSection::operator!= (const MetricSection& other) const
2002 return compare (other) != 0;
2006 operator<< (std::ostream& o, const Meter& m) {
2007 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2011 operator<< (std::ostream& o, const Tempo& t) {
2012 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2016 operator<< (std::ostream& o, const MetricSection& section) {
2018 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2020 const TempoSection* ts;
2021 const MeterSection* ms;
2023 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2024 o << *((Tempo*) ts);
2025 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2026 o << *((Meter*) ms);