2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "evoral/types.hpp"
30 #include "ardour/debug.h"
31 #include "ardour/tempo.h"
32 #include "ardour/utils.h"
38 using namespace ARDOUR;
41 using Timecode::BBT_Time;
43 /* _default tempo is 4/4 qtr=120 */
45 Meter TempoMap::_default_meter (4.0, 4.0);
46 Tempo TempoMap::_default_tempo (120.0);
48 double Tempo::frames_per_beat (framecnt_t sr, const Meter& meter) const
50 return (60.0 * sr) / _beats_per_minute;
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()));
62 Meter::frames_per_division (const Tempo& tempo, framecnt_t sr) const
64 return ((60.0 * sr) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
67 /***********************************************************************/
69 const string TempoSection::xml_state_node_name = "Tempo";
71 TempoSection::TempoSection (const XMLNode& node)
72 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
74 const XMLProperty *prop;
76 LocaleGuard lg (X_("POSIX"));
78 if ((prop = node.property ("start")) == 0) {
79 error << _("TempoSection XML node has no \"start\" property") << endmsg;
80 throw failed_constructor();
83 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
87 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
88 throw failed_constructor();
93 if ((prop = node.property ("beats-per-minute")) == 0) {
94 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
95 throw failed_constructor();
98 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
99 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
100 throw failed_constructor();
103 if ((prop = node.property ("note-type")) == 0) {
104 /* older session, make note type be quarter by default */
107 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
108 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
109 throw failed_constructor();
113 if ((prop = node.property ("movable")) == 0) {
114 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
115 throw failed_constructor();
118 set_movable (string_is_affirmative (prop->value()));
122 TempoSection::get_state() const
124 XMLNode *root = new XMLNode (xml_state_node_name);
126 LocaleGuard lg (X_("POSIX"));
128 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
132 root->add_property ("start", buf);
133 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
134 root->add_property ("beats-per-minute", buf);
135 snprintf (buf, sizeof (buf), "%f", _note_type);
136 root->add_property ("note-type", buf);
137 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
138 root->add_property ("movable", buf);
143 /***********************************************************************/
145 const string MeterSection::xml_state_node_name = "Meter";
147 MeterSection::MeterSection (const XMLNode& node)
148 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
150 const XMLProperty *prop;
152 LocaleGuard lg (X_("POSIX"));
154 if ((prop = node.property ("start")) == 0) {
155 error << _("MeterSection XML node has no \"start\" property") << endmsg;
156 throw failed_constructor();
159 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
163 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
164 throw failed_constructor();
169 if ((prop = node.property ("beats-per-bar")) == 0) {
170 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
171 throw failed_constructor();
174 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
175 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
176 throw failed_constructor();
179 if ((prop = node.property ("note-type")) == 0) {
180 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
181 throw failed_constructor();
184 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
185 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
186 throw failed_constructor();
189 if ((prop = node.property ("movable")) == 0) {
190 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
191 throw failed_constructor();
194 set_movable (string_is_affirmative (prop->value()));
198 MeterSection::get_state() const
200 XMLNode *root = new XMLNode (xml_state_node_name);
202 LocaleGuard lg (X_("POSIX"));
204 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
208 root->add_property ("start", buf);
209 snprintf (buf, sizeof (buf), "%f", _note_type);
210 root->add_property ("note-type", buf);
211 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
212 root->add_property ("beats-per-bar", buf);
213 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
214 root->add_property ("movable", buf);
219 /***********************************************************************/
221 struct MetricSectionSorter {
222 bool operator() (const MetricSection* a, const MetricSection* b) {
223 return a->start() < b->start();
227 TempoMap::TempoMap (framecnt_t fr)
229 metrics = new Metrics;
231 last_bbt_valid = false;
238 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
239 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
241 t->set_movable (false);
242 m->set_movable (false);
244 /* note: frame time is correct (zero) for both of these */
246 metrics->push_back (t);
247 metrics->push_back (m);
250 TempoMap::~TempoMap ()
255 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
257 if (when == section.start() || !section.movable()) {
261 Glib::RWLock::WriterLock lm (lock);
262 MetricSectionSorter cmp;
264 if (when.beats != 1) {
266 /* position by audio frame, then recompute BBT timestamps from the audio ones */
268 framepos_t frame = frame_time (when);
269 // cerr << "nominal frame time = " << frame << endl;
271 framepos_t prev_frame = round_to_type (frame, -1, Beat);
272 framepos_t next_frame = round_to_type (frame, 1, Beat);
274 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
276 /* use the closest beat */
278 if ((frame - prev_frame) < (next_frame - frame)) {
284 // cerr << "actual frame time = " << frame << endl;
285 section.set_frame (frame);
286 // cerr << "frame time = " << section.frame() << endl;
287 timestamp_metrics (false);
288 // cerr << "new BBT time = " << section.start() << endl;
293 /* positioned at bar start already, so just put it there */
295 section.set_start (when);
297 timestamp_metrics (true);
305 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
307 if (move_metric_section (tempo, when) == 0) {
308 PropertyChanged (PropertyChange ());
313 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
315 if (move_metric_section (meter, when) == 0) {
316 PropertyChanged (PropertyChange ());
321 TempoMap::remove_tempo (const TempoSection& tempo)
323 bool removed = false;
326 Glib::RWLock::WriterLock lm (lock);
329 for (i = metrics->begin(); i != metrics->end(); ++i) {
330 if (dynamic_cast<TempoSection*> (*i) != 0) {
331 if (tempo.frame() == (*i)->frame()) {
332 if ((*i)->movable()) {
343 PropertyChanged (PropertyChange ());
348 TempoMap::remove_meter (const MeterSection& tempo)
350 bool removed = false;
353 Glib::RWLock::WriterLock lm (lock);
356 for (i = metrics->begin(); i != metrics->end(); ++i) {
357 if (dynamic_cast<MeterSection*> (*i) != 0) {
358 if (tempo.frame() == (*i)->frame()) {
359 if ((*i)->movable()) {
370 PropertyChanged (PropertyChange ());
375 TempoMap::do_insert (MetricSection* section, bool with_bbt)
377 /* First of all, check to see if the new MetricSection is in the
378 middle of a bar. If so, we need to fix the bar that we are in
379 to have a different meter.
382 assert (section->start().ticks == 0);
384 if (section->start().beats != 1) {
386 /* Here's the tempo and metric where we are proposing to insert `section' */
387 TempoMetric tm = metric_at (section->start ());
389 /* This is where we will put the `corrective' new meter; at the start of
390 the bar that we are inserting into the middle of.
392 BBT_Time where_correction = section->start();
393 where_correction.beats = 1;
394 where_correction.ticks = 0;
396 /* Put in the meter change to make the bar before our `section' the right
399 do_insert (new MeterSection (where_correction, section->start().beats, tm.meter().note_divisor ()), true);
401 /* This is where the new stuff will now go; the start of the next bar
402 (after the one whose meter we just fixed).
404 BBT_Time where_new (where_correction.bars + 1, 1, 0);
406 /* Change back to the original meter */
407 do_insert (new MeterSection (where_new, tm.meter().beats_per_bar(), tm.meter().note_divisor()), true);
409 /* And set up `section' for where it should be, ready to be inserted */
410 section->set_start (where_new);
415 /* Look for any existing MetricSection that is of the same type and
416 at the same time as the new one, and remove it before adding
420 Metrics::iterator to_remove = metrics->end ();
422 for (i = metrics->begin(); i != metrics->end(); ++i) {
424 int const c = (*i)->compare (section, with_bbt);
427 /* this section is before the one to be added; go back round */
430 /* this section is after the one to be added; there can't be any at the same time */
434 /* hacky comparison of type */
435 bool const a = dynamic_cast<TempoSection*> (*i) != 0;
436 bool const b = dynamic_cast<TempoSection*> (section) != 0;
444 if (to_remove != metrics->end()) {
445 /* remove the MetricSection at the same time as the one we are about to add */
446 metrics->erase (to_remove);
449 /* Add the given MetricSection */
451 for (i = metrics->begin(); i != metrics->end(); ++i) {
453 if ((*i)->compare (section, with_bbt) < 0) {
457 metrics->insert (i, section);
461 if (i == metrics->end()) {
462 metrics->insert (metrics->end(), section);
465 timestamp_metrics (with_bbt);
469 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
472 Glib::RWLock::WriterLock lm (lock);
474 /* new tempos always start on a beat */
477 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
480 PropertyChanged (PropertyChange ());
484 TempoMap::add_tempo (const Tempo& tempo, framepos_t where)
487 Glib::RWLock::WriterLock lm (lock);
488 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
491 PropertyChanged (PropertyChange ());
495 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
497 bool replaced = false;
500 Glib::RWLock::WriterLock lm (lock);
503 for (i = metrics->begin(); i != metrics->end(); ++i) {
506 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
508 *((Tempo *) ts) = replacement;
511 timestamp_metrics (true);
519 PropertyChanged (PropertyChange ());
524 TempoMap::add_meter (const Meter& meter, BBT_Time where)
527 Glib::RWLock::WriterLock lm (lock);
529 /* a new meter always starts a new bar on the first beat. so
530 round the start time appropriately. remember that
531 `where' is based on the existing tempo map, not
532 the result after we insert the new meter.
536 if (where.beats != 1) {
541 /* new meters *always* start on a beat. */
544 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
547 PropertyChanged (PropertyChange ());
551 TempoMap::add_meter (const Meter& meter, framepos_t where)
554 Glib::RWLock::WriterLock lm (lock);
555 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
558 PropertyChanged (PropertyChange ());
562 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
564 bool replaced = false;
567 Glib::RWLock::WriterLock lm (lock);
570 for (i = metrics->begin(); i != metrics->end(); ++i) {
572 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
574 *((Meter*) ms) = replacement;
577 timestamp_metrics (true);
584 PropertyChanged (PropertyChange ());
589 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
591 Tempo newtempo (beats_per_minute, note_type);
594 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
595 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
596 *((Tempo*) t) = newtempo;
597 PropertyChanged (PropertyChange ());
604 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
606 Tempo newtempo (beats_per_minute, note_type);
612 /* find the TempoSection immediately preceding "where"
615 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
617 if ((*i)->frame() > where) {
623 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
633 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
642 *((Tempo*)prev) = newtempo;
643 PropertyChanged (PropertyChange ());
647 TempoMap::first_meter () const
649 const MeterSection *m = 0;
651 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
652 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
657 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
663 TempoMap::first_tempo () const
665 const TempoSection *t = 0;
667 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
668 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
673 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
679 TempoMap::timestamp_metrics (bool use_bbt)
687 meter = &first_meter ();
688 tempo = &first_tempo ();
692 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
694 framepos_t current = 0;
695 framepos_t section_frames;
699 for (i = metrics->begin(); i != metrics->end(); ++i) {
703 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
705 current += section_frames;
709 (*i)->set_frame (current);
711 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
713 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
716 fatal << _("programming error: unhandled MetricSection type") << endmsg;
723 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
726 MetricSection* prev = 0;
728 for (i = metrics->begin(); i != metrics->end(); ++i) {
731 TempoMetric metric (*meter, *tempo);
734 metric.set_start (prev->start());
735 metric.set_frame (prev->frame());
737 // metric will be at frames=0 bbt=1|1|0 by default
738 // which is correct for our purpose
741 bbt_time_with_metric ((*i)->frame(), bbt, metric);
743 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
750 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
751 /* round up to next beat */
757 if (bbt.beats != 1) {
758 /* round up to next bar */
764 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
766 (*i)->set_start (bbt);
768 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
770 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
771 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
773 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
775 fatal << _("programming error: unhandled MetricSection type") << endmsg;
784 // cerr << "###############################################\n\n\n" << endl;
789 TempoMap::metric_at (framepos_t frame) const
791 TempoMetric m (first_meter(), first_tempo());
795 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
796 at something, because we insert the default tempo and meter during
797 TempoMap construction.
799 now see if we can find better candidates.
802 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
804 if ((*i)->frame() > frame) {
808 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
809 m.set_tempo (*tempo);
810 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
811 m.set_meter (*meter);
814 m.set_frame ((*i)->frame ());
815 m.set_start ((*i)->start ());
822 TempoMap::metric_at (BBT_Time bbt) const
824 TempoMetric m (first_meter(), first_tempo());
828 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
829 at something, because we insert the default tempo and meter during
830 TempoMap construction.
832 now see if we can find better candidates.
835 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
837 BBT_Time section_start ((*i)->start());
839 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
843 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
844 m.set_tempo (*tempo);
845 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
846 m.set_meter (*meter);
849 m.set_frame ((*i)->frame ());
850 m.set_start (section_start);
857 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) const
860 Glib::RWLock::ReaderLock lm (lock);
861 bbt_time_unlocked (frame, bbt);
866 TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt) const
868 bbt_time_with_metric (frame, bbt, metric_at (frame));
872 TempoMap::bbt_time_with_metric (framepos_t frame, BBT_Time& bbt, const TempoMetric& metric) const
874 framecnt_t frame_diff;
876 const double beats_per_bar = metric.meter().beats_per_bar();
877 const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / BBT_Time::ticks_per_beat;
879 /* now compute how far beyond that point we actually are. */
881 frame_diff = frame - metric.frame();
883 bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
884 uint32_t xtra_beats = bbt.ticks / (uint32_t)BBT_Time::ticks_per_beat;
885 bbt.ticks %= (uint32_t)BBT_Time::ticks_per_beat;
887 bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
888 bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
889 bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
891 /* if we have a fractional number of beats per bar, we see if
892 we're in the last beat (the fractional one). if so, we
893 round ticks appropriately and bump to the next bar. */
894 double beat_fraction = beats_per_bar - floor(beats_per_bar);
895 /* XXX one problem here is that I'm not sure how to handle
896 fractional beats that don't evenly divide ticks_per_beat.
897 If they aren't handled consistently, I would guess we'll
898 continue to have strange discrepancies occuring. Perhaps
899 this will also behave badly in the case of meters like
900 0.1/4, but I can't be bothered to test that.
902 uint32_t ticks_on_last_beat = (uint32_t)floor(BBT_Time::ticks_per_beat * beat_fraction);
904 if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) {
905 bbt.ticks -= ticks_on_last_beat;
910 bbt.beats++; // correction for 1-based counting, see above for matching operation.
912 // cerr << "-----\t RETURN " << bbt << endl;
916 TempoMap::count_frames_between (const BBT_Time& start, const BBT_Time& end) const
918 /* for this to work with fractional measure types, start and end have to be
919 "legal" BBT types, that means that the beats and ticks should be inside
923 framecnt_t frames = 0;
924 framepos_t start_frame = 0;
925 framepos_t end_frame = 0;
927 TempoMetric m = metric_at (start);
929 uint32_t bar_offset = start.bars - m.start().bars;
931 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
932 + start.ticks/BBT_Time::ticks_per_beat;
935 start_frame = m.frame() + (framepos_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
939 bar_offset = end.bars - m.start().bars;
941 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
942 + end.ticks/BBT_Time::ticks_per_beat;
944 end_frame = m.frame() + (framepos_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
946 frames = end_frame - start_frame;
953 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
955 /* this is used in timestamping the metrics by actually counting the beats */
957 framecnt_t frames = 0;
958 uint32_t bar = start.bars;
959 double beat = (double) start.beats;
960 double beats_counted = 0;
961 double beats_per_bar = 0;
962 double beat_frames = 0;
964 beats_per_bar = meter.beats_per_bar();
965 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
969 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
971 if (beat >= beats_per_bar) {
976 if (beat > beats_per_bar) {
978 /* this is a fractional beat at the end of a fractional bar
979 so it should only count for the fraction
982 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
991 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
992 // << " bpb were " << beats_per_bar
993 // << " fpb was " << beat_frames
996 frames = (framecnt_t) llrint (floor (beats_counted * beat_frames));
1003 TempoMap::frame_time (const BBT_Time& bbt) const
1005 BBT_Time start ; /* 1|1|0 */
1007 return count_frames_between (start, bbt);
1011 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) const
1013 framecnt_t frames = 0;
1016 bbt_time(pos, when);
1019 Glib::RWLock::ReaderLock lm (lock);
1020 frames = bbt_duration_at_unlocked (when, bbt,dir);
1027 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
1029 framecnt_t frames = 0;
1031 double beats_per_bar;
1034 result.bars = max(1U, when.bars + dir * bbt.bars) ;
1038 TempoMetric metric = metric_at(result);
1039 beats_per_bar = metric.meter().beats_per_bar();
1041 /* Reduce things to legal bbt values we have to handle possible
1042 fractional=shorter beats at the end of measures and things like 0|11|9000
1043 as a duration in a 4.5/4 measure the musical decision is that the
1044 fractional beat is also a beat , although a shorter one
1048 result.beats = when.beats + bbt.beats;
1049 result.ticks = when.ticks + bbt.ticks;
1051 while (result.beats >= (beats_per_bar + 1)) {
1053 result.beats -= (uint32_t) ceil(beats_per_bar);
1054 metric = metric_at(result); // maybe there is a meter change
1055 beats_per_bar = metric.meter().beats_per_bar();
1059 /* We now counted the beats and landed in the target measure, now deal
1060 with ticks this seems complicated, but we want to deal with the
1061 corner case of a sequence of time signatures like 0.2/4-0.7/4 and
1062 with request like bbt = 3|2|9000 ,so we repeat the same loop but add
1066 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1069 uint32_t ticks_at_beat = (uint32_t) (result.beats == ceil(beats_per_bar) ?
1070 (1 - (ceil(beats_per_bar) - beats_per_bar))* BBT_Time::ticks_per_beat
1071 : BBT_Time::ticks_per_beat );
1073 while (result.ticks >= ticks_at_beat) {
1075 result.ticks -= ticks_at_beat;
1076 if (result.beats >= (beats_per_bar + 1)) {
1079 metric = metric_at(result); // maybe there is a meter change
1080 beats_per_bar = metric.meter().beats_per_bar();
1082 ticks_at_beat= (uint32_t) (result.beats == ceil(beats_per_bar) ?
1083 (1 - (ceil(beats_per_bar) - beats_per_bar) ) * BBT_Time::ticks_per_beat
1084 : BBT_Time::ticks_per_beat);
1089 uint32_t b = bbt.beats;
1092 while (b > when.beats) {
1094 result.bars = max(1U, result.bars);
1095 metric = metric_at(result); // maybe there is a meter change
1096 beats_per_bar = metric.meter().beats_per_bar();
1097 if (b >= ceil(beats_per_bar)) {
1098 b -= (uint32_t) ceil(beats_per_bar);
1100 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1103 result.beats = when.beats - b;
1107 if (bbt.ticks <= when.ticks) {
1108 result.ticks = when.ticks - bbt.ticks;
1111 uint32_t ticks_at_beat= (uint32_t) BBT_Time::ticks_per_beat;
1112 uint32_t t = bbt.ticks - when.ticks;
1116 if (result.beats == 1) {
1118 result.bars = max(1U, result.bars) ;
1119 metric = metric_at(result); // maybe there is a meter change
1120 beats_per_bar = metric.meter().beats_per_bar();
1121 result.beats = (uint32_t) ceil(beats_per_bar);
1122 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * BBT_Time::ticks_per_beat) ;
1125 ticks_at_beat = (uint32_t) BBT_Time::ticks_per_beat;
1128 if (t <= ticks_at_beat) {
1129 result.ticks = ticks_at_beat - t;
1133 } while (t > ticks_at_beat);
1141 frames = count_frames_between(result, when);
1143 frames = count_frames_between(when,result);
1152 TempoMap::round_to_bar (framepos_t fr, int dir)
1155 Glib::RWLock::ReaderLock lm (lock);
1156 return round_to_type (fr, dir, Bar);
1162 TempoMap::round_to_beat (framepos_t fr, int dir)
1165 Glib::RWLock::ReaderLock lm (lock);
1166 return round_to_type (fr, dir, Beat);
1171 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1174 uint32_t ticks_one_half_subdivisions_worth;
1175 uint32_t ticks_one_subdivisions_worth;
1176 uint32_t difference;
1178 bbt_time(fr, the_beat);
1180 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1181 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1187 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1190 /* right on the subdivision, so the difference is just the subdivision ticks */
1191 difference = ticks_one_subdivisions_worth;
1194 /* not on subdivision, compute distance to next subdivision */
1196 difference = ticks_one_subdivisions_worth - mod;
1199 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1201 } else if (dir < 0) {
1203 /* round to previous */
1205 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1208 /* right on the subdivision, so the difference is just the subdivision ticks */
1209 difference = ticks_one_subdivisions_worth;
1211 /* not on subdivision, compute distance to previous subdivision, which
1212 is just the modulus.
1219 the_beat = bbt_subtract (the_beat, BBT_Time (0, 0, difference));
1221 /* can't go backwards from wherever pos is, so just return it */
1226 /* round to nearest */
1228 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1229 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1230 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1232 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1233 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1237 return frame_time (the_beat);
1241 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1243 TempoMetric metric = metric_at (frame);
1246 BBT_Time one_bar (1,0,0);
1247 BBT_Time one_beat (0,1,0);
1249 bbt_time_with_metric (frame, bbt, metric);
1253 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, dir, bbt));
1257 /* find bar position preceding frame */
1260 bbt = bbt_subtract (bbt, one_bar);
1268 } else if (dir > 0) {
1270 /* find bar position following frame */
1273 bbt = bbt_add (bbt, one_bar, metric);
1281 /* "true" rounding */
1286 midbar_beats = metric.meter().beats_per_bar() / 2 + 1;
1287 midbar_ticks = BBT_Time::ticks_per_beat * fmod (midbar_beats, 1.0f);
1288 midbar_beats = floor (midbar_beats);
1290 BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1303 /* force beats & ticks to their values at the start of a bar */
1309 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1313 /* find beat position preceding frame */
1316 bbt = bbt_subtract (bbt, one_beat);
1324 } else if (dir > 0) {
1326 /* find beat position following frame */
1329 bbt = bbt_add (bbt, one_beat, metric);
1337 /* "true" rounding */
1339 /* round to nearest beat */
1340 if (bbt.ticks >= (BBT_Time::ticks_per_beat/2)) {
1343 bbt = bbt_add (bbt, one_beat, metric);
1350 /* force ticks to the value at the start of a beat */
1356 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)));
1357 return metric.frame() + count_frames_between (metric.start(), bbt);
1360 TempoMap::BBTPointList *
1361 TempoMap::get_points (framepos_t lower, framepos_t upper) const
1364 Metrics::const_iterator i;
1365 BBTPointList *points;
1367 const MeterSection* meter;
1368 const MeterSection* m;
1369 const TempoSection* tempo;
1370 const TempoSection* t;
1373 double beats_per_bar;
1376 double frames_per_bar;
1382 meter = &first_meter ();
1383 tempo = &first_tempo ();
1385 /* find the starting point */
1387 for (i = metrics->begin(); i != metrics->end(); ++i) {
1389 if ((*i)->frame() > lower) {
1393 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1395 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1402 meter -> the Meter for "lower"
1403 tempo -> the Tempo for "lower"
1404 i -> for first new metric after "lower", possibly metrics->end()
1406 Now start generating points.
1409 beats_per_bar = meter->beats_per_bar ();
1410 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1411 beat_frames = meter->frames_per_division (*tempo,_frame_rate);
1413 if (meter->frame() > tempo->frame()) {
1414 bar = meter->start().bars;
1415 beat = meter->start().beats;
1416 current = meter->frame();
1418 bar = tempo->start().bars;
1419 beat = tempo->start().beats;
1420 current = tempo->frame();
1423 /* initialize current to point to the bar/beat just prior to the
1424 lower frame bound passed in. assumes that current is initialized
1425 above to be on a beat.
1428 delta_bars = (lower-current) / frames_per_bar;
1429 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1430 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1432 // adjust bars and beats too
1433 bar += (uint32_t) (floor(delta_bars));
1434 beat += (uint32_t) (floor(delta_beats));
1436 points = new BBTPointList;
1440 if (i == metrics->end()) {
1442 // cerr << "== limit set to end of request @ " << limit << endl;
1444 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1445 limit = (*i)->frame();
1448 limit = min (limit, upper);
1450 while (current < limit) {
1452 /* if we're at the start of a bar, add bar point */
1455 if (current >= lower) {
1456 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1457 points->push_back (BBTPoint (*meter, *tempo,(framepos_t)rint(current), Bar, bar, 1));
1462 /* add some beats if we can */
1464 beat_frame = current;
1466 while (beat <= ceil(beats_per_bar) && beat_frame < limit) {
1467 if (beat_frame >= lower) {
1468 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1469 points->push_back (BBTPoint (*meter, *tempo, (framepos_t) rint(beat_frame), Beat, bar, beat));
1471 beat_frame += beat_frames;
1472 current+= beat_frames;
1477 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1478 // << (beat > ceil(beats_per_bar))
1481 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1483 /* we walked an entire bar. its
1484 important to move `current' forward
1485 by the actual frames_per_bar, not move it to
1486 an integral beat_frame, so that metrics with
1487 non-integral beats-per-bar have
1488 their bar positions set
1489 correctly. consider a metric with
1490 9-1/2 beats-per-bar. the bar we
1491 just filled had 10 beat marks,
1492 but the bar end is 1/2 beat before
1494 And it is also possible that a tempo
1495 change occured in the middle of a bar,
1496 so we subtract the possible extra fraction from the current
1499 if (beat > ceil (beats_per_bar)) {
1500 /* next bar goes where the numbers suggest */
1501 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1502 // cerr << "++ next bar from numbers\n";
1504 /* next bar goes where the next metric is */
1506 // cerr << "++ next bar at next metric\n";
1514 /* if we're done, then we're done */
1516 if (current >= upper) {
1520 /* i is an iterator that refers to the next metric (or none).
1521 if there is a next metric, move to it, and continue.
1524 if (i != metrics->end()) {
1526 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1528 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1530 /* new MeterSection, beat always returns to 1 */
1534 current = (*i)->frame ();
1535 // cerr << "loop around with current @ " << current << endl;
1537 beats_per_bar = meter->beats_per_bar ();
1538 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1539 beat_frames = meter->frames_per_division (*tempo, _frame_rate);
1550 TempoMap::tempo_section_at (framepos_t frame) const
1552 Glib::RWLock::ReaderLock lm (lock);
1553 Metrics::const_iterator i;
1554 TempoSection* prev = 0;
1556 for (i = metrics->begin(); i != metrics->end(); ++i) {
1559 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1561 if ((*i)->frame() > frame) {
1577 TempoMap::tempo_at (framepos_t frame) const
1579 TempoMetric m (metric_at (frame));
1585 TempoMap::meter_at (framepos_t frame) const
1587 TempoMetric m (metric_at (frame));
1592 TempoMap::get_state ()
1594 Metrics::const_iterator i;
1595 XMLNode *root = new XMLNode ("TempoMap");
1598 Glib::RWLock::ReaderLock lm (lock);
1599 for (i = metrics->begin(); i != metrics->end(); ++i) {
1600 root->add_child_nocopy ((*i)->get_state());
1608 TempoMap::set_state (const XMLNode& node, int /*version*/)
1611 Glib::RWLock::WriterLock lm (lock);
1614 XMLNodeConstIterator niter;
1615 Metrics old_metrics (*metrics);
1619 nlist = node.children();
1621 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1622 XMLNode* child = *niter;
1624 if (child->name() == TempoSection::xml_state_node_name) {
1627 metrics->push_back (new TempoSection (*child));
1630 catch (failed_constructor& err){
1631 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1632 *metrics = old_metrics;
1636 } else if (child->name() == MeterSection::xml_state_node_name) {
1639 metrics->push_back (new MeterSection (*child));
1642 catch (failed_constructor& err) {
1643 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1644 *metrics = old_metrics;
1650 if (niter == nlist.end()) {
1652 MetricSectionSorter cmp;
1653 metrics->sort (cmp);
1654 timestamp_metrics (true);
1658 PropertyChanged (PropertyChange ());
1664 TempoMap::dump (std::ostream& o) const
1666 const MeterSection* m;
1667 const TempoSection* t;
1669 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1671 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1672 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1673 << t->movable() << ')' << endl;
1674 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1675 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1676 << " (move? " << m->movable() << ')' << endl;
1682 TempoMap::n_tempos() const
1684 Glib::RWLock::ReaderLock lm (lock);
1687 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1688 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1697 TempoMap::n_meters() const
1699 Glib::RWLock::ReaderLock lm (lock);
1702 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1703 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1712 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1714 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1715 if ((*i)->frame() >= where && (*i)->movable ()) {
1716 (*i)->set_frame ((*i)->frame() + amount);
1720 timestamp_metrics (false);
1722 PropertyChanged (PropertyChange ());
1726 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1728 TempoMetric metric = metric_at (start);
1729 return bbt_add (start, other, metric);
1733 * add the BBT interval @param increment to @param start and return the result
1736 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
1738 BBT_Time result = start;
1739 BBT_Time op = increment; /* argument is const, but we need to modify it */
1740 uint32_t ticks = result.ticks + op.ticks;
1742 if (ticks >= BBT_Time::ticks_per_beat) {
1744 result.ticks = ticks % (uint32_t) BBT_Time::ticks_per_beat;
1746 result.ticks += op.ticks;
1749 /* now comes the complicated part. we have to add one beat a time,
1750 checking for a new metric on every beat.
1753 /* grab all meter sections */
1755 list<const MeterSection*> meter_sections;
1757 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1758 const MeterSection* ms;
1759 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1760 meter_sections.push_back (ms);
1764 assert (!meter_sections.empty());
1766 list<const MeterSection*>::const_iterator next_meter;
1767 const Meter* meter = 0;
1769 /* go forwards through the meter sections till we get to the one
1770 covering the current value of result. this positions i to point to
1771 the next meter section too, or the end.
1774 for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1776 if (result < (*next_meter)->start()) {
1777 /* this metric is past the result time. stop looking, we have what we need */
1781 if (result == (*next_meter)->start()) {
1782 /* this meter section starts at result, push i beyond it so that it points
1783 to the NEXT section, opwise we will get stuck later, and use this meter section.
1785 meter = *next_meter;
1790 meter = *next_meter;
1793 assert (meter != 0);
1795 /* OK, now have the meter for the bar start we are on, and i is an iterator
1796 that points to the metric after the one we are currently dealing with
1797 (or to metrics->end(), of course)
1802 /* given the current meter, have we gone past the end of the bar ? */
1804 if (result.beats >= meter->beats_per_bar()) {
1805 /* move to next bar, first beat */
1816 /* check if we need to use a new meter section: has adding beats to result taken us
1817 to or after the start of the next meter section? in which case, use it.
1820 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1821 meter = *next_meter;
1826 /* finally, add bars */
1828 result.bars += op.bars++;
1834 * subtract the BBT interval @param decrement from @param start and return the result
1837 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1839 BBT_Time result = start;
1840 BBT_Time op = decrement; /* argument is const, but we need to modify it */
1842 if (op.ticks > result.ticks) {
1843 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1845 result.ticks = BBT_Time::ticks_per_beat - (op.ticks - result.ticks);
1847 result.ticks -= op.ticks;
1850 /* now comes the complicated part. we have to subtract one beat a time,
1851 checking for a new metric on every beat.
1854 /* grab all meter sections */
1856 list<const MeterSection*> meter_sections;
1858 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1859 const MeterSection* ms;
1860 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1861 meter_sections.push_back (ms);
1865 assert (!meter_sections.empty());
1867 /* go backwards through the meter sections till we get to the one
1868 covering the current value of result. this positions i to point to
1869 the next (previous) meter section too, or the end.
1872 const MeterSection* meter = 0;
1873 list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't
1874 // support const_reverse_iterator::operator!=()
1876 for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1878 /* when we find the first meter section that is before or at result, use it,
1879 and set next_meter to the previous one
1882 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1883 meter = *next_meter;
1889 assert (meter != 0);
1891 /* OK, now have the meter for the bar start we are on, and i is an iterator
1892 that points to the metric after the one we are currently dealing with
1893 (or to metrics->end(), of course)
1898 /* have we reached the start of the bar? if so, move to the last beat of the previous
1899 bar. opwise, just step back 1 beat.
1902 if (result.beats == 1) {
1904 /* move to previous bar, last beat */
1906 if (result.bars <= 1) {
1907 /* i'm sorry dave, i can't do that */
1908 throw std::out_of_range ("illegal BBT subtraction");
1912 result.beats = meter->beats_per_bar();
1923 /* check if we need to use a new meter section: has subtracting beats to result taken us
1924 to before the start of the current meter section? in which case, use the prior one.
1927 if (result < meter->start() && next_meter != meter_sections.rend()) {
1928 meter = *next_meter;
1933 /* finally, subtract bars */
1935 if (op.bars >= result.bars) {
1936 /* i'm sorry dave, i can't do that */
1937 throw std::out_of_range ("illegal BBT subtraction");
1940 result.bars -= op.bars;
1944 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1945 * pos can be -ve, if required.
1948 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1950 Metrics::const_iterator i;
1951 const TempoSection* tempo;
1952 const MeterSection* meter;
1954 /* Find the starting metrics for tempo & meter */
1956 for (i = metrics->begin(); i != metrics->end(); ++i) {
1958 /* This is a bit of a hack, but pos could be -ve, and if it is,
1959 we consider the initial metric changes (at time 0) to actually
1960 be in effect at pos.
1962 framepos_t f = (*i)->frame ();
1963 if (pos < 0 && f == 0) {
1971 const TempoSection* t;
1972 const MeterSection* m;
1974 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1976 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1983 meter -> the Meter for "pos"
1984 tempo -> the Tempo for "pos"
1985 i -> for first new metric after "pos", possibly metrics->end()
1990 /* Distance to the end of this section in frames */
1991 framecnt_t distance_frames = i == metrics->end() ? max_framepos : ((*i)->frame() - pos);
1993 /* Distance to the end in beats */
1994 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate, *meter);
1996 /* Amount to subtract this time */
1997 double const sub = min (distance_beats, beats);
2001 pos += sub * tempo->frames_per_beat (_frame_rate, *meter);
2003 /* Move on if there's anything to move to */
2004 if (i != metrics->end ()) {
2005 const TempoSection* t;
2006 const MeterSection* m;
2008 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2010 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2021 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
2023 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
2025 Metrics::const_iterator i;
2026 const TempoSection* tempo = 0;
2027 const MeterSection* meter = 0;
2029 /* Find the starting metrics for tempo & meter */
2031 for (i = metrics->begin(); i != metrics->end(); ++i) {
2033 /* This is a bit of a hack, but pos could be -ve, and if it is,
2034 we consider the initial metric changes (at time 0) to actually
2035 be in effect at pos.
2037 framepos_t f = (*i)->frame ();
2038 if (pos < 0 && f == 0) {
2042 if ((*i)->frame() > pos) {
2046 const TempoSection* t;
2047 const MeterSection* m;
2049 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2051 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2056 bool no_more_metrics = false;
2058 /* Move i back to the metric before "pos" */
2059 if (i != metrics->begin ()) {
2062 no_more_metrics = true;
2067 meter -> the Meter for "pos"
2068 tempo -> the Tempo for "pos"
2069 i -> the first metric before "pos", possibly metrics->end()
2074 /* Distance to the end of this section in frames */
2075 framecnt_t distance_frames = no_more_metrics ? max_framepos : (pos - (*i)->frame());
2077 /* Distance to the end in beats */
2078 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate, *meter);
2080 /* Amount to subtract this time */
2081 double const sub = min (distance_beats, beats);
2085 pos -= sub * tempo->frames_per_beat (_frame_rate, *meter);
2087 /* Move i, tempo and meter back, if there's anything to move to.
2088 This is more complicated than the forward case, as we have to
2089 a) move back to the previous change in tempo or metric
2090 then b) scan back further to the last change in the opposite thing
2091 so that tempo/meter are both set up correctly.
2093 e.g. if we have (where M is a meter change and T a tempo change):
2096 and we move i back to M2, we must also move tempo back to T3 so
2097 that tempo/meter continue to reflect the current state.
2099 Moving further back we'd move i to T3, and meter to M1, then
2100 i to T2 and meter (still) to M1, etc.
2102 XXX: this is slightly farcical.
2105 if (i != metrics->begin ()) {
2109 bool found_tempo = false;
2110 bool found_meter = false;
2112 const TempoSection* t;
2113 const MeterSection* m;
2115 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2118 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2123 Metrics::const_iterator j = i;
2124 while (j != metrics->begin ()) {
2126 if (found_tempo && ((m = dynamic_cast<const MeterSection*> (*j)) != 0)) {
2129 } else if (found_meter && ((t = dynamic_cast<const TempoSection*> (*j)) != 0)) {
2135 no_more_metrics = true;
2142 /** Add the BBT interval op to pos and return the result */
2144 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2146 Metrics::const_iterator i;
2147 const MeterSection* meter;
2148 const MeterSection* m;
2149 const TempoSection* tempo;
2150 const TempoSection* t;
2151 double frames_per_beat;
2153 meter = &first_meter ();
2154 tempo = &first_tempo ();
2159 /* find the starting metrics for tempo & meter */
2161 for (i = metrics->begin(); i != metrics->end(); ++i) {
2163 if ((*i)->frame() > pos) {
2167 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2169 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2176 meter -> the Meter for "pos"
2177 tempo -> the Tempo for "pos"
2178 i -> for first new metric after "pos", possibly metrics->end()
2181 /* now comes the complicated part. we have to add one beat a time,
2182 checking for a new metric on every beat.
2185 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2194 /* check if we need to use a new metric section: has adding frames moved us
2195 to or after the start of the next metric section? in which case, use it.
2198 if (i != metrics->end()) {
2199 if ((*i)->frame() <= pos) {
2201 /* about to change tempo or meter, so add the
2202 * number of frames for the bars we've just
2203 * traversed before we change the
2204 * frames_per_beat value.
2207 pos += llrint (frames_per_beat * (bars * meter->beats_per_bar()));
2210 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2212 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2216 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2223 pos += llrint (frames_per_beat * (bars * meter->beats_per_bar()));
2229 /* given the current meter, have we gone past the end of the bar ? */
2234 /* check if we need to use a new metric section: has adding frames moved us
2235 to or after the start of the next metric section? in which case, use it.
2238 if (i != metrics->end()) {
2239 if ((*i)->frame() <= pos) {
2241 /* about to change tempo or meter, so add the
2242 * number of frames for the beats we've just
2243 * traversed before we change the
2244 * frames_per_beat value.
2247 pos += llrint (beats * frames_per_beat);
2250 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2252 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2256 frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
2261 pos += llrint (beats * frames_per_beat);
2264 if (op.ticks >= BBT_Time::ticks_per_beat) {
2265 pos += llrint (frames_per_beat + /* extra beat */
2266 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2267 (double) BBT_Time::ticks_per_beat)));
2269 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2276 /** Count the number of beats that are equivalent to distance when going forward,
2280 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2282 Metrics::const_iterator i;
2283 const TempoSection* tempo;
2284 const MeterSection* meter;
2286 /* Find the starting metrics for tempo & meter */
2288 for (i = metrics->begin(); i != metrics->end(); ++i) {
2290 if ((*i)->frame() > pos) {
2294 const TempoSection* t;
2295 const MeterSection* m;
2297 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2299 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2306 meter -> the Meter for "pos"
2307 tempo -> the Tempo for "pos"
2308 i -> the first metric after "pos", possibly metrics->end()
2311 Evoral::MusicalTime beats = 0;
2315 /* End of this section */
2316 framepos_t const end = i == metrics->end() ? max_framepos : (*i)->frame ();
2318 /* Distance to the end in frames */
2319 framecnt_t const distance_to_end = end - pos;
2321 /* Amount to subtract this time */
2322 double const sub = min (distance, distance_to_end);
2327 beats += sub / tempo->frames_per_beat (_frame_rate, *meter);
2329 /* Move on if there's anything to move to */
2330 if (i != metrics->end ()) {
2331 const TempoSection* t;
2332 const MeterSection* m;
2334 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2336 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2347 /** Compare the time of this with that of another MetricSection.
2348 * @param with_bbt True to compare using start(), false to use frame().
2349 * @return -1 for less than, 0 for equal, 1 for greater than.
2353 MetricSection::compare (MetricSection* other, bool with_bbt) const
2356 if (start() == other->start()) {
2358 } else if (start() < other->start()) {
2364 if (frame() == other->frame()) {
2366 } else if (frame() < other->frame()) {