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.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
28 #include "evoral/types.hpp"
29 #include "ardour/debug.h"
30 #include "ardour/tempo.h"
36 using namespace ARDOUR;
39 using Timecode::BBT_Time;
41 /* _default tempo is 4/4 qtr=120 */
43 Meter TempoMap::_default_meter (4.0, 4.0);
44 Tempo TempoMap::_default_tempo (120.0);
46 /***********************************************************************/
49 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
51 /* This is tempo- and meter-sensitive. The number it returns
52 is based on the interval between any two lines in the
53 grid that is constructed from tempo and meter sections.
55 The return value IS NOT interpretable in terms of "beats".
58 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
62 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
64 return frames_per_grid (tempo, sr) * _divisions_per_bar;
67 /***********************************************************************/
69 const string TempoSection::xml_state_node_name = "Tempo";
71 TempoSection::TempoSection (const XMLNode& node)
72 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
74 const XMLProperty *prop;
76 LocaleGuard lg (X_("POSIX"));
78 if ((prop = node.property ("start")) == 0) {
79 error << _("TempoSection XML node has no \"start\" property") << endmsg;
80 throw failed_constructor();
83 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
87 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
88 throw failed_constructor();
93 if ((prop = node.property ("beats-per-minute")) == 0) {
94 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
95 throw failed_constructor();
98 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
99 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
100 throw failed_constructor();
103 if ((prop = node.property ("note-type")) == 0) {
104 /* older session, make note type be quarter by default */
107 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
108 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
109 throw failed_constructor();
113 if ((prop = node.property ("movable")) == 0) {
114 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
115 throw failed_constructor();
118 set_movable (string_is_affirmative (prop->value()));
120 if ((prop = node.property ("bar-offset")) == 0) {
123 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
124 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
125 throw failed_constructor();
131 TempoSection::get_state() const
133 XMLNode *root = new XMLNode (xml_state_node_name);
135 LocaleGuard lg (X_("POSIX"));
137 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
141 root->add_property ("start", buf);
142 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
143 root->add_property ("beats-per-minute", buf);
144 snprintf (buf, sizeof (buf), "%f", _note_type);
145 root->add_property ("note-type", buf);
146 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
147 // root->add_property ("bar-offset", buf);
148 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
149 root->add_property ("movable", buf);
156 TempoSection::update_bar_offset_from_bbt (const Meter& m)
158 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
159 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
161 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
165 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
169 if (_bar_offset < 0.0) {
174 new_start.bars = start().bars;
176 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
177 new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
178 new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
180 /* remember the 1-based counting properties of beats */
181 new_start.beats += 1;
183 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
184 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
186 set_start (new_start);
189 /***********************************************************************/
191 const string MeterSection::xml_state_node_name = "Meter";
193 MeterSection::MeterSection (const XMLNode& node)
194 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
196 const XMLProperty *prop;
198 LocaleGuard lg (X_("POSIX"));
200 if ((prop = node.property ("start")) == 0) {
201 error << _("MeterSection XML node has no \"start\" property") << endmsg;
202 throw failed_constructor();
205 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
209 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
210 throw failed_constructor();
215 /* beats-per-bar is old; divisions-per-bar is new */
217 if ((prop = node.property ("divisions-per-bar")) == 0) {
218 if ((prop = node.property ("beats-per-bar")) == 0) {
219 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
220 throw failed_constructor();
224 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
225 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
226 throw failed_constructor();
229 if ((prop = node.property ("note-type")) == 0) {
230 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
231 throw failed_constructor();
234 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
235 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
236 throw failed_constructor();
239 if ((prop = node.property ("movable")) == 0) {
240 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
241 throw failed_constructor();
244 set_movable (string_is_affirmative (prop->value()));
248 MeterSection::get_state() const
250 XMLNode *root = new XMLNode (xml_state_node_name);
252 LocaleGuard lg (X_("POSIX"));
254 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
258 root->add_property ("start", buf);
259 snprintf (buf, sizeof (buf), "%f", _note_type);
260 root->add_property ("note-type", buf);
261 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
262 root->add_property ("divisions-per-bar", buf);
263 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
264 root->add_property ("movable", buf);
269 /***********************************************************************/
271 struct MetricSectionSorter {
272 bool operator() (const MetricSection* a, const MetricSection* b) {
273 return a->start() < b->start();
277 TempoMap::TempoMap (framecnt_t fr)
286 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
287 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
289 t->set_movable (false);
290 m->set_movable (false);
292 /* note: frame time is correct (zero) for both of these */
294 metrics.push_back (t);
295 metrics.push_back (m);
298 TempoMap::~TempoMap ()
303 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
305 bool removed = false;
308 Glib::Threads::RWLock::WriterLock lm (lock);
311 for (i = metrics.begin(); i != metrics.end(); ++i) {
312 if (dynamic_cast<TempoSection*> (*i) != 0) {
313 if (tempo.frame() == (*i)->frame()) {
314 if ((*i)->movable()) {
323 if (removed && complete_operation) {
324 recompute_map (false);
328 if (removed && complete_operation) {
329 PropertyChanged (PropertyChange ());
334 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
336 bool removed = false;
339 Glib::Threads::RWLock::WriterLock lm (lock);
342 for (i = metrics.begin(); i != metrics.end(); ++i) {
343 if (dynamic_cast<MeterSection*> (*i) != 0) {
344 if (tempo.frame() == (*i)->frame()) {
345 if ((*i)->movable()) {
354 if (removed && complete_operation) {
355 recompute_map (true);
359 if (removed && complete_operation) {
360 PropertyChanged (PropertyChange ());
365 TempoMap::do_insert (MetricSection* section)
367 bool need_add = true;
369 assert (section->start().ticks == 0);
371 /* we only allow new meters to be inserted on beat 1 of an existing
375 if (dynamic_cast<MeterSection*>(section)) {
377 /* we need to (potentially) update the BBT times of tempo
378 sections based on this new meter.
381 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
383 BBT_Time corrected = section->start();
387 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
388 section->start(), corrected) << endmsg;
390 section->set_start (corrected);
396 /* Look for any existing MetricSection that is of the same type and
397 in the same bar as the new one, and remove it before adding
398 the new one. Note that this means that if we find a matching,
399 existing section, we can break out of the loop since we're
400 guaranteed that there is only one such match.
403 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
405 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
406 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
408 if (iter_is_tempo && insert_is_tempo) {
412 if ((*i)->start().bars == section->start().bars &&
413 (*i)->start().beats == section->start().beats) {
415 if (!(*i)->movable()) {
417 /* can't (re)move this section, so overwrite
418 * its data content (but not its properties as
422 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
430 } else if (!iter_is_tempo && !insert_is_tempo) {
434 if ((*i)->start().bars == section->start().bars) {
436 if (!(*i)->movable()) {
438 /* can't (re)move this section, so overwrite
439 * its data content (but not its properties as
443 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
453 /* non-matching types, so we don't care */
457 /* Add the given MetricSection, if we didn't just reset an existing
465 for (i = metrics.begin(); i != metrics.end(); ++i) {
466 if ((*i)->start() > section->start()) {
471 metrics.insert (i, section);
476 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
478 const TempoSection& first (first_tempo());
480 if (ts.start() != first.start()) {
481 remove_tempo (ts, false);
482 add_tempo (tempo, where);
485 Glib::Threads::RWLock::WriterLock lm (lock);
486 /* cannot move the first tempo section */
487 *((Tempo*)&first) = tempo;
488 recompute_map (false);
492 PropertyChanged (PropertyChange ());
496 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
499 Glib::Threads::RWLock::WriterLock lm (lock);
501 /* new tempos always start on a beat */
504 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
506 /* find the meter to use to set the bar offset of this
510 const Meter* meter = &first_meter();
512 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
513 at something, because we insert the default tempo and meter during
514 TempoMap construction.
516 now see if we can find better candidates.
519 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
521 const MeterSection* m;
523 if (where < (*i)->start()) {
527 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
532 ts->update_bar_offset_from_bbt (*meter);
538 recompute_map (false);
542 PropertyChanged (PropertyChange ());
546 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
548 const MeterSection& first (first_meter());
550 if (ms.start() != first.start()) {
551 remove_meter (ms, false);
552 add_meter (meter, where);
555 Glib::Threads::RWLock::WriterLock lm (lock);
556 /* cannot move the first meter section */
557 *((Meter*)&first) = meter;
558 recompute_map (true);
562 PropertyChanged (PropertyChange ());
566 TempoMap::add_meter (const Meter& meter, BBT_Time where)
569 Glib::Threads::RWLock::WriterLock lm (lock);
571 /* a new meter always starts a new bar on the first beat. so
572 round the start time appropriately. remember that
573 `where' is based on the existing tempo map, not
574 the result after we insert the new meter.
578 if (where.beats != 1) {
583 /* new meters *always* start on a beat. */
586 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
587 recompute_map (true);
592 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
597 PropertyChanged (PropertyChange ());
601 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
603 Tempo newtempo (beats_per_minute, note_type);
606 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
607 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
609 Glib::Threads::RWLock::WriterLock lm (lock);
610 *((Tempo*) t) = newtempo;
611 recompute_map (false);
613 PropertyChanged (PropertyChange ());
620 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
622 Tempo newtempo (beats_per_minute, note_type);
628 /* find the TempoSection immediately preceding "where"
631 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
633 if ((*i)->frame() > where) {
639 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
649 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
659 Glib::Threads::RWLock::WriterLock lm (lock);
660 /* cannot move the first tempo section */
661 *((Tempo*)prev) = newtempo;
662 recompute_map (false);
665 PropertyChanged (PropertyChange ());
669 TempoMap::first_meter () const
671 const MeterSection *m = 0;
673 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
674 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
679 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
685 TempoMap::first_tempo () const
687 const TempoSection *t = 0;
689 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
690 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
695 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
701 TempoMap::require_map_to (framepos_t pos)
703 Glib::Threads::RWLock::WriterLock lm (lock);
705 if (_map.empty() || _map.back().frame < pos) {
711 TempoMap::require_map_to (const BBT_Time& bbt)
713 Glib::Threads::RWLock::WriterLock lm (lock);
715 /* since we have no idea where BBT is if its off the map, see the last
716 * point in the map is past BBT, and if not add an arbitrary amount of
720 int additional_minutes = 1;
723 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
726 /* add some more distance, using bigger steps each time */
727 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
728 additional_minutes *= 2;
733 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
735 /* CALLER MUST HOLD WRITE LOCK */
737 MeterSection* meter = 0;
738 TempoSection* tempo = 0;
739 double current_frame;
741 Metrics::iterator next_metric;
745 /* we will actually stop once we hit
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 */
887 double current_frame_exact;
888 framepos_t bar_start_frame;
890 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
892 if (current.beats == 1) {
893 bar_start_frame = current_frame;
898 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
899 current_frame_exact = current_frame;
901 while (current_frame < end) {
904 current_frame_exact += beat_frames;
905 current_frame = llrint(current_frame_exact);
907 if (current.beats > meter->divisions_per_bar()) {
912 if (next_metric != metrics.end()) {
914 /* no operator >= so invert operator < */
916 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
918 if (!(current < (*next_metric)->start())) {
921 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
925 /* new tempo section: if its on a beat,
926 * we don't have to do anything other
927 * than recompute various distances,
928 * done further below as we transition
929 * the next metric section.
931 * if its not on the beat, we have to
932 * compute the duration of the beat it
933 * is within, which will be different
934 * from the preceding following ones
935 * since it takes part of its duration
936 * from the preceding tempo and part
937 * from this new tempo.
940 if (tempo->start().ticks != 0) {
942 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
944 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
945 tempo->start(), current_frame, tempo->bar_offset()));
947 /* back up to previous beat */
948 current_frame_exact -= beat_frames;
949 current_frame = llrint(current_frame_exact);
951 /* set tempo section location
952 * based on offset from last
955 tempo->set_frame (bar_start_frame +
956 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
958 /* advance to the location of
959 * the new (adjusted) beat. do
960 * this by figuring out the
961 * offset within the beat that
962 * would have been there
964 * change. then stretch the
968 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
970 current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
971 current_frame = llrint(current_frame_exact);
973 /* next metric doesn't have to
974 * match this precisely to
977 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
981 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
982 tempo->start(), current_frame));
983 tempo->set_frame (current_frame);
986 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
990 /* new meter section: always defines the
994 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
995 meter->start(), current, current_frame));
997 assert (current.beats == 1);
999 meter->set_frame (current_frame);
1002 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1004 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1005 beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
1009 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1010 /* same position so go back and set this one up before advancing
1018 if (current.beats == 1) {
1019 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1020 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
1021 bar_start_frame = current_frame;
1023 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1024 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
1027 if (next_metric == metrics.end()) {
1028 /* no more metrics - we've timestamped them all, stop here */
1029 if (end == max_framepos) {
1030 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
1031 current.bars, current.beats, current_frame));
1039 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1041 Glib::Threads::RWLock::ReaderLock lm (lock);
1042 TempoMetric m (first_meter(), first_tempo());
1044 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1045 at something, because we insert the default tempo and meter during
1046 TempoMap construction.
1048 now see if we can find better candidates.
1051 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1053 if ((*i)->frame() > frame) {
1068 TempoMap::metric_at (BBT_Time bbt) const
1070 Glib::Threads::RWLock::ReaderLock lm (lock);
1071 TempoMetric m (first_meter(), first_tempo());
1073 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1074 at something, because we insert the default tempo and meter during
1075 TempoMap construction.
1077 now see if we can find better candidates.
1080 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1082 BBT_Time section_start ((*i)->start());
1084 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1095 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1097 require_map_to (frame);
1099 Glib::Threads::RWLock::ReaderLock lm (lock);
1105 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1109 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1113 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1115 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1118 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1121 if (_map.empty() || _map.back().frame < frame) {
1122 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1125 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1129 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1131 /* CALLER MUST HOLD READ LOCK */
1133 bbt.bars = (*i).bar;
1134 bbt.beats = (*i).beat;
1136 if ((*i).frame == frame) {
1139 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1140 BBT_Time::ticks_per_beat);
1145 TempoMap::frame_time (const BBT_Time& bbt)
1148 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1152 if (bbt.beats < 1) {
1153 throw std::logic_error ("beats are counted from one");
1156 require_map_to (bbt);
1158 Glib::Threads::RWLock::ReaderLock lm (lock);
1160 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1161 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1163 if (bbt.ticks != 0) {
1164 return ((*e).frame - (*s).frame) +
1165 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1167 return ((*e).frame - (*s).frame);
1172 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1175 bbt_time (pos, when);
1177 Glib::Threads::RWLock::ReaderLock lm (lock);
1178 return bbt_duration_at_unlocked (when, bbt, dir);
1182 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
1184 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1188 /* round back to the previous precise beat */
1189 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1190 BBTPointList::const_iterator start (wi);
1192 assert (wi != _map.end());
1197 while (wi != _map.end() && bars < bbt.bars) {
1199 if ((*wi).is_bar()) {
1203 assert (wi != _map.end());
1205 while (wi != _map.end() && beats < bbt.beats) {
1209 assert (wi != _map.end());
1211 /* add any additional frames related to ticks in the added value */
1213 if (bbt.ticks != 0) {
1214 return ((*wi).frame - (*start).frame) +
1215 (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1217 return ((*wi).frame - (*start).frame);
1222 TempoMap::round_to_bar (framepos_t fr, int dir)
1224 return round_to_type (fr, dir, Bar);
1228 TempoMap::round_to_beat (framepos_t fr, int dir)
1230 return round_to_type (fr, dir, Beat);
1234 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1236 require_map_to (fr);
1238 Glib::Threads::RWLock::ReaderLock lm (lock);
1239 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1241 uint32_t ticks_one_subdivisions_worth;
1242 uint32_t difference;
1244 bbt_time (fr, the_beat, i);
1246 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1247 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1249 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1253 /* round to next (even if we're on a subdivision */
1255 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1258 /* right on the subdivision, so the difference is just the subdivision ticks */
1259 the_beat.ticks += ticks_one_subdivisions_worth;
1262 /* not on subdivision, compute distance to next subdivision */
1264 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1267 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1268 assert (i != _map.end());
1270 assert (i != _map.end());
1271 the_beat.ticks -= BBT_Time::ticks_per_beat;
1275 } else if (dir < 0) {
1277 /* round to previous (even if we're on a subdivision) */
1279 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1282 /* right on the subdivision, so the difference is just the subdivision ticks */
1283 difference = ticks_one_subdivisions_worth;
1285 /* not on subdivision, compute distance to previous subdivision, which
1286 is just the modulus.
1292 if (the_beat.ticks < difference) {
1293 if (i == _map.begin()) {
1294 /* can't go backwards from wherever pos is, so just return it */
1298 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1300 the_beat.ticks -= difference;
1304 /* round to nearest */
1308 /* compute the distance to the previous and next subdivision */
1310 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1312 /* closer to the next subdivision, so shift forward */
1314 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1316 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1318 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1319 assert (i != _map.end());
1321 assert (i != _map.end());
1322 the_beat.ticks -= BBT_Time::ticks_per_beat;
1323 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1326 } else if (rem > 0) {
1328 /* closer to previous subdivision, so shift backward */
1330 if (rem > the_beat.ticks) {
1331 if (i == _map.begin()) {
1332 /* can't go backwards past zero, so ... */
1335 /* step back to previous beat */
1337 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1338 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1340 the_beat.ticks = lrint (the_beat.ticks - rem);
1341 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1344 /* on the subdivision, do nothing */
1348 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1349 (*i).tempo->frames_per_beat (_frame_rate);
1353 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1355 require_map_to (frame);
1357 Glib::Threads::RWLock::ReaderLock lm (lock);
1358 BBTPointList::const_iterator fi;
1361 fi = bbt_after_or_at (frame);
1363 fi = bbt_before_or_at (frame);
1366 assert (fi != _map.end());
1368 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,
1369 (type == Bar ? "bar" : "beat")));
1374 /* find bar previous to 'frame' */
1376 if (fi == _map.begin()) {
1380 if ((*fi).is_bar() && (*fi).frame == frame) {
1384 while (!(*fi).is_bar()) {
1385 if (fi == _map.begin()) {
1390 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1391 (*fi).bar, (*fi).beat, (*fi).frame));
1394 } else if (dir > 0) {
1396 /* find bar following 'frame' */
1398 if ((*fi).is_bar() && (*fi).frame == frame) {
1402 while (!(*fi).is_bar()) {
1404 if (fi == _map.end()) {
1410 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1411 (*fi).bar, (*fi).beat, (*fi).frame));
1416 /* true rounding: find nearest bar */
1418 BBTPointList::const_iterator prev = fi;
1419 BBTPointList::const_iterator next = fi;
1421 if ((*fi).frame == frame) {
1425 while ((*prev).beat != 1) {
1426 if (prev == _map.begin()) {
1432 while ((next != _map.end()) && (*next).beat != 1) {
1436 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1437 return (*prev).frame;
1439 return (*next).frame;
1449 if (fi == _map.begin()) {
1453 if ((*fi).frame >= frame) {
1454 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1457 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1458 (*fi).bar, (*fi).beat, (*fi).frame));
1460 } else if (dir > 0) {
1461 if ((*fi).frame <= frame) {
1462 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1465 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1466 (*fi).bar, (*fi).beat, (*fi).frame));
1469 /* find beat nearest to frame */
1470 if ((*fi).frame == frame) {
1474 BBTPointList::const_iterator prev = fi;
1475 BBTPointList::const_iterator next = fi;
1477 /* fi is already the beat before_or_at frame, and
1478 we've just established that its not at frame, so its
1479 the beat before frame.
1483 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1484 return (*prev).frame;
1486 return (*next).frame;
1498 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1499 TempoMap::BBTPointList::const_iterator& end,
1500 framepos_t lower, framepos_t upper)
1503 Glib::Threads::RWLock::WriterLock lm (lock);
1504 if (_map.empty() || (_map.back().frame < upper)) {
1505 recompute_map (false, upper);
1509 begin = lower_bound (_map.begin(), _map.end(), lower);
1510 end = upper_bound (_map.begin(), _map.end(), upper);
1514 TempoMap::tempo_section_at (framepos_t frame) const
1516 Glib::Threads::RWLock::ReaderLock lm (lock);
1517 Metrics::const_iterator i;
1518 TempoSection* prev = 0;
1520 for (i = metrics.begin(); i != metrics.end(); ++i) {
1523 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1525 if ((*i)->frame() > frame) {
1541 TempoMap::tempo_at (framepos_t frame) const
1543 TempoMetric m (metric_at (frame));
1549 TempoMap::meter_at (framepos_t frame) const
1551 TempoMetric m (metric_at (frame));
1556 TempoMap::get_state ()
1558 Metrics::const_iterator i;
1559 XMLNode *root = new XMLNode ("TempoMap");
1562 Glib::Threads::RWLock::ReaderLock lm (lock);
1563 for (i = metrics.begin(); i != metrics.end(); ++i) {
1564 root->add_child_nocopy ((*i)->get_state());
1572 TempoMap::set_state (const XMLNode& node, int /*version*/)
1575 Glib::Threads::RWLock::WriterLock lm (lock);
1578 XMLNodeConstIterator niter;
1579 Metrics old_metrics (metrics);
1580 MeterSection* last_meter = 0;
1583 nlist = node.children();
1585 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1586 XMLNode* child = *niter;
1588 if (child->name() == TempoSection::xml_state_node_name) {
1591 TempoSection* ts = new TempoSection (*child);
1592 metrics.push_back (ts);
1594 if (ts->bar_offset() < 0.0) {
1596 ts->update_bar_offset_from_bbt (*last_meter);
1601 catch (failed_constructor& err){
1602 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1603 metrics = old_metrics;
1607 } else if (child->name() == MeterSection::xml_state_node_name) {
1610 MeterSection* ms = new MeterSection (*child);
1611 metrics.push_back (ms);
1615 catch (failed_constructor& err) {
1616 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1617 metrics = old_metrics;
1623 if (niter == nlist.end()) {
1624 MetricSectionSorter cmp;
1628 /* check for multiple tempo/meters at the same location, which
1629 ardour2 somehow allowed.
1632 Metrics::iterator prev = metrics.end();
1633 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1634 if (prev != metrics.end()) {
1635 if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
1636 if ((*prev)->start() == (*i)->start()) {
1637 error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1640 } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
1641 if ((*prev)->start() == (*i)->start()) {
1642 error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1650 recompute_map (true, -1);
1653 PropertyChanged (PropertyChange ());
1659 TempoMap::dump (std::ostream& o) const
1661 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1662 const MeterSection* m;
1663 const TempoSection* t;
1665 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1667 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1668 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? "
1669 << t->movable() << ')' << endl;
1670 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1671 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1672 << " (movable? " << m->movable() << ')' << endl;
1678 TempoMap::n_tempos() const
1680 Glib::Threads::RWLock::ReaderLock lm (lock);
1683 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1684 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1693 TempoMap::n_meters() const
1695 Glib::Threads::RWLock::ReaderLock lm (lock);
1698 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1699 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1708 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1711 Glib::Threads::RWLock::WriterLock lm (lock);
1712 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1713 if ((*i)->frame() >= where && (*i)->movable ()) {
1714 (*i)->set_frame ((*i)->frame() + amount);
1718 /* now reset the BBT time of all metrics, based on their new
1719 * audio time. This is the only place where we do this reverse
1723 Metrics::iterator i;
1724 const MeterSection* meter;
1725 const TempoSection* tempo;
1729 meter = &first_meter ();
1730 tempo = &first_tempo ();
1735 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1738 MetricSection* prev = 0;
1740 for (i = metrics.begin(); i != metrics.end(); ++i) {
1743 TempoMetric metric (*meter, *tempo);
1746 metric.set_start (prev->start());
1747 metric.set_frame (prev->frame());
1749 // metric will be at frames=0 bbt=1|1|0 by default
1750 // which is correct for our purpose
1753 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1754 bbt_time ((*i)->frame(), bbt, bi);
1756 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1762 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1763 /* round up to next beat */
1769 if (bbt.beats != 1) {
1770 /* round up to next bar */
1776 // cerr << bbt << endl;
1778 (*i)->set_start (bbt);
1780 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1782 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1783 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1785 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1787 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1794 recompute_map (true);
1798 PropertyChanged (PropertyChange ());
1801 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1802 * pos can be -ve, if required.
1805 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1807 Glib::Threads::RWLock::ReaderLock lm (lock);
1808 Metrics::const_iterator next_tempo;
1809 const TempoSection* tempo = 0;
1811 /* Find the starting tempo metric */
1813 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1815 const TempoSection* t;
1817 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1819 /* This is a bit of a hack, but pos could be -ve, and if it is,
1820 we consider the initial metric changes (at time 0) to actually
1821 be in effect at pos.
1824 framepos_t f = (*next_tempo)->frame ();
1826 if (pos < 0 && f == 0) {
1840 tempo -> the Tempo for "pos"
1841 next_tempo -> first tempo after "pos", possibly metrics.end()
1844 DEBUG_TRACE (DEBUG::TempoMath,
1845 string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1846 pos, beats, *((const Tempo*)tempo), tempo->frame()));
1850 /* Distance to the end of this section in frames */
1851 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1853 /* Distance to the end in beats */
1854 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1856 /* Amount to subtract this time */
1857 double const delta = min (distance_beats, beats);
1859 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1860 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1861 distance_frames, distance_beats));
1865 pos += delta * tempo->frames_per_beat (_frame_rate);
1867 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1869 /* step forwards to next tempo section */
1871 if (next_tempo != metrics.end()) {
1873 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1875 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1876 *((const Tempo*)tempo), tempo->frame(),
1877 tempo->frames_per_beat (_frame_rate)));
1879 while (next_tempo != metrics.end ()) {
1883 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1893 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
1895 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1897 Glib::Threads::RWLock::ReaderLock lm (lock);
1898 Metrics::const_reverse_iterator prev_tempo;
1899 const TempoSection* tempo = 0;
1901 /* Find the starting tempo metric */
1903 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1905 const TempoSection* t;
1907 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1909 /* This is a bit of a hack, but pos could be -ve, and if it is,
1910 we consider the initial metric changes (at time 0) to actually
1911 be in effect at pos.
1914 framepos_t f = (*prev_tempo)->frame ();
1916 if (pos < 0 && f == 0) {
1920 /* this is slightly more complex than the forward case
1921 because we reach the tempo in effect at pos after
1922 passing through pos (rather before, as in the
1923 forward case). having done that, we then need to
1924 keep going to get the previous tempo (or
1930 /* first tempo with position at or
1934 } else if (f < pos) {
1935 /* some other tempo section that
1936 is even earlier than 'tempo'
1944 DEBUG_TRACE (DEBUG::TempoMath,
1945 string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1946 pos, beats, *((const Tempo*)tempo), tempo->frame(),
1947 prev_tempo == metrics.rend()));
1951 tempo -> the Tempo for "pos"
1952 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1957 /* Distance to the start of this section in frames */
1958 framecnt_t distance_frames = (pos - tempo->frame());
1960 /* Distance to the start in beats */
1961 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1963 /* Amount to subtract this time */
1964 double const sub = min (distance_beats, beats);
1966 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1967 tempo->frame(), distance_frames, distance_beats));
1971 pos -= sub * tempo->frames_per_beat (_frame_rate);
1973 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1974 (prev_tempo == metrics.rend())));
1976 /* step backwards to prior TempoSection */
1978 if (prev_tempo != metrics.rend()) {
1980 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1982 DEBUG_TRACE (DEBUG::TempoMath,
1983 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1984 *((const Tempo*)tempo), tempo->frame(),
1985 tempo->frames_per_beat (_frame_rate)));
1987 while (prev_tempo != metrics.rend ()) {
1991 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1996 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
2004 /** Add the BBT interval op to pos and return the result */
2006 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2008 Glib::Threads::RWLock::ReaderLock lm (lock);
2009 Metrics::const_iterator i;
2010 const MeterSection* meter;
2011 const MeterSection* m;
2012 const TempoSection* tempo;
2013 const TempoSection* t;
2014 double frames_per_beat;
2015 framepos_t effective_pos = max (pos, (framepos_t) 0);
2017 meter = &first_meter ();
2018 tempo = &first_tempo ();
2023 /* find the starting metrics for tempo & meter */
2025 for (i = metrics.begin(); i != metrics.end(); ++i) {
2027 if ((*i)->frame() > effective_pos) {
2031 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2033 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2040 meter -> the Meter for "pos"
2041 tempo -> the Tempo for "pos"
2042 i -> for first new metric after "pos", possibly metrics.end()
2045 /* now comes the complicated part. we have to add one beat a time,
2046 checking for a new metric on every beat.
2049 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2058 /* check if we need to use a new metric section: has adding frames moved us
2059 to or after the start of the next metric section? in which case, use it.
2062 if (i != metrics.end()) {
2063 if ((*i)->frame() <= pos) {
2065 /* about to change tempo or meter, so add the
2066 * number of frames for the bars we've just
2067 * traversed before we change the
2068 * frames_per_beat value.
2071 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2074 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2076 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2080 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2087 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2093 /* given the current meter, have we gone past the end of the bar ? */
2098 /* check if we need to use a new metric section: has adding frames moved us
2099 to or after the start of the next metric section? in which case, use it.
2102 if (i != metrics.end()) {
2103 if ((*i)->frame() <= pos) {
2105 /* about to change tempo or meter, so add the
2106 * number of frames for the beats we've just
2107 * traversed before we change the
2108 * frames_per_beat value.
2111 pos += llrint (beats * frames_per_beat);
2114 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2116 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2120 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2125 pos += llrint (beats * frames_per_beat);
2128 if (op.ticks >= BBT_Time::ticks_per_beat) {
2129 pos += llrint (frames_per_beat + /* extra beat */
2130 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2131 (double) BBT_Time::ticks_per_beat)));
2133 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2140 /** Count the number of beats that are equivalent to distance when going forward,
2144 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2146 Glib::Threads::RWLock::ReaderLock lm (lock);
2147 Metrics::const_iterator next_tempo;
2148 const TempoSection* tempo = 0;
2149 framepos_t effective_pos = max (pos, (framepos_t) 0);
2151 /* Find the relevant initial tempo metric */
2153 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2155 const TempoSection* t;
2157 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2159 if ((*next_tempo)->frame() > effective_pos) {
2169 tempo -> the Tempo for "pos"
2170 next_tempo -> the next tempo after "pos", possibly metrics.end()
2175 DEBUG_TRACE (DEBUG::TempoMath,
2176 string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
2177 pos, distance, *((const Tempo*)tempo), tempo->frame()));
2179 Evoral::MusicalTime beats = 0;
2183 /* End of this section */
2185 /* Distance to `end' in frames */
2186 framepos_t distance_to_end;
2188 if (next_tempo == metrics.end ()) {
2189 /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
2191 distance_to_end = max_framepos;
2193 end = (*next_tempo)->frame ();
2194 distance_to_end = end - pos;
2197 /* Amount to subtract this time */
2198 double const sub = min (distance, distance_to_end);
2200 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
2201 distance_to_end, sub));
2207 beats += sub / tempo->frames_per_beat (_frame_rate);
2209 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
2210 pos, beats, distance));
2212 /* Move on if there's anything to move to */
2214 if (next_tempo != metrics.end()) {
2216 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2218 DEBUG_TRACE (DEBUG::TempoMath,
2219 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2220 *((const Tempo*)tempo), tempo->frame(),
2221 tempo->frames_per_beat (_frame_rate)));
2223 while (next_tempo != metrics.end ()) {
2227 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2232 if (next_tempo == metrics.end()) {
2233 DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
2235 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
2236 **next_tempo, (*next_tempo)->frame()));
2246 TempoMap::BBTPointList::const_iterator
2247 TempoMap::bbt_before_or_at (framepos_t pos)
2249 /* CALLER MUST HOLD READ LOCK */
2251 BBTPointList::const_iterator i;
2254 /* not really correct, but we should catch pos < 0 at a higher
2257 return _map.begin();
2260 i = lower_bound (_map.begin(), _map.end(), pos);
2261 assert (i != _map.end());
2262 if ((*i).frame > pos) {
2263 assert (i != _map.begin());
2270 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2275 TempoMap::BBTPointList::const_iterator
2276 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2278 BBTPointList::const_iterator i;
2281 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2282 assert (i != _map.end());
2283 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2284 assert (i != _map.begin());
2290 TempoMap::BBTPointList::const_iterator
2291 TempoMap::bbt_after_or_at (framepos_t pos)
2293 /* CALLER MUST HOLD READ LOCK */
2295 BBTPointList::const_iterator i;
2297 if (_map.back().frame == pos) {
2299 assert (i != _map.begin());
2304 i = upper_bound (_map.begin(), _map.end(), pos);
2305 assert (i != _map.end());
2310 operator<< (std::ostream& o, const Meter& m) {
2311 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2315 operator<< (std::ostream& o, const Tempo& t) {
2316 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2320 operator<< (std::ostream& o, const MetricSection& section) {
2322 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2324 const TempoSection* ts;
2325 const MeterSection* ms;
2327 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2328 o << *((const Tempo*) ts);
2329 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2330 o << *((const Meter*) ms);