Move panner bypass state up to the PannerShell so that it is preserved even when...
[ardour2.git] / libs / ardour / tempo.cc
blobefda7aa1cdcbf33aaff2b4065b9d4afb2d9a3897
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 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 /***********************************************************************/
55 double
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;
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 (framecnt_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 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)) {
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, 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 ());
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, 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 ());
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 (framepos_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 framepos_t current = 0;
653 framepos_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 > BBT_Time::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 (framepos_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 (framepos_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 (framepos_t frame, BBT_Time& bbt) const
826 bbt_time_with_metric (frame, bbt, metric_at (frame));
829 void
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;
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 framecnt_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
879 "legal" BBT types, that means that the beats and ticks should be inside
880 a bar
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()));
897 m = metric_at(end);
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;
908 return frames;
912 framecnt_t
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);
927 frames = 0;
929 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
931 if (beat >= beats_per_bar) {
932 beat = 1;
933 ++bar;
934 ++beats_counted;
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);
945 } else {
946 ++beat;
947 ++beats_counted;
951 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
952 // << " bpb were " << beats_per_bar
953 // << " fpb was " << beat_frames
954 // << endl;
956 frames = (framecnt_t) llrint (floor (beats_counted * beat_frames));
958 return frames;
962 framepos_t
963 TempoMap::frame_time (const BBT_Time& bbt) const
965 BBT_Time start ; /* 1|1|0 */
967 return count_frames_between (start, bbt);
970 framecnt_t
971 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) const
973 framecnt_t frames = 0;
975 BBT_Time when;
976 bbt_time(pos, when);
979 Glib::RWLock::ReaderLock lm (lock);
980 frames = bbt_duration_at_unlocked (when, bbt,dir);
983 return frames;
986 framecnt_t
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;
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();
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
1007 if (dir >= 0) {
1008 result.beats = when.beats + bbt.beats;
1009 result.ticks = when.ticks + bbt.ticks;
1011 while (result.beats >= (beats_per_bar + 1)) {
1012 result.bars++;
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
1023 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))* BBT_Time::ticks_per_beat
1031 : BBT_Time::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) ) * BBT_Time::ticks_per_beat
1044 : BBT_Time::ticks_per_beat);
1048 } else {
1049 uint32_t b = bbt.beats;
1051 /* count beats */
1052 while (b > when.beats) {
1053 --result.bars;
1054 result.bars = max(1U, result.bars);
1055 metric = metric_at(result); // maybe there is a meter change
1056 beats_per_bar = metric.meter().beats_per_bar();
1057 if (b >= ceil(beats_per_bar)) {
1058 b -= (uint32_t) ceil(beats_per_bar);
1059 } else {
1060 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1063 result.beats = when.beats - b;
1065 /* count ticks */
1067 if (bbt.ticks <= when.ticks) {
1068 result.ticks = when.ticks - bbt.ticks;
1069 } else {
1071 uint32_t ticks_at_beat= (uint32_t) BBT_Time::ticks_per_beat;
1072 uint32_t t = bbt.ticks - when.ticks;
1074 do {
1076 if (result.beats == 1) {
1077 --result.bars;
1078 result.bars = max(1U, result.bars) ;
1079 metric = metric_at(result); // maybe there is a meter change
1080 beats_per_bar = metric.meter().beats_per_bar();
1081 result.beats = (uint32_t) ceil(beats_per_bar);
1082 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * BBT_Time::ticks_per_beat) ;
1083 } else {
1084 --result.beats;
1085 ticks_at_beat = (uint32_t) BBT_Time::ticks_per_beat;
1088 if (t <= ticks_at_beat) {
1089 result.ticks = ticks_at_beat - t;
1090 } else {
1091 t-= ticks_at_beat;
1093 } while (t > ticks_at_beat);
1100 if (dir < 0) {
1101 frames = count_frames_between(result, when);
1102 } else {
1103 frames = count_frames_between(when,result);
1106 return frames;
1111 framepos_t
1112 TempoMap::round_to_bar (framepos_t fr, int dir)
1115 Glib::RWLock::ReaderLock lm (lock);
1116 return round_to_type (fr, dir, Bar);
1121 framepos_t
1122 TempoMap::round_to_beat (framepos_t fr, int dir)
1125 Glib::RWLock::ReaderLock lm (lock);
1126 return round_to_type (fr, dir, Beat);
1130 framepos_t
1131 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1133 BBT_Time the_beat;
1134 uint32_t ticks_one_half_subdivisions_worth;
1135 uint32_t ticks_one_subdivisions_worth;
1136 uint32_t difference;
1138 bbt_time(fr, the_beat);
1140 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1141 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1143 if (dir > 0) {
1145 /* round to next */
1147 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1149 if (mod == 0) {
1150 /* right on the subdivision, so the difference is just the subdivision ticks */
1151 difference = ticks_one_subdivisions_worth;
1153 } else {
1154 /* not on subdivision, compute distance to next subdivision */
1156 difference = ticks_one_subdivisions_worth - mod;
1159 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1161 } else if (dir < 0) {
1163 /* round to previous */
1165 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1167 if (mod == 0) {
1168 /* right on the subdivision, so the difference is just the subdivision ticks */
1169 difference = ticks_one_subdivisions_worth;
1170 } else {
1171 /* not on subdivision, compute distance to previous subdivision, which
1172 is just the modulus.
1175 difference = mod;
1178 try {
1179 the_beat = bbt_subtract (the_beat, BBT_Time (0, 0, difference));
1180 } catch (...) {
1181 /* can't go backwards from wherever pos is, so just return it */
1182 return fr;
1185 } else {
1186 /* round to nearest */
1188 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1189 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1190 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1191 } else {
1192 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1193 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1197 return frame_time (the_beat);
1200 framepos_t
1201 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1203 TempoMetric metric = metric_at (frame);
1204 BBT_Time bbt;
1205 BBT_Time start;
1206 BBT_Time one_bar (1,0,0);
1207 BBT_Time one_beat (0,1,0);
1209 bbt_time_with_metric (frame, bbt, metric);
1211 switch (type) {
1212 case Bar:
1213 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, dir, bbt));
1215 if (dir < 0) {
1217 /* find bar position preceding frame */
1219 try {
1220 bbt = bbt_subtract (bbt, one_bar);
1223 catch (...) {
1224 return frame;
1228 } else if (dir > 0) {
1230 /* find bar position following frame */
1232 try {
1233 bbt = bbt_add (bbt, one_bar, metric);
1235 catch (...) {
1236 return frame;
1239 } else {
1241 /* "true" rounding */
1243 float midbar_beats;
1244 float midbar_ticks;
1246 midbar_beats = metric.meter().beats_per_bar() / 2 + 1;
1247 midbar_ticks = BBT_Time::ticks_per_beat * fmod (midbar_beats, 1.0f);
1248 midbar_beats = floor (midbar_beats);
1250 BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1252 if (bbt < midbar) {
1253 /* round down */
1254 bbt.beats = 1;
1255 bbt.ticks = 0;
1256 } else {
1257 /* round up */
1258 bbt.bars++;
1259 bbt.beats = 1;
1260 bbt.ticks = 0;
1263 /* force beats & ticks to their values at the start of a bar */
1264 bbt.beats = 1;
1265 bbt.ticks = 0;
1266 break;
1268 case Beat:
1269 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1271 if (dir < 0) {
1273 /* find beat position preceding frame */
1275 try {
1276 bbt = bbt_subtract (bbt, one_beat);
1279 catch (...) {
1280 return frame;
1284 } else if (dir > 0) {
1286 /* find beat position following frame */
1288 try {
1289 bbt = bbt_add (bbt, one_beat, metric);
1291 catch (...) {
1292 return frame;
1295 } else {
1297 /* "true" rounding */
1299 /* round to nearest beat */
1300 if (bbt.ticks >= (BBT_Time::ticks_per_beat/2)) {
1302 try {
1303 bbt = bbt_add (bbt, one_beat, metric);
1305 catch (...) {
1306 return frame;
1310 /* force ticks to the value at the start of a beat */
1311 bbt.ticks = 0;
1312 break;
1316 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)));
1317 return metric.frame() + count_frames_between (metric.start(), bbt);
1320 TempoMap::BBTPointList *
1321 TempoMap::get_points (framepos_t lower, framepos_t upper) const
1324 Metrics::const_iterator i;
1325 BBTPointList *points;
1326 double current;
1327 const MeterSection* meter;
1328 const MeterSection* m;
1329 const TempoSection* tempo;
1330 const TempoSection* t;
1331 uint32_t bar;
1332 uint32_t beat;
1333 double beats_per_bar;
1334 double beat_frame;
1335 double beat_frames;
1336 double frames_per_bar;
1337 double delta_bars;
1338 double delta_beats;
1339 double dummy;
1340 framepos_t limit;
1342 meter = &first_meter ();
1343 tempo = &first_tempo ();
1345 /* find the starting point */
1347 for (i = metrics->begin(); i != metrics->end(); ++i) {
1349 if ((*i)->frame() > lower) {
1350 break;
1353 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1354 tempo = t;
1355 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1356 meter = m;
1360 /* We now have:
1362 meter -> the Meter for "lower"
1363 tempo -> the Tempo for "lower"
1364 i -> for first new metric after "lower", possibly metrics->end()
1366 Now start generating points.
1369 beats_per_bar = meter->beats_per_bar ();
1370 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1371 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1373 if (meter->frame() > tempo->frame()) {
1374 bar = meter->start().bars;
1375 beat = meter->start().beats;
1376 current = meter->frame();
1377 } else {
1378 bar = tempo->start().bars;
1379 beat = tempo->start().beats;
1380 current = tempo->frame();
1383 /* initialize current to point to the bar/beat just prior to the
1384 lower frame bound passed in. assumes that current is initialized
1385 above to be on a beat.
1388 delta_bars = (lower-current) / frames_per_bar;
1389 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1390 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1392 // adjust bars and beats too
1393 bar += (uint32_t) (floor(delta_bars));
1394 beat += (uint32_t) (floor(delta_beats));
1396 points = new BBTPointList;
1398 do {
1400 if (i == metrics->end()) {
1401 limit = upper;
1402 // cerr << "== limit set to end of request @ " << limit << endl;
1403 } else {
1404 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1405 limit = (*i)->frame();
1408 limit = min (limit, upper);
1410 while (current < limit) {
1412 /* if we're at the start of a bar, add bar point */
1414 if (beat == 1) {
1415 if (current >= lower) {
1416 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1417 points->push_back (BBTPoint (*meter, *tempo,(framepos_t)rint(current), Bar, bar, 1));
1422 /* add some beats if we can */
1424 beat_frame = current;
1426 while (beat <= ceil(beats_per_bar) && beat_frame < limit) {
1427 if (beat_frame >= lower) {
1428 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1429 points->push_back (BBTPoint (*meter, *tempo, (framepos_t) rint(beat_frame), Beat, bar, beat));
1431 beat_frame += beat_frames;
1432 current+= beat_frames;
1434 beat++;
1437 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1438 // << (beat > ceil(beats_per_bar))
1439 // << endl;
1441 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1443 /* we walked an entire bar. its
1444 important to move `current' forward
1445 by the actual frames_per_bar, not move it to
1446 an integral beat_frame, so that metrics with
1447 non-integral beats-per-bar have
1448 their bar positions set
1449 correctly. consider a metric with
1450 9-1/2 beats-per-bar. the bar we
1451 just filled had 10 beat marks,
1452 but the bar end is 1/2 beat before
1453 the last beat mark.
1454 And it is also possible that a tempo
1455 change occured in the middle of a bar,
1456 so we subtract the possible extra fraction from the current
1459 if (beat > ceil (beats_per_bar)) {
1460 /* next bar goes where the numbers suggest */
1461 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1462 // cerr << "++ next bar from numbers\n";
1463 } else {
1464 /* next bar goes where the next metric is */
1465 current = limit;
1466 // cerr << "++ next bar at next metric\n";
1468 bar++;
1469 beat = 1;
1474 /* if we're done, then we're done */
1476 if (current >= upper) {
1477 break;
1480 /* i is an iterator that refers to the next metric (or none).
1481 if there is a next metric, move to it, and continue.
1484 if (i != metrics->end()) {
1486 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1487 tempo = t;
1488 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1489 meter = m;
1490 /* new MeterSection, beat always returns to 1 */
1491 beat = 1;
1494 current = (*i)->frame ();
1495 // cerr << "loop around with current @ " << current << endl;
1497 beats_per_bar = meter->beats_per_bar ();
1498 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1499 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1501 ++i;
1504 } while (1);
1506 return points;
1509 const TempoSection&
1510 TempoMap::tempo_section_at (framepos_t frame) const
1512 Glib::RWLock::ReaderLock lm (lock);
1513 Metrics::const_iterator i;
1514 TempoSection* prev = 0;
1516 for (i = metrics->begin(); i != metrics->end(); ++i) {
1517 TempoSection* t;
1519 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1521 if ((*i)->frame() > frame) {
1522 break;
1525 prev = t;
1529 if (prev == 0) {
1530 fatal << endmsg;
1533 return *prev;
1536 const Tempo&
1537 TempoMap::tempo_at (framepos_t frame) const
1539 TempoMetric m (metric_at (frame));
1540 return m.tempo();
1544 const Meter&
1545 TempoMap::meter_at (framepos_t frame) const
1547 TempoMetric m (metric_at (frame));
1548 return m.meter();
1551 XMLNode&
1552 TempoMap::get_state ()
1554 Metrics::const_iterator i;
1555 XMLNode *root = new XMLNode ("TempoMap");
1558 Glib::RWLock::ReaderLock lm (lock);
1559 for (i = metrics->begin(); i != metrics->end(); ++i) {
1560 root->add_child_nocopy ((*i)->get_state());
1564 return *root;
1568 TempoMap::set_state (const XMLNode& node, int /*version*/)
1571 Glib::RWLock::WriterLock lm (lock);
1573 XMLNodeList nlist;
1574 XMLNodeConstIterator niter;
1575 Metrics old_metrics (*metrics);
1577 metrics->clear();
1579 nlist = node.children();
1581 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1582 XMLNode* child = *niter;
1584 if (child->name() == TempoSection::xml_state_node_name) {
1586 try {
1587 metrics->push_back (new TempoSection (*child));
1590 catch (failed_constructor& err){
1591 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1592 *metrics = old_metrics;
1593 break;
1596 } else if (child->name() == MeterSection::xml_state_node_name) {
1598 try {
1599 metrics->push_back (new MeterSection (*child));
1602 catch (failed_constructor& err) {
1603 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1604 *metrics = old_metrics;
1605 break;
1610 if (niter == nlist.end()) {
1612 MetricSectionSorter cmp;
1613 metrics->sort (cmp);
1614 timestamp_metrics (true);
1618 PropertyChanged (PropertyChange ());
1620 return 0;
1623 void
1624 TempoMap::dump (std::ostream& o) const
1626 const MeterSection* m;
1627 const TempoSection* t;
1629 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1631 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1632 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1633 << t->movable() << ')' << endl;
1634 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1635 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1636 << " (move? " << m->movable() << ')' << endl;
1642 TempoMap::n_tempos() const
1644 Glib::RWLock::ReaderLock lm (lock);
1645 int cnt = 0;
1647 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1648 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1649 cnt++;
1653 return cnt;
1657 TempoMap::n_meters() const
1659 Glib::RWLock::ReaderLock lm (lock);
1660 int cnt = 0;
1662 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1663 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1664 cnt++;
1668 return cnt;
1671 void
1672 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1674 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1675 if ((*i)->frame() >= where) {
1676 (*i)->set_frame ((*i)->frame() + amount);
1680 timestamp_metrics (false);
1682 PropertyChanged (PropertyChange ());
1685 BBT_Time
1686 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1688 TempoMetric metric = metric_at (start);
1689 return bbt_add (start, other, metric);
1693 * add the BBT interval @param increment to @param start and return the result
1695 BBT_Time
1696 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
1698 BBT_Time result = start;
1699 BBT_Time op = increment; /* argument is const, but we need to modify it */
1700 uint32_t ticks = result.ticks + op.ticks;
1702 if (ticks >= BBT_Time::ticks_per_beat) {
1703 op.beats++;
1704 result.ticks = ticks % (uint32_t) BBT_Time::ticks_per_beat;
1705 } else {
1706 result.ticks += op.ticks;
1709 /* now comes the complicated part. we have to add one beat a time,
1710 checking for a new metric on every beat.
1713 /* grab all meter sections */
1715 list<const MeterSection*> meter_sections;
1717 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1718 const MeterSection* ms;
1719 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1720 meter_sections.push_back (ms);
1724 assert (!meter_sections.empty());
1726 list<const MeterSection*>::const_iterator next_meter;
1727 const Meter* meter = 0;
1729 /* go forwards through the meter sections till we get to the one
1730 covering the current value of result. this positions i to point to
1731 the next meter section too, or the end.
1734 for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1736 if (result < (*next_meter)->start()) {
1737 /* this metric is past the result time. stop looking, we have what we need */
1738 break;
1741 if (result == (*next_meter)->start()) {
1742 /* this meter section starts at result, push i beyond it so that it points
1743 to the NEXT section, opwise we will get stuck later, and use this meter section.
1745 meter = *next_meter;
1746 ++next_meter;
1747 break;
1750 meter = *next_meter;
1753 assert (meter != 0);
1755 /* OK, now have the meter for the bar start we are on, and i is an iterator
1756 that points to the metric after the one we are currently dealing with
1757 (or to metrics->end(), of course)
1760 while (op.beats) {
1762 /* given the current meter, have we gone past the end of the bar ? */
1764 if (result.beats >= meter->beats_per_bar()) {
1765 /* move to next bar, first beat */
1766 result.bars++;
1767 result.beats = 1;
1768 } else {
1769 result.beats++;
1772 /* one down ... */
1774 op.beats--;
1776 /* check if we need to use a new meter section: has adding beats to result taken us
1777 to or after the start of the next meter section? in which case, use it.
1780 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1781 meter = *next_meter;
1782 ++next_meter;
1786 /* finally, add bars */
1788 result.bars += op.bars++;
1790 return result;
1794 * subtract the BBT interval @param decrement from @param start and return the result
1796 BBT_Time
1797 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1799 BBT_Time result = start;
1800 BBT_Time op = decrement; /* argument is const, but we need to modify it */
1802 if (op.ticks > result.ticks) {
1803 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1804 op.beats++;
1805 result.ticks = BBT_Time::ticks_per_beat - (op.ticks - result.ticks);
1806 } else {
1807 result.ticks -= op.ticks;
1810 /* now comes the complicated part. we have to subtract one beat a time,
1811 checking for a new metric on every beat.
1814 /* grab all meter sections */
1816 list<const MeterSection*> meter_sections;
1818 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1819 const MeterSection* ms;
1820 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1821 meter_sections.push_back (ms);
1825 assert (!meter_sections.empty());
1827 /* go backwards through the meter sections till we get to the one
1828 covering the current value of result. this positions i to point to
1829 the next (previous) meter section too, or the end.
1832 const MeterSection* meter = 0;
1833 list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't
1834 // support const_reverse_iterator::operator!=()
1836 for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1838 /* when we find the first meter section that is before or at result, use it,
1839 and set next_meter to the previous one
1842 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1843 meter = *next_meter;
1844 ++next_meter;
1845 break;
1849 assert (meter != 0);
1851 /* OK, now have the meter for the bar start we are on, and i is an iterator
1852 that points to the metric after the one we are currently dealing with
1853 (or to metrics->end(), of course)
1856 while (op.beats) {
1858 /* have we reached the start of the bar? if so, move to the last beat of the previous
1859 bar. opwise, just step back 1 beat.
1862 if (result.beats == 1) {
1864 /* move to previous bar, last beat */
1866 if (result.bars <= 1) {
1867 /* i'm sorry dave, i can't do that */
1868 throw std::out_of_range ("illegal BBT subtraction");
1871 result.bars--;
1872 result.beats = meter->beats_per_bar();
1873 } else {
1875 /* back one beat */
1877 result.beats--;
1880 /* one down ... */
1881 op.beats--;
1883 /* check if we need to use a new meter section: has subtracting beats to result taken us
1884 to before the start of the current meter section? in which case, use the prior one.
1887 if (result < meter->start() && next_meter != meter_sections.rend()) {
1888 meter = *next_meter;
1889 ++next_meter;
1893 /* finally, subtract bars */
1895 if (op.bars >= result.bars) {
1896 /* i'm sorry dave, i can't do that */
1897 throw std::out_of_range ("illegal BBT subtraction");
1900 result.bars -= op.bars;
1901 return result;
1904 /** Add the BBT interval op to pos and return the result */
1905 framepos_t
1906 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
1908 /* XXX: this is a little inaccurate as small errors are introduced
1909 every time a probably-fractional product of something and
1910 frames_per_beat is rounded. Other errors can be introduced
1911 by op.ticks' integer nature.
1914 Metrics::const_iterator i;
1915 const MeterSection* meter;
1916 const MeterSection* m;
1917 const TempoSection* tempo;
1918 const TempoSection* t;
1919 framecnt_t frames_per_beat;
1921 meter = &first_meter ();
1922 tempo = &first_tempo ();
1924 assert (meter);
1925 assert (tempo);
1927 /* find the starting metrics for tempo & meter */
1929 for (i = metrics->begin(); i != metrics->end(); ++i) {
1931 if ((*i)->frame() > pos) {
1932 break;
1935 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1936 tempo = t;
1937 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1938 meter = m;
1942 /* We now have:
1944 meter -> the Meter for "pos"
1945 tempo -> the Tempo for "pos"
1946 i -> for first new metric after "pos", possibly metrics->end()
1949 /* now comes the complicated part. we have to add one beat a time,
1950 checking for a new metric on every beat.
1953 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
1955 while (op.bars) {
1957 pos += llrint (frames_per_beat * meter->beats_per_bar());
1958 op.bars--;
1960 /* check if we need to use a new metric section: has adding frames moved us
1961 to or after the start of the next metric section? in which case, use it.
1964 if (i != metrics->end()) {
1965 if ((*i)->frame() <= pos) {
1967 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1968 tempo = t;
1969 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1970 meter = m;
1972 ++i;
1973 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
1980 while (op.beats) {
1982 /* given the current meter, have we gone past the end of the bar ? */
1984 pos += frames_per_beat;
1985 op.beats--;
1987 /* check if we need to use a new metric section: has adding frames moved us
1988 to or after the start of the next metric section? in which case, use it.
1991 if (i != metrics->end()) {
1992 if ((*i)->frame() <= pos) {
1994 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1995 tempo = t;
1996 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1997 meter = m;
1999 ++i;
2000 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2005 if (op.ticks) {
2006 if (op.ticks >= BBT_Time::ticks_per_beat) {
2007 pos += frames_per_beat;
2008 pos += llrint (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / (double) BBT_Time::ticks_per_beat));
2009 } else {
2010 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2014 return pos;
2017 /** Count the number of beats that are equivalent to distance when starting at pos */
2018 double
2019 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2021 Metrics::const_iterator i;
2022 double beats = 0;
2023 const MeterSection* meter;
2024 const MeterSection* m;
2025 const TempoSection* tempo;
2026 const TempoSection* t;
2027 double frames_per_beat;
2029 double ddist = distance;
2030 double dpos = pos;
2032 meter = &first_meter ();
2033 tempo = &first_tempo ();
2035 assert (meter);
2036 assert (tempo);
2038 /* find the starting metrics for tempo & meter */
2040 for (i = metrics->begin(); i != metrics->end(); ++i) {
2042 if ((*i)->frame() > pos) {
2043 break;
2046 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2047 tempo = t;
2048 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2049 meter = m;
2053 /* We now have:
2055 meter -> the Meter for "pos"
2056 tempo -> the Tempo for "pos"
2057 i -> for first new metric after "pos", possibly metrics->end()
2060 /* now comes the complicated part. we have to add one beat a time,
2061 checking for a new metric on every beat.
2064 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2066 while (ddist > 0) {
2068 /* if we're nearly at the end, but have a fractional beat left,
2069 compute the fraction and then its all over
2072 if (ddist < frames_per_beat) {
2073 beats += ddist / frames_per_beat;
2074 break;
2077 /* walk one beat */
2079 ddist -= frames_per_beat;
2080 dpos += frames_per_beat;
2081 beats += 1.0;
2083 /* check if we need to use a new metric section: has adding frames moved us
2084 to or after the start of the next metric section? in which case, use it.
2087 if (i != metrics->end()) {
2088 if ((*i)->frame() <= (framepos_t) dpos) {
2090 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2091 tempo = t;
2092 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2093 meter = m;
2095 ++i;
2096 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2102 return beats;
2106 /** Compare the time of this with that of another MetricSection.
2107 * @param with_bbt True to compare using start(), false to use frame().
2108 * @return -1 for less than, 0 for equal, 1 for greater than.
2112 MetricSection::compare (MetricSection* other, bool with_bbt) const
2114 if (with_bbt) {
2115 if (start() == other->start()) {
2116 return 0;
2117 } else if (start() < other->start()) {
2118 return -1;
2119 } else {
2120 return 1;
2122 } else {
2123 if (frame() == other->frame()) {
2124 return 0;
2125 } else if (frame() < other->frame()) {
2126 return -1;
2127 } else {
2128 return 1;
2132 /* NOTREACHED */
2133 return 0;