2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <glibmm/thread.h>
29 #include "pbd/xml++.h"
30 #include "ardour/debug.h"
31 #include "ardour/tempo.h"
32 #include "ardour/utils.h"
38 using namespace ARDOUR;
41 using Timecode::BBT_Time;
43 /* _default tempo is 4/4 qtr=120 */
45 Meter TempoMap::_default_meter (4.0, 4.0);
46 Tempo TempoMap::_default_tempo (120.0);
48 double Tempo::frames_per_beat (framecnt_t sr, const Meter& meter) const
50 return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
53 /***********************************************************************/
56 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
58 return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
61 /***********************************************************************/
63 const string TempoSection::xml_state_node_name = "Tempo";
65 TempoSection::TempoSection (const XMLNode& node)
66 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
68 const XMLProperty *prop;
70 LocaleGuard lg (X_("POSIX"));
72 if ((prop = node.property ("start")) == 0) {
73 error << _("TempoSection XML node has no \"start\" property") << endmsg;
74 throw failed_constructor();
77 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
81 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
82 throw failed_constructor();
87 if ((prop = node.property ("beats-per-minute")) == 0) {
88 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
89 throw failed_constructor();
92 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
93 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
94 throw failed_constructor();
97 if ((prop = node.property ("note-type")) == 0) {
98 /* older session, make note type be quarter by default */
101 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
102 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
103 throw failed_constructor();
107 if ((prop = node.property ("movable")) == 0) {
108 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
109 throw failed_constructor();
112 set_movable (string_is_affirmative (prop->value()));
116 TempoSection::get_state() const
118 XMLNode *root = new XMLNode (xml_state_node_name);
120 LocaleGuard lg (X_("POSIX"));
122 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
126 root->add_property ("start", buf);
127 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
128 root->add_property ("beats-per-minute", buf);
129 snprintf (buf, sizeof (buf), "%f", _note_type);
130 root->add_property ("note-type", buf);
131 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
132 root->add_property ("movable", buf);
137 /***********************************************************************/
139 const string MeterSection::xml_state_node_name = "Meter";
141 MeterSection::MeterSection (const XMLNode& node)
142 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
144 const XMLProperty *prop;
146 LocaleGuard lg (X_("POSIX"));
148 if ((prop = node.property ("start")) == 0) {
149 error << _("MeterSection XML node has no \"start\" property") << endmsg;
150 throw failed_constructor();
153 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
157 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
158 throw failed_constructor();
163 if ((prop = node.property ("beats-per-bar")) == 0) {
164 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
165 throw failed_constructor();
168 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
169 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
170 throw failed_constructor();
173 if ((prop = node.property ("note-type")) == 0) {
174 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
175 throw failed_constructor();
178 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
179 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
180 throw failed_constructor();
183 if ((prop = node.property ("movable")) == 0) {
184 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
185 throw failed_constructor();
188 set_movable (string_is_affirmative (prop->value()));
192 MeterSection::get_state() const
194 XMLNode *root = new XMLNode (xml_state_node_name);
196 LocaleGuard lg (X_("POSIX"));
198 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
202 root->add_property ("start", buf);
203 snprintf (buf, sizeof (buf), "%f", _note_type);
204 root->add_property ("note-type", buf);
205 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
206 root->add_property ("beats-per-bar", buf);
207 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
208 root->add_property ("movable", buf);
213 /***********************************************************************/
215 struct MetricSectionSorter {
216 bool operator() (const MetricSection* a, const MetricSection* b) {
217 return a->start() < b->start();
221 TempoMap::TempoMap (framecnt_t fr)
223 metrics = new Metrics;
225 last_bbt_valid = false;
232 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
233 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
235 t->set_movable (false);
236 m->set_movable (false);
238 /* note: frame time is correct (zero) for both of these */
240 metrics->push_back (t);
241 metrics->push_back (m);
244 TempoMap::~TempoMap ()
249 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
251 if (when == section.start() || !section.movable()) {
255 Glib::RWLock::WriterLock lm (lock);
256 MetricSectionSorter cmp;
258 if (when.beats != 1) {
260 /* position by audio frame, then recompute BBT timestamps from the audio ones */
262 framepos_t frame = frame_time (when);
263 // cerr << "nominal frame time = " << frame << endl;
265 framepos_t prev_frame = round_to_type (frame, -1, Beat);
266 framepos_t next_frame = round_to_type (frame, 1, Beat);
268 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
270 /* use the closest beat */
272 if ((frame - prev_frame) < (next_frame - frame)) {
278 // cerr << "actual frame time = " << frame << endl;
279 section.set_frame (frame);
280 // cerr << "frame time = " << section.frame() << endl;
281 timestamp_metrics (false);
282 // cerr << "new BBT time = " << section.start() << endl;
287 /* positioned at bar start already, so just put it there */
289 section.set_start (when);
291 timestamp_metrics (true);
299 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
301 if (move_metric_section (tempo, when) == 0) {
302 PropertyChanged (PropertyChange ());
307 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
309 if (move_metric_section (meter, when) == 0) {
310 PropertyChanged (PropertyChange ());
315 TempoMap::remove_tempo (const TempoSection& tempo)
317 bool removed = false;
320 Glib::RWLock::WriterLock lm (lock);
323 for (i = metrics->begin(); i != metrics->end(); ++i) {
324 if (dynamic_cast<TempoSection*> (*i) != 0) {
325 if (tempo.frame() == (*i)->frame()) {
326 if ((*i)->movable()) {
337 PropertyChanged (PropertyChange ());
342 TempoMap::remove_meter (const MeterSection& tempo)
344 bool removed = false;
347 Glib::RWLock::WriterLock lm (lock);
350 for (i = metrics->begin(); i != metrics->end(); ++i) {
351 if (dynamic_cast<MeterSection*> (*i) != 0) {
352 if (tempo.frame() == (*i)->frame()) {
353 if ((*i)->movable()) {
364 PropertyChanged (PropertyChange ());
369 TempoMap::do_insert (MetricSection* section, bool with_bbt)
371 /* First of all, check to see if the new MetricSection is in the
372 middle of a bar. If so, we need to fix the bar that we are in
373 to have a different meter.
376 assert (section->start().ticks == 0);
378 if (section->start().beats != 1) {
380 /* Here's the tempo and metric where we are proposing to insert `section' */
381 TempoMetric tm = metric_at (section->start ());
383 /* This is where we will put the `corrective' new meter; at the start of
384 the bar that we are inserting into the middle of.
386 BBT_Time where_correction = section->start();
387 where_correction.beats = 1;
388 where_correction.ticks = 0;
390 /* Put in the meter change to make the bar before our `section' the right
393 do_insert (new MeterSection (where_correction, section->start().beats, tm.meter().note_divisor ()), true);
395 /* This is where the new stuff will now go; the start of the next bar
396 (after the one whose meter we just fixed).
398 BBT_Time where_new (where_correction.bars + 1, 1, 0);
400 /* Change back to the original meter */
401 do_insert (new MeterSection (where_new, tm.meter().beats_per_bar(), tm.meter().note_divisor()), true);
403 /* And set up `section' for where it should be, ready to be inserted */
404 section->set_start (where_new);
409 /* Look for any existing MetricSection that is of the same type and
410 at the same time as the new one, and remove it before adding
414 Metrics::iterator to_remove = metrics->end ();
416 for (i = metrics->begin(); i != metrics->end(); ++i) {
418 int const c = (*i)->compare (section, with_bbt);
421 /* this section is before the one to be added; go back round */
424 /* this section is after the one to be added; there can't be any at the same time */
428 /* hacky comparison of type */
429 bool const a = dynamic_cast<TempoSection*> (*i) != 0;
430 bool const b = dynamic_cast<TempoSection*> (section) != 0;
438 if (to_remove != metrics->end()) {
439 /* remove the MetricSection at the same time as the one we are about to add */
440 metrics->erase (to_remove);
443 /* Add the given MetricSection */
445 for (i = metrics->begin(); i != metrics->end(); ++i) {
447 if ((*i)->compare (section, with_bbt) < 0) {
451 metrics->insert (i, section);
455 if (i == metrics->end()) {
456 metrics->insert (metrics->end(), section);
459 timestamp_metrics (with_bbt);
463 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
466 Glib::RWLock::WriterLock lm (lock);
468 /* new tempos always start on a beat */
471 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
474 PropertyChanged (PropertyChange ());
478 TempoMap::add_tempo (const Tempo& tempo, framepos_t where)
481 Glib::RWLock::WriterLock lm (lock);
482 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
485 PropertyChanged (PropertyChange ());
489 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
491 bool replaced = false;
494 Glib::RWLock::WriterLock lm (lock);
497 for (i = metrics->begin(); i != metrics->end(); ++i) {
500 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
502 *((Tempo *) ts) = replacement;
505 timestamp_metrics (true);
513 PropertyChanged (PropertyChange ());
518 TempoMap::add_meter (const Meter& meter, BBT_Time where)
521 Glib::RWLock::WriterLock lm (lock);
523 /* a new meter always starts a new bar on the first beat. so
524 round the start time appropriately. remember that
525 `where' is based on the existing tempo map, not
526 the result after we insert the new meter.
530 if (where.beats != 1) {
535 /* new meters *always* start on a beat. */
538 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
541 PropertyChanged (PropertyChange ());
545 TempoMap::add_meter (const Meter& meter, framepos_t where)
548 Glib::RWLock::WriterLock lm (lock);
549 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
552 PropertyChanged (PropertyChange ());
556 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
558 bool replaced = false;
561 Glib::RWLock::WriterLock lm (lock);
564 for (i = metrics->begin(); i != metrics->end(); ++i) {
566 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
568 *((Meter*) ms) = replacement;
571 timestamp_metrics (true);
578 PropertyChanged (PropertyChange ());
583 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
585 Tempo newtempo (beats_per_minute, note_type);
588 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
589 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
590 *((Tempo*) t) = newtempo;
591 PropertyChanged (PropertyChange ());
598 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
600 Tempo newtempo (beats_per_minute, note_type);
606 /* find the TempoSection immediately preceding "where"
609 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
611 if ((*i)->frame() > where) {
617 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
627 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
636 *((Tempo*)prev) = newtempo;
637 PropertyChanged (PropertyChange ());
641 TempoMap::first_meter () const
643 const MeterSection *m = 0;
645 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
646 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
651 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
657 TempoMap::first_tempo () const
659 const TempoSection *t = 0;
661 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
662 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
667 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
673 TempoMap::timestamp_metrics (bool use_bbt)
681 meter = &first_meter ();
682 tempo = &first_tempo ();
686 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
688 framepos_t current = 0;
689 framepos_t section_frames;
693 for (i = metrics->begin(); i != metrics->end(); ++i) {
697 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
699 current += section_frames;
703 (*i)->set_frame (current);
705 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
707 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
710 fatal << _("programming error: unhandled MetricSection type") << endmsg;
717 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
720 MetricSection* prev = 0;
722 for (i = metrics->begin(); i != metrics->end(); ++i) {
725 TempoMetric metric (*meter, *tempo);
728 metric.set_start (prev->start());
729 metric.set_frame (prev->frame());
731 // metric will be at frames=0 bbt=1|1|0 by default
732 // which is correct for our purpose
735 bbt_time_with_metric ((*i)->frame(), bbt, metric);
737 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
744 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
745 /* round up to next beat */
751 if (bbt.beats != 1) {
752 /* round up to next bar */
758 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
760 (*i)->set_start (bbt);
762 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
764 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
765 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
767 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
769 fatal << _("programming error: unhandled MetricSection type") << endmsg;
778 // cerr << "###############################################\n\n\n" << endl;
783 TempoMap::metric_at (framepos_t frame) const
785 TempoMetric m (first_meter(), first_tempo());
789 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
790 at something, because we insert the default tempo and meter during
791 TempoMap construction.
793 now see if we can find better candidates.
796 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
798 if ((*i)->frame() > frame) {
802 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
803 m.set_tempo (*tempo);
804 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
805 m.set_meter (*meter);
808 m.set_frame ((*i)->frame ());
809 m.set_start ((*i)->start ());
816 TempoMap::metric_at (BBT_Time bbt) const
818 TempoMetric m (first_meter(), first_tempo());
822 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
823 at something, because we insert the default tempo and meter during
824 TempoMap construction.
826 now see if we can find better candidates.
829 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
831 BBT_Time section_start ((*i)->start());
833 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
837 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
838 m.set_tempo (*tempo);
839 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
840 m.set_meter (*meter);
843 m.set_frame ((*i)->frame ());
844 m.set_start (section_start);
851 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) const
854 Glib::RWLock::ReaderLock lm (lock);
855 bbt_time_unlocked (frame, bbt);
860 TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt) const
862 bbt_time_with_metric (frame, bbt, metric_at (frame));
866 TempoMap::bbt_time_with_metric (framepos_t frame, BBT_Time& bbt, const TempoMetric& metric) const
868 framecnt_t frame_diff;
870 const double beats_per_bar = metric.meter().beats_per_bar();
871 const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / BBT_Time::ticks_per_beat;
873 /* now compute how far beyond that point we actually are. */
875 frame_diff = frame - metric.frame();
877 bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
878 uint32_t xtra_beats = bbt.ticks / (uint32_t)BBT_Time::ticks_per_beat;
879 bbt.ticks %= (uint32_t)BBT_Time::ticks_per_beat;
881 bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
882 bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
883 bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
885 /* if we have a fractional number of beats per bar, we see if
886 we're in the last beat (the fractional one). if so, we
887 round ticks appropriately and bump to the next bar. */
888 double beat_fraction = beats_per_bar - floor(beats_per_bar);
889 /* XXX one problem here is that I'm not sure how to handle
890 fractional beats that don't evenly divide ticks_per_beat.
891 If they aren't handled consistently, I would guess we'll
892 continue to have strange discrepancies occuring. Perhaps
893 this will also behave badly in the case of meters like
894 0.1/4, but I can't be bothered to test that.
896 uint32_t ticks_on_last_beat = (uint32_t)floor(BBT_Time::ticks_per_beat * beat_fraction);
898 if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) {
899 bbt.ticks -= ticks_on_last_beat;
904 bbt.beats++; // correction for 1-based counting, see above for matching operation.
906 // cerr << "-----\t RETURN " << bbt << endl;
910 TempoMap::count_frames_between (const BBT_Time& start, const BBT_Time& end) const
912 /* for this to work with fractional measure types, start and end have to be
913 "legal" BBT types, that means that the beats and ticks should be inside
917 framecnt_t frames = 0;
918 framepos_t start_frame = 0;
919 framepos_t end_frame = 0;
921 TempoMetric m = metric_at (start);
923 uint32_t bar_offset = start.bars - m.start().bars;
925 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
926 + start.ticks/BBT_Time::ticks_per_beat;
929 start_frame = m.frame() + (framepos_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
933 bar_offset = end.bars - m.start().bars;
935 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
936 + end.ticks/BBT_Time::ticks_per_beat;
938 end_frame = m.frame() + (framepos_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
940 frames = end_frame - start_frame;
947 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
949 /* this is used in timestamping the metrics by actually counting the beats */
951 framecnt_t frames = 0;
952 uint32_t bar = start.bars;
953 double beat = (double) start.beats;
954 double beats_counted = 0;
955 double beats_per_bar = 0;
956 double beat_frames = 0;
958 beats_per_bar = meter.beats_per_bar();
959 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
963 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
965 if (beat >= beats_per_bar) {
970 if (beat > beats_per_bar) {
972 /* this is a fractional beat at the end of a fractional bar
973 so it should only count for the fraction
976 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
985 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
986 // << " bpb were " << beats_per_bar
987 // << " fpb was " << beat_frames
990 frames = (framecnt_t) llrint (floor (beats_counted * beat_frames));
997 TempoMap::frame_time (const BBT_Time& bbt) const
999 BBT_Time start ; /* 1|1|0 */
1001 return count_frames_between (start, bbt);
1005 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) const
1007 framecnt_t frames = 0;
1010 bbt_time(pos, when);
1013 Glib::RWLock::ReaderLock lm (lock);
1014 frames = bbt_duration_at_unlocked (when, bbt,dir);
1021 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
1023 framecnt_t frames = 0;
1025 double beats_per_bar;
1028 result.bars = max(1U, when.bars + dir * bbt.bars) ;
1032 TempoMetric metric = metric_at(result);
1033 beats_per_bar = metric.meter().beats_per_bar();
1035 /* Reduce things to legal bbt values we have to handle possible
1036 fractional=shorter beats at the end of measures and things like 0|11|9000
1037 as a duration in a 4.5/4 measure the musical decision is that the
1038 fractional beat is also a beat , although a shorter one
1042 result.beats = when.beats + bbt.beats;
1043 result.ticks = when.ticks + bbt.ticks;
1045 while (result.beats >= (beats_per_bar + 1)) {
1047 result.beats -= (uint32_t) ceil(beats_per_bar);
1048 metric = metric_at(result); // maybe there is a meter change
1049 beats_per_bar = metric.meter().beats_per_bar();
1053 /* We now counted the beats and landed in the target measure, now deal
1054 with ticks this seems complicated, but we want to deal with the
1055 corner case of a sequence of time signatures like 0.2/4-0.7/4 and
1056 with request like bbt = 3|2|9000 ,so we repeat the same loop but add
1060 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1063 uint32_t ticks_at_beat = (uint32_t) (result.beats == ceil(beats_per_bar) ?
1064 (1 - (ceil(beats_per_bar) - beats_per_bar))* BBT_Time::ticks_per_beat
1065 : BBT_Time::ticks_per_beat );
1067 while (result.ticks >= ticks_at_beat) {
1069 result.ticks -= ticks_at_beat;
1070 if (result.beats >= (beats_per_bar + 1)) {
1073 metric = metric_at(result); // maybe there is a meter change
1074 beats_per_bar = metric.meter().beats_per_bar();
1076 ticks_at_beat= (uint32_t) (result.beats == ceil(beats_per_bar) ?
1077 (1 - (ceil(beats_per_bar) - beats_per_bar) ) * BBT_Time::ticks_per_beat
1078 : BBT_Time::ticks_per_beat);
1083 uint32_t b = bbt.beats;
1086 while (b > when.beats) {
1088 result.bars = max(1U, result.bars);
1089 metric = metric_at(result); // maybe there is a meter change
1090 beats_per_bar = metric.meter().beats_per_bar();
1091 if (b >= ceil(beats_per_bar)) {
1092 b -= (uint32_t) ceil(beats_per_bar);
1094 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1097 result.beats = when.beats - b;
1101 if (bbt.ticks <= when.ticks) {
1102 result.ticks = when.ticks - bbt.ticks;
1105 uint32_t ticks_at_beat= (uint32_t) BBT_Time::ticks_per_beat;
1106 uint32_t t = bbt.ticks - when.ticks;
1110 if (result.beats == 1) {
1112 result.bars = max(1U, result.bars) ;
1113 metric = metric_at(result); // maybe there is a meter change
1114 beats_per_bar = metric.meter().beats_per_bar();
1115 result.beats = (uint32_t) ceil(beats_per_bar);
1116 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * BBT_Time::ticks_per_beat) ;
1119 ticks_at_beat = (uint32_t) BBT_Time::ticks_per_beat;
1122 if (t <= ticks_at_beat) {
1123 result.ticks = ticks_at_beat - t;
1127 } while (t > ticks_at_beat);
1135 frames = count_frames_between(result, when);
1137 frames = count_frames_between(when,result);
1146 TempoMap::round_to_bar (framepos_t fr, int dir)
1149 Glib::RWLock::ReaderLock lm (lock);
1150 return round_to_type (fr, dir, Bar);
1156 TempoMap::round_to_beat (framepos_t fr, int dir)
1159 Glib::RWLock::ReaderLock lm (lock);
1160 return round_to_type (fr, dir, Beat);
1165 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1168 uint32_t ticks_one_half_subdivisions_worth;
1169 uint32_t ticks_one_subdivisions_worth;
1170 uint32_t difference;
1172 bbt_time(fr, the_beat);
1174 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1175 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1181 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1184 /* right on the subdivision, so the difference is just the subdivision ticks */
1185 difference = ticks_one_subdivisions_worth;
1188 /* not on subdivision, compute distance to next subdivision */
1190 difference = ticks_one_subdivisions_worth - mod;
1193 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1195 } else if (dir < 0) {
1197 /* round to previous */
1199 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1202 /* right on the subdivision, so the difference is just the subdivision ticks */
1203 difference = ticks_one_subdivisions_worth;
1205 /* not on subdivision, compute distance to previous subdivision, which
1206 is just the modulus.
1213 the_beat = bbt_subtract (the_beat, BBT_Time (0, 0, difference));
1215 /* can't go backwards from wherever pos is, so just return it */
1220 /* round to nearest */
1222 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1223 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1224 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1226 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1227 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1231 return frame_time (the_beat);
1235 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1237 TempoMetric metric = metric_at (frame);
1240 BBT_Time one_bar (1,0,0);
1241 BBT_Time one_beat (0,1,0);
1243 bbt_time_with_metric (frame, bbt, metric);
1247 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, dir, bbt));
1251 /* find bar position preceding frame */
1254 bbt = bbt_subtract (bbt, one_bar);
1262 } else if (dir > 0) {
1264 /* find bar position following frame */
1267 bbt = bbt_add (bbt, one_bar, metric);
1275 /* "true" rounding */
1280 midbar_beats = metric.meter().beats_per_bar() / 2 + 1;
1281 midbar_ticks = BBT_Time::ticks_per_beat * fmod (midbar_beats, 1.0f);
1282 midbar_beats = floor (midbar_beats);
1284 BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1297 /* force beats & ticks to their values at the start of a bar */
1303 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1307 /* find beat position preceding frame */
1310 bbt = bbt_subtract (bbt, one_beat);
1318 } else if (dir > 0) {
1320 /* find beat position following frame */
1323 bbt = bbt_add (bbt, one_beat, metric);
1331 /* "true" rounding */
1333 /* round to nearest beat */
1334 if (bbt.ticks >= (BBT_Time::ticks_per_beat/2)) {
1337 bbt = bbt_add (bbt, one_beat, metric);
1344 /* force ticks to the value at the start of a beat */
1350 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)));
1351 return metric.frame() + count_frames_between (metric.start(), bbt);
1354 TempoMap::BBTPointList *
1355 TempoMap::get_points (framepos_t lower, framepos_t upper) const
1358 Metrics::const_iterator i;
1359 BBTPointList *points;
1361 const MeterSection* meter;
1362 const MeterSection* m;
1363 const TempoSection* tempo;
1364 const TempoSection* t;
1367 double beats_per_bar;
1370 double frames_per_bar;
1376 meter = &first_meter ();
1377 tempo = &first_tempo ();
1379 /* find the starting point */
1381 for (i = metrics->begin(); i != metrics->end(); ++i) {
1383 if ((*i)->frame() > lower) {
1387 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1389 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1396 meter -> the Meter for "lower"
1397 tempo -> the Tempo for "lower"
1398 i -> for first new metric after "lower", possibly metrics->end()
1400 Now start generating points.
1403 beats_per_bar = meter->beats_per_bar ();
1404 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1405 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1407 if (meter->frame() > tempo->frame()) {
1408 bar = meter->start().bars;
1409 beat = meter->start().beats;
1410 current = meter->frame();
1412 bar = tempo->start().bars;
1413 beat = tempo->start().beats;
1414 current = tempo->frame();
1417 /* initialize current to point to the bar/beat just prior to the
1418 lower frame bound passed in. assumes that current is initialized
1419 above to be on a beat.
1422 delta_bars = (lower-current) / frames_per_bar;
1423 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1424 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1426 // adjust bars and beats too
1427 bar += (uint32_t) (floor(delta_bars));
1428 beat += (uint32_t) (floor(delta_beats));
1430 points = new BBTPointList;
1434 if (i == metrics->end()) {
1436 // cerr << "== limit set to end of request @ " << limit << endl;
1438 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1439 limit = (*i)->frame();
1442 limit = min (limit, upper);
1444 while (current < limit) {
1446 /* if we're at the start of a bar, add bar point */
1449 if (current >= lower) {
1450 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1451 points->push_back (BBTPoint (*meter, *tempo,(framepos_t)rint(current), Bar, bar, 1));
1456 /* add some beats if we can */
1458 beat_frame = current;
1460 while (beat <= ceil(beats_per_bar) && beat_frame < limit) {
1461 if (beat_frame >= lower) {
1462 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1463 points->push_back (BBTPoint (*meter, *tempo, (framepos_t) rint(beat_frame), Beat, bar, beat));
1465 beat_frame += beat_frames;
1466 current+= beat_frames;
1471 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1472 // << (beat > ceil(beats_per_bar))
1475 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1477 /* we walked an entire bar. its
1478 important to move `current' forward
1479 by the actual frames_per_bar, not move it to
1480 an integral beat_frame, so that metrics with
1481 non-integral beats-per-bar have
1482 their bar positions set
1483 correctly. consider a metric with
1484 9-1/2 beats-per-bar. the bar we
1485 just filled had 10 beat marks,
1486 but the bar end is 1/2 beat before
1488 And it is also possible that a tempo
1489 change occured in the middle of a bar,
1490 so we subtract the possible extra fraction from the current
1493 if (beat > ceil (beats_per_bar)) {
1494 /* next bar goes where the numbers suggest */
1495 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1496 // cerr << "++ next bar from numbers\n";
1498 /* next bar goes where the next metric is */
1500 // cerr << "++ next bar at next metric\n";
1508 /* if we're done, then we're done */
1510 if (current >= upper) {
1514 /* i is an iterator that refers to the next metric (or none).
1515 if there is a next metric, move to it, and continue.
1518 if (i != metrics->end()) {
1520 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1522 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1524 /* new MeterSection, beat always returns to 1 */
1528 current = (*i)->frame ();
1529 // cerr << "loop around with current @ " << current << endl;
1531 beats_per_bar = meter->beats_per_bar ();
1532 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1533 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1544 TempoMap::tempo_section_at (framepos_t frame) const
1546 Glib::RWLock::ReaderLock lm (lock);
1547 Metrics::const_iterator i;
1548 TempoSection* prev = 0;
1550 for (i = metrics->begin(); i != metrics->end(); ++i) {
1553 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1555 if ((*i)->frame() > frame) {
1571 TempoMap::tempo_at (framepos_t frame) const
1573 TempoMetric m (metric_at (frame));
1579 TempoMap::meter_at (framepos_t frame) const
1581 TempoMetric m (metric_at (frame));
1586 TempoMap::get_state ()
1588 Metrics::const_iterator i;
1589 XMLNode *root = new XMLNode ("TempoMap");
1592 Glib::RWLock::ReaderLock lm (lock);
1593 for (i = metrics->begin(); i != metrics->end(); ++i) {
1594 root->add_child_nocopy ((*i)->get_state());
1602 TempoMap::set_state (const XMLNode& node, int /*version*/)
1605 Glib::RWLock::WriterLock lm (lock);
1608 XMLNodeConstIterator niter;
1609 Metrics old_metrics (*metrics);
1613 nlist = node.children();
1615 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1616 XMLNode* child = *niter;
1618 if (child->name() == TempoSection::xml_state_node_name) {
1621 metrics->push_back (new TempoSection (*child));
1624 catch (failed_constructor& err){
1625 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1626 *metrics = old_metrics;
1630 } else if (child->name() == MeterSection::xml_state_node_name) {
1633 metrics->push_back (new MeterSection (*child));
1636 catch (failed_constructor& err) {
1637 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1638 *metrics = old_metrics;
1644 if (niter == nlist.end()) {
1646 MetricSectionSorter cmp;
1647 metrics->sort (cmp);
1648 timestamp_metrics (true);
1652 PropertyChanged (PropertyChange ());
1658 TempoMap::dump (std::ostream& o) const
1660 const MeterSection* m;
1661 const TempoSection* t;
1663 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1665 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1666 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1667 << t->movable() << ')' << endl;
1668 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1669 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1670 << " (move? " << m->movable() << ')' << endl;
1676 TempoMap::n_tempos() const
1678 Glib::RWLock::ReaderLock lm (lock);
1681 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1682 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1691 TempoMap::n_meters() const
1693 Glib::RWLock::ReaderLock lm (lock);
1696 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1697 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1706 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1708 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1709 if ((*i)->frame() >= where && (*i)->movable ()) {
1710 (*i)->set_frame ((*i)->frame() + amount);
1714 timestamp_metrics (false);
1716 PropertyChanged (PropertyChange ());
1720 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1722 TempoMetric metric = metric_at (start);
1723 return bbt_add (start, other, metric);
1727 * add the BBT interval @param increment to @param start and return the result
1730 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
1732 BBT_Time result = start;
1733 BBT_Time op = increment; /* argument is const, but we need to modify it */
1734 uint32_t ticks = result.ticks + op.ticks;
1736 if (ticks >= BBT_Time::ticks_per_beat) {
1738 result.ticks = ticks % (uint32_t) BBT_Time::ticks_per_beat;
1740 result.ticks += op.ticks;
1743 /* now comes the complicated part. we have to add one beat a time,
1744 checking for a new metric on every beat.
1747 /* grab all meter sections */
1749 list<const MeterSection*> meter_sections;
1751 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1752 const MeterSection* ms;
1753 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1754 meter_sections.push_back (ms);
1758 assert (!meter_sections.empty());
1760 list<const MeterSection*>::const_iterator next_meter;
1761 const Meter* meter = 0;
1763 /* go forwards through the meter sections till we get to the one
1764 covering the current value of result. this positions i to point to
1765 the next meter section too, or the end.
1768 for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1770 if (result < (*next_meter)->start()) {
1771 /* this metric is past the result time. stop looking, we have what we need */
1775 if (result == (*next_meter)->start()) {
1776 /* this meter section starts at result, push i beyond it so that it points
1777 to the NEXT section, opwise we will get stuck later, and use this meter section.
1779 meter = *next_meter;
1784 meter = *next_meter;
1787 assert (meter != 0);
1789 /* OK, now have the meter for the bar start we are on, and i is an iterator
1790 that points to the metric after the one we are currently dealing with
1791 (or to metrics->end(), of course)
1796 /* given the current meter, have we gone past the end of the bar ? */
1798 if (result.beats >= meter->beats_per_bar()) {
1799 /* move to next bar, first beat */
1810 /* check if we need to use a new meter section: has adding beats to result taken us
1811 to or after the start of the next meter section? in which case, use it.
1814 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1815 meter = *next_meter;
1820 /* finally, add bars */
1822 result.bars += op.bars++;
1828 * subtract the BBT interval @param decrement from @param start and return the result
1831 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1833 BBT_Time result = start;
1834 BBT_Time op = decrement; /* argument is const, but we need to modify it */
1836 if (op.ticks > result.ticks) {
1837 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1839 result.ticks = BBT_Time::ticks_per_beat - (op.ticks - result.ticks);
1841 result.ticks -= op.ticks;
1844 /* now comes the complicated part. we have to subtract one beat a time,
1845 checking for a new metric on every beat.
1848 /* grab all meter sections */
1850 list<const MeterSection*> meter_sections;
1852 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1853 const MeterSection* ms;
1854 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1855 meter_sections.push_back (ms);
1859 assert (!meter_sections.empty());
1861 /* go backwards through the meter sections till we get to the one
1862 covering the current value of result. this positions i to point to
1863 the next (previous) meter section too, or the end.
1866 const MeterSection* meter = 0;
1867 list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't
1868 // support const_reverse_iterator::operator!=()
1870 for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1872 /* when we find the first meter section that is before or at result, use it,
1873 and set next_meter to the previous one
1876 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1877 meter = *next_meter;
1883 assert (meter != 0);
1885 /* OK, now have the meter for the bar start we are on, and i is an iterator
1886 that points to the metric after the one we are currently dealing with
1887 (or to metrics->end(), of course)
1892 /* have we reached the start of the bar? if so, move to the last beat of the previous
1893 bar. opwise, just step back 1 beat.
1896 if (result.beats == 1) {
1898 /* move to previous bar, last beat */
1900 if (result.bars <= 1) {
1901 /* i'm sorry dave, i can't do that */
1902 throw std::out_of_range ("illegal BBT subtraction");
1906 result.beats = meter->beats_per_bar();
1917 /* check if we need to use a new meter section: has subtracting beats to result taken us
1918 to before the start of the current meter section? in which case, use the prior one.
1921 if (result < meter->start() && next_meter != meter_sections.rend()) {
1922 meter = *next_meter;
1927 /* finally, subtract bars */
1929 if (op.bars >= result.bars) {
1930 /* i'm sorry dave, i can't do that */
1931 throw std::out_of_range ("illegal BBT subtraction");
1934 result.bars -= op.bars;
1938 /** Add the BBT interval op to pos and return the result */
1940 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
1942 /* XXX: this is a little inaccurate as small errors are introduced
1943 every time a probably-fractional product of something and
1944 frames_per_beat is rounded. Other errors can be introduced
1945 by op.ticks' integer nature.
1948 Metrics::const_iterator i;
1949 const MeterSection* meter;
1950 const MeterSection* m;
1951 const TempoSection* tempo;
1952 const TempoSection* t;
1953 framecnt_t frames_per_beat;
1955 meter = &first_meter ();
1956 tempo = &first_tempo ();
1961 /* find the starting metrics for tempo & meter */
1963 for (i = metrics->begin(); i != metrics->end(); ++i) {
1965 if ((*i)->frame() > pos) {
1969 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1971 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1978 meter -> the Meter for "pos"
1979 tempo -> the Tempo for "pos"
1980 i -> for first new metric after "pos", possibly metrics->end()
1983 /* now comes the complicated part. we have to add one beat a time,
1984 checking for a new metric on every beat.
1987 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
1991 pos += llrint (frames_per_beat * meter->beats_per_bar());
1994 /* check if we need to use a new metric section: has adding frames moved us
1995 to or after the start of the next metric section? in which case, use it.
1998 if (i != metrics->end()) {
1999 if ((*i)->frame() <= pos) {
2001 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2003 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2007 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2016 /* given the current meter, have we gone past the end of the bar ? */
2018 pos += frames_per_beat;
2021 /* check if we need to use a new metric section: has adding frames moved us
2022 to or after the start of the next metric section? in which case, use it.
2025 if (i != metrics->end()) {
2026 if ((*i)->frame() <= pos) {
2028 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2030 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2034 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2040 if (op.ticks >= BBT_Time::ticks_per_beat) {
2041 pos += frames_per_beat;
2042 pos += llrint (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / (double) BBT_Time::ticks_per_beat));
2044 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2051 /** Count the number of beats that are equivalent to distance when starting at pos */
2053 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2055 Metrics::const_iterator i;
2057 const MeterSection* meter;
2058 const MeterSection* m;
2059 const TempoSection* tempo;
2060 const TempoSection* t;
2061 double frames_per_beat;
2063 double ddist = distance;
2066 meter = &first_meter ();
2067 tempo = &first_tempo ();
2072 /* find the starting metrics for tempo & meter */
2074 for (i = metrics->begin(); i != metrics->end(); ++i) {
2076 if ((*i)->frame() > pos) {
2080 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2082 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2089 meter -> the Meter for "pos"
2090 tempo -> the Tempo for "pos"
2091 i -> for first new metric after "pos", possibly metrics->end()
2094 /* now comes the complicated part. we have to add one beat a time,
2095 checking for a new metric on every beat.
2098 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2102 /* if we're nearly at the end, but have a fractional beat left,
2103 compute the fraction and then its all over
2106 if (ddist < frames_per_beat) {
2107 beats += ddist / frames_per_beat;
2113 ddist -= frames_per_beat;
2114 dpos += frames_per_beat;
2117 /* check if we need to use a new metric section: has adding frames moved us
2118 to or after the start of the next metric section? in which case, use it.
2121 if (i != metrics->end()) {
2122 if ((*i)->frame() <= (framepos_t) dpos) {
2124 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2126 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2130 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2140 /** Compare the time of this with that of another MetricSection.
2141 * @param with_bbt True to compare using start(), false to use frame().
2142 * @return -1 for less than, 0 for equal, 1 for greater than.
2146 MetricSection::compare (MetricSection* other, bool with_bbt) const
2149 if (start() == other->start()) {
2151 } else if (start() < other->start()) {
2157 if (frame() == other->frame()) {
2159 } else if (frame() < other->frame()) {