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 MetricSection* last_metric_section;
780 double divisions_per_bar;
782 double current_frame;
784 Metrics::iterator next_metric;
788 /* compute 1 mins worth */
789 end = _frame_rate * 60;
791 end = _map.back().frame;
795 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
799 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
800 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
806 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
807 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
813 /* assumes that the first meter & tempo are at frame zero */
815 meter->set_frame (0);
816 tempo->set_frame (0);
817 last_metric_section = tempo;
819 /* assumes that the first meter & tempo are at 1|1|0 */
824 divisions_per_bar = meter->divisions_per_bar ();
825 beat_frames = meter->frames_per_division (*tempo,_frame_rate);
827 if (reassign_tempo_bbt) {
829 MeterSection* rmeter = meter;
831 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
833 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
835 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
837 /* reassign the BBT time of this tempo section
838 * based on its bar offset position.
841 ts->update_bbt_time_from_bar_offset (*rmeter);
843 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
846 fatal << _("programming error: unhandled MetricSection type") << endmsg;
852 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2 dpb %3 fpb %4\n",
853 *((Meter*)meter), *((Tempo*)tempo), divisions_per_bar, beat_frames));
855 next_metric = metrics->begin();
856 ++next_metric; // skip meter (or tempo)
857 ++next_metric; // skip tempo (or meter)
859 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
860 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
862 while (current_frame < end) {
865 current_frame += beat_frames;
867 if (current.beats > meter->divisions_per_bar()) {
872 if (next_metric != metrics->end()) {
874 bool exact_start_match_required = true;
876 /* no operator >= so invert operator < */
878 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
880 if (!(current < (*next_metric)->start())) {
883 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
887 /* new tempo section: if its on a beat,
888 * we don't have to do anything other
889 * than recompute various distances,
890 * done further below as we transition
891 * the next metric section.
893 * if its not on the beat, we have to
894 * compute the duration of the beat it
895 * is within, which will be different
896 * from the preceding following ones
897 * since it takes part of its duration
898 * from the preceding tempo and part
899 * from this new tempo.
902 if (tempo->start().ticks != 0) {
904 double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);
906 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
907 tempo->start(), current_frame, tempo->bar_offset()));
909 /* back up to previous beat */
910 current_frame -= beat_frames;
911 /* set tempo section location based on offset from last beat */
912 tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
913 /* advance to the location of the new (adjusted) beat */
914 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
915 /* next metric doesn't have to
916 * match this precisely to
919 exact_start_match_required = false;
920 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
924 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
925 tempo->start(), current_frame));
926 tempo->set_frame (current_frame);
929 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
933 /* new meter section: always defines the
937 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
938 meter->start(), current, current_frame));
940 assert (current.beats == 1);
942 meter->set_frame (current_frame);
945 divisions_per_bar = meter->divisions_per_bar ();
946 beat_frames = meter->frames_per_division (*tempo, _frame_rate);
948 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
949 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
951 last_metric_section = *next_metric;
954 if (next_metric != metrics->end() && ((*next_metric)->start() == current)) {
955 /* same position so go back and set this one up before advancing
962 if (current.beats == 1) {
963 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
964 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
966 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
967 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
973 TempoMap::metric_at (framepos_t frame) const
975 Glib::RWLock::ReaderLock lm (lock);
976 TempoMetric m (first_meter(), first_tempo());
980 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
981 at something, because we insert the default tempo and meter during
982 TempoMap construction.
984 now see if we can find better candidates.
987 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
989 // cerr << "Looking at a metric section " << **i << endl;
991 if ((*i)->frame() > frame) {
995 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
996 m.set_tempo (*tempo);
997 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
998 m.set_meter (*meter);
1001 m.set_frame ((*i)->frame ());
1002 m.set_start ((*i)->start ());
1005 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1010 TempoMap::metric_at (BBT_Time bbt) const
1012 Glib::RWLock::ReaderLock lm (lock);
1013 TempoMetric m (first_meter(), first_tempo());
1017 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1018 at something, because we insert the default tempo and meter during
1019 TempoMap construction.
1021 now see if we can find better candidates.
1024 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1026 BBT_Time section_start ((*i)->start());
1028 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1032 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1033 m.set_tempo (*tempo);
1034 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1035 m.set_meter (*meter);
1038 m.set_frame ((*i)->frame ());
1039 m.set_start (section_start);
1046 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1049 Glib::RWLock::ReaderLock lm (lock);
1050 BBTPointList::const_iterator i = bbt_before_or_at (frame);
1051 bbt_time_unlocked (frame, bbt, i);
1056 TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1058 bbt.bars = (*i).bar;
1059 bbt.beats = (*i).beat;
1061 if ((*i).frame == frame) {
1064 bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
1065 BBT_Time::ticks_per_bar_division);
1070 TempoMap::frame_time (const BBT_Time& bbt)
1072 Glib::RWLock::ReaderLock lm (lock);
1074 BBTPointList::const_iterator s = bbt_point_for (BBT_Time (1, 1, 0));
1075 BBTPointList::const_iterator e = bbt_point_for (BBT_Time (bbt.bars, bbt.beats, 0));
1077 if (bbt.ticks != 0) {
1078 return ((*e).frame - (*s).frame) +
1079 llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
1081 return ((*e).frame - (*s).frame);
1086 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1088 Glib::RWLock::ReaderLock lm (lock);
1089 framecnt_t frames = 0;
1092 bbt_time (pos, when);
1093 frames = bbt_duration_at_unlocked (when, bbt,dir);
1099 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1101 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1105 /* round back to the previous precise beat */
1106 BBTPointList::const_iterator wi = bbt_point_for (BBT_Time (when.bars, when.beats, 0));
1107 BBTPointList::const_iterator start (wi);
1108 double tick_frames = 0;
1110 assert (wi != _map.end());
1112 /* compute how much rounding we did because of non-zero ticks */
1114 if (when.ticks != 0) {
1115 tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
1121 while (wi != _map.end() && bars < bbt.bars) {
1123 if ((*wi).beat == 1) {
1127 assert (wi != _map.end());
1129 while (wi != _map.end() && beats < bbt.beats) {
1133 assert (wi != _map.end());
1135 /* add any additional frames related to ticks in the added value */
1137 if (bbt.ticks != 0) {
1138 tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
1141 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1145 TempoMap::round_to_bar (framepos_t fr, int dir)
1148 Glib::RWLock::ReaderLock lm (lock);
1149 return round_to_type (fr, dir, Bar);
1154 TempoMap::round_to_beat (framepos_t fr, int dir)
1157 Glib::RWLock::ReaderLock lm (lock);
1158 return round_to_type (fr, dir, Beat);
1163 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1165 Glib::RWLock::ReaderLock lm (lock);
1166 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1168 uint32_t ticks_one_subdivisions_worth;
1169 uint32_t difference;
1171 bbt_time_unlocked (fr, the_beat, i);
1173 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1174 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1176 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
1180 /* round to next (even if we're on a subdivision */
1182 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1185 /* right on the subdivision, so the difference is just the subdivision ticks */
1186 the_beat.ticks += ticks_one_subdivisions_worth;
1189 /* not on subdivision, compute distance to next subdivision */
1191 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1194 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1195 assert (i != _map.end());
1197 assert (i != _map.end());
1198 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1202 } else if (dir < 0) {
1204 /* round to previous (even if we're on a subdivision) */
1206 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1209 /* right on the subdivision, so the difference is just the subdivision ticks */
1210 difference = ticks_one_subdivisions_worth;
1212 /* not on subdivision, compute distance to previous subdivision, which
1213 is just the modulus.
1219 if (the_beat.ticks < difference) {
1220 if (i == _map.begin()) {
1221 /* can't go backwards from wherever pos is, so just return it */
1225 the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
1227 the_beat.ticks -= difference;
1231 /* round to nearest */
1235 /* compute the distance to the previous and next subdivision */
1237 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1239 /* closer to the next subdivision, so shift forward */
1241 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1243 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1245 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1246 assert (i != _map.end());
1248 assert (i != _map.end());
1249 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1250 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1253 } else if (rem > 0) {
1255 /* closer to previous subdivision, so shift backward */
1257 if (rem > the_beat.ticks) {
1258 if (i == _map.begin()) {
1259 /* can't go backwards past zero, so ... */
1262 /* step back to previous beat */
1264 the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
1265 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1267 the_beat.ticks = lrint (the_beat.ticks - rem);
1268 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1271 /* on the subdivision, do nothing */
1275 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
1276 (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
1280 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1282 BBTPointList::const_iterator fi;
1285 fi = bbt_after_or_at (frame);
1287 fi = bbt_before_or_at (frame);
1290 assert (fi != _map.end());
1292 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));
1297 /* find bar previous to 'frame' */
1299 if ((*fi).beat == 1 && (*fi).frame == frame) {
1303 while ((*fi).beat > 1) {
1304 if (fi == _map.begin()) {
1309 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1310 (*fi).bar, (*fi).beat, (*fi).frame));
1313 } else if (dir > 0) {
1315 /* find bar following 'frame' */
1317 if ((*fi).beat == 1 && (*fi).frame == frame) {
1321 while ((*fi).beat != 1) {
1323 if (fi == _map.end()) {
1329 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1330 (*fi).bar, (*fi).beat, (*fi).frame));
1335 /* true rounding: find nearest bar */
1337 BBTPointList::const_iterator prev = fi;
1338 BBTPointList::const_iterator next = fi;
1340 if ((*fi).frame == frame) {
1344 while ((*prev).beat != 1) {
1345 if (prev == _map.begin()) {
1351 while ((*next).beat != 1) {
1353 if (next == _map.end()) {
1359 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1360 return (*prev).frame;
1362 return (*next).frame;
1371 if ((*fi).frame >= frame) {
1372 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1375 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1376 (*fi).bar, (*fi).beat, (*fi).frame));
1378 } else if (dir > 0) {
1379 if ((*fi).frame <= frame) {
1380 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1383 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1384 (*fi).bar, (*fi).beat, (*fi).frame));
1387 /* find beat nearest to frame */
1388 if ((*fi).frame == frame) {
1392 BBTPointList::const_iterator prev = fi;
1393 BBTPointList::const_iterator next = fi;
1397 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1398 return (*prev).frame;
1400 return (*next).frame;
1412 TempoMap::map (TempoMap::BBTPointList::const_iterator& begin,
1413 TempoMap::BBTPointList::const_iterator& end,
1414 framepos_t lower, framepos_t upper)
1416 if (_map.empty() || upper >= _map.back().frame) {
1417 recompute_map (false, upper);
1420 begin = lower_bound (_map.begin(), _map.end(), lower);
1421 end = upper_bound (_map.begin(), _map.end(), upper);
1425 TempoMap::tempo_section_at (framepos_t frame) const
1427 Glib::RWLock::ReaderLock lm (lock);
1428 Metrics::const_iterator i;
1429 TempoSection* prev = 0;
1431 for (i = metrics->begin(); i != metrics->end(); ++i) {
1434 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1436 if ((*i)->frame() > frame) {
1452 TempoMap::tempo_at (framepos_t frame) const
1454 TempoMetric m (metric_at (frame));
1460 TempoMap::meter_at (framepos_t frame) const
1462 TempoMetric m (metric_at (frame));
1467 TempoMap::get_state ()
1469 Metrics::const_iterator i;
1470 XMLNode *root = new XMLNode ("TempoMap");
1473 Glib::RWLock::ReaderLock lm (lock);
1474 for (i = metrics->begin(); i != metrics->end(); ++i) {
1475 root->add_child_nocopy ((*i)->get_state());
1483 TempoMap::set_state (const XMLNode& node, int /*version*/)
1486 Glib::RWLock::WriterLock lm (lock);
1489 XMLNodeConstIterator niter;
1490 Metrics old_metrics (*metrics);
1491 MeterSection* last_meter = 0;
1495 nlist = node.children();
1497 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1498 XMLNode* child = *niter;
1500 if (child->name() == TempoSection::xml_state_node_name) {
1503 TempoSection* ts = new TempoSection (*child);
1504 metrics->push_back (ts);
1506 if (ts->bar_offset() < 0.0) {
1508 ts->update_bar_offset_from_bbt (*last_meter);
1513 catch (failed_constructor& err){
1514 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1515 *metrics = old_metrics;
1519 } else if (child->name() == MeterSection::xml_state_node_name) {
1522 MeterSection* ms = new MeterSection (*child);
1523 metrics->push_back (ms);
1527 catch (failed_constructor& err) {
1528 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1529 *metrics = old_metrics;
1535 if (niter == nlist.end()) {
1537 MetricSectionSorter cmp;
1538 metrics->sort (cmp);
1539 recompute_map (true);
1543 PropertyChanged (PropertyChange ());
1549 TempoMap::dump (std::ostream& o) const
1551 const MeterSection* m;
1552 const TempoSection* t;
1554 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1556 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1557 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? "
1558 << t->movable() << ')' << endl;
1559 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1560 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1561 << " (movable? " << m->movable() << ')' << endl;
1567 TempoMap::n_tempos() const
1569 Glib::RWLock::ReaderLock lm (lock);
1572 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1573 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1582 TempoMap::n_meters() const
1584 Glib::RWLock::ReaderLock lm (lock);
1587 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1588 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1597 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1599 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1600 if ((*i)->frame() >= where && (*i)->movable ()) {
1601 (*i)->set_frame ((*i)->frame() + amount);
1605 timestamp_metrics_from_audio_time ();
1607 PropertyChanged (PropertyChange ());
1610 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1611 * pos can be -ve, if required.
1614 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
1616 Metrics::const_iterator i;
1617 const TempoSection* tempo;
1619 /* Find the starting tempo */
1621 for (i = metrics->begin(); i != metrics->end(); ++i) {
1623 /* This is a bit of a hack, but pos could be -ve, and if it is,
1624 we consider the initial metric changes (at time 0) to actually
1625 be in effect at pos.
1627 framepos_t f = (*i)->frame ();
1628 if (pos < 0 && f == 0) {
1636 const TempoSection* t;
1638 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1645 tempo -> the Tempo for "pos"
1646 i -> for first new metric after "pos", possibly metrics->end()
1651 /* Distance to the end of this section in frames */
1652 framecnt_t distance_frames = i == metrics->end() ? max_framepos : ((*i)->frame() - pos);
1654 /* Distance to the end in beats */
1655 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1657 /* Amount to subtract this time */
1658 double const sub = min (distance_beats, beats);
1662 pos += sub * tempo->frames_per_beat (_frame_rate);
1664 /* Move on if there's anything to move to */
1665 if (i != metrics->end ()) {
1666 const TempoSection* t;
1668 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1679 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1681 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
1683 Metrics::const_iterator i;
1684 const TempoSection* tempo = 0;
1685 const TempoSection* t;
1687 /* Find the starting tempo */
1689 for (i = metrics->begin(); i != metrics->end(); ++i) {
1691 /* This is a bit of a hack, but pos could be -ve, and if it is,
1692 we consider the initial metric changes (at time 0) to actually
1693 be in effect at pos.
1695 framepos_t f = (*i)->frame ();
1696 if (pos < 0 && f == 0) {
1700 if ((*i)->frame() > pos) {
1704 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1709 bool no_more_tempos = false;
1711 /* Move i back to the tempo before "pos" */
1712 if (i != metrics->begin ()) {
1713 while (i != metrics->begin ()) {
1715 t = dynamic_cast<TempoSection*> (*i);
1721 no_more_tempos = true;
1726 tempo -> the Tempo for "pos"
1727 i -> the first metric before "pos", unless no_more_tempos is true
1732 /* Distance to the end of this section in frames */
1733 framecnt_t distance_frames = no_more_tempos ? max_framepos : (pos - (*i)->frame());
1735 /* Distance to the end in beats */
1736 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1738 /* Amount to subtract this time */
1739 double const sub = min (distance_beats, beats);
1743 pos -= sub * tempo->frames_per_beat (_frame_rate);
1745 /* Move i and tempo back, if there's anything to move to */
1746 if (i != metrics->begin ()) {
1747 while (i != metrics->begin ()) {
1749 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1755 no_more_tempos = true;
1762 /** Add the BBT interval op to pos and return the result */
1764 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
1766 Metrics::const_iterator i;
1767 const MeterSection* meter;
1768 const MeterSection* m;
1769 const TempoSection* tempo;
1770 const TempoSection* t;
1771 double frames_per_beat;
1773 meter = &first_meter ();
1774 tempo = &first_tempo ();
1779 /* find the starting metrics for tempo & meter */
1781 for (i = metrics->begin(); i != metrics->end(); ++i) {
1783 if ((*i)->frame() > pos) {
1787 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1789 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1796 meter -> the Meter for "pos"
1797 tempo -> the Tempo for "pos"
1798 i -> for first new metric after "pos", possibly metrics->end()
1801 /* now comes the complicated part. we have to add one beat a time,
1802 checking for a new metric on every beat.
1805 frames_per_beat = tempo->frames_per_beat (_frame_rate);
1814 /* check if we need to use a new metric section: has adding frames moved us
1815 to or after the start of the next metric section? in which case, use it.
1818 if (i != metrics->end()) {
1819 if ((*i)->frame() <= pos) {
1821 /* about to change tempo or meter, so add the
1822 * number of frames for the bars we've just
1823 * traversed before we change the
1824 * frames_per_beat value.
1827 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
1830 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1832 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1836 frames_per_beat = tempo->frames_per_beat (_frame_rate);
1843 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
1849 /* given the current meter, have we gone past the end of the bar ? */
1854 /* check if we need to use a new metric section: has adding frames moved us
1855 to or after the start of the next metric section? in which case, use it.
1858 if (i != metrics->end()) {
1859 if ((*i)->frame() <= pos) {
1861 /* about to change tempo or meter, so add the
1862 * number of frames for the beats we've just
1863 * traversed before we change the
1864 * frames_per_beat value.
1867 pos += llrint (beats * frames_per_beat);
1870 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1872 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1876 frames_per_beat = tempo->frames_per_beat (_frame_rate);
1881 pos += llrint (beats * frames_per_beat);
1884 if (op.ticks >= BBT_Time::ticks_per_bar_division) {
1885 pos += llrint (frames_per_beat + /* extra beat */
1886 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_bar_division) /
1887 (double) BBT_Time::ticks_per_bar_division)));
1889 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_bar_division));
1896 /** Count the number of beats that are equivalent to distance when going forward,
1900 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
1902 BBTPointList::const_iterator i = bbt_after_or_at (pos);
1903 Evoral::MusicalTime beats = 0;
1904 framepos_t end = pos + distance;
1906 require_map_to (end);
1908 /* if our starting BBTPoint is after pos, add a fractional beat
1909 to represent that distance.
1912 if ((*i).frame != pos) {
1913 beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1916 while (i != _map.end() && (*i).frame < end) {
1920 assert (i != _map.end());
1922 /* if our ending BBTPoint is after the end, subtract a fractional beat
1923 to represent that distance.
1926 if ((*i).frame > end) {
1927 beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1933 TempoMap::BBTPointList::const_iterator
1934 TempoMap::bbt_before_or_at (framepos_t pos)
1936 require_map_to (pos);
1937 BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), pos);
1938 assert (i != _map.end());
1939 if ((*i).frame > pos) {
1940 assert (i != _map.begin());
1946 TempoMap::BBTPointList::const_iterator
1947 TempoMap::bbt_after_or_at (framepos_t pos)
1949 require_map_to (pos);
1950 BBTPointList::const_iterator i = upper_bound (_map.begin(), _map.end(), pos);
1951 assert (i != _map.end());
1956 bool operator() (const BBT_Time& a, const BBT_Time& b) {
1961 TempoMap::BBTPointList::const_iterator
1962 TempoMap::bbt_point_for (const BBT_Time& bbt)
1965 int additional_minutes = 1;
1967 while (_map.empty() || _map.back().bar < (bbt.bars + 1)) {
1968 /* add some more distance, using bigger steps each time */
1969 require_map_to (_map.back().frame + (_frame_rate * 60 * additional_minutes));
1970 additional_minutes *= 2;
1973 BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
1974 assert (i != _map.end());
1979 /** Compare the time of this with that of another MetricSection.
1980 * @param with_bbt True to compare using start(), false to use frame().
1981 * @return -1 for less than, 0 for equal, 1 for greater than.
1985 MetricSection::compare (const MetricSection& other) const
1987 if (start() == other.start()) {
1989 } else if (start() < other.start()) {
2000 MetricSection::operator== (const MetricSection& other) const
2002 return compare (other) == 0;
2006 MetricSection::operator!= (const MetricSection& other) const
2008 return compare (other) != 0;
2012 operator<< (std::ostream& o, const Meter& m) {
2013 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2017 operator<< (std::ostream& o, const Tempo& t) {
2018 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2022 operator<< (std::ostream& o, const MetricSection& section) {
2024 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2026 const TempoSection* ts;
2027 const MeterSection* ms;
2029 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2030 o << *((Tempo*) ts);
2031 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2032 o << *((Meter*) ms);