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);
49 Tempo::frames_per_beat (framecnt_t sr) const
51 return (60.0 * sr) / _beats_per_minute;
54 /***********************************************************************/
57 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
59 /* This is tempo- and meter-sensitive. The number it returns
60 is based on the interval between any two lines in the
61 grid that is constructed from tempo and meter sections.
63 The return value IS NOT interpretable in terms of "beats".
66 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
70 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
72 return frames_per_grid (tempo, sr) * _divisions_per_bar;
75 /***********************************************************************/
77 const string TempoSection::xml_state_node_name = "Tempo";
79 TempoSection::TempoSection (const XMLNode& node)
80 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
82 const XMLProperty *prop;
84 LocaleGuard lg (X_("POSIX"));
86 if ((prop = node.property ("start")) == 0) {
87 error << _("TempoSection XML node has no \"start\" property") << endmsg;
88 throw failed_constructor();
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
96 throw failed_constructor();
101 if ((prop = node.property ("beats-per-minute")) == 0) {
102 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
103 throw failed_constructor();
106 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
107 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
108 throw failed_constructor();
111 if ((prop = node.property ("note-type")) == 0) {
112 /* older session, make note type be quarter by default */
115 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
116 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
117 throw failed_constructor();
121 if ((prop = node.property ("movable")) == 0) {
122 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
123 throw failed_constructor();
126 set_movable (string_is_affirmative (prop->value()));
128 if ((prop = node.property ("bar-offset")) == 0) {
131 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
132 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
133 throw failed_constructor();
139 TempoSection::get_state() const
141 XMLNode *root = new XMLNode (xml_state_node_name);
143 LocaleGuard lg (X_("POSIX"));
145 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
149 root->add_property ("start", buf);
150 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
151 root->add_property ("beats-per-minute", buf);
152 snprintf (buf, sizeof (buf), "%f", _note_type);
153 root->add_property ("note-type", buf);
154 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
155 // root->add_property ("bar-offset", buf);
156 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
157 root->add_property ("movable", buf);
164 TempoSection::update_bar_offset_from_bbt (const Meter& m)
166 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
167 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
169 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
173 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
177 if (_bar_offset < 0.0) {
182 new_start.bars = start().bars;
184 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
185 new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_beat);
186 new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat);
188 /* remember the 1-based counting properties of beats */
189 new_start.beats += 1;
191 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
192 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
194 set_start (new_start);
197 /***********************************************************************/
199 const string MeterSection::xml_state_node_name = "Meter";
201 MeterSection::MeterSection (const XMLNode& node)
202 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
204 const XMLProperty *prop;
206 LocaleGuard lg (X_("POSIX"));
208 if ((prop = node.property ("start")) == 0) {
209 error << _("MeterSection XML node has no \"start\" property") << endmsg;
210 throw failed_constructor();
213 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
217 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
218 throw failed_constructor();
223 /* beats-per-bar is old; divisions-per-bar is new */
225 if ((prop = node.property ("divisions-per-bar")) == 0) {
226 if ((prop = node.property ("beats-per-bar")) == 0) {
227 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
228 throw failed_constructor();
232 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
233 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
234 throw failed_constructor();
237 if ((prop = node.property ("note-type")) == 0) {
238 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
239 throw failed_constructor();
242 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
243 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
244 throw failed_constructor();
247 if ((prop = node.property ("movable")) == 0) {
248 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
249 throw failed_constructor();
252 set_movable (string_is_affirmative (prop->value()));
256 MeterSection::get_state() const
258 XMLNode *root = new XMLNode (xml_state_node_name);
260 LocaleGuard lg (X_("POSIX"));
262 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
266 root->add_property ("start", buf);
267 snprintf (buf, sizeof (buf), "%f", _note_type);
268 root->add_property ("note-type", buf);
269 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
270 root->add_property ("divisions-per-bar", buf);
271 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
272 root->add_property ("movable", buf);
277 /***********************************************************************/
279 struct MetricSectionSorter {
280 bool operator() (const MetricSection* a, const MetricSection* b) {
281 return a->start() < b->start();
285 TempoMap::TempoMap (framecnt_t fr)
294 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
295 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
297 t->set_movable (false);
298 m->set_movable (false);
300 /* note: frame time is correct (zero) for both of these */
302 metrics.push_back (t);
303 metrics.push_back (m);
306 TempoMap::~TempoMap ()
311 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
313 bool removed = false;
316 Glib::RWLock::WriterLock lm (lock);
319 for (i = metrics.begin(); i != metrics.end(); ++i) {
320 if (dynamic_cast<TempoSection*> (*i) != 0) {
321 if (tempo.frame() == (*i)->frame()) {
322 if ((*i)->movable()) {
331 if (removed && complete_operation) {
332 recompute_map (false);
336 if (removed && complete_operation) {
337 PropertyChanged (PropertyChange ());
342 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
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()) {
362 if (removed && complete_operation) {
363 recompute_map (true);
367 if (removed && complete_operation) {
368 PropertyChanged (PropertyChange ());
373 TempoMap::do_insert (MetricSection* section)
375 bool need_add = true;
377 assert (section->start().ticks == 0);
379 /* we only allow new meters to be inserted on beat 1 of an existing
383 if (dynamic_cast<MeterSection*>(section)) {
385 /* we need to (potentially) update the BBT times of tempo
386 sections based on this new meter.
389 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
391 BBT_Time corrected = section->start();
395 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
396 section->start(), corrected) << endmsg;
398 section->set_start (corrected);
404 /* Look for any existing MetricSection that is of the same type and
405 at the same time as the new one, and remove it before adding
409 Metrics::iterator to_remove = metrics.end ();
411 for (i = metrics.begin(); i != metrics.end(); ++i) {
413 int const c = (*i)->compare (*section);
416 /* this section is before the one to be added; go back round */
419 /* this section is after the one to be added; there can't be any at the same time */
423 /* hacky comparison of type */
424 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
425 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
427 if (iter_is_tempo == insert_is_tempo) {
429 if (!(*i)->movable()) {
431 /* can't (re)move this section, so overwrite
432 * its data content (but not its properties as
436 if (!iter_is_tempo) {
437 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
439 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
450 if (to_remove != metrics.end()) {
451 /* remove the MetricSection at the same time as the one we are about to add */
452 metrics.erase (to_remove);
455 /* Add the given MetricSection */
458 for (i = metrics.begin(); i != metrics.end(); ++i) {
460 if ((*i)->compare (*section) < 0) {
464 metrics.insert (i, section);
468 if (i == metrics.end()) {
469 metrics.insert (metrics.end(), section);
475 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
477 const TempoSection& first (first_tempo());
480 remove_tempo (ts, false);
481 add_tempo (tempo, where);
484 Glib::RWLock::WriterLock lm (lock);
485 /* cannot move the first tempo section */
486 *((Tempo*)&first) = tempo;
487 recompute_map (false);
491 PropertyChanged (PropertyChange ());
495 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
498 Glib::RWLock::WriterLock lm (lock);
500 /* new tempos always start on a beat */
503 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
505 /* find the meter to use to set the bar offset of this
509 const Meter* meter = &first_meter();
511 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
512 at something, because we insert the default tempo and meter during
513 TempoMap construction.
515 now see if we can find better candidates.
518 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
520 const MeterSection* m;
522 if (where < (*i)->start()) {
526 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
531 ts->update_bar_offset_from_bbt (*meter);
537 recompute_map (false);
541 PropertyChanged (PropertyChange ());
545 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
547 const MeterSection& first (first_meter());
550 remove_meter (ms, false);
551 add_meter (meter, where);
554 Glib::RWLock::WriterLock lm (lock);
555 /* cannot move the first meter section */
556 *((Meter*)&first) = meter;
557 recompute_map (true);
561 PropertyChanged (PropertyChange ());
565 TempoMap::add_meter (const Meter& meter, BBT_Time where)
568 Glib::RWLock::WriterLock lm (lock);
570 /* a new meter always starts a new bar on the first beat. so
571 round the start time appropriately. remember that
572 `where' is based on the existing tempo map, not
573 the result after we insert the new meter.
577 if (where.beats != 1) {
582 /* new meters *always* start on a beat. */
585 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
586 recompute_map (true);
591 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
596 PropertyChanged (PropertyChange ());
600 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
602 Tempo newtempo (beats_per_minute, note_type);
605 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
606 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
608 Glib::RWLock::WriterLock lm (lock);
609 *((Tempo*) t) = newtempo;
610 recompute_map (false);
612 PropertyChanged (PropertyChange ());
619 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
621 Tempo newtempo (beats_per_minute, note_type);
627 /* find the TempoSection immediately preceding "where"
630 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
632 if ((*i)->frame() > where) {
638 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
648 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
658 Glib::RWLock::WriterLock lm (lock);
659 /* cannot move the first tempo section */
660 *((Tempo*)prev) = newtempo;
661 recompute_map (false);
664 PropertyChanged (PropertyChange ());
668 TempoMap::first_meter () const
670 const MeterSection *m = 0;
672 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
673 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
678 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
684 TempoMap::first_tempo () const
686 const TempoSection *t = 0;
688 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
689 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
694 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
700 TempoMap::require_map_to (framepos_t pos)
702 Glib::RWLock::WriterLock lm (lock);
704 if (_map.empty() || _map.back().frame < pos) {
710 TempoMap::require_map_to (const BBT_Time& bbt)
712 Glib::RWLock::WriterLock lm (lock);
714 /* since we have no idea where BBT is if its off the map, see the last
715 * point in the map is past BBT, and if not add an arbitrary amount of
719 int additional_minutes = 1;
722 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
725 /* add some more distance, using bigger steps each time */
726 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
727 additional_minutes *= 2;
732 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
734 /* CALLER MUST HOLD WRITE LOCK */
736 MeterSection* meter = 0;
737 TempoSection* tempo = 0;
738 double current_frame;
740 Metrics::iterator next_metric;
745 /* compute 1 mins worth */
746 end = _frame_rate * 60;
748 end = _map.back().frame;
751 if (!_map.empty ()) {
752 /* never allow the map to be shortened */
753 end = max (end, _map.back().frame);
757 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
759 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
762 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
768 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
771 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
777 /* assumes that the first meter & tempo are at frame zero */
779 meter->set_frame (0);
780 tempo->set_frame (0);
782 /* assumes that the first meter & tempo are at 1|1|0 */
787 if (reassign_tempo_bbt) {
789 MeterSection* rmeter = meter;
791 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
793 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
798 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
800 /* reassign the BBT time of this tempo section
801 * based on its bar offset position.
804 ts->update_bbt_time_from_bar_offset (*rmeter);
806 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
809 fatal << _("programming error: unhandled MetricSection type") << endmsg;
815 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
817 next_metric = metrics.begin();
818 ++next_metric; // skip meter (or tempo)
819 ++next_metric; // skip tempo (or meter)
823 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
824 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
827 /* silly call from Session::process() during startup
832 _extend_map (tempo, meter, next_metric, current, current_frame, end);
836 TempoMap::extend_map (framepos_t end)
838 /* CALLER MUST HOLD WRITE LOCK */
841 recompute_map (false, end);
845 BBTPointList::const_iterator i = _map.end();
846 Metrics::iterator next_metric;
850 BBT_Time last_metric_start;
852 if ((*i).tempo->frame() > (*i).meter->frame()) {
853 last_metric_start = (*i).tempo->start();
855 last_metric_start = (*i).meter->start();
858 /* find the metric immediately after the tempo + meter sections for the
859 * last point in the map
862 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
863 if ((*next_metric)->start() > last_metric_start) {
868 /* we cast away const here because this is the one place where we need
869 * to actually modify the frame time of each metric section.
872 _extend_map (const_cast<TempoSection*> ((*i).tempo),
873 const_cast<MeterSection*> ((*i).meter),
874 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
878 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
879 Metrics::iterator next_metric,
880 BBT_Time current, framepos_t current_frame, framepos_t end)
882 /* CALLER MUST HOLD WRITE LOCK */
886 double divisions_per_bar;
889 divisions_per_bar = meter->divisions_per_bar ();
890 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
892 while (current_frame < end) {
895 current_frame += beat_frames;
897 if (current.beats > meter->divisions_per_bar()) {
902 if (next_metric != metrics.end()) {
904 /* no operator >= so invert operator < */
906 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
908 if (!(current < (*next_metric)->start())) {
911 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
915 /* new tempo section: if its on a beat,
916 * we don't have to do anything other
917 * than recompute various distances,
918 * done further below as we transition
919 * the next metric section.
921 * if its not on the beat, we have to
922 * compute the duration of the beat it
923 * is within, which will be different
924 * from the preceding following ones
925 * since it takes part of its duration
926 * from the preceding tempo and part
927 * from this new tempo.
930 if (tempo->start().ticks != 0) {
932 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
934 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
935 tempo->start(), current_frame, tempo->bar_offset()));
937 /* back up to previous beat */
938 current_frame -= beat_frames;
940 /* set tempo section location based on offset from last beat */
942 double bar_offset_in_beats = (ts->bar_offset() * meter->divisions_per_bar()) - 1;
943 bar_offset_in_beats -= current.beats;
944 tempo->set_frame (current_frame + (bar_offset_in_beats * beat_frames));
946 /* advance to the location of the new (adjusted) beat */
947 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
948 /* next metric doesn't have to
949 * match this precisely to
952 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
956 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
957 tempo->start(), current_frame));
958 tempo->set_frame (current_frame);
961 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
965 /* new meter section: always defines the
969 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
970 meter->start(), current, current_frame));
972 assert (current.beats == 1);
974 meter->set_frame (current_frame);
977 divisions_per_bar = meter->divisions_per_bar ();
978 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
980 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
981 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
985 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
986 /* same position so go back and set this one up before advancing
993 if (current.beats == 1) {
994 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
995 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
997 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
998 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
1004 TempoMap::metric_at (framepos_t frame) const
1006 Glib::RWLock::ReaderLock lm (lock);
1007 TempoMetric m (first_meter(), first_tempo());
1011 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1012 at something, because we insert the default tempo and meter during
1013 TempoMap construction.
1015 now see if we can find better candidates.
1018 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1020 // cerr << "Looking at a metric section " << **i << endl;
1022 if ((*i)->frame() > frame) {
1026 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1027 m.set_tempo (*tempo);
1028 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1029 m.set_meter (*meter);
1032 m.set_frame ((*i)->frame ());
1033 m.set_start ((*i)->start ());
1036 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1041 TempoMap::metric_at (BBT_Time bbt) const
1043 Glib::RWLock::ReaderLock lm (lock);
1044 TempoMetric m (first_meter(), first_tempo());
1048 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1049 at something, because we insert the default tempo and meter during
1050 TempoMap construction.
1052 now see if we can find better candidates.
1055 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1057 BBT_Time section_start ((*i)->start());
1059 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1063 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1064 m.set_tempo (*tempo);
1065 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1066 m.set_meter (*meter);
1069 m.set_frame ((*i)->frame ());
1070 m.set_start (section_start);
1077 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1079 require_map_to (frame);
1081 Glib::RWLock::ReaderLock lm (lock);
1082 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1086 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1088 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1091 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1094 if (_map.empty() || _map.back().frame < frame) {
1095 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1098 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1102 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1104 /* CALLER MUST HOLD READ LOCK */
1106 bbt.bars = (*i).bar;
1107 bbt.beats = (*i).beat;
1109 if ((*i).frame == frame) {
1112 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1113 BBT_Time::ticks_per_beat);
1118 TempoMap::frame_time (const BBT_Time& bbt)
1120 require_map_to (bbt);
1122 Glib::RWLock::ReaderLock lm (lock);
1124 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1125 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1127 if (bbt.ticks != 0) {
1128 return ((*e).frame - (*s).frame) +
1129 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1131 return ((*e).frame - (*s).frame);
1136 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1138 Glib::RWLock::ReaderLock lm (lock);
1139 framecnt_t frames = 0;
1142 bbt_time (pos, when);
1143 frames = bbt_duration_at_unlocked (when, bbt,dir);
1149 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1151 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1155 /* round back to the previous precise beat */
1156 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1157 BBTPointList::const_iterator start (wi);
1158 double tick_frames = 0;
1160 assert (wi != _map.end());
1162 /* compute how much rounding we did because of non-zero ticks */
1164 if (when.ticks != 0) {
1165 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1171 while (wi != _map.end() && bars < bbt.bars) {
1173 if ((*wi).is_bar()) {
1177 assert (wi != _map.end());
1179 while (wi != _map.end() && beats < bbt.beats) {
1183 assert (wi != _map.end());
1185 /* add any additional frames related to ticks in the added value */
1187 if (bbt.ticks != 0) {
1188 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1191 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1195 TempoMap::round_to_bar (framepos_t fr, int dir)
1197 return round_to_type (fr, dir, Bar);
1201 TempoMap::round_to_beat (framepos_t fr, int dir)
1203 return round_to_type (fr, dir, Beat);
1207 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1209 require_map_to (fr);
1211 Glib::RWLock::ReaderLock lm (lock);
1212 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1214 uint32_t ticks_one_subdivisions_worth;
1215 uint32_t difference;
1217 bbt_time (fr, the_beat, i);
1219 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1220 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1222 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1226 /* round to next (even if we're on a subdivision */
1228 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1231 /* right on the subdivision, so the difference is just the subdivision ticks */
1232 the_beat.ticks += ticks_one_subdivisions_worth;
1235 /* not on subdivision, compute distance to next subdivision */
1237 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1240 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1241 assert (i != _map.end());
1243 assert (i != _map.end());
1244 the_beat.ticks -= BBT_Time::ticks_per_beat;
1248 } else if (dir < 0) {
1250 /* round to previous (even if we're on a subdivision) */
1252 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1255 /* right on the subdivision, so the difference is just the subdivision ticks */
1256 difference = ticks_one_subdivisions_worth;
1258 /* not on subdivision, compute distance to previous subdivision, which
1259 is just the modulus.
1265 if (the_beat.ticks < difference) {
1266 if (i == _map.begin()) {
1267 /* can't go backwards from wherever pos is, so just return it */
1271 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1273 the_beat.ticks -= difference;
1277 /* round to nearest */
1281 /* compute the distance to the previous and next subdivision */
1283 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1285 /* closer to the next subdivision, so shift forward */
1287 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1289 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1291 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1292 assert (i != _map.end());
1294 assert (i != _map.end());
1295 the_beat.ticks -= BBT_Time::ticks_per_beat;
1296 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1299 } else if (rem > 0) {
1301 /* closer to previous subdivision, so shift backward */
1303 if (rem > the_beat.ticks) {
1304 if (i == _map.begin()) {
1305 /* can't go backwards past zero, so ... */
1308 /* step back to previous beat */
1310 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1311 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1313 the_beat.ticks = lrint (the_beat.ticks - rem);
1314 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1317 /* on the subdivision, do nothing */
1321 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1322 (*i).tempo->frames_per_beat (_frame_rate);
1326 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1328 require_map_to (frame);
1330 Glib::RWLock::ReaderLock lm (lock);
1331 BBTPointList::const_iterator fi;
1334 fi = bbt_after_or_at (frame);
1336 fi = bbt_before_or_at (frame);
1339 assert (fi != _map.end());
1341 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to bars in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame));
1346 /* find bar previous to 'frame' */
1348 if (fi == _map.begin()) {
1352 if ((*fi).is_bar() && (*fi).frame == frame) {
1356 while (!(*fi).is_bar()) {
1357 if (fi == _map.begin()) {
1362 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1363 (*fi).bar, (*fi).beat, (*fi).frame));
1366 } else if (dir > 0) {
1368 /* find bar following 'frame' */
1370 if ((*fi).is_bar() && (*fi).frame == frame) {
1374 while (!(*fi).is_bar()) {
1376 if (fi == _map.end()) {
1382 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1383 (*fi).bar, (*fi).beat, (*fi).frame));
1388 /* true rounding: find nearest bar */
1390 BBTPointList::const_iterator prev = fi;
1391 BBTPointList::const_iterator next = fi;
1393 if ((*fi).frame == frame) {
1397 while ((*prev).beat != 1) {
1398 if (prev == _map.begin()) {
1404 while ((next != _map.end()) && (*next).beat != 1) {
1408 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1409 return (*prev).frame;
1411 return (*next).frame;
1421 if (fi == _map.begin()) {
1425 if ((*fi).frame >= frame) {
1426 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1429 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1430 (*fi).bar, (*fi).beat, (*fi).frame));
1432 } else if (dir > 0) {
1433 if ((*fi).frame <= frame) {
1434 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1437 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1438 (*fi).bar, (*fi).beat, (*fi).frame));
1441 /* find beat nearest to frame */
1442 if ((*fi).frame == frame) {
1446 BBTPointList::const_iterator prev = fi;
1447 BBTPointList::const_iterator next = fi;
1448 if (prev != _map.begin()) {
1453 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1454 return (*prev).frame;
1456 return (*next).frame;
1468 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1469 TempoMap::BBTPointList::const_iterator& end,
1470 framepos_t lower, framepos_t upper)
1473 Glib::RWLock::WriterLock lm (lock);
1474 if (_map.empty() || (_map.back().frame < upper)) {
1475 recompute_map (false, upper);
1479 begin = lower_bound (_map.begin(), _map.end(), lower);
1480 end = upper_bound (_map.begin(), _map.end(), upper);
1484 TempoMap::tempo_section_at (framepos_t frame) const
1486 Glib::RWLock::ReaderLock lm (lock);
1487 Metrics::const_iterator i;
1488 TempoSection* prev = 0;
1490 for (i = metrics.begin(); i != metrics.end(); ++i) {
1493 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1495 if ((*i)->frame() > frame) {
1511 TempoMap::tempo_at (framepos_t frame) const
1513 TempoMetric m (metric_at (frame));
1519 TempoMap::meter_at (framepos_t frame) const
1521 TempoMetric m (metric_at (frame));
1526 TempoMap::get_state ()
1528 Metrics::const_iterator i;
1529 XMLNode *root = new XMLNode ("TempoMap");
1532 Glib::RWLock::ReaderLock lm (lock);
1533 for (i = metrics.begin(); i != metrics.end(); ++i) {
1534 root->add_child_nocopy ((*i)->get_state());
1542 TempoMap::set_state (const XMLNode& node, int /*version*/)
1545 Glib::RWLock::WriterLock lm (lock);
1548 XMLNodeConstIterator niter;
1549 Metrics old_metrics (metrics);
1550 MeterSection* last_meter = 0;
1554 nlist = node.children();
1556 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1557 XMLNode* child = *niter;
1559 if (child->name() == TempoSection::xml_state_node_name) {
1562 TempoSection* ts = new TempoSection (*child);
1563 metrics.push_back (ts);
1565 if (ts->bar_offset() < 0.0) {
1567 ts->update_bar_offset_from_bbt (*last_meter);
1572 catch (failed_constructor& err){
1573 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1574 metrics = old_metrics;
1578 } else if (child->name() == MeterSection::xml_state_node_name) {
1581 MeterSection* ms = new MeterSection (*child);
1582 metrics.push_back (ms);
1586 catch (failed_constructor& err) {
1587 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1588 metrics = old_metrics;
1594 if (niter == nlist.end()) {
1595 MetricSectionSorter cmp;
1599 recompute_map (true);
1602 PropertyChanged (PropertyChange ());
1608 TempoMap::dump (std::ostream& o) const
1610 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1611 const MeterSection* m;
1612 const TempoSection* t;
1614 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1616 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1617 o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
1618 << t->movable() << ')' << endl;
1619 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1620 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1621 << " (movable? " << m->movable() << ')' << endl;
1627 TempoMap::n_tempos() const
1629 Glib::RWLock::ReaderLock lm (lock);
1632 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1633 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1642 TempoMap::n_meters() const
1644 Glib::RWLock::ReaderLock lm (lock);
1647 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1648 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1657 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1660 Glib::RWLock::WriterLock lm (lock);
1661 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1662 if ((*i)->frame() >= where && (*i)->movable ()) {
1663 (*i)->set_frame ((*i)->frame() + amount);
1667 /* now reset the BBT time of all metrics, based on their new
1668 * audio time. This is the only place where we do this reverse
1672 Metrics::iterator i;
1673 const MeterSection* meter;
1674 const TempoSection* tempo;
1678 meter = &first_meter ();
1679 tempo = &first_tempo ();
1684 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1687 MetricSection* prev = 0;
1689 for (i = metrics.begin(); i != metrics.end(); ++i) {
1692 TempoMetric metric (*meter, *tempo);
1695 metric.set_start (prev->start());
1696 metric.set_frame (prev->frame());
1698 // metric will be at frames=0 bbt=1|1|0 by default
1699 // which is correct for our purpose
1702 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1703 bbt_time ((*i)->frame(), bbt, bi);
1705 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1711 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1712 /* round up to next beat */
1718 if (bbt.beats != 1) {
1719 /* round up to next bar */
1725 // cerr << bbt << endl;
1727 (*i)->set_start (bbt);
1729 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1731 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1732 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1734 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1736 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1743 recompute_map (true);
1747 PropertyChanged (PropertyChange ());
1750 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1751 * pos can be -ve, if required.
1754 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1756 Glib::RWLock::ReaderLock lm (lock);
1757 Metrics::const_iterator next_tempo;
1758 const TempoSection* tempo;
1760 /* Find the starting tempo metric */
1762 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1764 const TempoSection* t;
1766 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1768 /* This is a bit of a hack, but pos could be -ve, and if it is,
1769 we consider the initial metric changes (at time 0) to actually
1770 be in effect at pos.
1773 framepos_t f = (*next_tempo)->frame ();
1775 if (pos < 0 && f == 0) {
1789 tempo -> the Tempo for "pos"
1790 next_tempo -> first tempo after "pos", possibly metrics.end()
1793 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1794 pos, beats, *((Tempo*)tempo), tempo->frame()));
1798 /* Distance to the end of this section in frames */
1799 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1801 /* Distance to the end in beats */
1802 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1804 /* Amount to subtract this time */
1805 double const delta = min (distance_beats, beats);
1807 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1808 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1809 distance_frames, distance_beats));
1813 pos += delta * tempo->frames_per_beat (_frame_rate);
1815 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1817 /* step forwards to next tempo section */
1819 if (next_tempo != metrics.end()) {
1821 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1823 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1824 *((Tempo*)tempo), tempo->frame(),
1825 tempo->frames_per_beat (_frame_rate)));
1827 while (next_tempo != metrics.end ()) {
1831 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1841 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1843 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1845 Glib::RWLock::ReaderLock lm (lock);
1846 Metrics::const_reverse_iterator prev_tempo;
1847 const TempoSection* tempo = 0;
1849 /* Find the starting tempo metric */
1851 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1853 const TempoSection* t;
1855 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1857 /* This is a bit of a hack, but pos could be -ve, and if it is,
1858 we consider the initial metric changes (at time 0) to actually
1859 be in effect at pos.
1862 framepos_t f = (*prev_tempo)->frame ();
1864 if (pos < 0 && f == 0) {
1868 /* this is slightly more complex than the forward case
1869 because we reach the tempo in effect at pos after
1870 passing through pos (rather before, as in the
1871 forward case). having done that, we then need to
1872 keep going to get the previous tempo (or
1878 /* first tempo with position at or
1882 } else if (f < pos) {
1883 /* some other tempo section that
1884 is even earlier than 'tempo'
1892 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1893 pos, beats, *((Tempo*)tempo), tempo->frame(),
1894 prev_tempo == metrics.rend()));
1898 tempo -> the Tempo for "pos"
1899 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1904 /* Distance to the start of this section in frames */
1905 framecnt_t distance_frames = (pos - tempo->frame());
1907 /* Distance to the start in beats */
1908 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1910 /* Amount to subtract this time */
1911 double const sub = min (distance_beats, beats);
1913 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1914 tempo->frame(), distance_frames, distance_beats));
1918 pos -= sub * tempo->frames_per_beat (_frame_rate);
1920 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1921 (prev_tempo == metrics.rend())));
1923 /* step backwards to prior TempoSection */
1925 if (prev_tempo != metrics.rend()) {
1927 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1929 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1930 *((Tempo*)tempo), tempo->frame(),
1931 tempo->frames_per_beat (_frame_rate)));
1933 while (prev_tempo != metrics.rend ()) {
1937 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1942 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
1950 /** Add the BBT interval op to pos and return the result */
1952 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
1954 Glib::RWLock::ReaderLock lm (lock);
1955 Metrics::const_iterator i;
1956 const MeterSection* meter;
1957 const MeterSection* m;
1958 const TempoSection* tempo;
1959 const TempoSection* t;
1960 double frames_per_beat;
1962 meter = &first_meter ();
1963 tempo = &first_tempo ();
1968 /* find the starting metrics for tempo & meter */
1970 for (i = metrics.begin(); i != metrics.end(); ++i) {
1972 if ((*i)->frame() > pos) {
1976 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1978 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1985 meter -> the Meter for "pos"
1986 tempo -> the Tempo for "pos"
1987 i -> for first new metric after "pos", possibly metrics.end()
1990 /* now comes the complicated part. we have to add one beat a time,
1991 checking for a new metric on every beat.
1994 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2003 /* check if we need to use a new metric section: has adding frames moved us
2004 to or after the start of the next metric section? in which case, use it.
2007 if (i != metrics.end()) {
2008 if ((*i)->frame() <= pos) {
2010 /* about to change tempo or meter, so add the
2011 * number of frames for the bars we've just
2012 * traversed before we change the
2013 * frames_per_beat value.
2016 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2019 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2021 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2025 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2032 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2038 /* given the current meter, have we gone past the end of the bar ? */
2043 /* check if we need to use a new metric section: has adding frames moved us
2044 to or after the start of the next metric section? in which case, use it.
2047 if (i != metrics.end()) {
2048 if ((*i)->frame() <= pos) {
2050 /* about to change tempo or meter, so add the
2051 * number of frames for the beats we've just
2052 * traversed before we change the
2053 * frames_per_beat value.
2056 pos += llrint (beats * frames_per_beat);
2059 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2061 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2065 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2070 pos += llrint (beats * frames_per_beat);
2073 if (op.ticks >= BBT_Time::ticks_per_beat) {
2074 pos += llrint (frames_per_beat + /* extra beat */
2075 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2076 (double) BBT_Time::ticks_per_beat)));
2078 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2085 /** Count the number of beats that are equivalent to distance when going forward,
2089 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2091 Glib::RWLock::ReaderLock lm (lock);
2092 Metrics::const_iterator next_tempo;
2093 const TempoSection* tempo;
2095 /* Find the relevant initial tempo metric */
2097 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2099 const TempoSection* t;
2101 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2103 if ((*next_tempo)->frame() > pos) {
2113 tempo -> the Tempo for "pos"
2114 next_tempo -> the next tempo after "pos", possibly metrics.end()
2117 Evoral::MusicalTime beats = 0;
2121 /* End of this section */
2122 framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
2124 /* Distance to the end in frames */
2125 framecnt_t const distance_to_end = end - pos;
2127 /* Amount to subtract this time */
2128 double const sub = min (distance, distance_to_end);
2133 beats += sub / tempo->frames_per_beat (_frame_rate);
2135 /* Move on if there's anything to move to */
2137 if (next_tempo != metrics.end()) {
2139 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2141 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2142 *((Tempo*)tempo), tempo->frame(),
2143 tempo->frames_per_beat (_frame_rate)));
2145 while (next_tempo != metrics.end ()) {
2149 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2159 TempoMap::BBTPointList::const_iterator
2160 TempoMap::bbt_before_or_at (framepos_t pos)
2162 /* CALLER MUST HOLD READ LOCK */
2164 BBTPointList::const_iterator i;
2166 i = lower_bound (_map.begin(), _map.end(), pos);
2167 assert (i != _map.end());
2168 if ((*i).frame > pos) {
2169 assert (i != _map.begin());
2176 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2181 TempoMap::BBTPointList::const_iterator
2182 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2184 BBTPointList::const_iterator i;
2187 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2188 assert (i != _map.end());
2189 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2190 assert (i != _map.begin());
2196 TempoMap::BBTPointList::const_iterator
2197 TempoMap::bbt_after_or_at (framepos_t pos)
2199 /* CALLER MUST HOLD READ LOCK */
2201 BBTPointList::const_iterator i;
2203 if (_map.back().frame == pos) {
2205 assert (i != _map.begin());
2210 i = upper_bound (_map.begin(), _map.end(), pos);
2211 assert (i != _map.end());
2215 /** Compare the time of this with that of another MetricSection.
2216 * @param with_bbt True to compare using start(), false to use frame().
2217 * @return -1 for less than, 0 for equal, 1 for greater than.
2221 MetricSection::compare (const MetricSection& other) const
2223 if (start() == other.start()) {
2225 } else if (start() < other.start()) {
2236 MetricSection::operator== (const MetricSection& other) const
2238 return compare (other) == 0;
2242 MetricSection::operator!= (const MetricSection& other) const
2244 return compare (other) != 0;
2248 operator<< (std::ostream& o, const Meter& m) {
2249 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2253 operator<< (std::ostream& o, const Tempo& t) {
2254 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2258 operator<< (std::ostream& o, const MetricSection& section) {
2260 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2262 const TempoSection* ts;
2263 const MeterSection* ms;
2265 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2266 o << *((Tempo*) ts);
2267 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2268 o << *((Meter*) ms);