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/thread.h>
27 #include "pbd/xml++.h"
28 #include "evoral/types.hpp"
29 #include "ardour/debug.h"
30 #include "ardour/tempo.h"
31 #include "ardour/utils.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);
48 Tempo::frames_per_beat (framecnt_t sr) const
50 return (60.0 * sr) / _beats_per_minute;
53 /***********************************************************************/
56 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
58 /* This is tempo- and meter-sensitive. The number it returns
59 is based on the interval between any two lines in the
60 grid that is constructed from tempo and meter sections.
62 The return value IS NOT interpretable in terms of "beats".
65 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
69 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
71 return frames_per_grid (tempo, sr) * _divisions_per_bar;
74 /***********************************************************************/
76 const string TempoSection::xml_state_node_name = "Tempo";
78 TempoSection::TempoSection (const XMLNode& node)
79 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
81 const XMLProperty *prop;
83 LocaleGuard lg (X_("POSIX"));
85 if ((prop = node.property ("start")) == 0) {
86 error << _("TempoSection XML node has no \"start\" property") << endmsg;
87 throw failed_constructor();
90 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
94 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
95 throw failed_constructor();
100 if ((prop = node.property ("beats-per-minute")) == 0) {
101 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
102 throw failed_constructor();
105 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
106 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
107 throw failed_constructor();
110 if ((prop = node.property ("note-type")) == 0) {
111 /* older session, make note type be quarter by default */
114 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
115 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
116 throw failed_constructor();
120 if ((prop = node.property ("movable")) == 0) {
121 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
122 throw failed_constructor();
125 set_movable (string_is_affirmative (prop->value()));
127 if ((prop = node.property ("bar-offset")) == 0) {
130 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
131 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
132 throw failed_constructor();
138 TempoSection::get_state() const
140 XMLNode *root = new XMLNode (xml_state_node_name);
142 LocaleGuard lg (X_("POSIX"));
144 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
148 root->add_property ("start", buf);
149 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
150 root->add_property ("beats-per-minute", buf);
151 snprintf (buf, sizeof (buf), "%f", _note_type);
152 root->add_property ("note-type", buf);
153 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
154 // root->add_property ("bar-offset", buf);
155 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
156 root->add_property ("movable", buf);
163 TempoSection::update_bar_offset_from_bbt (const Meter& m)
165 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
166 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
168 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
172 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
176 if (_bar_offset < 0.0) {
181 new_start.bars = start().bars;
183 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
184 new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
185 new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
187 /* remember the 1-based counting properties of beats */
188 new_start.beats += 1;
190 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
191 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
193 set_start (new_start);
196 /***********************************************************************/
198 const string MeterSection::xml_state_node_name = "Meter";
200 MeterSection::MeterSection (const XMLNode& node)
201 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
203 const XMLProperty *prop;
205 LocaleGuard lg (X_("POSIX"));
207 if ((prop = node.property ("start")) == 0) {
208 error << _("MeterSection XML node has no \"start\" property") << endmsg;
209 throw failed_constructor();
212 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
216 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
217 throw failed_constructor();
222 /* beats-per-bar is old; divisions-per-bar is new */
224 if ((prop = node.property ("divisions-per-bar")) == 0) {
225 if ((prop = node.property ("beats-per-bar")) == 0) {
226 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
227 throw failed_constructor();
231 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
232 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
233 throw failed_constructor();
236 if ((prop = node.property ("note-type")) == 0) {
237 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
238 throw failed_constructor();
241 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
242 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
243 throw failed_constructor();
246 if ((prop = node.property ("movable")) == 0) {
247 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
248 throw failed_constructor();
251 set_movable (string_is_affirmative (prop->value()));
255 MeterSection::get_state() const
257 XMLNode *root = new XMLNode (xml_state_node_name);
259 LocaleGuard lg (X_("POSIX"));
261 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
265 root->add_property ("start", buf);
266 snprintf (buf, sizeof (buf), "%f", _note_type);
267 root->add_property ("note-type", buf);
268 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
269 root->add_property ("divisions-per-bar", buf);
270 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
271 root->add_property ("movable", buf);
276 /***********************************************************************/
278 struct MetricSectionSorter {
279 bool operator() (const MetricSection* a, const MetricSection* b) {
280 return a->start() < b->start();
284 TempoMap::TempoMap (framecnt_t fr)
293 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
294 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
296 t->set_movable (false);
297 m->set_movable (false);
299 /* note: frame time is correct (zero) for both of these */
301 metrics.push_back (t);
302 metrics.push_back (m);
305 TempoMap::~TempoMap ()
310 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
312 bool removed = false;
315 Glib::RWLock::WriterLock lm (lock);
318 for (i = metrics.begin(); i != metrics.end(); ++i) {
319 if (dynamic_cast<TempoSection*> (*i) != 0) {
320 if (tempo.frame() == (*i)->frame()) {
321 if ((*i)->movable()) {
330 if (removed && complete_operation) {
331 recompute_map (false);
335 if (removed && complete_operation) {
336 PropertyChanged (PropertyChange ());
341 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
343 bool removed = false;
346 Glib::RWLock::WriterLock lm (lock);
349 for (i = metrics.begin(); i != metrics.end(); ++i) {
350 if (dynamic_cast<MeterSection*> (*i) != 0) {
351 if (tempo.frame() == (*i)->frame()) {
352 if ((*i)->movable()) {
361 if (removed && complete_operation) {
362 recompute_map (true);
366 if (removed && complete_operation) {
367 PropertyChanged (PropertyChange ());
372 TempoMap::do_insert (MetricSection* section)
374 bool need_add = true;
376 assert (section->start().ticks == 0);
378 /* we only allow new meters to be inserted on beat 1 of an existing
382 if (dynamic_cast<MeterSection*>(section)) {
384 /* we need to (potentially) update the BBT times of tempo
385 sections based on this new meter.
388 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
390 BBT_Time corrected = section->start();
394 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
395 section->start(), corrected) << endmsg;
397 section->set_start (corrected);
403 /* Look for any existing MetricSection that is of the same type and
404 in the same bar as the new one, and remove it before adding
405 the new one. Note that this means that if we find a matching,
406 existing section, we can break out of the loop since we're
407 guaranteed that there is only one such match.
410 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
412 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
413 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
415 if (iter_is_tempo && insert_is_tempo) {
419 if ((*i)->start().bars == section->start().bars &&
420 (*i)->start().beats == section->start().beats) {
422 if (!(*i)->movable()) {
424 /* can't (re)move this section, so overwrite
425 * its data content (but not its properties as
429 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
437 } else if (!iter_is_tempo && !insert_is_tempo) {
441 if ((*i)->start().bars == section->start().bars) {
443 if (!(*i)->movable()) {
445 /* can't (re)move this section, so overwrite
446 * its data content (but not its properties as
450 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
460 /* non-matching types, so we don't care */
464 /* Add the given MetricSection, if we didn't just reset an existing
472 for (i = metrics.begin(); i != metrics.end(); ++i) {
473 if ((*i)->start() > section->start()) {
478 metrics.insert (i, section);
483 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
485 const TempoSection& first (first_tempo());
487 if (ts.start() != first.start()) {
488 remove_tempo (ts, false);
489 add_tempo (tempo, where);
492 Glib::RWLock::WriterLock lm (lock);
493 /* cannot move the first tempo section */
494 *((Tempo*)&first) = tempo;
495 recompute_map (false);
499 PropertyChanged (PropertyChange ());
503 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
506 Glib::RWLock::WriterLock lm (lock);
508 /* new tempos always start on a beat */
511 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
513 /* find the meter to use to set the bar offset of this
517 const Meter* meter = &first_meter();
519 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
520 at something, because we insert the default tempo and meter during
521 TempoMap construction.
523 now see if we can find better candidates.
526 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
528 const MeterSection* m;
530 if (where < (*i)->start()) {
534 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
539 ts->update_bar_offset_from_bbt (*meter);
545 recompute_map (false);
549 PropertyChanged (PropertyChange ());
553 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
555 const MeterSection& first (first_meter());
557 if (ms.start() != first.start()) {
558 remove_meter (ms, false);
559 add_meter (meter, where);
562 Glib::RWLock::WriterLock lm (lock);
563 /* cannot move the first meter section */
564 *((Meter*)&first) = meter;
565 recompute_map (true);
569 PropertyChanged (PropertyChange ());
573 TempoMap::add_meter (const Meter& meter, BBT_Time where)
576 Glib::RWLock::WriterLock lm (lock);
578 /* a new meter always starts a new bar on the first beat. so
579 round the start time appropriately. remember that
580 `where' is based on the existing tempo map, not
581 the result after we insert the new meter.
585 if (where.beats != 1) {
590 /* new meters *always* start on a beat. */
593 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
594 recompute_map (true);
599 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
604 PropertyChanged (PropertyChange ());
608 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
610 Tempo newtempo (beats_per_minute, note_type);
613 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
614 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
616 Glib::RWLock::WriterLock lm (lock);
617 *((Tempo*) t) = newtempo;
618 recompute_map (false);
620 PropertyChanged (PropertyChange ());
627 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
629 Tempo newtempo (beats_per_minute, note_type);
635 /* find the TempoSection immediately preceding "where"
638 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
640 if ((*i)->frame() > where) {
646 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
656 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
666 Glib::RWLock::WriterLock lm (lock);
667 /* cannot move the first tempo section */
668 *((Tempo*)prev) = newtempo;
669 recompute_map (false);
672 PropertyChanged (PropertyChange ());
676 TempoMap::first_meter () const
678 const MeterSection *m = 0;
680 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
681 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
686 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
692 TempoMap::first_tempo () const
694 const TempoSection *t = 0;
696 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
697 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
702 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
708 TempoMap::require_map_to (framepos_t pos)
710 Glib::RWLock::WriterLock lm (lock);
712 if (_map.empty() || _map.back().frame < pos) {
718 TempoMap::require_map_to (const BBT_Time& bbt)
720 Glib::RWLock::WriterLock lm (lock);
722 /* since we have no idea where BBT is if its off the map, see the last
723 * point in the map is past BBT, and if not add an arbitrary amount of
727 int additional_minutes = 1;
730 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
733 /* add some more distance, using bigger steps each time */
734 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
735 additional_minutes *= 2;
740 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
742 /* CALLER MUST HOLD WRITE LOCK */
744 MeterSection* meter = 0;
745 TempoSection* tempo = 0;
746 double current_frame;
748 Metrics::iterator next_metric;
752 /* we will actually stop once we hit
758 if (!_map.empty ()) {
759 /* never allow the map to be shortened */
760 end = max (end, _map.back().frame);
764 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
766 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
769 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
775 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
778 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
784 /* assumes that the first meter & tempo are at frame zero */
786 meter->set_frame (0);
787 tempo->set_frame (0);
789 /* assumes that the first meter & tempo are at 1|1|0 */
794 if (reassign_tempo_bbt) {
796 MeterSection* rmeter = meter;
798 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
800 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
805 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
807 /* reassign the BBT time of this tempo section
808 * based on its bar offset position.
811 ts->update_bbt_time_from_bar_offset (*rmeter);
813 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
816 fatal << _("programming error: unhandled MetricSection type") << endmsg;
822 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
824 next_metric = metrics.begin();
825 ++next_metric; // skip meter (or tempo)
826 ++next_metric; // skip tempo (or meter)
830 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
831 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
834 /* silly call from Session::process() during startup
839 _extend_map (tempo, meter, next_metric, current, current_frame, end);
843 TempoMap::extend_map (framepos_t end)
845 /* CALLER MUST HOLD WRITE LOCK */
848 recompute_map (false, end);
852 BBTPointList::const_iterator i = _map.end();
853 Metrics::iterator next_metric;
857 BBT_Time last_metric_start;
859 if ((*i).tempo->frame() > (*i).meter->frame()) {
860 last_metric_start = (*i).tempo->start();
862 last_metric_start = (*i).meter->start();
865 /* find the metric immediately after the tempo + meter sections for the
866 * last point in the map
869 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
870 if ((*next_metric)->start() > last_metric_start) {
875 /* we cast away const here because this is the one place where we need
876 * to actually modify the frame time of each metric section.
879 _extend_map (const_cast<TempoSection*> ((*i).tempo),
880 const_cast<MeterSection*> ((*i).meter),
881 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
885 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
886 Metrics::iterator next_metric,
887 BBT_Time current, framepos_t current_frame, framepos_t end)
889 /* CALLER MUST HOLD WRITE LOCK */
894 framepos_t bar_start_frame;
896 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
898 if (current.beats == 1) {
899 bar_start_frame = current_frame;
904 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
906 while (current_frame < end) {
909 current_frame += beat_frames;
911 if (current.beats > meter->divisions_per_bar()) {
916 if (next_metric != metrics.end()) {
918 /* no operator >= so invert operator < */
920 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
922 if (!(current < (*next_metric)->start())) {
925 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
929 /* new tempo section: if its on a beat,
930 * we don't have to do anything other
931 * than recompute various distances,
932 * done further below as we transition
933 * the next metric section.
935 * if its not on the beat, we have to
936 * compute the duration of the beat it
937 * is within, which will be different
938 * from the preceding following ones
939 * since it takes part of its duration
940 * from the preceding tempo and part
941 * from this new tempo.
944 if (tempo->start().ticks != 0) {
946 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
948 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
949 tempo->start(), current_frame, tempo->bar_offset()));
951 /* back up to previous beat */
952 current_frame -= beat_frames;
954 /* set tempo section location
955 * based on offset from last
958 tempo->set_frame (bar_start_frame +
959 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
961 /* advance to the location of
962 * the new (adjusted) beat. do
963 * this by figuring out the
964 * offset within the beat that
965 * would have been there
967 * change. then stretch the
971 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
973 current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
975 /* next metric doesn't have to
976 * match this precisely to
979 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
983 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
984 tempo->start(), current_frame));
985 tempo->set_frame (current_frame);
988 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
992 /* new meter section: always defines the
996 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
997 meter->start(), current, current_frame));
999 assert (current.beats == 1);
1001 meter->set_frame (current_frame);
1004 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1006 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1007 beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
1011 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1012 /* same position so go back and set this one up before advancing
1019 if (current.beats == 1) {
1020 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1021 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
1022 bar_start_frame = current_frame;
1024 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1025 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
1028 if (next_metric == metrics.end()) {
1029 /* no more metrics - we've timestamped them all, stop here */
1030 if (end == max_framepos) {
1031 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
1032 current.bars, current.beats, current_frame));
1040 TempoMap::metric_at (framepos_t frame) const
1042 Glib::RWLock::ReaderLock lm (lock);
1043 TempoMetric m (first_meter(), first_tempo());
1047 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1048 at something, because we insert the default tempo and meter during
1049 TempoMap construction.
1051 now see if we can find better candidates.
1054 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1056 if ((*i)->frame() > frame) {
1060 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1061 m.set_tempo (*tempo);
1062 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1063 m.set_meter (*meter);
1066 m.set_frame ((*i)->frame ());
1067 m.set_start ((*i)->start ());
1074 TempoMap::metric_at (BBT_Time bbt) const
1076 Glib::RWLock::ReaderLock lm (lock);
1077 TempoMetric m (first_meter(), first_tempo());
1081 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1082 at something, because we insert the default tempo and meter during
1083 TempoMap construction.
1085 now see if we can find better candidates.
1088 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1090 BBT_Time section_start ((*i)->start());
1092 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1096 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1097 m.set_tempo (*tempo);
1098 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1099 m.set_meter (*meter);
1102 m.set_frame ((*i)->frame ());
1103 m.set_start (section_start);
1110 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1112 require_map_to (frame);
1114 Glib::RWLock::ReaderLock lm (lock);
1120 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1124 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1128 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1130 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1133 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1136 if (_map.empty() || _map.back().frame < frame) {
1137 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1140 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1144 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1146 /* CALLER MUST HOLD READ LOCK */
1148 bbt.bars = (*i).bar;
1149 bbt.beats = (*i).beat;
1151 if ((*i).frame == frame) {
1154 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1155 BBT_Time::ticks_per_beat);
1160 TempoMap::frame_time (const BBT_Time& bbt)
1163 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1167 if (bbt.beats < 1) {
1168 throw std::logic_error ("beats are counted from one");
1171 require_map_to (bbt);
1173 Glib::RWLock::ReaderLock lm (lock);
1175 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1176 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1178 if (bbt.ticks != 0) {
1179 return ((*e).frame - (*s).frame) +
1180 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1182 return ((*e).frame - (*s).frame);
1187 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1190 bbt_time (pos, when);
1192 Glib::RWLock::ReaderLock lm (lock);
1193 return bbt_duration_at_unlocked (when, bbt, dir);
1197 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
1199 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1203 /* round back to the previous precise beat */
1204 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1205 BBTPointList::const_iterator start (wi);
1206 double tick_frames = 0;
1208 assert (wi != _map.end());
1210 /* compute how much rounding we did because of non-zero ticks */
1212 if (when.ticks != 0) {
1213 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1219 while (wi != _map.end() && bars < bbt.bars) {
1221 if ((*wi).is_bar()) {
1225 assert (wi != _map.end());
1227 while (wi != _map.end() && beats < bbt.beats) {
1231 assert (wi != _map.end());
1233 /* add any additional frames related to ticks in the added value */
1235 if (bbt.ticks != 0) {
1236 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1239 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1243 TempoMap::round_to_bar (framepos_t fr, int dir)
1245 return round_to_type (fr, dir, Bar);
1249 TempoMap::round_to_beat (framepos_t fr, int dir)
1251 return round_to_type (fr, dir, Beat);
1255 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1257 require_map_to (fr);
1259 Glib::RWLock::ReaderLock lm (lock);
1260 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1262 uint32_t ticks_one_subdivisions_worth;
1263 uint32_t difference;
1265 bbt_time (fr, the_beat, i);
1267 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1268 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1270 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1274 /* round to next (even if we're on a subdivision */
1276 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1279 /* right on the subdivision, so the difference is just the subdivision ticks */
1280 the_beat.ticks += ticks_one_subdivisions_worth;
1283 /* not on subdivision, compute distance to next subdivision */
1285 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1288 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1289 assert (i != _map.end());
1291 assert (i != _map.end());
1292 the_beat.ticks -= BBT_Time::ticks_per_beat;
1296 } else if (dir < 0) {
1298 /* round to previous (even if we're on a subdivision) */
1300 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1303 /* right on the subdivision, so the difference is just the subdivision ticks */
1304 difference = ticks_one_subdivisions_worth;
1306 /* not on subdivision, compute distance to previous subdivision, which
1307 is just the modulus.
1313 if (the_beat.ticks < difference) {
1314 if (i == _map.begin()) {
1315 /* can't go backwards from wherever pos is, so just return it */
1319 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1321 the_beat.ticks -= difference;
1325 /* round to nearest */
1329 /* compute the distance to the previous and next subdivision */
1331 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1333 /* closer to the next subdivision, so shift forward */
1335 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1337 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1339 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1340 assert (i != _map.end());
1342 assert (i != _map.end());
1343 the_beat.ticks -= BBT_Time::ticks_per_beat;
1344 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1347 } else if (rem > 0) {
1349 /* closer to previous subdivision, so shift backward */
1351 if (rem > the_beat.ticks) {
1352 if (i == _map.begin()) {
1353 /* can't go backwards past zero, so ... */
1356 /* step back to previous beat */
1358 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1359 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1361 the_beat.ticks = lrint (the_beat.ticks - rem);
1362 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1365 /* on the subdivision, do nothing */
1369 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1370 (*i).tempo->frames_per_beat (_frame_rate);
1374 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1376 require_map_to (frame);
1378 Glib::RWLock::ReaderLock lm (lock);
1379 BBTPointList::const_iterator fi;
1382 fi = bbt_after_or_at (frame);
1384 fi = bbt_before_or_at (frame);
1387 assert (fi != _map.end());
1389 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,
1390 (type == Bar ? "bar" : "beat")));
1395 /* find bar previous to 'frame' */
1397 if (fi == _map.begin()) {
1401 if ((*fi).is_bar() && (*fi).frame == frame) {
1405 while (!(*fi).is_bar()) {
1406 if (fi == _map.begin()) {
1411 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1412 (*fi).bar, (*fi).beat, (*fi).frame));
1415 } else if (dir > 0) {
1417 /* find bar following 'frame' */
1419 if ((*fi).is_bar() && (*fi).frame == frame) {
1423 while (!(*fi).is_bar()) {
1425 if (fi == _map.end()) {
1431 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1432 (*fi).bar, (*fi).beat, (*fi).frame));
1437 /* true rounding: find nearest bar */
1439 BBTPointList::const_iterator prev = fi;
1440 BBTPointList::const_iterator next = fi;
1442 if ((*fi).frame == frame) {
1446 while ((*prev).beat != 1) {
1447 if (prev == _map.begin()) {
1453 while ((next != _map.end()) && (*next).beat != 1) {
1457 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1458 return (*prev).frame;
1460 return (*next).frame;
1470 if (fi == _map.begin()) {
1474 if ((*fi).frame >= frame) {
1475 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1478 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1479 (*fi).bar, (*fi).beat, (*fi).frame));
1481 } else if (dir > 0) {
1482 if ((*fi).frame <= frame) {
1483 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1486 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1487 (*fi).bar, (*fi).beat, (*fi).frame));
1490 /* find beat nearest to frame */
1491 if ((*fi).frame == frame) {
1495 BBTPointList::const_iterator prev = fi;
1496 BBTPointList::const_iterator next = fi;
1498 /* fi is already the beat before_or_at frame, and
1499 we've just established that its not at frame, so its
1500 the beat before frame.
1504 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1505 return (*prev).frame;
1507 return (*next).frame;
1519 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1520 TempoMap::BBTPointList::const_iterator& end,
1521 framepos_t lower, framepos_t upper)
1524 Glib::RWLock::WriterLock lm (lock);
1525 if (_map.empty() || (_map.back().frame < upper)) {
1526 recompute_map (false, upper);
1530 begin = lower_bound (_map.begin(), _map.end(), lower);
1531 end = upper_bound (_map.begin(), _map.end(), upper);
1535 TempoMap::tempo_section_at (framepos_t frame) const
1537 Glib::RWLock::ReaderLock lm (lock);
1538 Metrics::const_iterator i;
1539 TempoSection* prev = 0;
1541 for (i = metrics.begin(); i != metrics.end(); ++i) {
1544 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1546 if ((*i)->frame() > frame) {
1562 TempoMap::tempo_at (framepos_t frame) const
1564 TempoMetric m (metric_at (frame));
1570 TempoMap::meter_at (framepos_t frame) const
1572 TempoMetric m (metric_at (frame));
1577 TempoMap::get_state ()
1579 Metrics::const_iterator i;
1580 XMLNode *root = new XMLNode ("TempoMap");
1583 Glib::RWLock::ReaderLock lm (lock);
1584 for (i = metrics.begin(); i != metrics.end(); ++i) {
1585 root->add_child_nocopy ((*i)->get_state());
1593 TempoMap::set_state (const XMLNode& node, int /*version*/)
1596 Glib::RWLock::WriterLock lm (lock);
1599 XMLNodeConstIterator niter;
1600 Metrics old_metrics (metrics);
1601 MeterSection* last_meter = 0;
1604 nlist = node.children();
1606 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1607 XMLNode* child = *niter;
1609 if (child->name() == TempoSection::xml_state_node_name) {
1612 TempoSection* ts = new TempoSection (*child);
1613 metrics.push_back (ts);
1615 if (ts->bar_offset() < 0.0) {
1617 ts->update_bar_offset_from_bbt (*last_meter);
1622 catch (failed_constructor& err){
1623 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1624 metrics = old_metrics;
1628 } else if (child->name() == MeterSection::xml_state_node_name) {
1631 MeterSection* ms = new MeterSection (*child);
1632 metrics.push_back (ms);
1636 catch (failed_constructor& err) {
1637 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1638 metrics = old_metrics;
1644 if (niter == nlist.end()) {
1645 MetricSectionSorter cmp;
1649 recompute_map (true, -1);
1652 PropertyChanged (PropertyChange ());
1658 TempoMap::dump (std::ostream& o) const
1660 Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1661 const MeterSection* m;
1662 const TempoSection* t;
1664 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1666 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1667 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? "
1668 << t->movable() << ')' << endl;
1669 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1670 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1671 << " (movable? " << m->movable() << ')' << endl;
1677 TempoMap::n_tempos() const
1679 Glib::RWLock::ReaderLock lm (lock);
1682 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1683 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1692 TempoMap::n_meters() const
1694 Glib::RWLock::ReaderLock lm (lock);
1697 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1698 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1707 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1710 Glib::RWLock::WriterLock lm (lock);
1711 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1712 if ((*i)->frame() >= where && (*i)->movable ()) {
1713 (*i)->set_frame ((*i)->frame() + amount);
1717 /* now reset the BBT time of all metrics, based on their new
1718 * audio time. This is the only place where we do this reverse
1722 Metrics::iterator i;
1723 const MeterSection* meter;
1724 const TempoSection* tempo;
1728 meter = &first_meter ();
1729 tempo = &first_tempo ();
1734 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1737 MetricSection* prev = 0;
1739 for (i = metrics.begin(); i != metrics.end(); ++i) {
1742 TempoMetric metric (*meter, *tempo);
1745 metric.set_start (prev->start());
1746 metric.set_frame (prev->frame());
1748 // metric will be at frames=0 bbt=1|1|0 by default
1749 // which is correct for our purpose
1752 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1753 bbt_time ((*i)->frame(), bbt, bi);
1755 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1761 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1762 /* round up to next beat */
1768 if (bbt.beats != 1) {
1769 /* round up to next bar */
1775 // cerr << bbt << endl;
1777 (*i)->set_start (bbt);
1779 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1781 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1782 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1784 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1786 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1793 recompute_map (true);
1797 PropertyChanged (PropertyChange ());
1800 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1801 * pos can be -ve, if required.
1804 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1806 Glib::RWLock::ReaderLock lm (lock);
1807 Metrics::const_iterator next_tempo;
1808 const TempoSection* tempo = 0;
1810 /* Find the starting tempo metric */
1812 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1814 const TempoSection* t;
1816 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1818 /* This is a bit of a hack, but pos could be -ve, and if it is,
1819 we consider the initial metric changes (at time 0) to actually
1820 be in effect at pos.
1823 framepos_t f = (*next_tempo)->frame ();
1825 if (pos < 0 && f == 0) {
1839 tempo -> the Tempo for "pos"
1840 next_tempo -> first tempo after "pos", possibly metrics.end()
1843 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1844 pos, beats, *((Tempo*)tempo), tempo->frame()));
1848 /* Distance to the end of this section in frames */
1849 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1851 /* Distance to the end in beats */
1852 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1854 /* Amount to subtract this time */
1855 double const delta = min (distance_beats, beats);
1857 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1858 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1859 distance_frames, distance_beats));
1863 pos += delta * tempo->frames_per_beat (_frame_rate);
1865 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1867 /* step forwards to next tempo section */
1869 if (next_tempo != metrics.end()) {
1871 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1873 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1874 *((Tempo*)tempo), tempo->frame(),
1875 tempo->frames_per_beat (_frame_rate)));
1877 while (next_tempo != metrics.end ()) {
1881 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1891 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
1893 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1895 Glib::RWLock::ReaderLock lm (lock);
1896 Metrics::const_reverse_iterator prev_tempo;
1897 const TempoSection* tempo = 0;
1899 /* Find the starting tempo metric */
1901 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1903 const TempoSection* t;
1905 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1907 /* This is a bit of a hack, but pos could be -ve, and if it is,
1908 we consider the initial metric changes (at time 0) to actually
1909 be in effect at pos.
1912 framepos_t f = (*prev_tempo)->frame ();
1914 if (pos < 0 && f == 0) {
1918 /* this is slightly more complex than the forward case
1919 because we reach the tempo in effect at pos after
1920 passing through pos (rather before, as in the
1921 forward case). having done that, we then need to
1922 keep going to get the previous tempo (or
1928 /* first tempo with position at or
1932 } else if (f < pos) {
1933 /* some other tempo section that
1934 is even earlier than 'tempo'
1942 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1943 pos, beats, *((Tempo*)tempo), tempo->frame(),
1944 prev_tempo == metrics.rend()));
1948 tempo -> the Tempo for "pos"
1949 prev_tempo -> the first metric before "pos", possibly metrics.rend()
1954 /* Distance to the start of this section in frames */
1955 framecnt_t distance_frames = (pos - tempo->frame());
1957 /* Distance to the start in beats */
1958 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1960 /* Amount to subtract this time */
1961 double const sub = min (distance_beats, beats);
1963 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1964 tempo->frame(), distance_frames, distance_beats));
1968 pos -= sub * tempo->frames_per_beat (_frame_rate);
1970 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1971 (prev_tempo == metrics.rend())));
1973 /* step backwards to prior TempoSection */
1975 if (prev_tempo != metrics.rend()) {
1977 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1979 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1980 *((Tempo*)tempo), tempo->frame(),
1981 tempo->frames_per_beat (_frame_rate)));
1983 while (prev_tempo != metrics.rend ()) {
1987 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1992 pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
2000 /** Add the BBT interval op to pos and return the result */
2002 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2004 Glib::RWLock::ReaderLock lm (lock);
2005 Metrics::const_iterator i;
2006 const MeterSection* meter;
2007 const MeterSection* m;
2008 const TempoSection* tempo;
2009 const TempoSection* t;
2010 double frames_per_beat;
2011 framepos_t effective_pos = max (pos, (framepos_t) 0);
2013 meter = &first_meter ();
2014 tempo = &first_tempo ();
2019 /* find the starting metrics for tempo & meter */
2021 for (i = metrics.begin(); i != metrics.end(); ++i) {
2023 if ((*i)->frame() > effective_pos) {
2027 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2029 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2036 meter -> the Meter for "pos"
2037 tempo -> the Tempo for "pos"
2038 i -> for first new metric after "pos", possibly metrics.end()
2041 /* now comes the complicated part. we have to add one beat a time,
2042 checking for a new metric on every beat.
2045 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2054 /* check if we need to use a new metric section: has adding frames moved us
2055 to or after the start of the next metric section? in which case, use it.
2058 if (i != metrics.end()) {
2059 if ((*i)->frame() <= pos) {
2061 /* about to change tempo or meter, so add the
2062 * number of frames for the bars we've just
2063 * traversed before we change the
2064 * frames_per_beat value.
2067 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2070 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2072 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2076 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2083 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2089 /* given the current meter, have we gone past the end of the bar ? */
2094 /* check if we need to use a new metric section: has adding frames moved us
2095 to or after the start of the next metric section? in which case, use it.
2098 if (i != metrics.end()) {
2099 if ((*i)->frame() <= pos) {
2101 /* about to change tempo or meter, so add the
2102 * number of frames for the beats we've just
2103 * traversed before we change the
2104 * frames_per_beat value.
2107 pos += llrint (beats * frames_per_beat);
2110 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2112 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2116 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2121 pos += llrint (beats * frames_per_beat);
2124 if (op.ticks >= BBT_Time::ticks_per_beat) {
2125 pos += llrint (frames_per_beat + /* extra beat */
2126 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2127 (double) BBT_Time::ticks_per_beat)));
2129 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2136 /** Count the number of beats that are equivalent to distance when going forward,
2140 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2142 Glib::RWLock::ReaderLock lm (lock);
2143 Metrics::const_iterator next_tempo;
2144 const TempoSection* tempo = 0;
2145 framepos_t effective_pos = max (pos, (framepos_t) 0);
2147 /* Find the relevant initial tempo metric */
2149 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2151 const TempoSection* t;
2153 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2155 if ((*next_tempo)->frame() > effective_pos) {
2165 tempo -> the Tempo for "pos"
2166 next_tempo -> the next tempo after "pos", possibly metrics.end()
2171 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
2172 pos, distance, *((Tempo*)tempo), tempo->frame()));
2174 Evoral::MusicalTime beats = 0;
2178 /* End of this section */
2180 /* Distance to `end' in frames */
2181 framepos_t distance_to_end;
2183 if (next_tempo == metrics.end ()) {
2184 /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
2186 distance_to_end = max_framepos;
2188 end = (*next_tempo)->frame ();
2189 distance_to_end = end - pos;
2192 /* Amount to subtract this time */
2193 double const sub = min (distance, distance_to_end);
2195 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
2196 distance_to_end, sub));
2202 beats += sub / tempo->frames_per_beat (_frame_rate);
2204 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
2205 pos, beats, distance));
2207 /* Move on if there's anything to move to */
2209 if (next_tempo != metrics.end()) {
2211 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2213 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2214 *((Tempo*)tempo), tempo->frame(),
2215 tempo->frames_per_beat (_frame_rate)));
2217 while (next_tempo != metrics.end ()) {
2221 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2226 if (next_tempo == metrics.end()) {
2227 DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
2229 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
2230 **next_tempo, (*next_tempo)->frame()));
2240 TempoMap::BBTPointList::const_iterator
2241 TempoMap::bbt_before_or_at (framepos_t pos)
2243 /* CALLER MUST HOLD READ LOCK */
2245 BBTPointList::const_iterator i;
2248 /* not really correct, but we should catch pos < 0 at a higher
2251 return _map.begin();
2254 i = lower_bound (_map.begin(), _map.end(), pos);
2255 assert (i != _map.end());
2256 if ((*i).frame > pos) {
2257 assert (i != _map.begin());
2264 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2269 TempoMap::BBTPointList::const_iterator
2270 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2272 BBTPointList::const_iterator i;
2275 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2276 assert (i != _map.end());
2277 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2278 assert (i != _map.begin());
2284 TempoMap::BBTPointList::const_iterator
2285 TempoMap::bbt_after_or_at (framepos_t pos)
2287 /* CALLER MUST HOLD READ LOCK */
2289 BBTPointList::const_iterator i;
2291 if (_map.back().frame == pos) {
2293 assert (i != _map.begin());
2298 i = upper_bound (_map.begin(), _map.end(), pos);
2299 assert (i != _map.end());
2304 operator<< (std::ostream& o, const Meter& m) {
2305 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2309 operator<< (std::ostream& o, const Tempo& t) {
2310 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2314 operator<< (std::ostream& o, const MetricSection& section) {
2316 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2318 const TempoSection* ts;
2319 const MeterSection* ms;
2321 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2322 o << *((Tempo*) ts);
2323 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2324 o << *((Meter*) ms);