second (and hopefully) final part of changes to respond to header format changes...
[ArdourMidi.git] / libs / ardour / tempo.cc
blob9b06d8868fdf37118203358ea66ce22b430b4dc7
1 /*
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.
20 #include <algorithm>
21 #include <stdexcept>
23 #include <unistd.h>
25 #include <cmath>
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"
34 #include "i18n.h"
35 #include <locale.h>
37 using namespace std;
38 using namespace ARDOUR;
39 using namespace PBD;
41 /* _default tempo is 4/4 qtr=120 */
43 Meter TempoMap::_default_meter (4.0, 4.0);
44 Tempo TempoMap::_default_tempo (120.0);
46 const double Meter::ticks_per_beat = 1920.0;
48 double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
50 return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
53 /***********************************************************************/
55 double
56 Meter::frames_per_bar (const Tempo& tempo, nframes_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;
69 BBT_Time start;
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,
78 &start.bars,
79 &start.beats,
80 &start.ticks) < 3) {
81 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
82 throw failed_constructor();
85 set_start (start);
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 */
99 _note_type = 4.0;
100 } else {
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()));
115 XMLNode&
116 TempoSection::get_state() const
118 XMLNode *root = new XMLNode (xml_state_node_name);
119 char buf[256];
120 LocaleGuard lg (X_("POSIX"));
122 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
123 start().bars,
124 start().beats,
125 start().ticks);
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);
134 return *root;
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;
145 BBT_Time start;
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,
154 &start.bars,
155 &start.beats,
156 &start.ticks) < 3) {
157 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
158 throw failed_constructor();
161 set_start (start);
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()));
191 XMLNode&
192 MeterSection::get_state() const
194 XMLNode *root = new XMLNode (xml_state_node_name);
195 char buf[256];
196 LocaleGuard lg (X_("POSIX"));
198 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
199 start().bars,
200 start().beats,
201 start().ticks);
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);
210 return *root;
213 /***********************************************************************/
215 struct MetricSectionSorter {
216 bool operator() (const MetricSection* a, const MetricSection* b) {
217 return a->start() < b->start();
221 TempoMap::TempoMap (nframes64_t fr)
223 metrics = new Metrics;
224 _frame_rate = fr;
225 last_bbt_valid = false;
226 BBT_Time start;
228 start.bars = 1;
229 start.beats = 1;
230 start.ticks = 0;
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()) {
252 return -1;
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 nframes64_t frame = frame_time (when);
263 // cerr << "nominal frame time = " << frame << endl;
265 nframes64_t prev_frame = round_to_type (frame, -1, Beat);
266 nframes64_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)) {
273 frame = prev_frame;
274 } else {
275 frame = next_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;
283 metrics->sort (cmp);
285 } else {
287 /* positioned at bar start already, so just put it there */
289 section.set_start (when);
290 metrics->sort (cmp);
291 timestamp_metrics (true);
295 return 0;
298 void
299 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
301 if (move_metric_section (tempo, when) == 0) {
302 PropertyChanged (PropertyChange ());
306 void
307 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
309 if (move_metric_section (meter, when) == 0) {
310 PropertyChanged (PropertyChange ());
314 void
315 TempoMap::remove_tempo (const TempoSection& tempo)
317 bool removed = false;
320 Glib::RWLock::WriterLock lm (lock);
321 Metrics::iterator i;
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()) {
327 metrics->erase (i);
328 removed = true;
329 break;
336 if (removed) {
337 PropertyChanged (PropertyChange ());
341 void
342 TempoMap::remove_meter (const MeterSection& tempo)
344 bool removed = false;
347 Glib::RWLock::WriterLock lm (lock);
348 Metrics::iterator i;
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()) {
354 metrics->erase (i);
355 removed = true;
356 break;
363 if (removed) {
364 PropertyChanged (PropertyChange ());
368 void
369 TempoMap::do_insert (MetricSection* section, bool with_bbt)
371 Metrics::iterator i;
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
375 the new one.
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);
384 if (c < 0) {
385 /* this section is before the one to be added; go back round */
386 continue;
387 } else if (c > 0) {
388 /* this section is after the one to be added; there can't be any at the same time */
389 break;
392 /* hacky comparison of type */
393 bool const a = dynamic_cast<TempoSection*> (*i) != 0;
394 bool const b = dynamic_cast<TempoSection*> (section) != 0;
396 if (a == b) {
397 to_remove = i;
398 break;
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) {
412 continue;
415 metrics->insert (i, section);
416 break;
419 if (i == metrics->end()) {
420 metrics->insert (metrics->end(), section);
423 timestamp_metrics (with_bbt);
426 void
427 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
430 Glib::RWLock::WriterLock lm (lock);
432 /* new tempos always start on a beat */
433 where.ticks = 0;
435 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
438 PropertyChanged (PropertyChange ());
441 void
442 TempoMap::add_tempo (const Tempo& tempo, nframes64_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 ());
452 void
453 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
455 bool replaced = false;
458 Glib::RWLock::WriterLock lm (lock);
459 Metrics::iterator i;
461 for (i = metrics->begin(); i != metrics->end(); ++i) {
462 TempoSection *ts;
464 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
466 *((Tempo *) ts) = replacement;
468 replaced = true;
469 timestamp_metrics (true);
471 break;
476 if (replaced) {
477 PropertyChanged (PropertyChange ());
481 void
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) {
495 where.beats = 1;
496 where.bars++;
499 /* new meters *always* start on a beat. */
500 where.ticks = 0;
502 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
505 PropertyChanged (PropertyChange ());
508 void
509 TempoMap::add_meter (const Meter& meter, nframes64_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 ());
519 void
520 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
522 bool replaced = false;
525 Glib::RWLock::WriterLock lm (lock);
526 Metrics::iterator i;
528 for (i = metrics->begin(); i != metrics->end(); ++i) {
529 MeterSection *ms;
530 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
532 *((Meter*) ms) = replacement;
534 replaced = true;
535 timestamp_metrics (true);
536 break;
541 if (replaced) {
542 PropertyChanged (PropertyChange ());
546 void
547 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
549 Tempo newtempo (beats_per_minute, note_type);
550 TempoSection* t;
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 ());
556 break;
561 void
562 TempoMap::change_existing_tempo_at (nframes64_t where, double beats_per_minute, double note_type)
564 Tempo newtempo (beats_per_minute, note_type);
566 TempoSection* prev;
567 TempoSection* first;
568 Metrics::iterator i;
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) {
576 break;
579 TempoSection* t;
581 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
582 if (!first) {
583 first = t;
585 prev = t;
589 if (!prev) {
590 if (!first) {
591 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
592 return;
595 prev = first;
598 /* reset */
600 *((Tempo*)prev) = newtempo;
601 PropertyChanged (PropertyChange ());
604 const MeterSection&
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) {
611 return *m;
615 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
616 /*NOTREACHED*/
617 return *m;
620 const TempoSection&
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) {
627 return *t;
631 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
632 /*NOTREACHED*/
633 return *t;
636 void
637 TempoMap::timestamp_metrics (bool use_bbt)
639 Metrics::iterator i;
640 const Meter* meter;
641 const Tempo* tempo;
642 Meter *m;
643 Tempo *t;
645 meter = &first_meter ();
646 tempo = &first_tempo ();
648 if (use_bbt) {
650 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
652 nframes64_t current = 0;
653 nframes64_t section_frames;
654 BBT_Time start;
655 BBT_Time end;
657 for (i = metrics->begin(); i != metrics->end(); ++i) {
659 end = (*i)->start();
661 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
663 current += section_frames;
665 start = end;
667 (*i)->set_frame (current);
669 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
670 tempo = t;
671 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
672 meter = m;
673 } else {
674 fatal << _("programming error: unhandled MetricSection type") << endmsg;
675 /*NOTREACHED*/
679 } else {
681 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
683 bool first = true;
684 MetricSection* prev = 0;
686 for (i = metrics->begin(); i != metrics->end(); ++i) {
688 BBT_Time bbt;
689 TempoMetric metric (*meter, *tempo);
691 if (prev) {
692 metric.set_start (prev->start());
693 metric.set_frame (prev->frame());
694 } else {
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 << " => ";
704 if (first) {
705 first = false;
706 } else {
708 if (bbt.ticks > Meter::ticks_per_beat/2) {
709 /* round up to next beat */
710 bbt.beats += 1;
713 bbt.ticks = 0;
715 if (bbt.beats != 1) {
716 /* round up to next bar */
717 bbt.bars += 1;
718 bbt.beats = 1;
722 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
724 (*i)->set_start (bbt);
726 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
727 tempo = t;
728 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
729 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
730 meter = m;
731 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
732 } else {
733 fatal << _("programming error: unhandled MetricSection type") << endmsg;
734 /*NOTREACHED*/
737 prev = (*i);
741 // dump (cerr);
742 // cerr << "###############################################\n\n\n" << endl;
746 TempoMetric
747 TempoMap::metric_at (nframes64_t frame) const
749 TempoMetric m (first_meter(), first_tempo());
750 const Meter* meter;
751 const Tempo* 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) {
763 break;
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 ());
776 return m;
779 TempoMetric
780 TempoMap::metric_at (BBT_Time bbt) const
782 TempoMetric m (first_meter(), first_tempo());
783 const Meter* meter;
784 const Tempo* 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)) {
798 break;
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);
811 return m;
814 void
815 TempoMap::bbt_time (nframes64_t frame, BBT_Time& bbt) const
818 Glib::RWLock::ReaderLock lm (lock);
819 bbt_time_unlocked (frame, bbt);
823 void
824 TempoMap::bbt_time_unlocked (nframes64_t frame, BBT_Time& bbt) const
826 bbt_time_with_metric (frame, bbt, metric_at (frame));
829 void
830 TempoMap::bbt_time_with_metric (nframes64_t frame, BBT_Time& bbt, const TempoMetric& metric) const
832 nframes64_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()) / Meter::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)Meter::ticks_per_beat;
845 bbt.ticks %= (uint32_t)Meter::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(Meter::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;
866 bbt.beats = 0;
867 bbt.bars++;
870 bbt.beats++; // correction for 1-based counting, see above for matching operation.
872 // cerr << "-----\t RETURN " << bbt << endl;
875 nframes64_t
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 "legal" BBT types,
879 that means that the beats and ticks should be inside a bar
882 nframes64_t frames = 0;
883 nframes64_t start_frame = 0;
884 nframes64_t end_frame = 0;
886 TempoMetric m = metric_at (start);
888 uint32_t bar_offset = start.bars - m.start().bars;
890 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
891 + start.ticks/Meter::ticks_per_beat;
894 start_frame = m.frame() + (nframes64_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
896 m = metric_at(end);
898 bar_offset = end.bars - m.start().bars;
900 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
901 + end.ticks/Meter::ticks_per_beat;
903 end_frame = m.frame() + (nframes64_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
905 frames = end_frame - start_frame;
907 return frames;
911 nframes64_t
912 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
914 /* this is used in timestamping the metrics by actually counting the beats */
916 nframes64_t frames = 0;
917 uint32_t bar = start.bars;
918 double beat = (double) start.beats;
919 double beats_counted = 0;
920 double beats_per_bar = 0;
921 double beat_frames = 0;
923 beats_per_bar = meter.beats_per_bar();
924 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
926 frames = 0;
928 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
930 if (beat >= beats_per_bar) {
931 beat = 1;
932 ++bar;
933 ++beats_counted;
935 if (beat > beats_per_bar) {
937 /* this is a fractional beat at the end of a fractional bar
938 so it should only count for the fraction
941 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
944 } else {
945 ++beat;
946 ++beats_counted;
950 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
951 // << " bpb were " << beats_per_bar
952 // << " fpb was " << beat_frames
953 // << endl;
955 frames = (nframes64_t) floor (beats_counted * beat_frames);
957 return frames;
961 nframes64_t
962 TempoMap::frame_time (const BBT_Time& bbt) const
964 BBT_Time start ; /* 1|1|0 */
966 return count_frames_between ( start, bbt);
969 nframes64_t
970 TempoMap::bbt_duration_at (nframes64_t pos, const BBT_Time& bbt, int dir) const
972 nframes64_t frames = 0;
974 BBT_Time when;
975 bbt_time(pos, when);
978 Glib::RWLock::ReaderLock lm (lock);
979 frames = bbt_duration_at_unlocked (when, bbt,dir);
982 return frames;
985 nframes64_t
986 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
989 nframes64_t frames = 0;
991 double beats_per_bar;
992 BBT_Time result;
994 result.bars = max(1U, when.bars + dir * bbt.bars) ;
995 result.beats = 1;
996 result.ticks = 0;
998 TempoMetric metric = metric_at(result);
999 beats_per_bar = metric.meter().beats_per_bar();
1003 /*reduce things to legal bbt values
1004 we have to handle possible fractional=shorter beats at the end of measures
1005 and things like 0|11|9000 as a duration in a 4.5/4 measure
1006 the musical decision is that the fractional beat is also a beat , although a shorter one
1010 if (dir >= 0) {
1011 result.beats = when.beats + bbt.beats;
1012 result.ticks = when.ticks + bbt.ticks;
1014 while (result.beats >= (beats_per_bar + 1)) {
1015 result.bars++;
1016 result.beats -= (uint32_t) ceil(beats_per_bar);
1017 metric = metric_at(result); // maybe there is a meter change
1018 beats_per_bar = metric.meter().beats_per_bar();
1021 /*we now counted the beats and landed in the target measure, now deal with ticks
1022 this seems complicated, but we want to deal with the corner case of a sequence of time signatures like 0.2/4-0.7/4
1023 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
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))* Meter::ticks_per_beat
1031 : Meter::ticks_per_beat );
1033 while (result.ticks >= ticks_at_beat) {
1034 result.beats++;
1035 result.ticks -= ticks_at_beat;
1036 if (result.beats >= (beats_per_bar + 1)) {
1037 result.bars++;
1038 result.beats = 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) ) * Meter::ticks_per_beat
1044 : Meter::ticks_per_beat);
1049 } else {
1050 uint32_t b = bbt.beats;
1052 /* count 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);
1061 } else {
1062 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1065 result.beats = when.beats - b;
1067 /*count ticks */
1069 if (bbt.ticks <= when.ticks) {
1070 result.ticks = when.ticks - bbt.ticks;
1071 } else {
1073 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1074 uint32_t t = bbt.ticks - when.ticks;
1076 do {
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)) * Meter::ticks_per_beat) ;
1084 } else {
1085 result.beats --;
1086 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1089 if (t <= ticks_at_beat) {
1090 result.ticks = ticks_at_beat - t;
1091 } else {
1092 t-= ticks_at_beat;
1094 } while (t > ticks_at_beat);
1101 if (dir < 0 ) {
1102 frames = count_frames_between( result,when);
1103 } else {
1104 frames = count_frames_between(when,result);
1107 return frames;
1112 nframes64_t
1113 TempoMap::round_to_bar (nframes64_t fr, int dir)
1116 Glib::RWLock::ReaderLock lm (lock);
1117 return round_to_type (fr, dir, Bar);
1122 nframes64_t
1123 TempoMap::round_to_beat (nframes64_t fr, int dir)
1126 Glib::RWLock::ReaderLock lm (lock);
1127 return round_to_type (fr, dir, Beat);
1131 nframes64_t
1132 TempoMap::round_to_beat_subdivision (nframes64_t fr, int sub_num, int dir)
1134 BBT_Time the_beat;
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)Meter::ticks_per_beat / sub_num;
1142 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1144 if (dir > 0) {
1146 /* round to next */
1148 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1150 if (mod == 0) {
1151 /* right on the subdivision, so the difference is just the subdivision ticks */
1152 difference = ticks_one_subdivisions_worth;
1154 } else {
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;
1168 if (mod == 0) {
1169 /* right on the subdivision, so the difference is just the subdivision ticks */
1170 difference = ticks_one_subdivisions_worth;
1171 } else {
1172 /* not on subdivision, compute distance to previous subdivision, which
1173 is just the modulus.
1176 difference = mod;
1179 the_beat = bbt_subtract (the_beat, BBT_Time (0, 0, difference));
1181 } else {
1182 /* round to nearest */
1184 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1185 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1186 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1187 } else {
1188 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1189 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1193 return frame_time (the_beat);
1196 nframes64_t
1197 TempoMap::round_to_type (nframes64_t frame, int dir, BBTPointType type)
1199 TempoMetric metric = metric_at (frame);
1200 BBT_Time bbt;
1201 BBT_Time start;
1202 BBT_Time one_bar (1,0,0);
1203 BBT_Time one_beat (0,1,0);
1205 bbt_time_with_metric (frame, bbt, metric);
1207 switch (type) {
1208 case Bar:
1209 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, dir, bbt));
1211 if (dir < 0) {
1213 /* find bar position preceding frame */
1215 try {
1216 bbt = bbt_subtract (bbt, one_bar);
1219 catch (...) {
1220 return frame;
1224 } else if (dir > 0) {
1226 /* find bar position following frame */
1228 try {
1229 bbt = bbt_add (bbt, one_bar, metric);
1231 catch (...) {
1232 return frame;
1235 } else {
1237 /* "true" rounding */
1239 float midbar_beats;
1240 float midbar_ticks;
1242 midbar_beats = metric.meter().beats_per_bar() / 2 + 1;
1243 midbar_ticks = Meter::ticks_per_beat * fmod (midbar_beats, 1.0f);
1244 midbar_beats = floor (midbar_beats);
1246 BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1248 if (bbt < midbar) {
1249 /* round down */
1250 bbt.beats = 1;
1251 bbt.ticks = 0;
1252 } else {
1253 /* round up */
1254 bbt.bars++;
1255 bbt.beats = 1;
1256 bbt.ticks = 0;
1259 /* force beats & ticks to their values at the start of a bar */
1260 bbt.beats = 1;
1261 bbt.ticks = 0;
1262 break;
1264 case Beat:
1265 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1267 if (dir < 0) {
1269 /* find beat position preceding frame */
1271 try {
1272 bbt = bbt_subtract (bbt, one_beat);
1275 catch (...) {
1276 return frame;
1280 } else if (dir > 0) {
1282 /* find beat position following frame */
1284 try {
1285 bbt = bbt_add (bbt, one_beat, metric);
1287 catch (...) {
1288 return frame;
1291 } else {
1293 /* "true" rounding */
1295 /* round to nearest beat */
1296 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1298 try {
1299 bbt = bbt_add (bbt, one_beat, metric);
1301 catch (...) {
1302 return frame;
1306 /* force ticks to the value at the start of a beat */
1307 bbt.ticks = 0;
1308 break;
1312 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)));
1313 return metric.frame() + count_frames_between (metric.start(), bbt);
1316 TempoMap::BBTPointList *
1317 TempoMap::get_points (nframes64_t lower, nframes64_t upper) const
1320 Metrics::const_iterator i;
1321 BBTPointList *points;
1322 double current;
1323 const MeterSection* meter;
1324 const MeterSection* m;
1325 const TempoSection* tempo;
1326 const TempoSection* t;
1327 uint32_t bar;
1328 uint32_t beat;
1329 double beats_per_bar;
1330 double beat_frame;
1331 double beat_frames;
1332 double frames_per_bar;
1333 double delta_bars;
1334 double delta_beats;
1335 double dummy;
1336 nframes64_t limit;
1338 meter = &first_meter ();
1339 tempo = &first_tempo ();
1341 /* find the starting point */
1343 for (i = metrics->begin(); i != metrics->end(); ++i) {
1345 if ((*i)->frame() > lower) {
1346 break;
1349 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1350 tempo = t;
1351 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1352 meter = m;
1356 /* We now have:
1358 meter -> the Meter for "lower"
1359 tempo -> the Tempo for "lower"
1360 i -> for first new metric after "lower", possibly metrics->end()
1362 Now start generating points.
1365 beats_per_bar = meter->beats_per_bar ();
1366 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1367 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1369 if (meter->frame() > tempo->frame()) {
1370 bar = meter->start().bars;
1371 beat = meter->start().beats;
1372 current = meter->frame();
1373 } else {
1374 bar = tempo->start().bars;
1375 beat = tempo->start().beats;
1376 current = tempo->frame();
1379 /* initialize current to point to the bar/beat just prior to the
1380 lower frame bound passed in. assumes that current is initialized
1381 above to be on a beat.
1384 delta_bars = (lower-current) / frames_per_bar;
1385 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1386 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1388 // adjust bars and beats too
1389 bar += (uint32_t) (floor(delta_bars));
1390 beat += (uint32_t) (floor(delta_beats));
1392 points = new BBTPointList;
1394 do {
1396 if (i == metrics->end()) {
1397 limit = upper;
1398 // cerr << "== limit set to end of request @ " << limit << endl;
1399 } else {
1400 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1401 limit = (*i)->frame();
1404 limit = min (limit, upper);
1406 while (current < limit) {
1408 /* if we're at the start of a bar, add bar point */
1410 if (beat == 1) {
1411 if (current >= lower) {
1412 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1413 points->push_back (BBTPoint (*meter, *tempo,(nframes64_t)rint(current), Bar, bar, 1));
1418 /* add some beats if we can */
1420 beat_frame = current;
1422 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1423 if (beat_frame >= lower) {
1424 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1425 points->push_back (BBTPoint (*meter, *tempo, (nframes64_t) rint(beat_frame), Beat, bar, beat));
1427 beat_frame += beat_frames;
1428 current+= beat_frames;
1430 beat++;
1433 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1434 // << (beat > ceil(beats_per_bar))
1435 // << endl;
1437 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1439 /* we walked an entire bar. its
1440 important to move `current' forward
1441 by the actual frames_per_bar, not move it to
1442 an integral beat_frame, so that metrics with
1443 non-integral beats-per-bar have
1444 their bar positions set
1445 correctly. consider a metric with
1446 9-1/2 beats-per-bar. the bar we
1447 just filled had 10 beat marks,
1448 but the bar end is 1/2 beat before
1449 the last beat mark.
1450 And it is also possible that a tempo
1451 change occured in the middle of a bar,
1452 so we subtract the possible extra fraction from the current
1455 if (beat > ceil (beats_per_bar)) {
1456 /* next bar goes where the numbers suggest */
1457 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1458 // cerr << "++ next bar from numbers\n";
1459 } else {
1460 /* next bar goes where the next metric is */
1461 current = limit;
1462 // cerr << "++ next bar at next metric\n";
1464 bar++;
1465 beat = 1;
1470 /* if we're done, then we're done */
1472 if (current >= upper) {
1473 break;
1476 /* i is an iterator that refers to the next metric (or none).
1477 if there is a next metric, move to it, and continue.
1480 if (i != metrics->end()) {
1482 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1483 tempo = t;
1484 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1485 meter = m;
1486 /* new MeterSection, beat always returns to 1 */
1487 beat = 1;
1490 current = (*i)->frame ();
1491 // cerr << "loop around with current @ " << current << endl;
1493 beats_per_bar = meter->beats_per_bar ();
1494 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1495 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1497 ++i;
1500 } while (1);
1502 return points;
1505 const TempoSection&
1506 TempoMap::tempo_section_at (nframes64_t frame)
1508 Glib::RWLock::ReaderLock lm (lock);
1509 Metrics::iterator i;
1510 TempoSection* prev = 0;
1512 for (i = metrics->begin(); i != metrics->end(); ++i) {
1513 TempoSection* t;
1515 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1517 if ((*i)->frame() > frame) {
1518 break;
1521 prev = t;
1525 if (prev == 0) {
1526 fatal << endmsg;
1529 return *prev;
1532 const Tempo&
1533 TempoMap::tempo_at (nframes64_t frame) const
1535 TempoMetric m (metric_at (frame));
1536 return m.tempo();
1540 const Meter&
1541 TempoMap::meter_at (nframes64_t frame) const
1543 TempoMetric m (metric_at (frame));
1544 return m.meter();
1547 XMLNode&
1548 TempoMap::get_state ()
1550 Metrics::const_iterator i;
1551 XMLNode *root = new XMLNode ("TempoMap");
1554 Glib::RWLock::ReaderLock lm (lock);
1555 for (i = metrics->begin(); i != metrics->end(); ++i) {
1556 root->add_child_nocopy ((*i)->get_state());
1560 return *root;
1564 TempoMap::set_state (const XMLNode& node, int /*version*/)
1567 Glib::RWLock::WriterLock lm (lock);
1569 XMLNodeList nlist;
1570 XMLNodeConstIterator niter;
1571 Metrics old_metrics (*metrics);
1573 metrics->clear();
1575 nlist = node.children();
1577 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1578 XMLNode* child = *niter;
1580 if (child->name() == TempoSection::xml_state_node_name) {
1582 try {
1583 metrics->push_back (new TempoSection (*child));
1586 catch (failed_constructor& err){
1587 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1588 *metrics = old_metrics;
1589 break;
1592 } else if (child->name() == MeterSection::xml_state_node_name) {
1594 try {
1595 metrics->push_back (new MeterSection (*child));
1598 catch (failed_constructor& err) {
1599 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1600 *metrics = old_metrics;
1601 break;
1606 if (niter == nlist.end()) {
1608 MetricSectionSorter cmp;
1609 metrics->sort (cmp);
1610 timestamp_metrics (true);
1614 PropertyChanged (PropertyChange ());
1616 return 0;
1619 void
1620 TempoMap::dump (std::ostream& o) const
1622 const MeterSection* m;
1623 const TempoSection* t;
1625 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1627 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1628 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1629 << t->movable() << ')' << endl;
1630 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1631 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1632 << " (move? " << m->movable() << ')' << endl;
1638 TempoMap::n_tempos() const
1640 Glib::RWLock::ReaderLock lm (lock);
1641 int cnt = 0;
1643 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1644 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1645 cnt++;
1649 return cnt;
1653 TempoMap::n_meters() const
1655 Glib::RWLock::ReaderLock lm (lock);
1656 int cnt = 0;
1658 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1659 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1660 cnt++;
1664 return cnt;
1667 void
1668 TempoMap::insert_time (nframes64_t where, nframes64_t amount)
1670 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1671 if ((*i)->frame() >= where) {
1672 (*i)->set_frame ((*i)->frame() + amount);
1676 timestamp_metrics (false);
1678 PropertyChanged (PropertyChange ());
1681 BBT_Time
1682 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1684 TempoMetric metric = metric_at (start);
1685 return bbt_add (start, other, metric);
1689 * add the BBT interval @param increment to @param start and return the result
1691 BBT_Time
1692 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
1694 BBT_Time result = start;
1695 BBT_Time op = increment; /* argument is const, but we need to modify it */
1696 uint32_t ticks = result.ticks + op.ticks;
1698 if (ticks >= Meter::ticks_per_beat) {
1699 op.beats++;
1700 result.ticks = ticks % (uint32_t) Meter::ticks_per_beat;
1701 } else {
1702 result.ticks += op.ticks;
1705 /* now comes the complicated part. we have to add one beat a time,
1706 checking for a new metric on every beat.
1709 /* grab all meter sections */
1711 list<const MeterSection*> meter_sections;
1713 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1714 const MeterSection* ms;
1715 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1716 meter_sections.push_back (ms);
1720 assert (!meter_sections.empty());
1722 list<const MeterSection*>::const_iterator next_meter;
1723 const Meter* meter = 0;
1725 /* go forwards through the meter sections till we get to the one
1726 covering the current value of result. this positions i to point to
1727 the next meter section too, or the end.
1730 for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1732 if (result < (*next_meter)->start()) {
1733 /* this metric is past the result time. stop looking, we have what we need */
1734 break;
1737 if (result == (*next_meter)->start()) {
1738 /* this meter section starts at result, push i beyond it so that it points
1739 to the NEXT section, opwise we will get stuck later, and use this meter section.
1741 meter = *next_meter;
1742 ++next_meter;
1743 break;
1746 meter = *next_meter;
1749 assert (meter != 0);
1751 /* OK, now have the meter for the bar start we are on, and i is an iterator
1752 that points to the metric after the one we are currently dealing with
1753 (or to metrics->end(), of course)
1756 while (op.beats) {
1758 /* given the current meter, have we gone past the end of the bar ? */
1760 if (result.beats >= meter->beats_per_bar()) {
1761 /* move to next bar, first beat */
1762 result.bars++;
1763 result.beats = 1;
1764 } else {
1765 result.beats++;
1768 /* one down ... */
1770 op.beats--;
1772 /* check if we need to use a new meter section: has adding beats to result taken us
1773 to or after the start of the next meter section? in which case, use it.
1776 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1777 meter = *next_meter;
1778 ++next_meter;
1782 /* finally, add bars */
1784 result.bars += op.bars++;
1786 return result;
1790 * subtract the BBT interval @param decrement from @param start and return the result
1792 BBT_Time
1793 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1795 BBT_Time result = start;
1796 BBT_Time op = decrement; /* argument is const, but we need to modify it */
1798 if (op.ticks > result.ticks) {
1799 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1800 op.beats++;
1801 result.ticks = Meter::ticks_per_beat - (op.ticks - result.ticks);
1802 } else {
1803 result.ticks -= op.ticks;
1806 /* now comes the complicated part. we have to subtract one beat a time,
1807 checking for a new metric on every beat.
1810 /* grab all meter sections */
1812 list<const MeterSection*> meter_sections;
1814 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1815 const MeterSection* ms;
1816 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1817 meter_sections.push_back (ms);
1821 assert (!meter_sections.empty());
1823 /* go backwards through the meter sections till we get to the one
1824 covering the current value of result. this positions i to point to
1825 the next (previous) meter section too, or the end.
1828 const MeterSection* meter = 0;
1829 list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't
1830 // support const_reverse_iterator::operator!=()
1832 for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1834 /* when we find the first meter section that is before or at result, use it,
1835 and set next_meter to the previous one
1838 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1839 meter = *next_meter;
1840 ++next_meter;
1841 break;
1845 assert (meter != 0);
1847 /* OK, now have the meter for the bar start we are on, and i is an iterator
1848 that points to the metric after the one we are currently dealing with
1849 (or to metrics->end(), of course)
1852 while (op.beats) {
1854 /* have we reached the start of the bar? if so, move to the last beat of the previous
1855 bar. opwise, just step back 1 beat.
1858 if (result.beats == 1) {
1860 /* move to previous bar, last beat */
1862 if (result.bars <= 1) {
1863 /* i'm sorry dave, i can't do that */
1864 throw std::out_of_range ("illegal BBT subtraction");
1867 result.bars--;
1868 result.beats = meter->beats_per_bar();
1869 } else {
1871 /* back one beat */
1873 result.beats--;
1876 /* one down ... */
1877 op.beats--;
1879 /* check if we need to use a new meter section: has subtracting beats to result taken us
1880 to before the start of the current meter section? in which case, use the prior one.
1883 if (result < meter->start() && next_meter != meter_sections.rend()) {
1884 meter = *next_meter;
1885 ++next_meter;
1889 /* finally, subtract bars */
1891 if (op.bars >= result.bars) {
1892 /* i'm sorry dave, i can't do that */
1893 throw std::out_of_range ("illegal BBT subtraction");
1896 result.bars -= op.bars;
1897 return result;
1900 /** Compare the time of this with that of another MetricSection.
1901 * @param with_bbt True to compare using ::start(), false to use ::frame().
1902 * @return -1 for less than, 0 for equal, 1 for greater than.
1906 MetricSection::compare (MetricSection* other, bool with_bbt) const
1908 if (with_bbt) {
1909 if (start() == other->start()) {
1910 return 0;
1911 } else if (start() < other->start()) {
1912 return -1;
1913 } else {
1914 return 1;
1916 } else {
1917 if (frame() == other->frame()) {
1918 return 0;
1919 } else if (frame() < other->frame()) {
1920 return -1;
1921 } else {
1922 return 1;
1926 /* NOTREACHED */
1927 return 0;