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/Beats.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 XMLProperty const * prop;
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);
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 XMLProperty const * prop;
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);
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 // these leak memory, well Metrics does
288 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
289 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
291 t->set_movable (false);
292 m->set_movable (false);
294 /* note: frame time is correct (zero) for both of these */
296 metrics.push_back (t);
297 metrics.push_back (m);
300 TempoMap::~TempoMap ()
305 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
307 bool removed = false;
310 Glib::Threads::RWLock::WriterLock lm (lock);
311 if ((removed = remove_tempo_locked (tempo))) {
312 if (complete_operation) {
313 recompute_map (true);
318 if (removed && complete_operation) {
319 PropertyChanged (PropertyChange ());
324 TempoMap::remove_tempo_locked (const TempoSection& tempo)
328 for (i = metrics.begin(); i != metrics.end(); ++i) {
329 if (dynamic_cast<TempoSection*> (*i) != 0) {
330 if (tempo.frame() == (*i)->frame()) {
331 if ((*i)->movable()) {
343 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
345 bool removed = false;
348 Glib::Threads::RWLock::WriterLock lm (lock);
349 if ((removed = remove_meter_locked (tempo))) {
350 if (complete_operation) {
351 recompute_map (true);
356 if (removed && complete_operation) {
357 PropertyChanged (PropertyChange ());
362 TempoMap::remove_meter_locked (const MeterSection& tempo)
366 for (i = metrics.begin(); i != metrics.end(); ++i) {
367 if (dynamic_cast<MeterSection*> (*i) != 0) {
368 if (tempo.frame() == (*i)->frame()) {
369 if ((*i)->movable()) {
381 TempoMap::do_insert (MetricSection* section)
383 bool need_add = true;
385 assert (section->start().ticks == 0);
387 /* we only allow new meters to be inserted on beat 1 of an existing
391 if (dynamic_cast<MeterSection*>(section)) {
393 /* we need to (potentially) update the BBT times of tempo
394 sections based on this new meter.
397 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
399 BBT_Time corrected = section->start();
403 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
404 section->start(), corrected) << endmsg;
406 section->set_start (corrected);
412 /* Look for any existing MetricSection that is of the same type and
413 in the same bar as the new one, and remove it before adding
414 the new one. Note that this means that if we find a matching,
415 existing section, we can break out of the loop since we're
416 guaranteed that there is only one such match.
419 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
421 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
422 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
424 if (iter_is_tempo && insert_is_tempo) {
428 if ((*i)->start().bars == section->start().bars &&
429 (*i)->start().beats == section->start().beats) {
431 if (!(*i)->movable()) {
433 /* can't (re)move this section, so overwrite
434 * its data content (but not its properties as
438 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
446 } else if (!iter_is_tempo && !insert_is_tempo) {
450 if ((*i)->start().bars == section->start().bars) {
452 if (!(*i)->movable()) {
454 /* can't (re)move this section, so overwrite
455 * its data content (but not its properties as
459 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
469 /* non-matching types, so we don't care */
473 /* Add the given MetricSection, if we didn't just reset an existing
481 for (i = metrics.begin(); i != metrics.end(); ++i) {
482 if ((*i)->start() > section->start()) {
487 metrics.insert (i, section);
492 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
495 Glib::Threads::RWLock::WriterLock lm (lock);
496 TempoSection& first (first_tempo());
498 if (ts.start() != first.start()) {
499 remove_tempo_locked (ts);
500 add_tempo_locked (tempo, where, true);
503 /* cannot move the first tempo section */
504 *static_cast<Tempo*>(&first) = tempo;
505 recompute_map (false);
510 PropertyChanged (PropertyChange ());
514 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
517 Glib::Threads::RWLock::WriterLock lm (lock);
518 add_tempo_locked (tempo, where, true);
522 PropertyChanged (PropertyChange ());
526 TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute)
528 /* new tempos always start on a beat */
531 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
533 /* find the meter to use to set the bar offset of this
537 const Meter* meter = &first_meter();
539 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
540 at something, because we insert the default tempo and meter during
541 TempoMap construction.
543 now see if we can find better candidates.
546 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
548 const MeterSection* m;
550 if (where < (*i)->start()) {
554 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
559 ts->update_bar_offset_from_bbt (*meter);
566 recompute_map (false);
571 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
574 Glib::Threads::RWLock::WriterLock lm (lock);
575 MeterSection& first (first_meter());
577 if (ms.start() != first.start()) {
578 remove_meter_locked (ms);
579 add_meter_locked (meter, where, true);
581 /* cannot move the first meter section */
582 *static_cast<Meter*>(&first) = meter;
583 recompute_map (true);
587 PropertyChanged (PropertyChange ());
591 TempoMap::add_meter (const Meter& meter, BBT_Time where)
594 Glib::Threads::RWLock::WriterLock lm (lock);
595 add_meter_locked (meter, where, true);
600 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
605 PropertyChanged (PropertyChange ());
609 TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
611 /* a new meter always starts a new bar on the first beat. so
612 round the start time appropriately. remember that
613 `where' is based on the existing tempo map, not
614 the result after we insert the new meter.
618 if (where.beats != 1) {
623 /* new meters *always* start on a beat. */
626 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
629 recompute_map (true);
635 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
637 Tempo newtempo (beats_per_minute, note_type);
640 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
641 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
643 Glib::Threads::RWLock::WriterLock lm (lock);
644 *((Tempo*) t) = newtempo;
645 recompute_map (false);
647 PropertyChanged (PropertyChange ());
654 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
656 Tempo newtempo (beats_per_minute, note_type);
662 /* find the TempoSection immediately preceding "where"
665 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
667 if ((*i)->frame() > where) {
673 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
683 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
693 Glib::Threads::RWLock::WriterLock lm (lock);
694 /* cannot move the first tempo section */
695 *((Tempo*)prev) = newtempo;
696 recompute_map (false);
699 PropertyChanged (PropertyChange ());
703 TempoMap::first_meter () const
705 const MeterSection *m = 0;
707 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
708 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
713 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
714 abort(); /*NOTREACHED*/
719 TempoMap::first_meter ()
723 /* CALLER MUST HOLD LOCK */
725 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
726 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
731 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
732 abort(); /*NOTREACHED*/
737 TempoMap::first_tempo () const
739 const TempoSection *t = 0;
741 /* CALLER MUST HOLD LOCK */
743 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
744 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
749 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
750 abort(); /*NOTREACHED*/
755 TempoMap::first_tempo ()
759 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
760 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
765 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
766 abort(); /*NOTREACHED*/
771 TempoMap::require_map_to (framepos_t pos)
773 Glib::Threads::RWLock::WriterLock lm (lock);
775 if (_map.empty() || _map.back().frame < pos) {
781 TempoMap::require_map_to (const BBT_Time& bbt)
783 Glib::Threads::RWLock::WriterLock lm (lock);
785 /* since we have no idea where BBT is if its off the map, see the last
786 * point in the map is past BBT, and if not add an arbitrary amount of
790 int additional_minutes = 1;
793 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
796 /* add some more distance, using bigger steps each time */
797 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
798 additional_minutes *= 2;
803 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
805 /* CALLER MUST HOLD WRITE LOCK */
807 MeterSection* meter = 0;
808 TempoSection* tempo = 0;
809 double current_frame;
811 Metrics::iterator next_metric;
815 /* we will actually stop once we hit
821 if (!_map.empty ()) {
822 /* never allow the map to be shortened */
823 end = max (end, _map.back().frame);
827 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
829 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
832 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
840 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
843 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
851 /* assumes that the first meter & tempo are at frame zero */
853 meter->set_frame (0);
854 tempo->set_frame (0);
856 /* assumes that the first meter & tempo are at 1|1|0 */
861 if (reassign_tempo_bbt) {
863 MeterSection* rmeter = meter;
865 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
867 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
872 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
874 /* reassign the BBT time of this tempo section
875 * based on its bar offset position.
878 ts->update_bbt_time_from_bar_offset (*rmeter);
880 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
883 fatal << _("programming error: unhandled MetricSection type") << endmsg;
884 abort(); /*NOTREACHED*/
889 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
891 next_metric = metrics.begin();
892 ++next_metric; // skip meter (or tempo)
893 ++next_metric; // skip tempo (or meter)
897 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
898 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
901 /* silly call from Session::process() during startup
906 _extend_map (tempo, meter, next_metric, current, current_frame, end);
910 TempoMap::extend_map (framepos_t end)
912 /* CALLER MUST HOLD WRITE LOCK */
915 recompute_map (false, end);
919 BBTPointList::const_iterator i = _map.end();
920 Metrics::iterator next_metric;
924 BBT_Time last_metric_start;
926 if ((*i).tempo->frame() > (*i).meter->frame()) {
927 last_metric_start = (*i).tempo->start();
929 last_metric_start = (*i).meter->start();
932 /* find the metric immediately after the tempo + meter sections for the
933 * last point in the map
936 for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
937 if ((*next_metric)->start() > last_metric_start) {
942 /* we cast away const here because this is the one place where we need
943 * to actually modify the frame time of each metric section.
946 _extend_map (const_cast<TempoSection*> ((*i).tempo),
947 const_cast<MeterSection*> ((*i).meter),
948 next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
952 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
953 Metrics::iterator next_metric,
954 BBT_Time current, framepos_t current_frame, framepos_t end)
956 /* CALLER MUST HOLD WRITE LOCK */
961 double current_frame_exact;
962 framepos_t bar_start_frame;
964 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
966 if (current.beats == 1) {
967 bar_start_frame = current_frame;
972 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
973 current_frame_exact = current_frame;
975 while (current_frame < end) {
978 current_frame_exact += beat_frames;
979 current_frame = llrint(current_frame_exact);
981 if (current.beats > meter->divisions_per_bar()) {
986 if (next_metric != metrics.end()) {
988 /* no operator >= so invert operator < */
990 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
992 if (!(current < (*next_metric)->start())) {
995 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
999 /* new tempo section: if its on a beat,
1000 * we don't have to do anything other
1001 * than recompute various distances,
1002 * done further below as we transition
1003 * the next metric section.
1005 * if its not on the beat, we have to
1006 * compute the duration of the beat it
1007 * is within, which will be different
1008 * from the preceding following ones
1009 * since it takes part of its duration
1010 * from the preceding tempo and part
1011 * from this new tempo.
1014 if (tempo->start().ticks != 0) {
1016 double next_beat_frames = tempo->frames_per_beat (_frame_rate);
1018 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
1019 tempo->start(), current_frame, tempo->bar_offset()));
1021 /* back up to previous beat */
1022 current_frame_exact -= beat_frames;
1023 current_frame = llrint(current_frame_exact);
1025 /* set tempo section location
1026 * based on offset from last
1029 tempo->set_frame (bar_start_frame +
1030 llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
1032 /* advance to the location of
1033 * the new (adjusted) beat. do
1034 * this by figuring out the
1035 * offset within the beat that
1036 * would have been there
1038 * change. then stretch the
1042 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
1044 current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
1045 current_frame = llrint(current_frame_exact);
1047 /* next metric doesn't have to
1048 * match this precisely to
1049 * merit a reloop ...
1051 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
1055 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
1056 tempo->start(), current_frame));
1057 tempo->set_frame (current_frame);
1060 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
1064 /* new meter section: always defines the
1068 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
1069 meter->start(), current, current_frame));
1071 assert (current.beats == 1);
1073 meter->set_frame (current_frame);
1076 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
1078 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
1079 beat_frames, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo)));
1083 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1084 /* same position so go back and set this one up before advancing
1092 if (current.beats == 1) {
1093 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1094 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
1095 bar_start_frame = current_frame;
1097 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1098 _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
1101 if (next_metric == metrics.end()) {
1102 /* no more metrics - we've timestamped them all, stop here */
1103 if (end == max_framepos) {
1104 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n",
1105 current.bars, current.beats, current_frame));
1113 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1115 Glib::Threads::RWLock::ReaderLock lm (lock);
1116 TempoMetric m (first_meter(), first_tempo());
1118 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1119 at something, because we insert the default tempo and meter during
1120 TempoMap construction.
1122 now see if we can find better candidates.
1125 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1127 if ((*i)->frame() > frame) {
1142 TempoMap::metric_at (BBT_Time bbt) const
1144 Glib::Threads::RWLock::ReaderLock lm (lock);
1145 TempoMetric m (first_meter(), first_tempo());
1147 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1148 at something, because we insert the default tempo and meter during
1149 TempoMap construction.
1151 now see if we can find better candidates.
1154 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1156 BBT_Time section_start ((*i)->start());
1158 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1169 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1171 require_map_to (frame);
1173 Glib::Threads::RWLock::ReaderLock lm (lock);
1179 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1183 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1187 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1189 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1192 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1195 if (_map.empty() || _map.back().frame < frame) {
1196 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1199 return bbt_time (frame, bbt, bbt_before_or_at (frame));
1203 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1205 /* CALLER MUST HOLD READ LOCK */
1207 bbt.bars = (*i).bar;
1208 bbt.beats = (*i).beat;
1210 if ((*i).frame == frame) {
1213 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1214 BBT_Time::ticks_per_beat);
1219 TempoMap::frame_time (const BBT_Time& bbt)
1222 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1226 if (bbt.beats < 1) {
1227 throw std::logic_error ("beats are counted from one");
1230 require_map_to (bbt);
1232 Glib::Threads::RWLock::ReaderLock lm (lock);
1234 BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1235 BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1237 if (bbt.ticks != 0) {
1238 return ((*e).frame - (*s).frame) +
1239 llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1241 return ((*e).frame - (*s).frame);
1246 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1249 bbt_time (pos, when);
1251 Glib::Threads::RWLock::ReaderLock lm (lock);
1252 return bbt_duration_at_unlocked (when, bbt, dir);
1256 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/)
1258 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1262 /* round back to the previous precise beat */
1263 BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1264 BBTPointList::const_iterator start (wi);
1266 assert (wi != _map.end());
1271 while (wi != _map.end() && bars < bbt.bars) {
1273 if ((*wi).is_bar()) {
1277 assert (wi != _map.end());
1279 while (wi != _map.end() && beats < bbt.beats) {
1283 assert (wi != _map.end());
1285 /* add any additional frames related to ticks in the added value */
1287 if (bbt.ticks != 0) {
1288 return ((*wi).frame - (*start).frame) +
1289 (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1291 return ((*wi).frame - (*start).frame);
1296 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1298 return round_to_type (fr, dir, Bar);
1302 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1304 return round_to_type (fr, dir, Beat);
1308 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1310 require_map_to (fr);
1312 Glib::Threads::RWLock::ReaderLock lm (lock);
1313 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1315 uint32_t ticks_one_subdivisions_worth;
1317 bbt_time (fr, the_beat, i);
1319 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1320 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1322 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1326 /* round to next (or same iff dir == RoundUpMaybe) */
1328 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1330 if (mod == 0 && dir == RoundUpMaybe) {
1331 /* right on the subdivision, which is fine, so do nothing */
1333 } else if (mod == 0) {
1334 /* right on the subdivision, so the difference is just the subdivision ticks */
1335 the_beat.ticks += ticks_one_subdivisions_worth;
1338 /* not on subdivision, compute distance to next subdivision */
1340 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1343 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1344 assert (i != _map.end());
1346 assert (i != _map.end());
1347 the_beat.ticks -= BBT_Time::ticks_per_beat;
1351 } else if (dir < 0) {
1353 /* round to previous (or same iff dir == RoundDownMaybe) */
1355 uint32_t difference = the_beat.ticks % ticks_one_subdivisions_worth;
1357 if (difference == 0 && dir == RoundDownAlways) {
1358 /* right on the subdivision, but force-rounding down,
1359 so the difference is just the subdivision ticks */
1360 difference = ticks_one_subdivisions_worth;
1363 if (the_beat.ticks < difference) {
1364 if (i == _map.begin()) {
1365 /* can't go backwards from wherever pos is, so just return it */
1369 the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1371 the_beat.ticks -= difference;
1375 /* round to nearest */
1379 /* compute the distance to the previous and next subdivision */
1381 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1383 /* closer to the next subdivision, so shift forward */
1385 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1387 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1389 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1390 assert (i != _map.end());
1392 assert (i != _map.end());
1393 the_beat.ticks -= BBT_Time::ticks_per_beat;
1394 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1397 } else if (rem > 0) {
1399 /* closer to previous subdivision, so shift backward */
1401 if (rem > the_beat.ticks) {
1402 if (i == _map.begin()) {
1403 /* can't go backwards past zero, so ... */
1406 /* step back to previous beat */
1408 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1409 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1411 the_beat.ticks = lrint (the_beat.ticks - rem);
1412 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1415 /* on the subdivision, do nothing */
1419 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
1420 (*i).tempo->frames_per_beat (_frame_rate);
1424 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1426 require_map_to (frame);
1428 Glib::Threads::RWLock::ReaderLock lm (lock);
1429 BBTPointList::const_iterator fi;
1432 fi = bbt_after_or_at (frame);
1434 fi = bbt_before_or_at (frame);
1437 assert (fi != _map.end());
1439 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,
1440 (type == Bar ? "bar" : "beat")));
1445 /* find bar previous to 'frame' */
1447 if (fi == _map.begin()) {
1451 if ((*fi).is_bar() && (*fi).frame == frame) {
1452 if (dir == RoundDownMaybe) {
1458 while (!(*fi).is_bar()) {
1459 if (fi == _map.begin()) {
1464 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1465 (*fi).bar, (*fi).beat, (*fi).frame));
1468 } else if (dir > 0) {
1470 /* find bar following 'frame' */
1472 if ((*fi).is_bar() && (*fi).frame == frame) {
1473 if (dir == RoundUpMaybe) {
1479 while (!(*fi).is_bar()) {
1481 if (fi == _map.end()) {
1487 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1488 (*fi).bar, (*fi).beat, (*fi).frame));
1493 /* true rounding: find nearest bar */
1495 BBTPointList::const_iterator prev = fi;
1496 BBTPointList::const_iterator next = fi;
1498 if ((*fi).frame == frame) {
1502 while ((*prev).beat != 1) {
1503 if (prev == _map.begin()) {
1509 while ((next != _map.end()) && (*next).beat != 1) {
1513 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1514 return (*prev).frame;
1516 return (*next).frame;
1526 if (fi == _map.begin()) {
1530 if ((*fi).frame > frame || ((*fi).frame == frame && dir == RoundDownAlways)) {
1531 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1534 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1535 (*fi).bar, (*fi).beat, (*fi).frame));
1537 } else if (dir > 0) {
1538 if ((*fi).frame < frame || ((*fi).frame == frame && dir == RoundUpAlways)) {
1539 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1542 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1543 (*fi).bar, (*fi).beat, (*fi).frame));
1546 /* find beat nearest to frame */
1547 if ((*fi).frame == frame) {
1551 BBTPointList::const_iterator prev = fi;
1552 BBTPointList::const_iterator next = fi;
1554 /* fi is already the beat before_or_at frame, and
1555 we've just established that its not at frame, so its
1556 the beat before frame.
1560 if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1561 return (*prev).frame;
1563 return (*next).frame;
1569 abort(); /* NOTREACHED */
1574 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
1575 TempoMap::BBTPointList::const_iterator& end,
1576 framepos_t lower, framepos_t upper)
1579 Glib::Threads::RWLock::WriterLock lm (lock);
1580 if (_map.empty() || (_map.back().frame < upper)) {
1581 recompute_map (false, upper);
1585 begin = lower_bound (_map.begin(), _map.end(), lower);
1586 end = upper_bound (_map.begin(), _map.end(), upper);
1590 TempoMap::tempo_section_at (framepos_t frame) const
1592 Glib::Threads::RWLock::ReaderLock lm (lock);
1593 Metrics::const_iterator i;
1594 TempoSection* prev = 0;
1596 for (i = metrics.begin(); i != metrics.end(); ++i) {
1599 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1601 if ((*i)->frame() > frame) {
1611 abort(); /*NOTREACHED*/
1618 TempoMap::tempo_at (framepos_t frame) const
1620 TempoMetric m (metric_at (frame));
1625 TempoMap::meter_section_at (framepos_t frame) const
1627 Glib::Threads::RWLock::ReaderLock lm (lock);
1628 Metrics::const_iterator i;
1629 MeterSection* prev = 0;
1631 for (i = metrics.begin(); i != metrics.end(); ++i) {
1634 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1636 if ((*i)->frame() > frame) {
1646 abort(); /*NOTREACHED*/
1653 TempoMap::meter_at (framepos_t frame) const
1655 TempoMetric m (metric_at (frame));
1660 TempoMap::get_state ()
1662 Metrics::const_iterator i;
1663 XMLNode *root = new XMLNode ("TempoMap");
1666 Glib::Threads::RWLock::ReaderLock lm (lock);
1667 for (i = metrics.begin(); i != metrics.end(); ++i) {
1668 root->add_child_nocopy ((*i)->get_state());
1676 TempoMap::set_state (const XMLNode& node, int /*version*/)
1679 Glib::Threads::RWLock::WriterLock lm (lock);
1682 XMLNodeConstIterator niter;
1683 Metrics old_metrics (metrics);
1684 MeterSection* last_meter = 0;
1687 nlist = node.children();
1689 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1690 XMLNode* child = *niter;
1692 if (child->name() == TempoSection::xml_state_node_name) {
1695 TempoSection* ts = new TempoSection (*child);
1696 metrics.push_back (ts);
1698 if (ts->bar_offset() < 0.0) {
1700 ts->update_bar_offset_from_bbt (*last_meter);
1705 catch (failed_constructor& err){
1706 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1707 metrics = old_metrics;
1711 } else if (child->name() == MeterSection::xml_state_node_name) {
1714 MeterSection* ms = new MeterSection (*child);
1715 metrics.push_back (ms);
1719 catch (failed_constructor& err) {
1720 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1721 metrics = old_metrics;
1727 if (niter == nlist.end()) {
1728 MetricSectionSorter cmp;
1732 /* check for multiple tempo/meters at the same location, which
1733 ardour2 somehow allowed.
1736 Metrics::iterator prev = metrics.end();
1737 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1738 if (prev != metrics.end()) {
1739 if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
1740 if ((*prev)->start() == (*i)->start()) {
1741 cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1742 error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1745 } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
1746 if ((*prev)->start() == (*i)->start()) {
1747 cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1748 error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1756 recompute_map (true, -1);
1759 PropertyChanged (PropertyChange ());
1765 TempoMap::dump (std::ostream& o) const
1767 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1768 const MeterSection* m;
1769 const TempoSection* t;
1771 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1773 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1774 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? "
1775 << t->movable() << ')' << endl;
1776 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1777 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1778 << " (movable? " << m->movable() << ')' << endl;
1784 TempoMap::n_tempos() const
1786 Glib::Threads::RWLock::ReaderLock lm (lock);
1789 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1790 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1799 TempoMap::n_meters() const
1801 Glib::Threads::RWLock::ReaderLock lm (lock);
1804 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1805 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1814 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1817 Glib::Threads::RWLock::WriterLock lm (lock);
1818 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1819 if ((*i)->frame() >= where && (*i)->movable ()) {
1820 (*i)->set_frame ((*i)->frame() + amount);
1824 /* now reset the BBT time of all metrics, based on their new
1825 * audio time. This is the only place where we do this reverse
1829 Metrics::iterator i;
1830 const MeterSection* meter;
1831 const TempoSection* tempo;
1835 meter = &first_meter ();
1836 tempo = &first_tempo ();
1841 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1844 MetricSection* prev = 0;
1846 for (i = metrics.begin(); i != metrics.end(); ++i) {
1849 TempoMetric metric (*meter, *tempo);
1852 metric.set_start (prev->start());
1853 metric.set_frame (prev->frame());
1855 // metric will be at frames=0 bbt=1|1|0 by default
1856 // which is correct for our purpose
1859 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1860 bbt_time ((*i)->frame(), bbt, bi);
1862 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1868 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1869 /* round up to next beat */
1875 if (bbt.beats != 1) {
1876 /* round up to next bar */
1882 // cerr << bbt << endl;
1884 (*i)->set_start (bbt);
1886 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1888 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1889 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1891 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1893 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1894 abort(); /*NOTREACHED*/
1900 recompute_map (true);
1904 PropertyChanged (PropertyChange ());
1907 TempoMap::remove_time (framepos_t where, framecnt_t amount)
1911 std::list<MetricSection*> metric_kill_list;
1913 TempoSection* last_tempo = NULL;
1914 MeterSection* last_meter = NULL;
1915 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
1916 bool meter_after = false; // is there a meter marker likewise?
1918 Glib::Threads::RWLock::WriterLock lm (lock);
1919 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1920 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
1921 metric_kill_list.push_back(*i);
1922 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
1925 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
1929 else if ((*i)->frame() >= where) {
1930 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
1931 (*i)->set_frame ((*i)->frame() - amount);
1932 if ((*i)->frame() == where) {
1933 // marker was immediately after end of range
1934 tempo_after = dynamic_cast<TempoSection*> (*i);
1935 meter_after = dynamic_cast<MeterSection*> (*i);
1941 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
1942 if (last_tempo && !tempo_after) {
1943 metric_kill_list.remove(last_tempo);
1944 last_tempo->set_frame(where);
1947 if (last_meter && !meter_after) {
1948 metric_kill_list.remove(last_meter);
1949 last_meter->set_frame(where);
1953 //remove all the remaining metrics
1954 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
1960 recompute_map (true);
1963 PropertyChanged (PropertyChange ());
1967 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1968 * pos can be -ve, if required.
1971 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
1973 Glib::Threads::RWLock::ReaderLock lm (lock);
1974 Metrics::const_iterator next_tempo;
1975 const TempoSection* tempo = 0;
1977 /* Find the starting tempo metric */
1979 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1981 const TempoSection* t;
1983 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1985 /* This is a bit of a hack, but pos could be -ve, and if it is,
1986 we consider the initial metric changes (at time 0) to actually
1987 be in effect at pos.
1990 framepos_t f = (*next_tempo)->frame ();
1992 if (pos < 0 && f == 0) {
2006 tempo -> the Tempo for "pos"
2007 next_tempo -> first tempo after "pos", possibly metrics.end()
2011 DEBUG_TRACE (DEBUG::TempoMath,
2012 string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
2013 pos, beats, *((const Tempo*)tempo), tempo->frame()));
2017 /* Distance to the end of this section in frames */
2018 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
2020 /* Distance to the end in beats */
2021 Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
2022 distance_frames, tempo->frames_per_beat (_frame_rate));
2024 /* Amount to subtract this time */
2025 Evoral::Beats const delta = min (distance_beats, beats);
2027 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
2028 (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
2029 distance_frames, distance_beats));
2033 pos += delta.to_ticks(tempo->frames_per_beat (_frame_rate));
2035 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
2037 /* step forwards to next tempo section */
2039 if (next_tempo != metrics.end()) {
2041 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2043 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2044 *((const Tempo*)tempo), tempo->frame(),
2045 tempo->frames_per_beat (_frame_rate)));
2047 while (next_tempo != metrics.end ()) {
2051 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2061 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2063 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2065 Glib::Threads::RWLock::ReaderLock lm (lock);
2066 Metrics::const_reverse_iterator prev_tempo;
2067 const TempoSection* tempo = 0;
2069 /* Find the starting tempo metric */
2071 for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
2073 const TempoSection* t;
2075 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
2077 /* This is a bit of a hack, but pos could be -ve, and if it is,
2078 we consider the initial metric changes (at time 0) to actually
2079 be in effect at pos.
2082 framepos_t f = (*prev_tempo)->frame ();
2084 if (pos < 0 && f == 0) {
2088 /* this is slightly more complex than the forward case
2089 because we reach the tempo in effect at pos after
2090 passing through pos (rather before, as in the
2091 forward case). having done that, we then need to
2092 keep going to get the previous tempo (or
2098 /* first tempo with position at or
2102 } else if (f < pos) {
2103 /* some other tempo section that
2104 is even earlier than 'tempo'
2113 DEBUG_TRACE (DEBUG::TempoMath,
2114 string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
2115 pos, beats, *((const Tempo*)tempo), tempo->frame(),
2116 prev_tempo == metrics.rend()));
2120 tempo -> the Tempo for "pos"
2121 prev_tempo -> the first metric before "pos", possibly metrics.rend()
2126 /* Distance to the start of this section in frames */
2127 framecnt_t distance_frames = (pos - tempo->frame());
2129 /* Distance to the start in beats */
2130 Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate(
2131 distance_frames, tempo->frames_per_beat (_frame_rate));
2133 /* Amount to subtract this time */
2134 Evoral::Beats const sub = min (distance_beats, beats);
2136 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
2137 tempo->frame(), distance_frames, distance_beats));
2141 pos -= sub.to_double() * tempo->frames_per_beat (_frame_rate);
2143 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
2144 (prev_tempo == metrics.rend())));
2146 /* step backwards to prior TempoSection */
2148 if (prev_tempo != metrics.rend()) {
2150 tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
2152 DEBUG_TRACE (DEBUG::TempoMath,
2153 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2154 *((const Tempo*)tempo), tempo->frame(),
2155 tempo->frames_per_beat (_frame_rate)));
2157 while (prev_tempo != metrics.rend ()) {
2161 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
2166 pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate));
2167 beats = Evoral::Beats();
2174 /** Add the BBT interval op to pos and return the result */
2176 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2178 Glib::Threads::RWLock::ReaderLock lm (lock);
2179 Metrics::const_iterator i;
2180 const MeterSection* meter;
2181 const MeterSection* m;
2182 const TempoSection* tempo;
2183 const TempoSection* t;
2184 double frames_per_beat;
2185 framepos_t effective_pos = max (pos, (framepos_t) 0);
2187 meter = &first_meter ();
2188 tempo = &first_tempo ();
2193 /* find the starting metrics for tempo & meter */
2195 for (i = metrics.begin(); i != metrics.end(); ++i) {
2197 if ((*i)->frame() > effective_pos) {
2201 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2203 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2210 meter -> the Meter for "pos"
2211 tempo -> the Tempo for "pos"
2212 i -> for first new metric after "pos", possibly metrics.end()
2215 /* now comes the complicated part. we have to add one beat a time,
2216 checking for a new metric on every beat.
2219 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2228 /* check if we need to use a new metric section: has adding frames moved us
2229 to or after the start of the next metric section? in which case, use it.
2232 if (i != metrics.end()) {
2233 if ((*i)->frame() <= pos) {
2235 /* about to change tempo or meter, so add the
2236 * number of frames for the bars we've just
2237 * traversed before we change the
2238 * frames_per_beat value.
2241 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2244 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2246 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2250 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2257 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2263 /* given the current meter, have we gone past the end of the bar ? */
2268 /* check if we need to use a new metric section: has adding frames moved us
2269 to or after the start of the next metric section? in which case, use it.
2272 if (i != metrics.end()) {
2273 if ((*i)->frame() <= pos) {
2275 /* about to change tempo or meter, so add the
2276 * number of frames for the beats we've just
2277 * traversed before we change the
2278 * frames_per_beat value.
2281 pos += llrint (beats * frames_per_beat);
2284 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2286 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2290 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2295 pos += llrint (beats * frames_per_beat);
2298 if (op.ticks >= BBT_Time::ticks_per_beat) {
2299 pos += llrint (frames_per_beat + /* extra beat */
2300 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2301 (double) BBT_Time::ticks_per_beat)));
2303 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2310 /** Count the number of beats that are equivalent to distance when going forward,
2314 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2316 Glib::Threads::RWLock::ReaderLock lm (lock);
2317 Metrics::const_iterator next_tempo;
2318 const TempoSection* tempo = 0;
2319 framepos_t effective_pos = max (pos, (framepos_t) 0);
2321 /* Find the relevant initial tempo metric */
2323 for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2325 const TempoSection* t;
2327 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2329 if ((*next_tempo)->frame() > effective_pos) {
2339 tempo -> the Tempo for "pos"
2340 next_tempo -> the next tempo after "pos", possibly metrics.end()
2344 DEBUG_TRACE (DEBUG::TempoMath,
2345 string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n",
2346 pos, distance, *((const Tempo*)tempo), tempo->frame()));
2348 Evoral::Beats beats = Evoral::Beats();
2352 /* End of this section */
2354 /* Distance to `end' in frames */
2355 framepos_t distance_to_end;
2357 if (next_tempo == metrics.end ()) {
2358 /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */
2360 distance_to_end = max_framepos;
2362 end = (*next_tempo)->frame ();
2363 distance_to_end = end - pos;
2366 /* Amount to subtract this time in frames */
2367 framecnt_t const sub = min (distance, distance_to_end);
2369 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()),
2370 distance_to_end, sub));
2376 beats += Evoral::Beats::ticks_at_rate(sub, tempo->frames_per_beat (_frame_rate));
2378 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n",
2379 pos, beats, distance));
2381 /* Move on if there's anything to move to */
2383 if (next_tempo != metrics.end()) {
2385 tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2387 DEBUG_TRACE (DEBUG::TempoMath,
2388 string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2389 *((const Tempo*)tempo), tempo->frame(),
2390 tempo->frames_per_beat (_frame_rate)));
2392 while (next_tempo != metrics.end ()) {
2396 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2401 if (next_tempo == metrics.end()) {
2402 DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n");
2404 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n",
2405 **next_tempo, (*next_tempo)->frame()));
2415 TempoMap::BBTPointList::const_iterator
2416 TempoMap::bbt_before_or_at (framepos_t pos)
2418 /* CALLER MUST HOLD READ LOCK */
2420 BBTPointList::const_iterator i;
2423 /* not really correct, but we should catch pos < 0 at a higher
2426 return _map.begin();
2429 i = lower_bound (_map.begin(), _map.end(), pos);
2430 assert (i != _map.end());
2431 if ((*i).frame > pos) {
2432 assert (i != _map.begin());
2439 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2444 TempoMap::BBTPointList::const_iterator
2445 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2447 BBTPointList::const_iterator i;
2450 i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2451 assert (i != _map.end());
2452 if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2453 assert (i != _map.begin());
2459 TempoMap::BBTPointList::const_iterator
2460 TempoMap::bbt_after_or_at (framepos_t pos)
2462 /* CALLER MUST HOLD READ LOCK */
2464 BBTPointList::const_iterator i;
2466 if (_map.back().frame == pos) {
2468 assert (i != _map.begin());
2473 i = upper_bound (_map.begin(), _map.end(), pos);
2474 assert (i != _map.end());
2479 operator<< (std::ostream& o, const Meter& m) {
2480 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2484 operator<< (std::ostream& o, const Tempo& t) {
2485 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2489 operator<< (std::ostream& o, const MetricSection& section) {
2491 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2493 const TempoSection* ts;
2494 const MeterSection* ms;
2496 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2497 o << *((const Tempo*) ts);
2498 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2499 o << *((const Meter*) ms);