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 = 0; /* (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 in the same bar as the new one, and remove it before adding
406 the new one. Note that this means that if we find a matching,
407 existing section, we can break out of the loop since we're
408 guaranteed that there is only one such match.
411 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
413 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
414 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
416 if (iter_is_tempo && insert_is_tempo) {
420 if ((*i)->start().bars == section->start().bars &&
421 (*i)->start().beats == section->start().beats) {
423 if (!(*i)->movable()) {
425 /* can't (re)move this section, so overwrite
426 * its data content (but not its properties as
430 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
438 } else if (!iter_is_tempo && !insert_is_tempo) {
442 if ((*i)->start().bars == section->start().bars) {
444 if (!(*i)->movable()) {
446 /* can't (re)move this section, so overwrite
447 * its data content (but not its properties as
451 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
461 /* non-matching types, so we don't care */
465 /* Add the given MetricSection, if we didn't just reset an existing
473 for (i = metrics.begin(); i != metrics.end(); ++i) {
474 if ((*i)->start() > section->start()) {
479 metrics.insert (i, section);
484 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
486 const TempoSection& first (first_tempo());
488 if (ts.start() != first.start()) {
489 remove_tempo (ts, false);
490 add_tempo (tempo, where);
493 Glib::RWLock::WriterLock lm (lock);
494 /* cannot move the first tempo section */
495 *((Tempo*)&first) = tempo;
496 recompute_map (false);
500 PropertyChanged (PropertyChange ());
504 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
507 Glib::RWLock::WriterLock lm (lock);
509 /* new tempos always start on a beat */
512 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
514 /* find the meter to use to set the bar offset of this
518 const Meter* meter = &first_meter();
520 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
521 at something, because we insert the default tempo and meter during
522 TempoMap construction.
524 now see if we can find better candidates.
527 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
529 const MeterSection* m;
531 if (where < (*i)->start()) {
535 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
540 ts->update_bar_offset_from_bbt (*meter);
546 recompute_map (false);
550 PropertyChanged (PropertyChange ());
554 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
556 const MeterSection& first (first_meter());
558 if (ms.start() != first.start()) {
559 remove_meter (ms, false);
560 add_meter (meter, where);
563 Glib::RWLock::WriterLock lm (lock);
564 /* cannot move the first meter section */
565 *((Meter*)&first) = meter;
566 recompute_map (true);
570 PropertyChanged (PropertyChange ());
574 TempoMap::add_meter (const Meter& meter, BBT_Time where)
577 Glib::RWLock::WriterLock lm (lock);
579 /* a new meter always starts a new bar on the first beat. so
580 round the start time appropriately. remember that
581 `where' is based on the existing tempo map, not
582 the result after we insert the new meter.
586 if (where.beats != 1) {
591 /* new meters *always* start on a beat. */
594 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
595 recompute_map (true);
600 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
605 PropertyChanged (PropertyChange ());
609 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
611 Tempo newtempo (beats_per_minute, note_type);
614 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
615 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
617 Glib::RWLock::WriterLock lm (lock);
618 *((Tempo*) t) = newtempo;
619 recompute_map (false);
621 PropertyChanged (PropertyChange ());
628 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
630 Tempo newtempo (beats_per_minute, note_type);
636 /* find the TempoSection immediately preceding "where"
639 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
641 if ((*i)->frame() > where) {
647 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
657 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
667 Glib::RWLock::WriterLock lm (lock);
668 /* cannot move the first tempo section */
669 *((Tempo*)prev) = newtempo;
670 recompute_map (false);
673 PropertyChanged (PropertyChange ());
677 TempoMap::first_meter () const
679 const MeterSection *m = 0;
681 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
682 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
687 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
693 TempoMap::first_tempo () const
695 const TempoSection *t = 0;
697 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
698 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
703 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
709 TempoMap::require_map_to (framepos_t pos)
711 Glib::RWLock::WriterLock lm (lock);
713 if (_map.empty() || _map.back().frame < pos) {
719 TempoMap::require_map_to (const BBT_Time& bbt)
721 Glib::RWLock::WriterLock lm (lock);
723 /* since we have no idea where BBT is if its off the map, see the last
724 * point in the map is past BBT, and if not add an arbitrary amount of
728 int additional_minutes = 1;
731 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
734 /* add some more distance, using bigger steps each time */
735 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
736 additional_minutes *= 2;
741 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
743 /* CALLER MUST HOLD WRITE LOCK */
745 MeterSection* meter = 0;
746 TempoSection* tempo = 0;
747 double current_frame;
749 Metrics::iterator next_metric;
753 /* we will actually stop once we hit
759 if (!_map.empty ()) {
760 /* never allow the map to be shortened */
761 end = max (end, _map.back().frame);
765 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
767 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
770 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
776 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
779 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
785 /* assumes that the first meter & tempo are at frame zero */
787 meter->set_frame (0);
788 tempo->set_frame (0);
790 /* assumes that the first meter & tempo are at 1|1|0 */
795 if (reassign_tempo_bbt) {
797 MeterSection* rmeter = meter;
799 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
801 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
806 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
808 /* reassign the BBT time of this tempo section
809 * based on its bar offset position.
812 ts->update_bbt_time_from_bar_offset (*rmeter);
814 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
817 fatal << _("programming error: unhandled MetricSection type") << endmsg;
823 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
825 next_metric = metrics.begin();
826 ++next_metric; // skip meter (or tempo)
827 ++next_metric; // skip tempo (or meter)
831 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
832 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
835 /* silly call from Session::process() during startup
840 _extend_map (tempo, meter, next_metric, current, current_frame, end);
844 TempoMap::extend_map (framepos_t end)
846 /* CALLER MUST HOLD WRITE LOCK */
849 recompute_map (false, end);
853 BBTPointList::const_iterator i = _map.end();
854 Metrics::iterator next_metric;
858 BBT_Time last_metric_start;
860 if ((*i).tempo->frame() > (*i).meter->frame()) {
861 last_metric_start = (*i).tempo->start();
863 last_metric_start = (*i).meter->start();
866 /* find the metric immediately after the tempo + meter sections for the
867 * last point in the map
870 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
871 if ((*next_metric)->start() > last_metric_start) {
876 /* we cast away const here because this is the one place where we need
877 * to actually modify the frame time of each metric section.
880 _extend_map (const_cast<TempoSection*> ((*i).tempo),
881 const_cast<MeterSection*> ((*i).meter),
882 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
886 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
887 Metrics::iterator next_metric,
888 BBT_Time current, framepos_t current_frame, framepos_t end)
890 /* CALLER MUST HOLD WRITE LOCK */
894 double divisions_per_bar;
896 framepos_t bar_start_frame;
898 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
900 if (current.beats == 1) {
901 bar_start_frame = current_frame;
906 divisions_per_bar = meter->divisions_per_bar ();
907 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
909 while (current_frame < end) {
912 current_frame += beat_frames;
914 if (current.beats > meter->divisions_per_bar()) {
919 if (next_metric != metrics.end()) {
921 /* no operator >= so invert operator < */
923 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
925 if (!(current < (*next_metric)->start())) {
928 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
932 /* new tempo section: if its on a beat,
933 * we don't have to do anything other
934 * than recompute various distances,
935 * done further below as we transition
936 * the next metric section.
938 * if its not on the beat, we have to
939 * compute the duration of the beat it
940 * is within, which will be different
941 * from the preceding following ones
942 * since it takes part of its duration
943 * from the preceding tempo and part
944 * from this new tempo.
947 if (tempo->start().ticks != 0) {
949 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
951 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
952 tempo->start(), current_frame, tempo->bar_offset()));
954 /* back up to previous beat */
955 current_frame -= beat_frames;
957 /* set tempo section location
958 * based on offset from last
961 tempo->set_frame (bar_start_frame +
962 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
964 /* advance to the location of
965 * the new (adjusted) beat. do
966 * this by figuring out the
967 * offset within the beat that
968 * would have been there
970 * change. then stretch the
974 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
976 current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
978 /* next metric doesn't have to
979 * match this precisely to
982 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
986 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
987 tempo->start(), current_frame));
988 tempo->set_frame (current_frame);
991 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
995 /* new meter section: always defines the
999 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
1000 meter->start(), current, current_frame));
1002 assert (current.beats == 1);
1004 meter->set_frame (current_frame);
1007 divisions_per_bar = meter->divisions_per_bar ();
1008 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1010 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1011 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
1015 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1016 /* same position so go back and set this one up before advancing
1023 if (current.beats == 1) {
1024 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1025 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
1026 bar_start_frame = current_frame;
1028 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1029 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
1032 if (next_metric == metrics.end()) {
1033 /* no more metrics - we've timestamped them all, stop here */
1034 if (end == max_framepos) {
1035 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
1036 current.bars, current.beats, current_frame));
1044 TempoMap::metric_at (framepos_t frame) const
1046 Glib::RWLock::ReaderLock lm (lock);
1047 TempoMetric m (first_meter(), first_tempo());
1051 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1052 at something, because we insert the default tempo and meter during
1053 TempoMap construction.
1055 now see if we can find better candidates.
1058 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1060 if ((*i)->frame() > frame) {
1064 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1065 m.set_tempo (*tempo);
1066 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1067 m.set_meter (*meter);
1070 m.set_frame ((*i)->frame ());
1071 m.set_start ((*i)->start ());
1078 TempoMap::metric_at (BBT_Time bbt) const
1080 Glib::RWLock::ReaderLock lm (lock);
1081 TempoMetric m (first_meter(), first_tempo());
1085 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1086 at something, because we insert the default tempo and meter during
1087 TempoMap construction.
1089 now see if we can find better candidates.
1092 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1094 BBT_Time section_start ((*i)->start());
1096 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1100 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1101 m.set_tempo (*tempo);
1102 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1103 m.set_meter (*meter);
1106 m.set_frame ((*i)->frame ());
1107 m.set_start (section_start);
1114 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1116 require_map_to (frame);
1118 Glib::RWLock::ReaderLock lm (lock);
1124 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1128 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1132 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1134 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1137 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1140 if (_map.empty() || _map.back().frame < frame) {
1141 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1144 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1148 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1150 /* CALLER MUST HOLD READ LOCK */
1152 bbt.bars = (*i).bar;
1153 bbt.beats = (*i).beat;
1155 if ((*i).frame == frame) {
1158 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1159 BBT_Time::ticks_per_beat);
1164 TempoMap::frame_time (const BBT_Time& bbt)
1167 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1171 if (bbt.beats < 1) {
1172 throw std::logic_error ("beats are counted from one");
1175 require_map_to (bbt);
1177 Glib::RWLock::ReaderLock lm (lock);
1179 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1180 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1182 if (bbt.ticks != 0) {
1183 return ((*e).frame - (*s).frame) +
1184 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1186 return ((*e).frame - (*s).frame);
1191 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1193 Glib::RWLock::ReaderLock lm (lock);
1194 framecnt_t frames = 0;
1197 bbt_time (pos, when);
1198 frames = bbt_duration_at_unlocked (when, bbt,dir);
1204 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1206 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1210 /* round back to the previous precise beat */
1211 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1212 BBTPointList::const_iterator start (wi);
1213 double tick_frames = 0;
1215 assert (wi != _map.end());
1217 /* compute how much rounding we did because of non-zero ticks */
1219 if (when.ticks != 0) {
1220 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1226 while (wi != _map.end() && bars < bbt.bars) {
1228 if ((*wi).is_bar()) {
1232 assert (wi != _map.end());
1234 while (wi != _map.end() && beats < bbt.beats) {
1238 assert (wi != _map.end());
1240 /* add any additional frames related to ticks in the added value */
1242 if (bbt.ticks != 0) {
1243 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1246 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1250 TempoMap::round_to_bar (framepos_t fr, int dir)
1252 return round_to_type (fr, dir, Bar);
1256 TempoMap::round_to_beat (framepos_t fr, int dir)
1258 return round_to_type (fr, dir, Beat);
1262 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1264 require_map_to (fr);
1266 Glib::RWLock::ReaderLock lm (lock);
1267 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1269 uint32_t ticks_one_subdivisions_worth;
1270 uint32_t difference;
1272 bbt_time (fr, the_beat, i);
1274 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1275 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1277 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1281 /* round to next (even if we're on a subdivision */
1283 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1286 /* right on the subdivision, so the difference is just the subdivision ticks */
1287 the_beat.ticks += ticks_one_subdivisions_worth;
1290 /* not on subdivision, compute distance to next subdivision */
1292 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1295 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1296 assert (i != _map.end());
1298 assert (i != _map.end());
1299 the_beat.ticks -= BBT_Time::ticks_per_beat;
1303 } else if (dir < 0) {
1305 /* round to previous (even if we're on a subdivision) */
1307 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1310 /* right on the subdivision, so the difference is just the subdivision ticks */
1311 difference = ticks_one_subdivisions_worth;
1313 /* not on subdivision, compute distance to previous subdivision, which
1314 is just the modulus.
1320 if (the_beat.ticks < difference) {
1321 if (i == _map.begin()) {
1322 /* can't go backwards from wherever pos is, so just return it */
1326 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1328 the_beat.ticks -= difference;
1332 /* round to nearest */
1336 /* compute the distance to the previous and next subdivision */
1338 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1340 /* closer to the next subdivision, so shift forward */
1342 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1344 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1346 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1347 assert (i != _map.end());
1349 assert (i != _map.end());
1350 the_beat.ticks -= BBT_Time::ticks_per_beat;
1351 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1354 } else if (rem > 0) {
1356 /* closer to previous subdivision, so shift backward */
1358 if (rem > the_beat.ticks) {
1359 if (i == _map.begin()) {
1360 /* can't go backwards past zero, so ... */
1363 /* step back to previous beat */
1365 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1366 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1368 the_beat.ticks = lrint (the_beat.ticks - rem);
1369 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1372 /* on the subdivision, do nothing */
1376 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1377 (*i).tempo->frames_per_beat (_frame_rate);
1381 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1383 require_map_to (frame);
1385 Glib::RWLock::ReaderLock lm (lock);
1386 BBTPointList::const_iterator fi;
1389 fi = bbt_after_or_at (frame);
1391 fi = bbt_before_or_at (frame);
1394 assert (fi != _map.end());
1396 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to %6 in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame,
1397 (type == Bar ? "bar" : "beat")));
1402 /* find bar previous to 'frame' */
1404 if (fi == _map.begin()) {
1408 if ((*fi).is_bar() && (*fi).frame == frame) {
1412 while (!(*fi).is_bar()) {
1413 if (fi == _map.begin()) {
1418 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1419 (*fi).bar, (*fi).beat, (*fi).frame));
1422 } else if (dir > 0) {
1424 /* find bar following 'frame' */
1426 if ((*fi).is_bar() && (*fi).frame == frame) {
1430 while (!(*fi).is_bar()) {
1432 if (fi == _map.end()) {
1438 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1439 (*fi).bar, (*fi).beat, (*fi).frame));
1444 /* true rounding: find nearest bar */
1446 BBTPointList::const_iterator prev = fi;
1447 BBTPointList::const_iterator next = fi;
1449 if ((*fi).frame == frame) {
1453 while ((*prev).beat != 1) {
1454 if (prev == _map.begin()) {
1460 while ((next != _map.end()) && (*next).beat != 1) {
1464 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1465 return (*prev).frame;
1467 return (*next).frame;
1477 if (fi == _map.begin()) {
1481 if ((*fi).frame >= frame) {
1482 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1485 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1486 (*fi).bar, (*fi).beat, (*fi).frame));
1488 } else if (dir > 0) {
1489 if ((*fi).frame <= frame) {
1490 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1493 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1494 (*fi).bar, (*fi).beat, (*fi).frame));
1497 /* find beat nearest to frame */
1498 if ((*fi).frame == frame) {
1502 BBTPointList::const_iterator prev = fi;
1503 BBTPointList::const_iterator next = fi;
1505 /* fi is already the beat before_or_at frame, and
1506 we've just established that its not at frame, so its
1507 the beat before frame.
1511 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1512 return (*prev).frame;
1514 return (*next).frame;
1526 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1527 TempoMap::BBTPointList::const_iterator& end,
1528 framepos_t lower, framepos_t upper)
1531 Glib::RWLock::WriterLock lm (lock);
1532 if (_map.empty() || (_map.back().frame < upper)) {
1533 recompute_map (false, upper);
1537 begin = lower_bound (_map.begin(), _map.end(), lower);
1538 end = upper_bound (_map.begin(), _map.end(), upper);
1542 TempoMap::tempo_section_at (framepos_t frame) const
1544 Glib::RWLock::ReaderLock lm (lock);
1545 Metrics::const_iterator i;
1546 TempoSection* prev = 0;
1548 for (i = metrics.begin(); i != metrics.end(); ++i) {
1551 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1553 if ((*i)->frame() > frame) {
1569 TempoMap::tempo_at (framepos_t frame) const
1571 TempoMetric m (metric_at (frame));
1577 TempoMap::meter_at (framepos_t frame) const
1579 TempoMetric m (metric_at (frame));
1584 TempoMap::get_state ()
1586 Metrics::const_iterator i;
1587 XMLNode *root = new XMLNode ("TempoMap");
1590 Glib::RWLock::ReaderLock lm (lock);
1591 for (i = metrics.begin(); i != metrics.end(); ++i) {
1592 root->add_child_nocopy ((*i)->get_state());
1600 TempoMap::set_state (const XMLNode& node, int /*version*/)
1603 Glib::RWLock::WriterLock lm (lock);
1606 XMLNodeConstIterator niter;
1607 Metrics old_metrics (metrics);
1608 MeterSection* last_meter = 0;
1611 nlist = node.children();
1613 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1614 XMLNode* child = *niter;
1616 if (child->name() == TempoSection::xml_state_node_name) {
1619 TempoSection* ts = new TempoSection (*child);
1620 metrics.push_back (ts);
1622 if (ts->bar_offset() < 0.0) {
1624 ts->update_bar_offset_from_bbt (*last_meter);
1629 catch (failed_constructor& err){
1630 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1631 metrics = old_metrics;
1635 } else if (child->name() == MeterSection::xml_state_node_name) {
1638 MeterSection* ms = new MeterSection (*child);
1639 metrics.push_back (ms);
1643 catch (failed_constructor& err) {
1644 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1645 metrics = old_metrics;
1651 if (niter == nlist.end()) {
1652 MetricSectionSorter cmp;
1656 recompute_map (true, -1);
1659 PropertyChanged (PropertyChange ());
1665 TempoMap::dump (std::ostream& o) const
1667 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1668 const MeterSection* m;
1669 const TempoSection* t;
1671 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1673 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1674 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? "
1675 << t->movable() << ')' << endl;
1676 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1677 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1678 << " (movable? " << m->movable() << ')' << endl;
1684 TempoMap::n_tempos() const
1686 Glib::RWLock::ReaderLock lm (lock);
1689 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1690 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1699 TempoMap::n_meters() const
1701 Glib::RWLock::ReaderLock lm (lock);
1704 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1705 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1714 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1717 Glib::RWLock::WriterLock lm (lock);
1718 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1719 if ((*i)->frame() >= where && (*i)->movable ()) {
1720 (*i)->set_frame ((*i)->frame() + amount);
1724 /* now reset the BBT time of all metrics, based on their new
1725 * audio time. This is the only place where we do this reverse
1729 Metrics::iterator i;
1730 const MeterSection* meter;
1731 const TempoSection* tempo;
1735 meter = &first_meter ();
1736 tempo = &first_tempo ();
1741 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1744 MetricSection* prev = 0;
1746 for (i = metrics.begin(); i != metrics.end(); ++i) {
1749 TempoMetric metric (*meter, *tempo);
1752 metric.set_start (prev->start());
1753 metric.set_frame (prev->frame());
1755 // metric will be at frames=0 bbt=1|1|0 by default
1756 // which is correct for our purpose
1759 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1760 bbt_time ((*i)->frame(), bbt, bi);
1762 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1768 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1769 /* round up to next beat */
1775 if (bbt.beats != 1) {
1776 /* round up to next bar */
1782 // cerr << bbt << endl;
1784 (*i)->set_start (bbt);
1786 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1788 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1789 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1791 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1793 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1800 recompute_map (true);
1804 PropertyChanged (PropertyChange ());
1807 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1808 * pos can be -ve, if required.
1811 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1813 Glib::RWLock::ReaderLock lm (lock);
1814 Metrics::const_iterator next_tempo;
1815 const TempoSection* tempo;
1817 /* Find the starting tempo metric */
1819 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1821 const TempoSection* t;
1823 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1825 /* This is a bit of a hack, but pos could be -ve, and if it is,
1826 we consider the initial metric changes (at time 0) to actually
1827 be in effect at pos.
1830 framepos_t f = (*next_tempo)->frame ();
1832 if (pos < 0 && f == 0) {
1846 tempo -> the Tempo for "pos"
1847 next_tempo -> first tempo after "pos", possibly metrics.end()
1850 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1851 pos, beats, *((Tempo*)tempo), tempo->frame()));
1855 /* Distance to the end of this section in frames */
1856 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1858 /* Distance to the end in beats */
1859 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1861 /* Amount to subtract this time */
1862 double const delta = min (distance_beats, beats);
1864 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1865 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1866 distance_frames, distance_beats));
1870 pos += delta * tempo->frames_per_beat (_frame_rate);
1872 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1874 /* step forwards to next tempo section */
1876 if (next_tempo != metrics.end()) {
1878 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1880 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1881 *((Tempo*)tempo), tempo->frame(),
1882 tempo->frames_per_beat (_frame_rate)));
1884 while (next_tempo != metrics.end ()) {
1888 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1898 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1900 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1902 Glib::RWLock::ReaderLock lm (lock);
1903 Metrics::const_reverse_iterator prev_tempo;
1904 const TempoSection* tempo = 0;
1906 /* Find the starting tempo metric */
1908 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1910 const TempoSection* t;
1912 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1914 /* This is a bit of a hack, but pos could be -ve, and if it is,
1915 we consider the initial metric changes (at time 0) to actually
1916 be in effect at pos.
1919 framepos_t f = (*prev_tempo)->frame ();
1921 if (pos < 0 && f == 0) {
1925 /* this is slightly more complex than the forward case
1926 because we reach the tempo in effect at pos after
1927 passing through pos (rather before, as in the
1928 forward case). having done that, we then need to
1929 keep going to get the previous tempo (or
1935 /* first tempo with position at or
1939 } else if (f < pos) {
1940 /* some other tempo section that
1941 is even earlier than 'tempo'
1949 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1950 pos, beats, *((Tempo*)tempo), tempo->frame(),
1951 prev_tempo == metrics.rend()));
1955 tempo -> the Tempo for "pos"
1956 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1961 /* Distance to the start of this section in frames */
1962 framecnt_t distance_frames = (pos - tempo->frame());
1964 /* Distance to the start in beats */
1965 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1967 /* Amount to subtract this time */
1968 double const sub = min (distance_beats, beats);
1970 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1971 tempo->frame(), distance_frames, distance_beats));
1975 pos -= sub * tempo->frames_per_beat (_frame_rate);
1977 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1978 (prev_tempo == metrics.rend())));
1980 /* step backwards to prior TempoSection */
1982 if (prev_tempo != metrics.rend()) {
1984 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1986 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1987 *((Tempo*)tempo), tempo->frame(),
1988 tempo->frames_per_beat (_frame_rate)));
1990 while (prev_tempo != metrics.rend ()) {
1994 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1999 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
2007 /** Add the BBT interval op to pos and return the result */
2009 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2011 Glib::RWLock::ReaderLock lm (lock);
2012 Metrics::const_iterator i;
2013 const MeterSection* meter;
2014 const MeterSection* m;
2015 const TempoSection* tempo;
2016 const TempoSection* t;
2017 double frames_per_beat;
2018 framepos_t effective_pos = max (pos, (framepos_t) 0);
2020 meter = &first_meter ();
2021 tempo = &first_tempo ();
2026 /* find the starting metrics for tempo & meter */
2028 for (i = metrics.begin(); i != metrics.end(); ++i) {
2030 if ((*i)->frame() > effective_pos) {
2034 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2036 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2043 meter -> the Meter for "pos"
2044 tempo -> the Tempo for "pos"
2045 i -> for first new metric after "pos", possibly metrics.end()
2048 /* now comes the complicated part. we have to add one beat a time,
2049 checking for a new metric on every beat.
2052 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2061 /* check if we need to use a new metric section: has adding frames moved us
2062 to or after the start of the next metric section? in which case, use it.
2065 if (i != metrics.end()) {
2066 if ((*i)->frame() <= pos) {
2068 /* about to change tempo or meter, so add the
2069 * number of frames for the bars we've just
2070 * traversed before we change the
2071 * frames_per_beat value.
2074 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2077 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2079 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2083 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2090 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2096 /* given the current meter, have we gone past the end of the bar ? */
2101 /* check if we need to use a new metric section: has adding frames moved us
2102 to or after the start of the next metric section? in which case, use it.
2105 if (i != metrics.end()) {
2106 if ((*i)->frame() <= pos) {
2108 /* about to change tempo or meter, so add the
2109 * number of frames for the beats we've just
2110 * traversed before we change the
2111 * frames_per_beat value.
2114 pos += llrint (beats * frames_per_beat);
2117 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2119 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2123 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2128 pos += llrint (beats * frames_per_beat);
2131 if (op.ticks >= BBT_Time::ticks_per_beat) {
2132 pos += llrint (frames_per_beat + /* extra beat */
2133 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2134 (double) BBT_Time::ticks_per_beat)));
2136 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2143 /** Count the number of beats that are equivalent to distance when going forward,
2147 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2149 Glib::RWLock::ReaderLock lm (lock);
2150 Metrics::const_iterator next_tempo;
2151 const TempoSection* tempo = 0;
2152 framepos_t effective_pos = max (pos, (framepos_t) 0);
2154 /* Find the relevant initial tempo metric */
2156 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2158 const TempoSection* t;
2160 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2162 if ((*next_tempo)->frame() > effective_pos) {
2172 tempo -> the Tempo for "pos"
2173 next_tempo -> the next tempo after "pos", possibly metrics.end()
2178 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
2179 pos, distance, *((Tempo*)tempo), tempo->frame()));
2181 Evoral::MusicalTime beats = 0;
2185 /* End of this section */
2186 framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
2188 /* Distance to the end in frames */
2189 framecnt_t const distance_to_end = end - pos;
2191 /* Amount to subtract this time */
2192 double const sub = min (distance, distance_to_end);
2194 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
2195 distance_to_end, sub));
2201 beats += sub / tempo->frames_per_beat (_frame_rate);
2203 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
2204 pos, beats, distance));
2206 /* Move on if there's anything to move to */
2208 if (next_tempo != metrics.end()) {
2210 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2212 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2213 *((Tempo*)tempo), tempo->frame(),
2214 tempo->frames_per_beat (_frame_rate)));
2216 while (next_tempo != metrics.end ()) {
2220 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2225 if (next_tempo == metrics.end()) {
2226 DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
2228 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
2229 **next_tempo, (*next_tempo)->frame()));
2239 TempoMap::BBTPointList::const_iterator
2240 TempoMap::bbt_before_or_at (framepos_t pos)
2242 /* CALLER MUST HOLD READ LOCK */
2244 BBTPointList::const_iterator i;
2247 /* not really correct, but we should catch pos < 0 at a higher
2250 return _map.begin();
2253 i = lower_bound (_map.begin(), _map.end(), pos);
2254 assert (i != _map.end());
2255 if ((*i).frame > pos) {
2256 assert (i != _map.begin());
2263 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2268 TempoMap::BBTPointList::const_iterator
2269 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2271 BBTPointList::const_iterator i;
2274 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2275 assert (i != _map.end());
2276 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2277 assert (i != _map.begin());
2283 TempoMap::BBTPointList::const_iterator
2284 TempoMap::bbt_after_or_at (framepos_t pos)
2286 /* CALLER MUST HOLD READ LOCK */
2288 BBTPointList::const_iterator i;
2290 if (_map.back().frame == pos) {
2292 assert (i != _map.begin());
2297 i = upper_bound (_map.begin(), _map.end(), pos);
2298 assert (i != _map.end());
2303 operator<< (std::ostream& o, const Meter& m) {
2304 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2308 operator<< (std::ostream& o, const Tempo& t) {
2309 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2313 operator<< (std::ostream& o, const MetricSection& section) {
2315 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2317 const TempoSection* ts;
2318 const MeterSection* ms;
2320 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2321 o << *((Tempo*) ts);
2322 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2323 o << *((Meter*) ms);