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.
28 #include <glibmm/thread.h>
29 #include "pbd/xml++.h"
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);
48 double Tempo::frames_per_beat (framecnt_t sr, const Meter& meter) const
50 return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
53 /***********************************************************************/
56 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
58 return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
61 /***********************************************************************/
63 const string TempoSection::xml_state_node_name = "Tempo";
65 TempoSection::TempoSection (const XMLNode& node)
66 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
68 const XMLProperty *prop;
70 LocaleGuard lg (X_("POSIX"));
72 if ((prop = node.property ("start")) == 0) {
73 error << _("TempoSection XML node has no \"start\" property") << endmsg;
74 throw failed_constructor();
77 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
81 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
82 throw failed_constructor();
87 if ((prop = node.property ("beats-per-minute")) == 0) {
88 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
89 throw failed_constructor();
92 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
93 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
94 throw failed_constructor();
97 if ((prop = node.property ("note-type")) == 0) {
98 /* older session, make note type be quarter by default */
101 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
102 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
103 throw failed_constructor();
107 if ((prop = node.property ("movable")) == 0) {
108 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
109 throw failed_constructor();
112 set_movable (string_is_affirmative (prop->value()));
116 TempoSection::get_state() const
118 XMLNode *root = new XMLNode (xml_state_node_name);
120 LocaleGuard lg (X_("POSIX"));
122 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
126 root->add_property ("start", buf);
127 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
128 root->add_property ("beats-per-minute", buf);
129 snprintf (buf, sizeof (buf), "%f", _note_type);
130 root->add_property ("note-type", buf);
131 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
132 root->add_property ("movable", buf);
137 /***********************************************************************/
139 const string MeterSection::xml_state_node_name = "Meter";
141 MeterSection::MeterSection (const XMLNode& node)
142 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
144 const XMLProperty *prop;
146 LocaleGuard lg (X_("POSIX"));
148 if ((prop = node.property ("start")) == 0) {
149 error << _("MeterSection XML node has no \"start\" property") << endmsg;
150 throw failed_constructor();
153 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
157 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
158 throw failed_constructor();
163 if ((prop = node.property ("beats-per-bar")) == 0) {
164 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
165 throw failed_constructor();
168 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
169 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
170 throw failed_constructor();
173 if ((prop = node.property ("note-type")) == 0) {
174 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
175 throw failed_constructor();
178 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
179 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
180 throw failed_constructor();
183 if ((prop = node.property ("movable")) == 0) {
184 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
185 throw failed_constructor();
188 set_movable (string_is_affirmative (prop->value()));
192 MeterSection::get_state() const
194 XMLNode *root = new XMLNode (xml_state_node_name);
196 LocaleGuard lg (X_("POSIX"));
198 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
202 root->add_property ("start", buf);
203 snprintf (buf, sizeof (buf), "%f", _note_type);
204 root->add_property ("note-type", buf);
205 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
206 root->add_property ("beats-per-bar", buf);
207 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
208 root->add_property ("movable", buf);
213 /***********************************************************************/
215 struct MetricSectionSorter {
216 bool operator() (const MetricSection* a, const MetricSection* b) {
217 return a->start() < b->start();
221 TempoMap::TempoMap (framecnt_t fr)
223 metrics = new Metrics;
225 last_bbt_valid = false;
232 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
233 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
235 t->set_movable (false);
236 m->set_movable (false);
238 /* note: frame time is correct (zero) for both of these */
240 metrics->push_back (t);
241 metrics->push_back (m);
244 TempoMap::~TempoMap ()
249 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
251 if (when == section.start() || !section.movable()) {
255 Glib::RWLock::WriterLock lm (lock);
256 MetricSectionSorter cmp;
258 if (when.beats != 1) {
260 /* position by audio frame, then recompute BBT timestamps from the audio ones */
262 framepos_t frame = frame_time (when);
263 // cerr << "nominal frame time = " << frame << endl;
265 framepos_t prev_frame = round_to_type (frame, -1, Beat);
266 framepos_t next_frame = round_to_type (frame, 1, Beat);
268 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
270 /* use the closest beat */
272 if ((frame - prev_frame) < (next_frame - frame)) {
278 // cerr << "actual frame time = " << frame << endl;
279 section.set_frame (frame);
280 // cerr << "frame time = " << section.frame() << endl;
281 timestamp_metrics (false);
282 // cerr << "new BBT time = " << section.start() << endl;
287 /* positioned at bar start already, so just put it there */
289 section.set_start (when);
291 timestamp_metrics (true);
299 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
301 if (move_metric_section (tempo, when) == 0) {
302 PropertyChanged (PropertyChange ());
307 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
309 if (move_metric_section (meter, when) == 0) {
310 PropertyChanged (PropertyChange ());
315 TempoMap::remove_tempo (const TempoSection& tempo)
317 bool removed = false;
320 Glib::RWLock::WriterLock lm (lock);
323 for (i = metrics->begin(); i != metrics->end(); ++i) {
324 if (dynamic_cast<TempoSection*> (*i) != 0) {
325 if (tempo.frame() == (*i)->frame()) {
326 if ((*i)->movable()) {
337 PropertyChanged (PropertyChange ());
342 TempoMap::remove_meter (const MeterSection& tempo)
344 bool removed = false;
347 Glib::RWLock::WriterLock lm (lock);
350 for (i = metrics->begin(); i != metrics->end(); ++i) {
351 if (dynamic_cast<MeterSection*> (*i) != 0) {
352 if (tempo.frame() == (*i)->frame()) {
353 if ((*i)->movable()) {
364 PropertyChanged (PropertyChange ());
369 TempoMap::do_insert (MetricSection* section, bool with_bbt)
373 /* Look for any existing MetricSection that is of the same type and
374 at the same time as the new one, and remove it before adding
378 Metrics::iterator to_remove = metrics->end ();
380 for (i = metrics->begin(); i != metrics->end(); ++i) {
382 int const c = (*i)->compare (section, with_bbt);
385 /* this section is before the one to be added; go back round */
388 /* this section is after the one to be added; there can't be any at the same time */
392 /* hacky comparison of type */
393 bool const a = dynamic_cast<TempoSection*> (*i) != 0;
394 bool const b = dynamic_cast<TempoSection*> (section) != 0;
402 if (to_remove != metrics->end()) {
403 /* remove the MetricSection at the same time as the one we are about to add */
404 metrics->erase (to_remove);
407 /* Add the given MetricSection */
409 for (i = metrics->begin(); i != metrics->end(); ++i) {
411 if ((*i)->compare (section, with_bbt) < 0) {
415 metrics->insert (i, section);
419 if (i == metrics->end()) {
420 metrics->insert (metrics->end(), section);
423 timestamp_metrics (with_bbt);
427 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
430 Glib::RWLock::WriterLock lm (lock);
432 /* new tempos always start on a beat */
435 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
438 PropertyChanged (PropertyChange ());
442 TempoMap::add_tempo (const Tempo& tempo, framepos_t where)
445 Glib::RWLock::WriterLock lm (lock);
446 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
449 PropertyChanged (PropertyChange ());
453 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
455 bool replaced = false;
458 Glib::RWLock::WriterLock lm (lock);
461 for (i = metrics->begin(); i != metrics->end(); ++i) {
464 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
466 *((Tempo *) ts) = replacement;
469 timestamp_metrics (true);
477 PropertyChanged (PropertyChange ());
482 TempoMap::add_meter (const Meter& meter, BBT_Time where)
485 Glib::RWLock::WriterLock lm (lock);
487 /* a new meter always starts a new bar on the first beat. so
488 round the start time appropriately. remember that
489 `where' is based on the existing tempo map, not
490 the result after we insert the new meter.
494 if (where.beats != 1) {
499 /* new meters *always* start on a beat. */
502 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
505 PropertyChanged (PropertyChange ());
509 TempoMap::add_meter (const Meter& meter, framepos_t where)
512 Glib::RWLock::WriterLock lm (lock);
513 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
516 PropertyChanged (PropertyChange ());
520 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
522 bool replaced = false;
525 Glib::RWLock::WriterLock lm (lock);
528 for (i = metrics->begin(); i != metrics->end(); ++i) {
530 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
532 *((Meter*) ms) = replacement;
535 timestamp_metrics (true);
542 PropertyChanged (PropertyChange ());
547 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
549 Tempo newtempo (beats_per_minute, note_type);
552 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
553 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
554 *((Tempo*) t) = newtempo;
555 PropertyChanged (PropertyChange ());
562 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
564 Tempo newtempo (beats_per_minute, note_type);
570 /* find the TempoSection immediately preceding "where"
573 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
575 if ((*i)->frame() > where) {
581 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
591 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
600 *((Tempo*)prev) = newtempo;
601 PropertyChanged (PropertyChange ());
605 TempoMap::first_meter () const
607 const MeterSection *m = 0;
609 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
610 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
615 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
621 TempoMap::first_tempo () const
623 const TempoSection *t = 0;
625 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
626 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
631 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
637 TempoMap::timestamp_metrics (bool use_bbt)
645 meter = &first_meter ();
646 tempo = &first_tempo ();
650 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
652 framepos_t current = 0;
653 framepos_t section_frames;
657 for (i = metrics->begin(); i != metrics->end(); ++i) {
661 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
663 current += section_frames;
667 (*i)->set_frame (current);
669 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
671 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
674 fatal << _("programming error: unhandled MetricSection type") << endmsg;
681 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
684 MetricSection* prev = 0;
686 for (i = metrics->begin(); i != metrics->end(); ++i) {
689 TempoMetric metric (*meter, *tempo);
692 metric.set_start (prev->start());
693 metric.set_frame (prev->frame());
695 // metric will be at frames=0 bbt=1|1|0 by default
696 // which is correct for our purpose
699 bbt_time_with_metric ((*i)->frame(), bbt, metric);
701 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
708 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
709 /* round up to next beat */
715 if (bbt.beats != 1) {
716 /* round up to next bar */
722 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
724 (*i)->set_start (bbt);
726 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
728 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
729 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
731 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
733 fatal << _("programming error: unhandled MetricSection type") << endmsg;
742 // cerr << "###############################################\n\n\n" << endl;
747 TempoMap::metric_at (framepos_t frame) const
749 TempoMetric m (first_meter(), first_tempo());
753 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
754 at something, because we insert the default tempo and meter during
755 TempoMap construction.
757 now see if we can find better candidates.
760 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
762 if ((*i)->frame() > frame) {
766 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
767 m.set_tempo (*tempo);
768 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
769 m.set_meter (*meter);
772 m.set_frame ((*i)->frame ());
773 m.set_start ((*i)->start ());
780 TempoMap::metric_at (BBT_Time bbt) const
782 TempoMetric m (first_meter(), first_tempo());
786 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
787 at something, because we insert the default tempo and meter during
788 TempoMap construction.
790 now see if we can find better candidates.
793 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
795 BBT_Time section_start ((*i)->start());
797 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
801 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
802 m.set_tempo (*tempo);
803 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
804 m.set_meter (*meter);
807 m.set_frame ((*i)->frame ());
808 m.set_start (section_start);
815 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) const
818 Glib::RWLock::ReaderLock lm (lock);
819 bbt_time_unlocked (frame, bbt);
824 TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt) const
826 bbt_time_with_metric (frame, bbt, metric_at (frame));
830 TempoMap::bbt_time_with_metric (framepos_t frame, BBT_Time& bbt, const TempoMetric& metric) const
832 framecnt_t frame_diff;
834 // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
836 const double beats_per_bar = metric.meter().beats_per_bar();
837 const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / BBT_Time::ticks_per_beat;
839 /* now compute how far beyond that point we actually are. */
841 frame_diff = frame - metric.frame();
843 bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
844 uint32_t xtra_beats = bbt.ticks / (uint32_t)BBT_Time::ticks_per_beat;
845 bbt.ticks %= (uint32_t)BBT_Time::ticks_per_beat;
847 bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
848 bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
849 bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
851 /* if we have a fractional number of beats per bar, we see if
852 we're in the last beat (the fractional one). if so, we
853 round ticks appropriately and bump to the next bar. */
854 double beat_fraction = beats_per_bar - floor(beats_per_bar);
855 /* XXX one problem here is that I'm not sure how to handle
856 fractional beats that don't evenly divide ticks_per_beat.
857 If they aren't handled consistently, I would guess we'll
858 continue to have strange discrepancies occuring. Perhaps
859 this will also behave badly in the case of meters like
860 0.1/4, but I can't be bothered to test that.
862 uint32_t ticks_on_last_beat = (uint32_t)floor(BBT_Time::ticks_per_beat * beat_fraction);
864 if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) {
865 bbt.ticks -= ticks_on_last_beat;
870 bbt.beats++; // correction for 1-based counting, see above for matching operation.
872 // cerr << "-----\t RETURN " << bbt << endl;
876 TempoMap::count_frames_between (const BBT_Time& start, const BBT_Time& end) const
878 /* for this to work with fractional measure types, start and end have to be
879 "legal" BBT types, that means that the beats and ticks should be inside
883 framecnt_t frames = 0;
884 framepos_t start_frame = 0;
885 framepos_t end_frame = 0;
887 TempoMetric m = metric_at (start);
889 uint32_t bar_offset = start.bars - m.start().bars;
891 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
892 + start.ticks/BBT_Time::ticks_per_beat;
895 start_frame = m.frame() + (framepos_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
899 bar_offset = end.bars - m.start().bars;
901 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
902 + end.ticks/BBT_Time::ticks_per_beat;
904 end_frame = m.frame() + (framepos_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
906 frames = end_frame - start_frame;
913 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
915 /* this is used in timestamping the metrics by actually counting the beats */
917 framecnt_t frames = 0;
918 uint32_t bar = start.bars;
919 double beat = (double) start.beats;
920 double beats_counted = 0;
921 double beats_per_bar = 0;
922 double beat_frames = 0;
924 beats_per_bar = meter.beats_per_bar();
925 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
929 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
931 if (beat >= beats_per_bar) {
936 if (beat > beats_per_bar) {
938 /* this is a fractional beat at the end of a fractional bar
939 so it should only count for the fraction
942 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
951 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
952 // << " bpb were " << beats_per_bar
953 // << " fpb was " << beat_frames
956 frames = (framecnt_t) llrint (floor (beats_counted * beat_frames));
963 TempoMap::frame_time (const BBT_Time& bbt) const
965 BBT_Time start ; /* 1|1|0 */
967 return count_frames_between ( start, bbt);
971 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) const
973 framecnt_t frames = 0;
979 Glib::RWLock::ReaderLock lm (lock);
980 frames = bbt_duration_at_unlocked (when, bbt,dir);
987 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
989 framecnt_t frames = 0;
991 double beats_per_bar;
994 result.bars = max(1U, when.bars + dir * bbt.bars) ;
998 TempoMetric metric = metric_at(result);
999 beats_per_bar = metric.meter().beats_per_bar();
1001 /* Reduce things to legal bbt values we have to handle possible
1002 fractional=shorter beats at the end of measures and things like 0|11|9000
1003 as a duration in a 4.5/4 measure the musical decision is that the
1004 fractional beat is also a beat , although a shorter one
1008 result.beats = when.beats + bbt.beats;
1009 result.ticks = when.ticks + bbt.ticks;
1011 while (result.beats >= (beats_per_bar + 1)) {
1013 result.beats -= (uint32_t) ceil(beats_per_bar);
1014 metric = metric_at(result); // maybe there is a meter change
1015 beats_per_bar = metric.meter().beats_per_bar();
1019 /* We now counted the beats and landed in the target measure, now deal
1020 with ticks this seems complicated, but we want to deal with the
1021 corner case of a sequence of time signatures like 0.2/4-0.7/4 and
1022 with request like bbt = 3|2|9000 ,so we repeat the same loop but add
1026 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1029 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1030 (1 - (ceil(beats_per_bar) - beats_per_bar))* BBT_Time::ticks_per_beat
1031 : BBT_Time::ticks_per_beat );
1033 while (result.ticks >= ticks_at_beat) {
1035 result.ticks -= ticks_at_beat;
1036 if (result.beats >= (beats_per_bar + 1)) {
1039 metric = metric_at(result); // maybe there is a meter change
1040 beats_per_bar = metric.meter().beats_per_bar();
1042 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1043 (1 - (ceil(beats_per_bar) - beats_per_bar) ) * BBT_Time::ticks_per_beat
1044 : BBT_Time::ticks_per_beat);
1050 uint32_t b = bbt.beats;
1053 while( b > when.beats ) {
1055 result.bars = max(1U,result.bars-- ) ;
1056 metric = metric_at(result); // maybe there is a meter change
1057 beats_per_bar = metric.meter().beats_per_bar();
1058 if (b >= ceil(beats_per_bar)) {
1060 b -= (uint32_t) ceil(beats_per_bar);
1062 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1065 result.beats = when.beats - b;
1069 if (bbt.ticks <= when.ticks) {
1070 result.ticks = when.ticks - bbt.ticks;
1073 uint32_t ticks_at_beat= (uint32_t) BBT_Time::ticks_per_beat;
1074 uint32_t t = bbt.ticks - when.ticks;
1078 if (result.beats == 1) {
1079 result.bars = max(1U, result.bars-- ) ;
1080 metric = metric_at(result); // maybe there is a meter change
1081 beats_per_bar = metric.meter().beats_per_bar();
1082 result.beats = (uint32_t) ceil(beats_per_bar);
1083 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * BBT_Time::ticks_per_beat) ;
1086 ticks_at_beat = (uint32_t) BBT_Time::ticks_per_beat;
1089 if (t <= ticks_at_beat) {
1090 result.ticks = ticks_at_beat - t;
1094 } while (t > ticks_at_beat);
1102 frames = count_frames_between( result,when);
1104 frames = count_frames_between(when,result);
1113 TempoMap::round_to_bar (framepos_t fr, int dir)
1116 Glib::RWLock::ReaderLock lm (lock);
1117 return round_to_type (fr, dir, Bar);
1123 TempoMap::round_to_beat (framepos_t fr, int dir)
1126 Glib::RWLock::ReaderLock lm (lock);
1127 return round_to_type (fr, dir, Beat);
1132 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1135 uint32_t ticks_one_half_subdivisions_worth;
1136 uint32_t ticks_one_subdivisions_worth;
1137 uint32_t difference;
1139 bbt_time(fr, the_beat);
1141 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1142 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1148 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1151 /* right on the subdivision, so the difference is just the subdivision ticks */
1152 difference = ticks_one_subdivisions_worth;
1155 /* not on subdivision, compute distance to next subdivision */
1157 difference = ticks_one_subdivisions_worth - mod;
1160 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1162 } else if (dir < 0) {
1164 /* round to previous */
1166 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1169 /* right on the subdivision, so the difference is just the subdivision ticks */
1170 difference = ticks_one_subdivisions_worth;
1172 /* not on subdivision, compute distance to previous subdivision, which
1173 is just the modulus.
1180 the_beat = bbt_subtract (the_beat, BBT_Time (0, 0, difference));
1182 /* can't go backwards from wherever pos is, so just return it */
1187 /* round to nearest */
1189 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1190 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1191 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1193 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1194 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1198 return frame_time (the_beat);
1202 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1204 TempoMetric metric = metric_at (frame);
1207 BBT_Time one_bar (1,0,0);
1208 BBT_Time one_beat (0,1,0);
1210 bbt_time_with_metric (frame, bbt, metric);
1214 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, dir, bbt));
1218 /* find bar position preceding frame */
1221 bbt = bbt_subtract (bbt, one_bar);
1229 } else if (dir > 0) {
1231 /* find bar position following frame */
1234 bbt = bbt_add (bbt, one_bar, metric);
1242 /* "true" rounding */
1247 midbar_beats = metric.meter().beats_per_bar() / 2 + 1;
1248 midbar_ticks = BBT_Time::ticks_per_beat * fmod (midbar_beats, 1.0f);
1249 midbar_beats = floor (midbar_beats);
1251 BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1264 /* force beats & ticks to their values at the start of a bar */
1270 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1274 /* find beat position preceding frame */
1277 bbt = bbt_subtract (bbt, one_beat);
1285 } else if (dir > 0) {
1287 /* find beat position following frame */
1290 bbt = bbt_add (bbt, one_beat, metric);
1298 /* "true" rounding */
1300 /* round to nearest beat */
1301 if (bbt.ticks >= (BBT_Time::ticks_per_beat/2)) {
1304 bbt = bbt_add (bbt, one_beat, metric);
1311 /* force ticks to the value at the start of a beat */
1317 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("\tat %1 count frames from %2 to %3 = %4\n", metric.frame(), metric.start(), bbt, count_frames_between (metric.start(), bbt)));
1318 return metric.frame() + count_frames_between (metric.start(), bbt);
1321 TempoMap::BBTPointList *
1322 TempoMap::get_points (framepos_t lower, framepos_t upper) const
1325 Metrics::const_iterator i;
1326 BBTPointList *points;
1328 const MeterSection* meter;
1329 const MeterSection* m;
1330 const TempoSection* tempo;
1331 const TempoSection* t;
1334 double beats_per_bar;
1337 double frames_per_bar;
1343 meter = &first_meter ();
1344 tempo = &first_tempo ();
1346 /* find the starting point */
1348 for (i = metrics->begin(); i != metrics->end(); ++i) {
1350 if ((*i)->frame() > lower) {
1354 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1356 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1363 meter -> the Meter for "lower"
1364 tempo -> the Tempo for "lower"
1365 i -> for first new metric after "lower", possibly metrics->end()
1367 Now start generating points.
1370 beats_per_bar = meter->beats_per_bar ();
1371 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1372 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1374 if (meter->frame() > tempo->frame()) {
1375 bar = meter->start().bars;
1376 beat = meter->start().beats;
1377 current = meter->frame();
1379 bar = tempo->start().bars;
1380 beat = tempo->start().beats;
1381 current = tempo->frame();
1384 /* initialize current to point to the bar/beat just prior to the
1385 lower frame bound passed in. assumes that current is initialized
1386 above to be on a beat.
1389 delta_bars = (lower-current) / frames_per_bar;
1390 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1391 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1393 // adjust bars and beats too
1394 bar += (uint32_t) (floor(delta_bars));
1395 beat += (uint32_t) (floor(delta_beats));
1397 points = new BBTPointList;
1401 if (i == metrics->end()) {
1403 // cerr << "== limit set to end of request @ " << limit << endl;
1405 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1406 limit = (*i)->frame();
1409 limit = min (limit, upper);
1411 while (current < limit) {
1413 /* if we're at the start of a bar, add bar point */
1416 if (current >= lower) {
1417 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1418 points->push_back (BBTPoint (*meter, *tempo,(framepos_t)rint(current), Bar, bar, 1));
1423 /* add some beats if we can */
1425 beat_frame = current;
1427 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1428 if (beat_frame >= lower) {
1429 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1430 points->push_back (BBTPoint (*meter, *tempo, (framepos_t) rint(beat_frame), Beat, bar, beat));
1432 beat_frame += beat_frames;
1433 current+= beat_frames;
1438 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1439 // << (beat > ceil(beats_per_bar))
1442 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1444 /* we walked an entire bar. its
1445 important to move `current' forward
1446 by the actual frames_per_bar, not move it to
1447 an integral beat_frame, so that metrics with
1448 non-integral beats-per-bar have
1449 their bar positions set
1450 correctly. consider a metric with
1451 9-1/2 beats-per-bar. the bar we
1452 just filled had 10 beat marks,
1453 but the bar end is 1/2 beat before
1455 And it is also possible that a tempo
1456 change occured in the middle of a bar,
1457 so we subtract the possible extra fraction from the current
1460 if (beat > ceil (beats_per_bar)) {
1461 /* next bar goes where the numbers suggest */
1462 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1463 // cerr << "++ next bar from numbers\n";
1465 /* next bar goes where the next metric is */
1467 // cerr << "++ next bar at next metric\n";
1475 /* if we're done, then we're done */
1477 if (current >= upper) {
1481 /* i is an iterator that refers to the next metric (or none).
1482 if there is a next metric, move to it, and continue.
1485 if (i != metrics->end()) {
1487 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1489 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1491 /* new MeterSection, beat always returns to 1 */
1495 current = (*i)->frame ();
1496 // cerr << "loop around with current @ " << current << endl;
1498 beats_per_bar = meter->beats_per_bar ();
1499 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1500 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1511 TempoMap::tempo_section_at (framepos_t frame) const
1513 Glib::RWLock::ReaderLock lm (lock);
1514 Metrics::const_iterator i;
1515 TempoSection* prev = 0;
1517 for (i = metrics->begin(); i != metrics->end(); ++i) {
1520 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1522 if ((*i)->frame() > frame) {
1538 TempoMap::tempo_at (framepos_t frame) const
1540 TempoMetric m (metric_at (frame));
1546 TempoMap::meter_at (framepos_t frame) const
1548 TempoMetric m (metric_at (frame));
1553 TempoMap::get_state ()
1555 Metrics::const_iterator i;
1556 XMLNode *root = new XMLNode ("TempoMap");
1559 Glib::RWLock::ReaderLock lm (lock);
1560 for (i = metrics->begin(); i != metrics->end(); ++i) {
1561 root->add_child_nocopy ((*i)->get_state());
1569 TempoMap::set_state (const XMLNode& node, int /*version*/)
1572 Glib::RWLock::WriterLock lm (lock);
1575 XMLNodeConstIterator niter;
1576 Metrics old_metrics (*metrics);
1580 nlist = node.children();
1582 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1583 XMLNode* child = *niter;
1585 if (child->name() == TempoSection::xml_state_node_name) {
1588 metrics->push_back (new TempoSection (*child));
1591 catch (failed_constructor& err){
1592 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1593 *metrics = old_metrics;
1597 } else if (child->name() == MeterSection::xml_state_node_name) {
1600 metrics->push_back (new MeterSection (*child));
1603 catch (failed_constructor& err) {
1604 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1605 *metrics = old_metrics;
1611 if (niter == nlist.end()) {
1613 MetricSectionSorter cmp;
1614 metrics->sort (cmp);
1615 timestamp_metrics (true);
1619 PropertyChanged (PropertyChange ());
1625 TempoMap::dump (std::ostream& o) const
1627 const MeterSection* m;
1628 const TempoSection* t;
1630 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1632 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1633 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1634 << t->movable() << ')' << endl;
1635 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1636 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1637 << " (move? " << m->movable() << ')' << endl;
1643 TempoMap::n_tempos() const
1645 Glib::RWLock::ReaderLock lm (lock);
1648 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1649 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1658 TempoMap::n_meters() const
1660 Glib::RWLock::ReaderLock lm (lock);
1663 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1664 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1673 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1675 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1676 if ((*i)->frame() >= where) {
1677 (*i)->set_frame ((*i)->frame() + amount);
1681 timestamp_metrics (false);
1683 PropertyChanged (PropertyChange ());
1687 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1689 TempoMetric metric = metric_at (start);
1690 return bbt_add (start, other, metric);
1694 * add the BBT interval @param increment to @param start and return the result
1697 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
1699 BBT_Time result = start;
1700 BBT_Time op = increment; /* argument is const, but we need to modify it */
1701 uint32_t ticks = result.ticks + op.ticks;
1703 if (ticks >= BBT_Time::ticks_per_beat) {
1705 result.ticks = ticks % (uint32_t) BBT_Time::ticks_per_beat;
1707 result.ticks += op.ticks;
1710 /* now comes the complicated part. we have to add one beat a time,
1711 checking for a new metric on every beat.
1714 /* grab all meter sections */
1716 list<const MeterSection*> meter_sections;
1718 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1719 const MeterSection* ms;
1720 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1721 meter_sections.push_back (ms);
1725 assert (!meter_sections.empty());
1727 list<const MeterSection*>::const_iterator next_meter;
1728 const Meter* meter = 0;
1730 /* go forwards through the meter sections till we get to the one
1731 covering the current value of result. this positions i to point to
1732 the next meter section too, or the end.
1735 for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1737 if (result < (*next_meter)->start()) {
1738 /* this metric is past the result time. stop looking, we have what we need */
1742 if (result == (*next_meter)->start()) {
1743 /* this meter section starts at result, push i beyond it so that it points
1744 to the NEXT section, opwise we will get stuck later, and use this meter section.
1746 meter = *next_meter;
1751 meter = *next_meter;
1754 assert (meter != 0);
1756 /* OK, now have the meter for the bar start we are on, and i is an iterator
1757 that points to the metric after the one we are currently dealing with
1758 (or to metrics->end(), of course)
1763 /* given the current meter, have we gone past the end of the bar ? */
1765 if (result.beats >= meter->beats_per_bar()) {
1766 /* move to next bar, first beat */
1777 /* check if we need to use a new meter section: has adding beats to result taken us
1778 to or after the start of the next meter section? in which case, use it.
1781 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1782 meter = *next_meter;
1787 /* finally, add bars */
1789 result.bars += op.bars++;
1795 * subtract the BBT interval @param decrement from @param start and return the result
1798 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1800 BBT_Time result = start;
1801 BBT_Time op = decrement; /* argument is const, but we need to modify it */
1803 if (op.ticks > result.ticks) {
1804 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1806 result.ticks = BBT_Time::ticks_per_beat - (op.ticks - result.ticks);
1808 result.ticks -= op.ticks;
1811 /* now comes the complicated part. we have to subtract one beat a time,
1812 checking for a new metric on every beat.
1815 /* grab all meter sections */
1817 list<const MeterSection*> meter_sections;
1819 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1820 const MeterSection* ms;
1821 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1822 meter_sections.push_back (ms);
1826 assert (!meter_sections.empty());
1828 /* go backwards through the meter sections till we get to the one
1829 covering the current value of result. this positions i to point to
1830 the next (previous) meter section too, or the end.
1833 const MeterSection* meter = 0;
1834 list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't
1835 // support const_reverse_iterator::operator!=()
1837 for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1839 /* when we find the first meter section that is before or at result, use it,
1840 and set next_meter to the previous one
1843 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1844 meter = *next_meter;
1850 assert (meter != 0);
1852 /* OK, now have the meter for the bar start we are on, and i is an iterator
1853 that points to the metric after the one we are currently dealing with
1854 (or to metrics->end(), of course)
1859 /* have we reached the start of the bar? if so, move to the last beat of the previous
1860 bar. opwise, just step back 1 beat.
1863 if (result.beats == 1) {
1865 /* move to previous bar, last beat */
1867 if (result.bars <= 1) {
1868 /* i'm sorry dave, i can't do that */
1869 throw std::out_of_range ("illegal BBT subtraction");
1873 result.beats = meter->beats_per_bar();
1884 /* check if we need to use a new meter section: has subtracting beats to result taken us
1885 to before the start of the current meter section? in which case, use the prior one.
1888 if (result < meter->start() && next_meter != meter_sections.rend()) {
1889 meter = *next_meter;
1894 /* finally, subtract bars */
1896 if (op.bars >= result.bars) {
1897 /* i'm sorry dave, i can't do that */
1898 throw std::out_of_range ("illegal BBT subtraction");
1901 result.bars -= op.bars;
1905 /** Add the BBT interval op to pos and return the result */
1907 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
1909 /* XXX: this is a little inaccurate as small errors are introduced
1910 every time a probably-fractional product of something and
1911 frames_per_beat is rounded. Other errors can be introduced
1912 by op.ticks' integer nature.
1915 Metrics::const_iterator i;
1916 const MeterSection* meter;
1917 const MeterSection* m;
1918 const TempoSection* tempo;
1919 const TempoSection* t;
1920 framecnt_t frames_per_beat;
1922 meter = &first_meter ();
1923 tempo = &first_tempo ();
1928 /* find the starting metrics for tempo & meter */
1930 for (i = metrics->begin(); i != metrics->end(); ++i) {
1932 if ((*i)->frame() > pos) {
1936 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1938 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1945 meter -> the Meter for "pos"
1946 tempo -> the Tempo for "pos"
1947 i -> for first new metric after "pos", possibly metrics->end()
1950 /* now comes the complicated part. we have to add one beat a time,
1951 checking for a new metric on every beat.
1954 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
1958 pos += llrint (frames_per_beat * meter->beats_per_bar());
1961 /* check if we need to use a new metric section: has adding frames moved us
1962 to or after the start of the next metric section? in which case, use it.
1965 if (i != metrics->end()) {
1966 if ((*i)->frame() <= pos) {
1968 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1970 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1974 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
1983 /* given the current meter, have we gone past the end of the bar ? */
1985 pos += frames_per_beat;
1988 /* check if we need to use a new metric section: has adding frames moved us
1989 to or after the start of the next metric section? in which case, use it.
1992 if (i != metrics->end()) {
1993 if ((*i)->frame() <= pos) {
1995 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1997 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2001 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2007 if (op.ticks >= BBT_Time::ticks_per_beat) {
2008 pos += frames_per_beat;
2009 pos += llrint (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / (double) BBT_Time::ticks_per_beat));
2011 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2018 /** Count the number of beats that are equivalent to distance when starting at pos */
2020 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2022 Metrics::const_iterator i;
2024 const MeterSection* meter;
2025 const MeterSection* m;
2026 const TempoSection* tempo;
2027 const TempoSection* t;
2028 double frames_per_beat;
2030 double ddist = distance;
2033 meter = &first_meter ();
2034 tempo = &first_tempo ();
2039 /* find the starting metrics for tempo & meter */
2041 for (i = metrics->begin(); i != metrics->end(); ++i) {
2043 if ((*i)->frame() > pos) {
2047 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2049 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2056 meter -> the Meter for "pos"
2057 tempo -> the Tempo for "pos"
2058 i -> for first new metric after "pos", possibly metrics->end()
2061 /* now comes the complicated part. we have to add one beat a time,
2062 checking for a new metric on every beat.
2065 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2069 /* if we're nearly at the end, but have a fractional beat left,
2070 compute the fraction and then its all over
2073 if (ddist < frames_per_beat) {
2074 beats += ddist / frames_per_beat;
2080 ddist -= frames_per_beat;
2081 dpos += frames_per_beat;
2084 /* check if we need to use a new metric section: has adding frames moved us
2085 to or after the start of the next metric section? in which case, use it.
2088 if (i != metrics->end()) {
2089 if ((*i)->frame() <= (framepos_t) dpos) {
2091 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2093 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2097 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2107 /** Compare the time of this with that of another MetricSection.
2108 * @param with_bbt True to compare using ::start(), false to use ::frame().
2109 * @return -1 for less than, 0 for equal, 1 for greater than.
2113 MetricSection::compare (MetricSection* other, bool with_bbt) const
2116 if (start() == other->start()) {
2118 } else if (start() < other->start()) {
2124 if (frame() == other->frame()) {
2126 } else if (frame() < other->frame()) {