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/lmath.h"
31 #include "ardour/tempo.h"
37 using namespace ARDOUR;
40 using Timecode::BBT_Time;
42 /* _default tempo is 4/4 qtr=120 */
44 Meter TempoMap::_default_meter (4.0, 4.0);
45 Tempo TempoMap::_default_tempo (120.0);
47 /***********************************************************************/
50 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
52 /* This is tempo- and meter-sensitive. The number it returns
53 is based on the interval between any two lines in the
54 grid that is constructed from tempo and meter sections.
56 The return value IS NOT interpretable in terms of "beats".
59 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
65 return frames_per_grid (tempo, sr) * _divisions_per_bar;
68 /***********************************************************************/
70 const string TempoSection::xml_state_node_name = "Tempo";
72 TempoSection::TempoSection (const XMLNode& node)
73 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
75 const XMLProperty *prop;
77 LocaleGuard lg (X_("C"));
79 if ((prop = node.property ("start")) == 0) {
80 error << _("TempoSection XML node has no \"start\" property") << endmsg;
81 throw failed_constructor();
84 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
88 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
89 throw failed_constructor();
94 if ((prop = node.property ("beats-per-minute")) == 0) {
95 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
96 throw failed_constructor();
99 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
100 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
101 throw failed_constructor();
104 if ((prop = node.property ("note-type")) == 0) {
105 /* older session, make note type be quarter by default */
108 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
109 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
110 throw failed_constructor();
114 if ((prop = node.property ("movable")) == 0) {
115 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
116 throw failed_constructor();
119 set_movable (string_is_affirmative (prop->value()));
121 if ((prop = node.property ("bar-offset")) == 0) {
124 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
125 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
126 throw failed_constructor();
132 TempoSection::get_state() const
134 XMLNode *root = new XMLNode (xml_state_node_name);
136 LocaleGuard lg (X_("C"));
138 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
142 root->add_property ("start", buf);
143 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
144 root->add_property ("beats-per-minute", buf);
145 snprintf (buf, sizeof (buf), "%f", _note_type);
146 root->add_property ("note-type", buf);
147 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
148 // root->add_property ("bar-offset", buf);
149 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
150 root->add_property ("movable", buf);
157 TempoSection::update_bar_offset_from_bbt (const Meter& m)
159 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
160 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
162 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
166 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
170 if (_bar_offset < 0.0) {
175 new_start.bars = start().bars;
177 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
178 new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
179 new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
181 /* remember the 1-based counting properties of beats */
182 new_start.beats += 1;
184 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
185 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
187 set_start (new_start);
190 /***********************************************************************/
192 const string MeterSection::xml_state_node_name = "Meter";
194 MeterSection::MeterSection (const XMLNode& node)
195 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
197 const XMLProperty *prop;
199 LocaleGuard lg (X_("C"));
201 if ((prop = node.property ("start")) == 0) {
202 error << _("MeterSection XML node has no \"start\" property") << endmsg;
203 throw failed_constructor();
206 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
210 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
211 throw failed_constructor();
216 /* beats-per-bar is old; divisions-per-bar is new */
218 if ((prop = node.property ("divisions-per-bar")) == 0) {
219 if ((prop = node.property ("beats-per-bar")) == 0) {
220 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
221 throw failed_constructor();
225 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
226 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
227 throw failed_constructor();
230 if ((prop = node.property ("note-type")) == 0) {
231 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
232 throw failed_constructor();
235 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
236 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
237 throw failed_constructor();
240 if ((prop = node.property ("movable")) == 0) {
241 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
242 throw failed_constructor();
245 set_movable (string_is_affirmative (prop->value()));
249 MeterSection::get_state() const
251 XMLNode *root = new XMLNode (xml_state_node_name);
253 LocaleGuard lg (X_("C"));
255 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
259 root->add_property ("start", buf);
260 snprintf (buf, sizeof (buf), "%f", _note_type);
261 root->add_property ("note-type", buf);
262 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
263 root->add_property ("divisions-per-bar", buf);
264 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
265 root->add_property ("movable", buf);
270 /***********************************************************************/
272 struct MetricSectionSorter {
273 bool operator() (const MetricSection* a, const MetricSection* b) {
274 return a->start() < b->start();
278 TempoMap::TempoMap (framecnt_t fr)
287 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
288 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
290 t->set_movable (false);
291 m->set_movable (false);
293 /* note: frame time is correct (zero) for both of these */
295 metrics.push_back (t);
296 metrics.push_back (m);
299 TempoMap::~TempoMap ()
304 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
306 bool removed = false;
309 Glib::Threads::RWLock::WriterLock lm (lock);
310 if ((removed = remove_tempo_locked (tempo))) {
311 if (complete_operation) {
312 recompute_map (true);
317 if (removed && complete_operation) {
318 PropertyChanged (PropertyChange ());
323 TempoMap::remove_tempo_locked (const TempoSection& tempo)
327 for (i = metrics.begin(); i != metrics.end(); ++i) {
328 if (dynamic_cast<TempoSection*> (*i) != 0) {
329 if (tempo.frame() == (*i)->frame()) {
330 if ((*i)->movable()) {
342 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
344 bool removed = false;
347 Glib::Threads::RWLock::WriterLock lm (lock);
348 if ((removed = remove_meter_locked (tempo))) {
349 if (complete_operation) {
350 recompute_map (true);
355 if (removed && complete_operation) {
356 PropertyChanged (PropertyChange ());
361 TempoMap::remove_meter_locked (const MeterSection& tempo)
365 for (i = metrics.begin(); i != metrics.end(); ++i) {
366 if (dynamic_cast<MeterSection*> (*i) != 0) {
367 if (tempo.frame() == (*i)->frame()) {
368 if ((*i)->movable()) {
380 TempoMap::do_insert (MetricSection* section)
382 bool need_add = true;
384 assert (section->start().ticks == 0);
386 /* we only allow new meters to be inserted on beat 1 of an existing
390 if (dynamic_cast<MeterSection*>(section)) {
392 /* we need to (potentially) update the BBT times of tempo
393 sections based on this new meter.
396 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
398 BBT_Time corrected = section->start();
402 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
403 section->start(), corrected) << endmsg;
405 section->set_start (corrected);
411 /* Look for any existing MetricSection that is of the same type and
412 in the same bar as the new one, and remove it before adding
413 the new one. Note that this means that if we find a matching,
414 existing section, we can break out of the loop since we're
415 guaranteed that there is only one such match.
418 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
420 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
421 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
423 if (iter_is_tempo && insert_is_tempo) {
427 if ((*i)->start().bars == section->start().bars &&
428 (*i)->start().beats == section->start().beats) {
430 if (!(*i)->movable()) {
432 /* can't (re)move this section, so overwrite
433 * its data content (but not its properties as
437 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
445 } else if (!iter_is_tempo && !insert_is_tempo) {
449 if ((*i)->start().bars == section->start().bars) {
451 if (!(*i)->movable()) {
453 /* can't (re)move this section, so overwrite
454 * its data content (but not its properties as
458 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
468 /* non-matching types, so we don't care */
472 /* Add the given MetricSection, if we didn't just reset an existing
480 for (i = metrics.begin(); i != metrics.end(); ++i) {
481 if ((*i)->start() > section->start()) {
486 metrics.insert (i, section);
491 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
494 Glib::Threads::RWLock::WriterLock lm (lock);
495 TempoSection& first (first_tempo());
497 if (ts.start() != first.start()) {
498 remove_tempo_locked (ts);
499 add_tempo_locked (tempo, where, true);
502 /* cannot move the first tempo section */
503 *static_cast<Tempo*>(&first) = tempo;
504 recompute_map (false);
509 PropertyChanged (PropertyChange ());
513 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
516 Glib::Threads::RWLock::WriterLock lm (lock);
517 add_tempo_locked (tempo, where, true);
521 PropertyChanged (PropertyChange ());
525 TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute)
527 /* new tempos always start on a beat */
530 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
532 /* find the meter to use to set the bar offset of this
536 const Meter* meter = &first_meter();
538 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
539 at something, because we insert the default tempo and meter during
540 TempoMap construction.
542 now see if we can find better candidates.
545 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
547 const MeterSection* m;
549 if (where < (*i)->start()) {
553 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
558 ts->update_bar_offset_from_bbt (*meter);
565 recompute_map (false);
570 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
573 Glib::Threads::RWLock::WriterLock lm (lock);
574 MeterSection& first (first_meter());
576 if (ms.start() != first.start()) {
577 remove_meter_locked (ms);
578 add_meter_locked (meter, where, true);
580 /* cannot move the first meter section */
581 *static_cast<Meter*>(&first) = meter;
582 recompute_map (true);
586 PropertyChanged (PropertyChange ());
590 TempoMap::add_meter (const Meter& meter, BBT_Time where)
593 Glib::Threads::RWLock::WriterLock lm (lock);
594 add_meter_locked (meter, where, true);
599 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
604 PropertyChanged (PropertyChange ());
608 TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
610 /* a new meter always starts a new bar on the first beat. so
611 round the start time appropriately. remember that
612 `where' is based on the existing tempo map, not
613 the result after we insert the new meter.
617 if (where.beats != 1) {
622 /* new meters *always* start on a beat. */
625 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
628 recompute_map (true);
634 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
636 Tempo newtempo (beats_per_minute, note_type);
639 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
640 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
642 Glib::Threads::RWLock::WriterLock lm (lock);
643 *((Tempo*) t) = newtempo;
644 recompute_map (false);
646 PropertyChanged (PropertyChange ());
653 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
655 Tempo newtempo (beats_per_minute, note_type);
661 /* find the TempoSection immediately preceding "where"
664 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
666 if ((*i)->frame() > where) {
672 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
682 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
692 Glib::Threads::RWLock::WriterLock lm (lock);
693 /* cannot move the first tempo section */
694 *((Tempo*)prev) = newtempo;
695 recompute_map (false);
698 PropertyChanged (PropertyChange ());
702 TempoMap::first_meter () const
704 const MeterSection *m = 0;
706 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
707 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
712 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
713 abort(); /*NOTREACHED*/
718 TempoMap::first_meter ()
722 /* CALLER MUST HOLD LOCK */
724 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
725 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
730 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
731 abort(); /*NOTREACHED*/
736 TempoMap::first_tempo () const
738 const TempoSection *t = 0;
740 /* CALLER MUST HOLD LOCK */
742 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
743 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
748 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
749 abort(); /*NOTREACHED*/
754 TempoMap::first_tempo ()
758 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
759 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
764 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
765 abort(); /*NOTREACHED*/
770 TempoMap::require_map_to (framepos_t pos)
772 Glib::Threads::RWLock::WriterLock lm (lock);
774 if (_map.empty() || _map.back().frame < pos) {
780 TempoMap::require_map_to (const BBT_Time& bbt)
782 Glib::Threads::RWLock::WriterLock lm (lock);
784 /* since we have no idea where BBT is if its off the map, see the last
785 * point in the map is past BBT, and if not add an arbitrary amount of
789 int additional_minutes = 1;
792 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
795 /* add some more distance, using bigger steps each time */
796 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
797 additional_minutes *= 2;
802 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
804 /* CALLER MUST HOLD WRITE LOCK */
806 MeterSection* meter = 0;
807 TempoSection* tempo = 0;
808 double current_frame;
810 Metrics::iterator next_metric;
814 /* we will actually stop once we hit
820 if (!_map.empty ()) {
821 /* never allow the map to be shortened */
822 end = max (end, _map.back().frame);
826 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
828 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
831 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
839 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
842 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
850 /* assumes that the first meter & tempo are at frame zero */
852 meter->set_frame (0);
853 tempo->set_frame (0);
855 /* assumes that the first meter & tempo are at 1|1|0 */
860 if (reassign_tempo_bbt) {
862 MeterSection* rmeter = meter;
864 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
866 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
871 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
873 /* reassign the BBT time of this tempo section
874 * based on its bar offset position.
877 ts->update_bbt_time_from_bar_offset (*rmeter);
879 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
882 fatal << _("programming error: unhandled MetricSection type") << endmsg;
883 abort(); /*NOTREACHED*/
888 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
890 next_metric = metrics.begin();
891 ++next_metric; // skip meter (or tempo)
892 ++next_metric; // skip tempo (or meter)
896 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
897 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
900 /* silly call from Session::process() during startup
905 _extend_map (tempo, meter, next_metric, current, current_frame, end);
909 TempoMap::extend_map (framepos_t end)
911 /* CALLER MUST HOLD WRITE LOCK */
914 recompute_map (false, end);
918 BBTPointList::const_iterator i = _map.end();
919 Metrics::iterator next_metric;
923 BBT_Time last_metric_start;
925 if ((*i).tempo->frame() > (*i).meter->frame()) {
926 last_metric_start = (*i).tempo->start();
928 last_metric_start = (*i).meter->start();
931 /* find the metric immediately after the tempo + meter sections for the
932 * last point in the map
935 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
936 if ((*next_metric)->start() > last_metric_start) {
941 /* we cast away const here because this is the one place where we need
942 * to actually modify the frame time of each metric section.
945 _extend_map (const_cast<TempoSection*> ((*i).tempo),
946 const_cast<MeterSection*> ((*i).meter),
947 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
951 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
952 Metrics::iterator next_metric,
953 BBT_Time current, framepos_t current_frame, framepos_t end)
955 /* CALLER MUST HOLD WRITE LOCK */
960 double current_frame_exact;
961 framepos_t bar_start_frame;
963 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
965 if (current.beats == 1) {
966 bar_start_frame = current_frame;
971 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
972 current_frame_exact = current_frame;
974 while (current_frame < end) {
977 current_frame_exact += beat_frames;
978 current_frame = llrint(current_frame_exact);
980 if (current.beats > meter->divisions_per_bar()) {
985 if (next_metric != metrics.end()) {
987 /* no operator >= so invert operator < */
989 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
991 if (!(current < (*next_metric)->start())) {
994 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
998 /* new tempo section: if its on a beat,
999 * we don't have to do anything other
1000 * than recompute various distances,
1001 * done further below as we transition
1002 * the next metric section.
1004 * if its not on the beat, we have to
1005 * compute the duration of the beat it
1006 * is within, which will be different
1007 * from the preceding following ones
1008 * since it takes part of its duration
1009 * from the preceding tempo and part
1010 * from this new tempo.
1013 if (tempo->start().ticks != 0) {
1015 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
1017 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
1018 tempo->start(), current_frame, tempo->bar_offset()));
1020 /* back up to previous beat */
1021 current_frame_exact -= beat_frames;
1022 current_frame = llrint(current_frame_exact);
1024 /* set tempo section location
1025 * based on offset from last
1028 tempo->set_frame (bar_start_frame +
1029 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
1031 /* advance to the location of
1032 * the new (adjusted) beat. do
1033 * this by figuring out the
1034 * offset within the beat that
1035 * would have been there
1037 * change. then stretch the
1041 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
1043 current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
1044 current_frame = llrint(current_frame_exact);
1046 /* next metric doesn't have to
1047 * match this precisely to
1048 * merit a reloop ...
1050 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
1054 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
1055 tempo->start(), current_frame));
1056 tempo->set_frame (current_frame);
1059 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
1063 /* new meter section: always defines the
1067 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
1068 meter->start(), current, current_frame));
1070 assert (current.beats == 1);
1072 meter->set_frame (current_frame);
1075 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1077 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1078 beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
1082 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1083 /* same position so go back and set this one up before advancing
1091 if (current.beats == 1) {
1092 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1093 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
1094 bar_start_frame = current_frame;
1096 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1097 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
1100 if (next_metric == metrics.end()) {
1101 /* no more metrics - we've timestamped them all, stop here */
1102 if (end == max_framepos) {
1103 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
1104 current.bars, current.beats, current_frame));
1112 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1114 Glib::Threads::RWLock::ReaderLock lm (lock);
1115 TempoMetric m (first_meter(), first_tempo());
1117 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1118 at something, because we insert the default tempo and meter during
1119 TempoMap construction.
1121 now see if we can find better candidates.
1124 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1126 if ((*i)->frame() > frame) {
1141 TempoMap::metric_at (BBT_Time bbt) const
1143 Glib::Threads::RWLock::ReaderLock lm (lock);
1144 TempoMetric m (first_meter(), first_tempo());
1146 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1147 at something, because we insert the default tempo and meter during
1148 TempoMap construction.
1150 now see if we can find better candidates.
1153 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1155 BBT_Time section_start ((*i)->start());
1157 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1168 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1170 require_map_to (frame);
1172 Glib::Threads::RWLock::ReaderLock lm (lock);
1178 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1182 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1186 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1188 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1191 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1194 if (_map.empty() || _map.back().frame < frame) {
1195 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1198 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1202 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1204 /* CALLER MUST HOLD READ LOCK */
1206 bbt.bars = (*i).bar;
1207 bbt.beats = (*i).beat;
1209 if ((*i).frame == frame) {
1212 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1213 BBT_Time::ticks_per_beat);
1218 TempoMap::frame_time (const BBT_Time& bbt)
1221 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1225 if (bbt.beats < 1) {
1226 throw std::logic_error ("beats are counted from one");
1229 require_map_to (bbt);
1231 Glib::Threads::RWLock::ReaderLock lm (lock);
1233 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1234 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1236 if (bbt.ticks != 0) {
1237 return ((*e).frame - (*s).frame) +
1238 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1240 return ((*e).frame - (*s).frame);
1245 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1248 bbt_time (pos, when);
1250 Glib::Threads::RWLock::ReaderLock lm (lock);
1251 return bbt_duration_at_unlocked (when, bbt, dir);
1255 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
1257 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1261 /* round back to the previous precise beat */
1262 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1263 BBTPointList::const_iterator start (wi);
1265 assert (wi != _map.end());
1270 while (wi != _map.end() && bars < bbt.bars) {
1272 if ((*wi).is_bar()) {
1276 assert (wi != _map.end());
1278 while (wi != _map.end() && beats < bbt.beats) {
1282 assert (wi != _map.end());
1284 /* add any additional frames related to ticks in the added value */
1286 if (bbt.ticks != 0) {
1287 return ((*wi).frame - (*start).frame) +
1288 (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1290 return ((*wi).frame - (*start).frame);
1295 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1297 return round_to_type (fr, dir, Bar);
1301 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1303 return round_to_type (fr, dir, Beat);
1307 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1309 require_map_to (fr);
1311 Glib::Threads::RWLock::ReaderLock lm (lock);
1312 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1314 uint32_t ticks_one_subdivisions_worth;
1316 bbt_time (fr, the_beat, i);
1318 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1319 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1321 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1325 /* round to next (or same iff dir == RoundUpMaybe) */
1327 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1329 if (mod == 0 && dir == RoundUpMaybe) {
1330 /* right on the subdivision, which is fine, so do nothing */
1332 } else if (mod == 0) {
1333 /* right on the subdivision, so the difference is just the subdivision ticks */
1334 the_beat.ticks += ticks_one_subdivisions_worth;
1337 /* not on subdivision, compute distance to next subdivision */
1339 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1342 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1343 assert (i != _map.end());
1345 assert (i != _map.end());
1346 the_beat.ticks -= BBT_Time::ticks_per_beat;
1350 } else if (dir < 0) {
1352 /* round to previous (or same iff dir == RoundDownMaybe) */
1354 uint32_t difference = the_beat.ticks % ticks_one_subdivisions_worth;
1356 if (difference == 0 && dir == RoundDownAlways) {
1357 /* right on the subdivision, but force-rounding down,
1358 so the difference is just the subdivision ticks */
1359 difference = ticks_one_subdivisions_worth;
1362 if (the_beat.ticks < difference) {
1363 if (i == _map.begin()) {
1364 /* can't go backwards from wherever pos is, so just return it */
1368 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1370 the_beat.ticks -= difference;
1374 /* round to nearest */
1378 /* compute the distance to the previous and next subdivision */
1380 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1382 /* closer to the next subdivision, so shift forward */
1384 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1386 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1388 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1389 assert (i != _map.end());
1391 assert (i != _map.end());
1392 the_beat.ticks -= BBT_Time::ticks_per_beat;
1393 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1396 } else if (rem > 0) {
1398 /* closer to previous subdivision, so shift backward */
1400 if (rem > the_beat.ticks) {
1401 if (i == _map.begin()) {
1402 /* can't go backwards past zero, so ... */
1405 /* step back to previous beat */
1407 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1408 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1410 the_beat.ticks = lrint (the_beat.ticks - rem);
1411 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1414 /* on the subdivision, do nothing */
1418 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1419 (*i).tempo->frames_per_beat (_frame_rate);
1423 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1425 require_map_to (frame);
1427 Glib::Threads::RWLock::ReaderLock lm (lock);
1428 BBTPointList::const_iterator fi;
1431 fi = bbt_after_or_at (frame);
1433 fi = bbt_before_or_at (frame);
1436 assert (fi != _map.end());
1438 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,
1439 (type == Bar ? "bar" : "beat")));
1444 /* find bar previous to 'frame' */
1446 if (fi == _map.begin()) {
1450 if ((*fi).is_bar() && (*fi).frame == frame) {
1451 if (dir == RoundDownMaybe) {
1457 while (!(*fi).is_bar()) {
1458 if (fi == _map.begin()) {
1463 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1464 (*fi).bar, (*fi).beat, (*fi).frame));
1467 } else if (dir > 0) {
1469 /* find bar following 'frame' */
1471 if ((*fi).is_bar() && (*fi).frame == frame) {
1472 if (dir == RoundUpMaybe) {
1478 while (!(*fi).is_bar()) {
1480 if (fi == _map.end()) {
1486 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1487 (*fi).bar, (*fi).beat, (*fi).frame));
1492 /* true rounding: find nearest bar */
1494 BBTPointList::const_iterator prev = fi;
1495 BBTPointList::const_iterator next = fi;
1497 if ((*fi).frame == frame) {
1501 while ((*prev).beat != 1) {
1502 if (prev == _map.begin()) {
1508 while ((next != _map.end()) && (*next).beat != 1) {
1512 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1513 return (*prev).frame;
1515 return (*next).frame;
1525 if (fi == _map.begin()) {
1529 if ((*fi).frame > frame || ((*fi).frame == frame && dir == RoundDownAlways)) {
1530 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1533 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1534 (*fi).bar, (*fi).beat, (*fi).frame));
1536 } else if (dir > 0) {
1537 if ((*fi).frame < frame || ((*fi).frame == frame && dir == RoundUpAlways)) {
1538 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1541 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1542 (*fi).bar, (*fi).beat, (*fi).frame));
1545 /* find beat nearest to frame */
1546 if ((*fi).frame == frame) {
1550 BBTPointList::const_iterator prev = fi;
1551 BBTPointList::const_iterator next = fi;
1553 /* fi is already the beat before_or_at frame, and
1554 we've just established that its not at frame, so its
1555 the beat before frame.
1559 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1560 return (*prev).frame;
1562 return (*next).frame;
1568 abort(); /* NOTREACHED */
1573 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1574 TempoMap::BBTPointList::const_iterator& end,
1575 framepos_t lower, framepos_t upper)
1578 Glib::Threads::RWLock::WriterLock lm (lock);
1579 if (_map.empty() || (_map.back().frame < upper)) {
1580 recompute_map (false, upper);
1584 begin = lower_bound (_map.begin(), _map.end(), lower);
1585 end = upper_bound (_map.begin(), _map.end(), upper);
1589 TempoMap::tempo_section_at (framepos_t frame) const
1591 Glib::Threads::RWLock::ReaderLock lm (lock);
1592 Metrics::const_iterator i;
1593 TempoSection* prev = 0;
1595 for (i = metrics.begin(); i != metrics.end(); ++i) {
1598 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1600 if ((*i)->frame() > frame) {
1610 abort(); /*NOTREACHED*/
1617 TempoMap::tempo_at (framepos_t frame) const
1619 TempoMetric m (metric_at (frame));
1624 TempoMap::meter_section_at (framepos_t frame) const
1626 Glib::Threads::RWLock::ReaderLock lm (lock);
1627 Metrics::const_iterator i;
1628 MeterSection* prev = 0;
1630 for (i = metrics.begin(); i != metrics.end(); ++i) {
1633 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1635 if ((*i)->frame() > frame) {
1645 abort(); /*NOTREACHED*/
1652 TempoMap::meter_at (framepos_t frame) const
1654 TempoMetric m (metric_at (frame));
1659 TempoMap::get_state ()
1661 Metrics::const_iterator i;
1662 XMLNode *root = new XMLNode ("TempoMap");
1665 Glib::Threads::RWLock::ReaderLock lm (lock);
1666 for (i = metrics.begin(); i != metrics.end(); ++i) {
1667 root->add_child_nocopy ((*i)->get_state());
1675 TempoMap::set_state (const XMLNode& node, int /*version*/)
1678 Glib::Threads::RWLock::WriterLock lm (lock);
1681 XMLNodeConstIterator niter;
1682 Metrics old_metrics (metrics);
1683 MeterSection* last_meter = 0;
1686 nlist = node.children();
1688 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1689 XMLNode* child = *niter;
1691 if (child->name() == TempoSection::xml_state_node_name) {
1694 TempoSection* ts = new TempoSection (*child);
1695 metrics.push_back (ts);
1697 if (ts->bar_offset() < 0.0) {
1699 ts->update_bar_offset_from_bbt (*last_meter);
1704 catch (failed_constructor& err){
1705 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1706 metrics = old_metrics;
1710 } else if (child->name() == MeterSection::xml_state_node_name) {
1713 MeterSection* ms = new MeterSection (*child);
1714 metrics.push_back (ms);
1718 catch (failed_constructor& err) {
1719 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1720 metrics = old_metrics;
1726 if (niter == nlist.end()) {
1727 MetricSectionSorter cmp;
1731 /* check for multiple tempo/meters at the same location, which
1732 ardour2 somehow allowed.
1735 Metrics::iterator prev = metrics.end();
1736 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1737 if (prev != metrics.end()) {
1738 if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
1739 if ((*prev)->start() == (*i)->start()) {
1740 cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1741 error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1744 } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
1745 if ((*prev)->start() == (*i)->start()) {
1746 cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1747 error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1755 recompute_map (true, -1);
1758 PropertyChanged (PropertyChange ());
1764 TempoMap::dump (std::ostream& o) const
1766 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1767 const MeterSection* m;
1768 const TempoSection* t;
1770 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1772 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1773 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? "
1774 << t->movable() << ')' << endl;
1775 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1776 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1777 << " (movable? " << m->movable() << ')' << endl;
1783 TempoMap::n_tempos() const
1785 Glib::Threads::RWLock::ReaderLock lm (lock);
1788 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1789 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1798 TempoMap::n_meters() const
1800 Glib::Threads::RWLock::ReaderLock lm (lock);
1803 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1804 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1813 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1816 Glib::Threads::RWLock::WriterLock lm (lock);
1817 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1818 if ((*i)->frame() >= where && (*i)->movable ()) {
1819 (*i)->set_frame ((*i)->frame() + amount);
1823 /* now reset the BBT time of all metrics, based on their new
1824 * audio time. This is the only place where we do this reverse
1828 Metrics::iterator i;
1829 const MeterSection* meter;
1830 const TempoSection* tempo;
1834 meter = &first_meter ();
1835 tempo = &first_tempo ();
1840 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1843 MetricSection* prev = 0;
1845 for (i = metrics.begin(); i != metrics.end(); ++i) {
1848 TempoMetric metric (*meter, *tempo);
1851 metric.set_start (prev->start());
1852 metric.set_frame (prev->frame());
1854 // metric will be at frames=0 bbt=1|1|0 by default
1855 // which is correct for our purpose
1858 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1859 bbt_time ((*i)->frame(), bbt, bi);
1861 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1867 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1868 /* round up to next beat */
1874 if (bbt.beats != 1) {
1875 /* round up to next bar */
1881 // cerr << bbt << endl;
1883 (*i)->set_start (bbt);
1885 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1887 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1888 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1890 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1892 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1893 abort(); /*NOTREACHED*/
1899 recompute_map (true);
1903 PropertyChanged (PropertyChange ());
1906 TempoMap::remove_time (framepos_t where, framecnt_t amount)
1910 std::list<MetricSection*> metric_kill_list;
1912 TempoSection* last_tempo = NULL;
1913 MeterSection* last_meter = NULL;
1915 Glib::Threads::RWLock::WriterLock lm (lock);
1916 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1917 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
1918 metric_kill_list.push_back(*i);
1919 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
1922 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
1926 else if ((*i)->frame() >= where) {
1927 (*i)->set_frame ((*i)->frame() - amount);
1932 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
1934 metric_kill_list.remove(last_tempo);
1935 last_tempo->set_frame(where);
1939 metric_kill_list.remove(last_meter);
1940 last_meter->set_frame(where);
1944 //remove all the remaining metrics
1945 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
1951 recompute_map (true);
1954 PropertyChanged (PropertyChange ());
1958 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1959 * pos can be -ve, if required.
1962 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
1964 Glib::Threads::RWLock::ReaderLock lm (lock);
1965 Metrics::const_iterator next_tempo;
1966 const TempoSection* tempo = 0;
1968 /* Find the starting tempo metric */
1970 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1972 const TempoSection* t;
1974 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1976 /* This is a bit of a hack, but pos could be -ve, and if it is,
1977 we consider the initial metric changes (at time 0) to actually
1978 be in effect at pos.
1981 framepos_t f = (*next_tempo)->frame ();
1983 if (pos < 0 && f == 0) {
1997 tempo -> the Tempo for "pos"
1998 next_tempo -> first tempo after "pos", possibly metrics.end()
2002 DEBUG_TRACE (DEBUG::TempoMath,
2003 string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
2004 pos, beats, *((const Tempo*)tempo), tempo->frame()));
2008 /* Distance to the end of this section in frames */
2009 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
2011 /* Distance to the end in beats */
2012 Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
2013 distance_frames, tempo->frames_per_beat (_frame_rate));
2015 /* Amount to subtract this time */
2016 Evoral::Beats const delta = min (distance_beats, beats);
2018 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
2019 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
2020 distance_frames, distance_beats));
2024 pos += delta.to_ticks(tempo->frames_per_beat (_frame_rate));
2026 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
2028 /* step forwards to next tempo section */
2030 if (next_tempo != metrics.end()) {
2032 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2034 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2035 *((const Tempo*)tempo), tempo->frame(),
2036 tempo->frames_per_beat (_frame_rate)));
2038 while (next_tempo != metrics.end ()) {
2042 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2052 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2054 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2056 Glib::Threads::RWLock::ReaderLock lm (lock);
2057 Metrics::const_reverse_iterator prev_tempo;
2058 const TempoSection* tempo = 0;
2060 /* Find the starting tempo metric */
2062 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
2064 const TempoSection* t;
2066 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
2068 /* This is a bit of a hack, but pos could be -ve, and if it is,
2069 we consider the initial metric changes (at time 0) to actually
2070 be in effect at pos.
2073 framepos_t f = (*prev_tempo)->frame ();
2075 if (pos < 0 && f == 0) {
2079 /* this is slightly more complex than the forward case
2080 because we reach the tempo in effect at pos after
2081 passing through pos (rather before, as in the
2082 forward case). having done that, we then need to
2083 keep going to get the previous tempo (or
2089 /* first tempo with position at or
2093 } else if (f < pos) {
2094 /* some other tempo section that
2095 is even earlier than 'tempo'
2104 DEBUG_TRACE (DEBUG::TempoMath,
2105 string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
2106 pos, beats, *((const Tempo*)tempo), tempo->frame(),
2107 prev_tempo == metrics.rend()));
2111 tempo -> the Tempo for "pos"
2112 prev_tempo -> the first metric before "pos", possibly metrics.rend()
2117 /* Distance to the start of this section in frames */
2118 framecnt_t distance_frames = (pos - tempo->frame());
2120 /* Distance to the start in beats */
2121 Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
2122 distance_frames, tempo->frames_per_beat (_frame_rate));
2124 /* Amount to subtract this time */
2125 Evoral::Beats const sub = min (distance_beats, beats);
2127 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
2128 tempo->frame(), distance_frames, distance_beats));
2132 pos -= sub.to_double() * tempo->frames_per_beat (_frame_rate);
2134 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
2135 (prev_tempo == metrics.rend())));
2137 /* step backwards to prior TempoSection */
2139 if (prev_tempo != metrics.rend()) {
2141 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
2143 DEBUG_TRACE (DEBUG::TempoMath,
2144 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2145 *((const Tempo*)tempo), tempo->frame(),
2146 tempo->frames_per_beat (_frame_rate)));
2148 while (prev_tempo != metrics.rend ()) {
2152 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
2157 pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate));
2158 beats = Evoral::Beats();
2165 /** Add the BBT interval op to pos and return the result */
2167 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2169 Glib::Threads::RWLock::ReaderLock lm (lock);
2170 Metrics::const_iterator i;
2171 const MeterSection* meter;
2172 const MeterSection* m;
2173 const TempoSection* tempo;
2174 const TempoSection* t;
2175 double frames_per_beat;
2176 framepos_t effective_pos = max (pos, (framepos_t) 0);
2178 meter = &first_meter ();
2179 tempo = &first_tempo ();
2184 /* find the starting metrics for tempo & meter */
2186 for (i = metrics.begin(); i != metrics.end(); ++i) {
2188 if ((*i)->frame() > effective_pos) {
2192 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2194 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2201 meter -> the Meter for "pos"
2202 tempo -> the Tempo for "pos"
2203 i -> for first new metric after "pos", possibly metrics.end()
2206 /* now comes the complicated part. we have to add one beat a time,
2207 checking for a new metric on every beat.
2210 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2219 /* check if we need to use a new metric section: has adding frames moved us
2220 to or after the start of the next metric section? in which case, use it.
2223 if (i != metrics.end()) {
2224 if ((*i)->frame() <= pos) {
2226 /* about to change tempo or meter, so add the
2227 * number of frames for the bars we've just
2228 * traversed before we change the
2229 * frames_per_beat value.
2232 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2235 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2237 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2241 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2248 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2254 /* given the current meter, have we gone past the end of the bar ? */
2259 /* check if we need to use a new metric section: has adding frames moved us
2260 to or after the start of the next metric section? in which case, use it.
2263 if (i != metrics.end()) {
2264 if ((*i)->frame() <= pos) {
2266 /* about to change tempo or meter, so add the
2267 * number of frames for the beats we've just
2268 * traversed before we change the
2269 * frames_per_beat value.
2272 pos += llrint (beats * frames_per_beat);
2275 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2277 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2281 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2286 pos += llrint (beats * frames_per_beat);
2289 if (op.ticks >= BBT_Time::ticks_per_beat) {
2290 pos += llrint (frames_per_beat + /* extra beat */
2291 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2292 (double) BBT_Time::ticks_per_beat)));
2294 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2301 /** Count the number of beats that are equivalent to distance when going forward,
2305 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2307 Glib::Threads::RWLock::ReaderLock lm (lock);
2308 Metrics::const_iterator next_tempo;
2309 const TempoSection* tempo = 0;
2310 framepos_t effective_pos = max (pos, (framepos_t) 0);
2312 /* Find the relevant initial tempo metric */
2314 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2316 const TempoSection* t;
2318 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2320 if ((*next_tempo)->frame() > effective_pos) {
2330 tempo -> the Tempo for "pos"
2331 next_tempo -> the next tempo after "pos", possibly metrics.end()
2335 DEBUG_TRACE (DEBUG::TempoMath,
2336 string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
2337 pos, distance, *((const Tempo*)tempo), tempo->frame()));
2339 Evoral::Beats beats = Evoral::Beats();
2343 /* End of this section */
2345 /* Distance to `end' in frames */
2346 framepos_t distance_to_end;
2348 if (next_tempo == metrics.end ()) {
2349 /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
2351 distance_to_end = max_framepos;
2353 end = (*next_tempo)->frame ();
2354 distance_to_end = end - pos;
2357 /* Amount to subtract this time in frames */
2358 framecnt_t const sub = min (distance, distance_to_end);
2360 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
2361 distance_to_end, sub));
2367 beats += Evoral::Beats::ticks_at_rate(sub, tempo->frames_per_beat (_frame_rate));
2369 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
2370 pos, beats, distance));
2372 /* Move on if there's anything to move to */
2374 if (next_tempo != metrics.end()) {
2376 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2378 DEBUG_TRACE (DEBUG::TempoMath,
2379 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2380 *((const Tempo*)tempo), tempo->frame(),
2381 tempo->frames_per_beat (_frame_rate)));
2383 while (next_tempo != metrics.end ()) {
2387 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2392 if (next_tempo == metrics.end()) {
2393 DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
2395 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
2396 **next_tempo, (*next_tempo)->frame()));
2406 TempoMap::BBTPointList::const_iterator
2407 TempoMap::bbt_before_or_at (framepos_t pos)
2409 /* CALLER MUST HOLD READ LOCK */
2411 BBTPointList::const_iterator i;
2414 /* not really correct, but we should catch pos < 0 at a higher
2417 return _map.begin();
2420 i = lower_bound (_map.begin(), _map.end(), pos);
2421 assert (i != _map.end());
2422 if ((*i).frame > pos) {
2423 assert (i != _map.begin());
2430 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2435 TempoMap::BBTPointList::const_iterator
2436 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2438 BBTPointList::const_iterator i;
2441 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2442 assert (i != _map.end());
2443 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2444 assert (i != _map.begin());
2450 TempoMap::BBTPointList::const_iterator
2451 TempoMap::bbt_after_or_at (framepos_t pos)
2453 /* CALLER MUST HOLD READ LOCK */
2455 BBTPointList::const_iterator i;
2457 if (_map.back().frame == pos) {
2459 assert (i != _map.begin());
2464 i = upper_bound (_map.begin(), _map.end(), pos);
2465 assert (i != _map.end());
2470 operator<< (std::ostream& o, const Meter& m) {
2471 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2475 operator<< (std::ostream& o, const Tempo& t) {
2476 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2480 operator<< (std::ostream& o, const MetricSection& section) {
2482 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2484 const TempoSection* ts;
2485 const MeterSection* ms;
2487 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2488 o << *((const Tempo*) ts);
2489 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2490 o << *((const Meter*) ms);