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)
287 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
288 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
290 t->set_movable (false);
291 m->set_movable (false);
293 /* note: frame time is correct (zero) for both of these */
295 metrics.push_back (t);
296 metrics.push_back (m);
299 TempoMap::~TempoMap ()
304 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
306 bool removed = false;
309 Glib::RWLock::WriterLock lm (lock);
312 for (i = metrics.begin(); i != metrics.end(); ++i) {
313 if (dynamic_cast<TempoSection*> (*i) != 0) {
314 if (tempo.frame() == (*i)->frame()) {
315 if ((*i)->movable()) {
324 if (removed && complete_operation) {
325 recompute_map (false);
329 if (removed && complete_operation) {
330 PropertyChanged (PropertyChange ());
335 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
337 bool removed = false;
340 Glib::RWLock::WriterLock lm (lock);
343 for (i = metrics.begin(); i != metrics.end(); ++i) {
344 if (dynamic_cast<MeterSection*> (*i) != 0) {
345 if (tempo.frame() == (*i)->frame()) {
346 if ((*i)->movable()) {
355 if (removed && complete_operation) {
356 recompute_map (true);
360 if (removed && complete_operation) {
361 PropertyChanged (PropertyChange ());
366 TempoMap::do_insert (MetricSection* section)
368 bool need_add = true;
370 assert (section->start().ticks == 0);
372 /* we only allow new meters to be inserted on beat 1 of an existing
376 if (dynamic_cast<MeterSection*>(section)) {
378 /* we need to (potentially) update the BBT times of tempo
379 sections based on this new meter.
382 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
384 BBT_Time corrected = section->start();
388 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
389 section->start(), corrected) << endmsg;
391 section->set_start (corrected);
397 /* Look for any existing MetricSection that is of the same type and
398 at the same time as the new one, and remove it before adding
402 Metrics::iterator to_remove = metrics.end ();
404 for (i = metrics.begin(); i != metrics.end(); ++i) {
406 int const c = (*i)->compare (*section);
409 /* this section is before the one to be added; go back round */
412 /* this section is after the one to be added; there can't be any at the same time */
416 /* hacky comparison of type */
417 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
418 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
420 if (iter_is_tempo == insert_is_tempo) {
422 if (!(*i)->movable()) {
424 /* can't (re)move this section, so overwrite
425 * its data content (but not its properties as
429 if (!iter_is_tempo) {
430 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
432 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
443 if (to_remove != metrics.end()) {
444 /* remove the MetricSection at the same time as the one we are about to add */
445 metrics.erase (to_remove);
448 /* Add the given MetricSection */
451 for (i = metrics.begin(); i != metrics.end(); ++i) {
453 if ((*i)->compare (*section) < 0) {
457 metrics.insert (i, section);
461 if (i == metrics.end()) {
462 metrics.insert (metrics.end(), section);
468 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
470 const TempoSection& first (first_tempo());
473 remove_tempo (ts, false);
474 add_tempo (tempo, where);
477 Glib::RWLock::WriterLock lm (lock);
478 /* cannot move the first tempo section */
479 *((Tempo*)&first) = tempo;
480 recompute_map (false);
484 PropertyChanged (PropertyChange ());
488 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
491 Glib::RWLock::WriterLock lm (lock);
493 /* new tempos always start on a beat */
496 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
498 /* find the meter to use to set the bar offset of this
502 const Meter* meter = &first_meter();
504 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
505 at something, because we insert the default tempo and meter during
506 TempoMap construction.
508 now see if we can find better candidates.
511 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
513 const MeterSection* m;
515 if (where < (*i)->start()) {
519 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
524 ts->update_bar_offset_from_bbt (*meter);
530 recompute_map (false);
534 PropertyChanged (PropertyChange ());
538 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
540 const MeterSection& first (first_meter());
543 remove_meter (ms, false);
544 add_meter (meter, where);
547 Glib::RWLock::WriterLock lm (lock);
548 /* cannot move the first meter section */
549 *((Meter*)&first) = meter;
550 recompute_map (true);
555 PropertyChanged (PropertyChange ());
559 TempoMap::add_meter (const Meter& meter, BBT_Time where)
562 Glib::RWLock::WriterLock lm (lock);
564 /* a new meter always starts a new bar on the first beat. so
565 round the start time appropriately. remember that
566 `where' is based on the existing tempo map, not
567 the result after we insert the new meter.
571 if (where.beats != 1) {
576 /* new meters *always* start on a beat. */
579 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
580 recompute_map (true);
585 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
590 PropertyChanged (PropertyChange ());
594 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
596 Tempo newtempo (beats_per_minute, note_type);
599 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
600 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
602 Glib::RWLock::WriterLock lm (lock);
603 *((Tempo*) t) = newtempo;
604 recompute_map (false);
606 PropertyChanged (PropertyChange ());
613 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
615 Tempo newtempo (beats_per_minute, note_type);
621 /* find the TempoSection immediately preceding "where"
624 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
626 if ((*i)->frame() > where) {
632 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
642 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
652 Glib::RWLock::WriterLock lm (lock);
653 /* cannot move the first tempo section */
654 *((Tempo*)prev) = newtempo;
655 recompute_map (false);
658 PropertyChanged (PropertyChange ());
662 TempoMap::first_meter () const
664 const MeterSection *m = 0;
666 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
667 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
672 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
678 TempoMap::first_tempo () const
680 const TempoSection *t = 0;
682 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
683 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
688 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
694 TempoMap::require_map_to (framepos_t pos)
696 Glib::RWLock::WriterLock lm (lock);
698 if (_map.empty() || _map.back().frame < pos) {
704 TempoMap::require_map_to (const BBT_Time& bbt)
706 Glib::RWLock::WriterLock lm (lock);
708 /* since we have no idea where BBT is if its off the map, see the last
709 * point in the map is past BBT, and if not add an arbitrary amount of
713 int additional_minutes = 1;
716 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
719 /* add some more distance, using bigger steps each time */
720 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
721 additional_minutes *= 2;
726 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
728 /* CALLER MUST HOLD WRITE LOCK */
730 MeterSection* meter = 0;
731 TempoSection* tempo = 0;
732 double current_frame;
734 Metrics::iterator next_metric;
739 /* compute 1 mins worth */
740 end = _frame_rate * 60;
742 end = _map.back().frame;
745 if (!_map.empty ()) {
746 /* never allow the map to be shortened */
747 end = max (end, _map.back().frame);
751 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
753 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
756 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
762 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
765 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
771 /* assumes that the first meter & tempo are at frame zero */
773 meter->set_frame (0);
774 tempo->set_frame (0);
776 /* assumes that the first meter & tempo are at 1|1|0 */
781 if (reassign_tempo_bbt) {
783 MeterSection* rmeter = meter;
785 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
787 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
792 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
794 /* reassign the BBT time of this tempo section
795 * based on its bar offset position.
798 ts->update_bbt_time_from_bar_offset (*rmeter);
800 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
803 fatal << _("programming error: unhandled MetricSection type") << endmsg;
809 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
811 next_metric = metrics.begin();
812 ++next_metric; // skip meter (or tempo)
813 ++next_metric; // skip tempo (or meter)
817 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
818 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
821 /* silly call from Session::process() during startup
826 _extend_map (tempo, meter, next_metric, current, current_frame, end);
830 TempoMap::extend_map (framepos_t end)
832 /* CALLER MUST HOLD WRITE LOCK */
835 recompute_map (false, end);
839 BBTPointList::const_iterator i = _map.end();
840 Metrics::iterator next_metric;
844 BBT_Time last_metric_start;
846 if ((*i).tempo->frame() > (*i).meter->frame()) {
847 last_metric_start = (*i).tempo->start();
849 last_metric_start = (*i).meter->start();
852 /* find the metric immediately after the tempo + meter sections for the
853 * last point in the map
856 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
857 if ((*next_metric)->start() > last_metric_start) {
862 /* we cast away const here because this is the one place where we need
863 * to actually modify the frame time of each metric section.
866 _extend_map (const_cast<TempoSection*> ((*i).tempo),
867 const_cast<MeterSection*> ((*i).meter),
868 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
872 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
873 Metrics::iterator next_metric,
874 BBT_Time current, framepos_t current_frame, framepos_t end)
876 /* CALLER MUST HOLD WRITE LOCK */
880 double divisions_per_bar;
883 divisions_per_bar = meter->divisions_per_bar ();
884 beat_frames = meter->frames_per_division (*tempo,_frame_rate);
886 while (current_frame < end) {
889 current_frame += beat_frames;
891 if (current.beats > meter->divisions_per_bar()) {
896 if (next_metric != metrics.end()) {
898 /* no operator >= so invert operator < */
900 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
902 if (!(current < (*next_metric)->start())) {
905 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
909 /* new tempo section: if its on a beat,
910 * we don't have to do anything other
911 * than recompute various distances,
912 * done further below as we transition
913 * the next metric section.
915 * if its not on the beat, we have to
916 * compute the duration of the beat it
917 * is within, which will be different
918 * from the preceding following ones
919 * since it takes part of its duration
920 * from the preceding tempo and part
921 * from this new tempo.
924 if (tempo->start().ticks != 0) {
926 double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);
928 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
929 tempo->start(), current_frame, tempo->bar_offset()));
931 /* back up to previous beat */
932 current_frame -= beat_frames;
933 /* set tempo section location based on offset from last beat */
934 tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
935 /* advance to the location of the new (adjusted) beat */
936 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
937 /* next metric doesn't have to
938 * match this precisely to
941 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
945 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
946 tempo->start(), current_frame));
947 tempo->set_frame (current_frame);
950 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
954 /* new meter section: always defines the
958 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
959 meter->start(), current, current_frame));
961 assert (current.beats == 1);
963 meter->set_frame (current_frame);
966 divisions_per_bar = meter->divisions_per_bar ();
967 beat_frames = meter->frames_per_division (*tempo, _frame_rate);
969 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
970 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
974 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
975 /* same position so go back and set this one up before advancing
982 if (current.beats == 1) {
983 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
984 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
986 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
987 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
993 TempoMap::metric_at (framepos_t frame) const
995 Glib::RWLock::ReaderLock lm (lock);
996 TempoMetric m (first_meter(), first_tempo());
1000 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1001 at something, because we insert the default tempo and meter during
1002 TempoMap construction.
1004 now see if we can find better candidates.
1007 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1009 // cerr << "Looking at a metric section " << **i << endl;
1011 if ((*i)->frame() > frame) {
1015 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1016 m.set_tempo (*tempo);
1017 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1018 m.set_meter (*meter);
1021 m.set_frame ((*i)->frame ());
1022 m.set_start ((*i)->start ());
1025 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1030 TempoMap::metric_at (BBT_Time bbt) const
1032 Glib::RWLock::ReaderLock lm (lock);
1033 TempoMetric m (first_meter(), first_tempo());
1037 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1038 at something, because we insert the default tempo and meter during
1039 TempoMap construction.
1041 now see if we can find better candidates.
1044 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1046 BBT_Time section_start ((*i)->start());
1048 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1052 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1053 m.set_tempo (*tempo);
1054 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1055 m.set_meter (*meter);
1058 m.set_frame ((*i)->frame ());
1059 m.set_start (section_start);
1066 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1068 require_map_to (frame);
1070 Glib::RWLock::ReaderLock lm (lock);
1071 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1075 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1077 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1080 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1083 if (_map.empty() || _map.back().frame < frame) {
1084 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1087 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1091 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1093 /* CALLER MUST HOLD READ LOCK */
1095 bbt.bars = (*i).bar;
1096 bbt.beats = (*i).beat;
1098 if ((*i).frame == frame) {
1101 bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
1102 BBT_Time::ticks_per_bar_division);
1107 TempoMap::frame_time (const BBT_Time& bbt)
1109 require_map_to (bbt);
1111 Glib::RWLock::ReaderLock lm (lock);
1113 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1114 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1116 if (bbt.ticks != 0) {
1117 return ((*e).frame - (*s).frame) +
1118 llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
1120 return ((*e).frame - (*s).frame);
1125 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1127 Glib::RWLock::ReaderLock lm (lock);
1128 framecnt_t frames = 0;
1131 bbt_time (pos, when);
1132 frames = bbt_duration_at_unlocked (when, bbt,dir);
1138 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1140 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1144 /* round back to the previous precise beat */
1145 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1146 BBTPointList::const_iterator start (wi);
1147 double tick_frames = 0;
1149 assert (wi != _map.end());
1151 /* compute how much rounding we did because of non-zero ticks */
1153 if (when.ticks != 0) {
1154 tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
1160 while (wi != _map.end() && bars < bbt.bars) {
1162 if ((*wi).is_bar()) {
1166 assert (wi != _map.end());
1168 while (wi != _map.end() && beats < bbt.beats) {
1172 assert (wi != _map.end());
1174 /* add any additional frames related to ticks in the added value */
1176 if (bbt.ticks != 0) {
1177 tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
1180 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1184 TempoMap::round_to_bar (framepos_t fr, int dir)
1186 return round_to_type (fr, dir, Bar);
1190 TempoMap::round_to_beat (framepos_t fr, int dir)
1192 return round_to_type (fr, dir, Beat);
1196 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1198 require_map_to (fr);
1200 Glib::RWLock::ReaderLock lm (lock);
1201 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1203 uint32_t ticks_one_subdivisions_worth;
1204 uint32_t difference;
1206 bbt_time (fr, the_beat, i);
1208 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1209 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1211 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
1215 /* round to next (even if we're on a subdivision */
1217 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1220 /* right on the subdivision, so the difference is just the subdivision ticks */
1221 the_beat.ticks += ticks_one_subdivisions_worth;
1224 /* not on subdivision, compute distance to next subdivision */
1226 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1229 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1230 assert (i != _map.end());
1232 assert (i != _map.end());
1233 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1237 } else if (dir < 0) {
1239 /* round to previous (even if we're on a subdivision) */
1241 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1244 /* right on the subdivision, so the difference is just the subdivision ticks */
1245 difference = ticks_one_subdivisions_worth;
1247 /* not on subdivision, compute distance to previous subdivision, which
1248 is just the modulus.
1254 if (the_beat.ticks < difference) {
1255 if (i == _map.begin()) {
1256 /* can't go backwards from wherever pos is, so just return it */
1260 the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
1262 the_beat.ticks -= difference;
1266 /* round to nearest */
1270 /* compute the distance to the previous and next subdivision */
1272 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1274 /* closer to the next subdivision, so shift forward */
1276 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1278 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1280 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1281 assert (i != _map.end());
1283 assert (i != _map.end());
1284 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1285 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1288 } else if (rem > 0) {
1290 /* closer to previous subdivision, so shift backward */
1292 if (rem > the_beat.ticks) {
1293 if (i == _map.begin()) {
1294 /* can't go backwards past zero, so ... */
1297 /* step back to previous beat */
1299 the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
1300 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1302 the_beat.ticks = lrint (the_beat.ticks - rem);
1303 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1306 /* on the subdivision, do nothing */
1310 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
1311 (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
1315 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1317 require_map_to (frame);
1319 Glib::RWLock::ReaderLock lm (lock);
1320 BBTPointList::const_iterator fi;
1323 fi = bbt_after_or_at (frame);
1325 fi = bbt_before_or_at (frame);
1328 assert (fi != _map.end());
1330 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));
1335 /* find bar previous to 'frame' */
1337 if (fi == _map.begin()) {
1341 if ((*fi).is_bar() && (*fi).frame == frame) {
1345 while (!(*fi).is_bar()) {
1346 if (fi == _map.begin()) {
1351 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1352 (*fi).bar, (*fi).beat, (*fi).frame));
1355 } else if (dir > 0) {
1357 /* find bar following 'frame' */
1359 if ((*fi).is_bar() && (*fi).frame == frame) {
1363 while (!(*fi).is_bar()) {
1365 if (fi == _map.end()) {
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));
1377 /* true rounding: find nearest bar */
1379 BBTPointList::const_iterator prev = fi;
1380 BBTPointList::const_iterator next = fi;
1382 if ((*fi).frame == frame) {
1386 while ((*prev).beat != 1) {
1387 if (prev == _map.begin()) {
1393 while ((next != _map.end()) && (*next).beat != 1) {
1397 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1398 return (*prev).frame;
1400 return (*next).frame;
1410 if (fi == _map.begin()) {
1414 if ((*fi).frame >= frame) {
1415 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1418 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1419 (*fi).bar, (*fi).beat, (*fi).frame));
1421 } else if (dir > 0) {
1422 if ((*fi).frame <= frame) {
1423 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1426 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1427 (*fi).bar, (*fi).beat, (*fi).frame));
1430 /* find beat nearest to frame */
1431 if ((*fi).frame == frame) {
1435 BBTPointList::const_iterator prev = fi;
1436 BBTPointList::const_iterator next = fi;
1437 if (prev != _map.begin()) {
1442 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1443 return (*prev).frame;
1445 return (*next).frame;
1457 TempoMap::map (TempoMap::BBTPointList::const_iterator& begin,
1458 TempoMap::BBTPointList::const_iterator& end,
1459 framepos_t lower, framepos_t upper)
1462 Glib::RWLock::WriterLock lm (lock);
1463 if (_map.empty() || (_map.back().frame < upper)) {
1464 recompute_map (false, upper);
1468 begin = lower_bound (_map.begin(), _map.end(), lower);
1469 end = upper_bound (_map.begin(), _map.end(), upper);
1473 TempoMap::tempo_section_at (framepos_t frame) const
1475 Glib::RWLock::ReaderLock lm (lock);
1476 Metrics::const_iterator i;
1477 TempoSection* prev = 0;
1479 for (i = metrics.begin(); i != metrics.end(); ++i) {
1482 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1484 if ((*i)->frame() > frame) {
1500 TempoMap::tempo_at (framepos_t frame) const
1502 TempoMetric m (metric_at (frame));
1508 TempoMap::meter_at (framepos_t frame) const
1510 TempoMetric m (metric_at (frame));
1515 TempoMap::get_state ()
1517 Metrics::const_iterator i;
1518 XMLNode *root = new XMLNode ("TempoMap");
1521 Glib::RWLock::ReaderLock lm (lock);
1522 for (i = metrics.begin(); i != metrics.end(); ++i) {
1523 root->add_child_nocopy ((*i)->get_state());
1531 TempoMap::set_state (const XMLNode& node, int /*version*/)
1534 Glib::RWLock::WriterLock lm (lock);
1537 XMLNodeConstIterator niter;
1538 Metrics old_metrics (metrics);
1539 MeterSection* last_meter = 0;
1543 nlist = node.children();
1545 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1546 XMLNode* child = *niter;
1548 if (child->name() == TempoSection::xml_state_node_name) {
1551 TempoSection* ts = new TempoSection (*child);
1552 metrics.push_back (ts);
1554 if (ts->bar_offset() < 0.0) {
1556 ts->update_bar_offset_from_bbt (*last_meter);
1561 catch (failed_constructor& err){
1562 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1563 metrics = old_metrics;
1567 } else if (child->name() == MeterSection::xml_state_node_name) {
1570 MeterSection* ms = new MeterSection (*child);
1571 metrics.push_back (ms);
1575 catch (failed_constructor& err) {
1576 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1577 metrics = old_metrics;
1583 if (niter == nlist.end()) {
1584 MetricSectionSorter cmp;
1588 recompute_map (true);
1591 PropertyChanged (PropertyChange ());
1597 TempoMap::dump (std::ostream& o) const
1599 Glib::RWLock::ReaderLock lm (lock);
1600 const MeterSection* m;
1601 const TempoSection* t;
1603 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1605 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1606 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? "
1607 << t->movable() << ')' << endl;
1608 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1609 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1610 << " (movable? " << m->movable() << ')' << endl;
1616 TempoMap::n_tempos() const
1618 Glib::RWLock::ReaderLock lm (lock);
1621 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1622 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1631 TempoMap::n_meters() const
1633 Glib::RWLock::ReaderLock lm (lock);
1636 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1637 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1646 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1649 Glib::RWLock::WriterLock lm (lock);
1650 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1651 if ((*i)->frame() >= where && (*i)->movable ()) {
1652 (*i)->set_frame ((*i)->frame() + amount);
1656 /* now reset the BBT time of all metrics, based on their new
1657 * audio time. This is the only place where we do this reverse
1661 Metrics::iterator i;
1662 const MeterSection* meter;
1663 const TempoSection* tempo;
1667 meter = &first_meter ();
1668 tempo = &first_tempo ();
1673 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1676 MetricSection* prev = 0;
1678 for (i = metrics.begin(); i != metrics.end(); ++i) {
1681 TempoMetric metric (*meter, *tempo);
1684 metric.set_start (prev->start());
1685 metric.set_frame (prev->frame());
1687 // metric will be at frames=0 bbt=1|1|0 by default
1688 // which is correct for our purpose
1691 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1692 bbt_time ((*i)->frame(), bbt, bi);
1694 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1700 if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
1701 /* round up to next beat */
1707 if (bbt.beats != 1) {
1708 /* round up to next bar */
1714 // cerr << bbt << endl;
1716 (*i)->set_start (bbt);
1718 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1720 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1721 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1723 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1725 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1732 recompute_map (true);
1736 PropertyChanged (PropertyChange ());
1739 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1740 * pos can be -ve, if required.
1743 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
1745 return framepos_plus_bbt (pos, BBT_Time (beats));
1748 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1750 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
1752 return framepos_minus_bbt (pos, BBT_Time (beats));
1756 TempoMap::framepos_minus_bbt (framepos_t pos, BBT_Time op)
1758 Glib::RWLock::ReaderLock lm (lock);
1759 BBTPointList::const_iterator i;
1760 framecnt_t extra_frames = 0;
1761 bool had_bars = (op.bars != 0);
1763 /* start from the bar|beat right before (or at) pos */
1765 i = bbt_before_or_at (pos);
1767 /* we know that (*i).frame is less than or equal to pos */
1768 extra_frames = pos - (*i).frame;
1770 /* walk backwards */
1772 while (i != _map.begin() && (op.bars || op.beats)) {
1776 if ((*i).is_bar()) {
1783 if ((had_bars && op.bars == 0) || !had_bars) {
1784 /* finished counting bars, or none to count,
1785 so decrement beat count
1793 /* handle ticks (assumed to be less than
1794 * BBT_Time::ticks_per_bar_division, as always.
1798 frameoffset_t tick_frames = llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1799 framepos_t pre_tick_frames = (*i).frame + extra_frames;
1800 if (tick_frames < pre_tick_frames) {
1801 return pre_tick_frames - tick_frames;
1805 return (*i).frame + extra_frames;
1809 /** Add the BBT interval op to pos and return the result */
1811 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
1813 Glib::RWLock::ReaderLock lm (lock);
1814 BBT_Time op_copy (op);
1815 int additional_minutes = 1;
1816 BBTPointList::const_iterator i;
1817 framecnt_t backup_frames = 0;
1818 bool had_bars = (op.bars != 0);
1822 i = bbt_before_or_at (pos);
1826 /* we know that (*i).frame is before or equal to pos */
1827 backup_frames = pos - (*i).frame;
1829 while (i != _map.end() && (op.bars || op.beats)) {
1834 if ((*i).is_bar()) {
1841 if ((had_bars && op.bars == 0) || !had_bars) {
1842 /* finished counting bars, or none to count,
1843 so decrement beat count
1852 if (i != _map.end()) {
1856 /* we hit the end of the map before finish the bbt walk.
1859 recompute_map (false, pos + (_frame_rate * 60 * additional_minutes));
1860 additional_minutes *= 2;
1862 /* go back and try again */
1863 warning << "reached end of map with op now at " << op << " end = "
1864 << _map.back().frame << ' ' << _map.back().bar << '|' << _map.back().beat << ", trying to walk "
1865 << op_copy << " ... retry"
1870 return (*i).frame - backup_frames +
1871 llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1873 return (*i).frame - backup_frames;
1877 /** Count the number of beats that are equivalent to distance when going forward,
1881 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
1883 framepos_t end = pos + distance;
1885 require_map_to (end);
1887 Glib::RWLock::ReaderLock lm (lock);
1888 BBTPointList::const_iterator i = bbt_after_or_at (pos);
1889 Evoral::MusicalTime beats = 0;
1891 /* if our starting BBTPoint is after pos, add a fractional beat
1892 to represent that distance.
1895 if ((*i).frame != pos) {
1896 beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1899 while (i != _map.end() && (*i).frame < end) {
1904 assert (i != _map.end());
1906 /* if our ending BBTPoint is after the end, subtract a fractional beat
1907 to represent that distance.
1910 if ((*i).frame > end) {
1911 beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1917 TempoMap::BBTPointList::const_iterator
1918 TempoMap::bbt_before_or_at (framepos_t pos)
1920 /* CALLER MUST HOLD READ LOCK */
1922 BBTPointList::const_iterator i;
1924 i = lower_bound (_map.begin(), _map.end(), pos);
1925 assert (i != _map.end());
1926 if ((*i).frame > pos) {
1927 assert (i != _map.begin());
1934 bool operator() (const BBT_Time& a, const BBT_Time& b) {
1939 TempoMap::BBTPointList::const_iterator
1940 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
1942 BBTPointList::const_iterator i;
1945 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
1946 assert (i != _map.end());
1947 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
1948 assert (i != _map.begin());
1954 TempoMap::BBTPointList::const_iterator
1955 TempoMap::bbt_after_or_at (framepos_t pos)
1957 /* CALLER MUST HOLD READ LOCK */
1959 BBTPointList::const_iterator i;
1961 if (_map.back().frame == pos) {
1963 assert (i != _map.begin());
1968 i = upper_bound (_map.begin(), _map.end(), pos);
1969 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);