2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "evoral/types.hpp"
30 #include "ardour/debug.h"
31 #include "ardour/tempo.h"
32 #include "ardour/utils.h"
38 using namespace ARDOUR;
41 using Timecode::BBT_Time;
43 /* _default tempo is 4/4 qtr=120 */
45 Meter TempoMap::_default_meter (4.0, 4.0);
46 Tempo TempoMap::_default_tempo (120.0);
49 Tempo::frames_per_beat (framecnt_t sr) const
51 return (60.0 * sr) / _beats_per_minute;
54 /***********************************************************************/
57 Meter::frames_per_division (const Tempo& tempo, framecnt_t sr) const
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_division (tempo, sr) * _divisions_per_bar;
68 /***********************************************************************/
70 const string TempoSection::xml_state_node_name = "Tempo";
72 TempoSection::TempoSection (const XMLNode& node)
73 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
75 const XMLProperty *prop;
77 LocaleGuard lg (X_("POSIX"));
79 if ((prop = node.property ("start")) == 0) {
80 error << _("TempoSection XML node has no \"start\" property") << endmsg;
81 throw failed_constructor();
84 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
88 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
89 throw failed_constructor();
94 if ((prop = node.property ("beats-per-minute")) == 0) {
95 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
96 throw failed_constructor();
99 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
100 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
101 throw failed_constructor();
104 if ((prop = node.property ("note-type")) == 0) {
105 /* older session, make note type be quarter by default */
108 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
109 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
110 throw failed_constructor();
114 if ((prop = node.property ("movable")) == 0) {
115 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
116 throw failed_constructor();
119 set_movable (string_is_affirmative (prop->value()));
121 if ((prop = node.property ("bar-offset")) == 0) {
124 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
125 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
126 throw failed_constructor();
132 TempoSection::get_state() const
134 XMLNode *root = new XMLNode (xml_state_node_name);
136 LocaleGuard lg (X_("POSIX"));
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_bar_division + start().ticks) /
160 (m.divisions_per_bar() * BBT_Time::ticks_per_bar_division);
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_bar_division * meter.divisions_per_bar() * _bar_offset;
178 new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_bar_division);
179 new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_bar_division);
181 /* remember the 1-based counting properties of beats */
182 new_start.beats += 1;
184 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
185 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
187 set_start (new_start);
190 /***********************************************************************/
192 const string MeterSection::xml_state_node_name = "Meter";
194 MeterSection::MeterSection (const XMLNode& node)
195 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
197 const XMLProperty *prop;
199 LocaleGuard lg (X_("POSIX"));
201 if ((prop = node.property ("start")) == 0) {
202 error << _("MeterSection XML node has no \"start\" property") << endmsg;
203 throw failed_constructor();
206 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
210 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
211 throw failed_constructor();
216 /* beats-per-bar is old; divisions-per-bar is new */
218 if ((prop = node.property ("divisions-per-bar")) == 0) {
219 if ((prop = node.property ("beats-per-bar")) == 0) {
220 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
221 throw failed_constructor();
225 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
226 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
227 throw failed_constructor();
230 if ((prop = node.property ("note-type")) == 0) {
231 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
232 throw failed_constructor();
235 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
236 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
237 throw failed_constructor();
240 if ((prop = node.property ("movable")) == 0) {
241 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
242 throw failed_constructor();
245 set_movable (string_is_affirmative (prop->value()));
249 MeterSection::get_state() const
251 XMLNode *root = new XMLNode (xml_state_node_name);
253 LocaleGuard lg (X_("POSIX"));
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)
280 metrics = new Metrics;
282 last_bbt_valid = false;
289 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
290 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
292 t->set_movable (false);
293 m->set_movable (false);
295 /* note: frame time is correct (zero) for both of these */
297 metrics->push_back (t);
298 metrics->push_back (m);
301 TempoMap::~TempoMap ()
306 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
308 bool removed = false;
311 Glib::RWLock::WriterLock lm (lock);
314 for (i = metrics->begin(); i != metrics->end(); ++i) {
315 if (dynamic_cast<TempoSection*> (*i) != 0) {
316 if (tempo.frame() == (*i)->frame()) {
317 if ((*i)->movable()) {
326 if (removed && complete_operation) {
327 recompute_map (false);
331 if (removed && complete_operation) {
332 PropertyChanged (PropertyChange ());
337 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
339 bool removed = false;
342 Glib::RWLock::WriterLock lm (lock);
345 for (i = metrics->begin(); i != metrics->end(); ++i) {
346 if (dynamic_cast<MeterSection*> (*i) != 0) {
347 if (tempo.frame() == (*i)->frame()) {
348 if ((*i)->movable()) {
357 if (removed && complete_operation) {
358 recompute_map (true);
364 if (removed && complete_operation) {
365 PropertyChanged (PropertyChange ());
370 TempoMap::do_insert (MetricSection* section)
372 /* CALLER MUST HOLD WRITE LOCK */
374 bool reassign_tempo_bbt = false;
375 bool need_add = true;
377 assert (section->start().ticks == 0);
379 /* we only allow new meters to be inserted on beat 1 of an existing
383 if (dynamic_cast<MeterSection*>(section)) {
385 /* we need to (potentially) update the BBT times of tempo
386 sections based on this new meter.
389 reassign_tempo_bbt = true;
391 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
393 BBT_Time corrected = section->start();
397 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
398 section->start(), corrected) << endmsg;
400 section->set_start (corrected);
406 /* Look for any existing MetricSection that is of the same type and
407 at the same time as the new one, and remove it before adding
411 Metrics::iterator to_remove = metrics->end ();
413 for (i = metrics->begin(); i != metrics->end(); ++i) {
415 int const c = (*i)->compare (*section);
418 /* this section is before the one to be added; go back round */
421 /* this section is after the one to be added; there can't be any at the same time */
425 /* hacky comparison of type */
426 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
427 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
429 if (iter_is_tempo == insert_is_tempo) {
431 if (!(*i)->movable()) {
433 /* can't (re)move this section, so overwrite it
436 if (!iter_is_tempo) {
437 *(dynamic_cast<MeterSection*>(*i)) = *(dynamic_cast<MeterSection*>(section));
439 *(dynamic_cast<TempoSection*>(*i)) = *(dynamic_cast<TempoSection*>(section));
450 if (to_remove != metrics->end()) {
451 /* remove the MetricSection at the same time as the one we are about to add */
452 metrics->erase (to_remove);
455 /* Add the given MetricSection */
458 for (i = metrics->begin(); i != metrics->end(); ++i) {
460 if ((*i)->compare (*section) < 0) {
464 metrics->insert (i, section);
468 if (i == metrics->end()) {
469 metrics->insert (metrics->end(), section);
473 recompute_map (reassign_tempo_bbt);
477 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
479 const TempoSection& first (first_tempo());
482 remove_tempo (ts, false);
483 add_tempo (tempo, where);
485 Glib::RWLock::WriterLock lm (lock);
486 /* cannot move the first tempo section */
487 *((Tempo*)&first) = tempo;
488 recompute_map (false);
491 PropertyChanged (PropertyChange ());
495 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
498 Glib::RWLock::WriterLock lm (lock);
500 /* new tempos always start on a beat */
503 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
505 /* find the meter to use to set the bar offset of this
509 const Meter* meter = &first_meter();
511 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
512 at something, because we insert the default tempo and meter during
513 TempoMap construction.
515 now see if we can find better candidates.
518 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
520 const MeterSection* m;
522 if (where < (*i)->start()) {
526 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
531 ts->update_bar_offset_from_bbt (*meter);
538 PropertyChanged (PropertyChange ());
542 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
544 const MeterSection& first (first_meter());
547 remove_meter (ms, false);
548 add_meter (meter, where);
550 Glib::RWLock::WriterLock lm (lock);
551 /* cannot move the first meter section */
552 *((Meter*)&first) = meter;
553 recompute_map (true);
556 PropertyChanged (PropertyChange ());
560 TempoMap::add_meter (const Meter& meter, BBT_Time where)
563 Glib::RWLock::WriterLock lm (lock);
565 /* a new meter always starts a new bar on the first beat. so
566 round the start time appropriately. remember that
567 `where' is based on the existing tempo map, not
568 the result after we insert the new meter.
572 if (where.beats != 1) {
577 /* new meters *always* start on a beat. */
580 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
584 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
589 PropertyChanged (PropertyChange ());
593 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
595 Tempo newtempo (beats_per_minute, note_type);
598 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
599 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
601 Glib::RWLock::WriterLock lm (lock);
602 *((Tempo*) t) = newtempo;
603 recompute_map (false);
605 PropertyChanged (PropertyChange ());
612 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
614 Tempo newtempo (beats_per_minute, note_type);
620 /* find the TempoSection immediately preceding "where"
623 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
625 if ((*i)->frame() > where) {
631 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
641 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
651 Glib::RWLock::WriterLock lm (lock);
652 /* cannot move the first tempo section */
653 *((Tempo*)prev) = newtempo;
654 recompute_map (false);
657 PropertyChanged (PropertyChange ());
661 TempoMap::first_meter () const
663 const MeterSection *m = 0;
665 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
666 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
671 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
677 TempoMap::first_tempo () const
679 const TempoSection *t = 0;
681 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
682 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
687 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
693 TempoMap::timestamp_metrics_from_audio_time ()
696 const MeterSection* meter;
697 const TempoSection* tempo;
701 meter = &first_meter ();
702 tempo = &first_tempo ();
707 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
710 MetricSection* prev = 0;
712 for (i = metrics->begin(); i != metrics->end(); ++i) {
715 TempoMetric metric (*meter, *tempo);
718 metric.set_start (prev->start());
719 metric.set_frame (prev->frame());
721 // metric will be at frames=0 bbt=1|1|0 by default
722 // which is correct for our purpose
725 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
726 bbt_time_unlocked ((*i)->frame(), bbt, bi);
728 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
734 if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
735 /* round up to next beat */
741 if (bbt.beats != 1) {
742 /* round up to next bar */
748 // cerr << bbt << endl;
750 (*i)->set_start (bbt);
752 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
754 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
755 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
757 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
759 fatal << _("programming error: unhandled MetricSection type") << endmsg;
767 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
775 TempoMap::require_map_to (framepos_t pos)
777 if (_map.empty() || _map.back().frame < pos) {
778 recompute_map (false, pos);
783 TempoMap::require_map_to (const BBT_Time& bbt)
785 if (_map.empty() || _map.back().bbt() < bbt) {
786 recompute_map (false, 99);
791 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
797 double divisions_per_bar;
799 double current_frame;
801 Metrics::iterator next_metric;
805 /* compute 1 mins worth */
806 end = _frame_rate * 60;
808 end = _map.back().frame;
812 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
816 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
817 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
823 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
824 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
830 /* assumes that the first meter & tempo are at frame zero */
832 meter->set_frame (0);
833 tempo->set_frame (0);
835 /* assumes that the first meter & tempo are at 1|1|0 */
840 divisions_per_bar = meter->divisions_per_bar ();
841 beat_frames = meter->frames_per_division (*tempo,_frame_rate);
843 if (reassign_tempo_bbt) {
845 MeterSection* rmeter = meter;
847 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
849 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
851 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
853 /* reassign the BBT time of this tempo section
854 * based on its bar offset position.
857 ts->update_bbt_time_from_bar_offset (*rmeter);
859 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
862 fatal << _("programming error: unhandled MetricSection type") << endmsg;
868 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2 dpb %3 fpb %4\n",
869 *((Meter*)meter), *((Tempo*)tempo), divisions_per_bar, beat_frames));
871 next_metric = metrics->begin();
872 ++next_metric; // skip meter (or tempo)
873 ++next_metric; // skip tempo (or meter)
875 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
876 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
878 while (current_frame < end) {
881 current_frame += beat_frames;
883 if (current.beats > meter->divisions_per_bar()) {
888 if (next_metric != metrics->end()) {
890 /* no operator >= so invert operator < */
892 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
894 if (!(current < (*next_metric)->start())) {
897 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
901 /* new tempo section: if its on a beat,
902 * we don't have to do anything other
903 * than recompute various distances,
904 * done further below as we transition
905 * the next metric section.
907 * if its not on the beat, we have to
908 * compute the duration of the beat it
909 * is within, which will be different
910 * from the preceding following ones
911 * since it takes part of its duration
912 * from the preceding tempo and part
913 * from this new tempo.
916 if (tempo->start().ticks != 0) {
918 double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);
920 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
921 tempo->start(), current_frame, tempo->bar_offset()));
923 /* back up to previous beat */
924 current_frame -= beat_frames;
925 /* set tempo section location based on offset from last beat */
926 tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
927 /* advance to the location of the new (adjusted) beat */
928 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
929 /* next metric doesn't have to
930 * match this precisely to
933 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
937 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
938 tempo->start(), current_frame));
939 tempo->set_frame (current_frame);
942 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
946 /* new meter section: always defines the
950 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
951 meter->start(), current, current_frame));
953 assert (current.beats == 1);
955 meter->set_frame (current_frame);
958 divisions_per_bar = meter->divisions_per_bar ();
959 beat_frames = meter->frames_per_division (*tempo, _frame_rate);
961 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
962 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
966 if (next_metric != metrics->end() && ((*next_metric)->start() == current)) {
967 /* same position so go back and set this one up before advancing
974 if (current.beats == 1) {
975 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
976 _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
978 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
979 _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
985 TempoMap::metric_at (framepos_t frame) const
987 Glib::RWLock::ReaderLock lm (lock);
988 TempoMetric m (first_meter(), first_tempo());
992 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
993 at something, because we insert the default tempo and meter during
994 TempoMap construction.
996 now see if we can find better candidates.
999 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1001 // cerr << "Looking at a metric section " << **i << endl;
1003 if ((*i)->frame() > frame) {
1007 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1008 m.set_tempo (*tempo);
1009 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1010 m.set_meter (*meter);
1013 m.set_frame ((*i)->frame ());
1014 m.set_start ((*i)->start ());
1017 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1022 TempoMap::metric_at (BBT_Time bbt) const
1024 Glib::RWLock::ReaderLock lm (lock);
1025 TempoMetric m (first_meter(), first_tempo());
1029 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1030 at something, because we insert the default tempo and meter during
1031 TempoMap construction.
1033 now see if we can find better candidates.
1036 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1038 BBT_Time section_start ((*i)->start());
1040 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1044 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1045 m.set_tempo (*tempo);
1046 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1047 m.set_meter (*meter);
1050 m.set_frame ((*i)->frame ());
1051 m.set_start (section_start);
1058 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1061 Glib::RWLock::ReaderLock lm (lock);
1062 BBTPointList::const_iterator i = bbt_before_or_at (frame);
1063 bbt_time_unlocked (frame, bbt, i);
1068 TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1070 bbt.bars = (*i).bar;
1071 bbt.beats = (*i).beat;
1073 if ((*i).frame == frame) {
1076 bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
1077 BBT_Time::ticks_per_bar_division);
1082 TempoMap::frame_time (const BBT_Time& bbt)
1084 Glib::RWLock::ReaderLock lm (lock);
1086 BBTPointList::const_iterator s = bbt_point_for (BBT_Time (1, 1, 0));
1087 BBTPointList::const_iterator e = bbt_point_for (BBT_Time (bbt.bars, bbt.beats, 0));
1089 if (bbt.ticks != 0) {
1090 return ((*e).frame - (*s).frame) +
1091 llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
1093 return ((*e).frame - (*s).frame);
1098 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1100 Glib::RWLock::ReaderLock lm (lock);
1101 framecnt_t frames = 0;
1104 bbt_time (pos, when);
1105 frames = bbt_duration_at_unlocked (when, bbt,dir);
1111 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1113 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1117 /* round back to the previous precise beat */
1118 BBTPointList::const_iterator wi = bbt_point_for (BBT_Time (when.bars, when.beats, 0));
1119 BBTPointList::const_iterator start (wi);
1120 double tick_frames = 0;
1122 assert (wi != _map.end());
1124 /* compute how much rounding we did because of non-zero ticks */
1126 if (when.ticks != 0) {
1127 tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
1133 while (wi != _map.end() && bars < bbt.bars) {
1135 if ((*wi).is_bar()) {
1139 assert (wi != _map.end());
1141 while (wi != _map.end() && beats < bbt.beats) {
1145 assert (wi != _map.end());
1147 /* add any additional frames related to ticks in the added value */
1149 if (bbt.ticks != 0) {
1150 tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
1153 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1157 TempoMap::round_to_bar (framepos_t fr, int dir)
1160 Glib::RWLock::ReaderLock lm (lock);
1161 return round_to_type (fr, dir, Bar);
1166 TempoMap::round_to_beat (framepos_t fr, int dir)
1169 Glib::RWLock::ReaderLock lm (lock);
1170 return round_to_type (fr, dir, Beat);
1175 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1177 Glib::RWLock::ReaderLock lm (lock);
1178 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1180 uint32_t ticks_one_subdivisions_worth;
1181 uint32_t difference;
1183 bbt_time_unlocked (fr, the_beat, i);
1185 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1186 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1188 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
1192 /* round to next (even if we're on a subdivision */
1194 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1197 /* right on the subdivision, so the difference is just the subdivision ticks */
1198 the_beat.ticks += ticks_one_subdivisions_worth;
1201 /* not on subdivision, compute distance to next subdivision */
1203 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1206 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1207 assert (i != _map.end());
1209 assert (i != _map.end());
1210 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1214 } else if (dir < 0) {
1216 /* round to previous (even if we're on a subdivision) */
1218 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1221 /* right on the subdivision, so the difference is just the subdivision ticks */
1222 difference = ticks_one_subdivisions_worth;
1224 /* not on subdivision, compute distance to previous subdivision, which
1225 is just the modulus.
1231 if (the_beat.ticks < difference) {
1232 if (i == _map.begin()) {
1233 /* can't go backwards from wherever pos is, so just return it */
1237 the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
1239 the_beat.ticks -= difference;
1243 /* round to nearest */
1247 /* compute the distance to the previous and next subdivision */
1249 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1251 /* closer to the next subdivision, so shift forward */
1253 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1255 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1257 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1258 assert (i != _map.end());
1260 assert (i != _map.end());
1261 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1262 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1265 } else if (rem > 0) {
1267 /* closer to previous subdivision, so shift backward */
1269 if (rem > the_beat.ticks) {
1270 if (i == _map.begin()) {
1271 /* can't go backwards past zero, so ... */
1274 /* step back to previous beat */
1276 the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
1277 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1279 the_beat.ticks = lrint (the_beat.ticks - rem);
1280 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1283 /* on the subdivision, do nothing */
1287 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
1288 (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
1292 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1294 BBTPointList::const_iterator fi;
1297 fi = bbt_after_or_at (frame);
1299 fi = bbt_before_or_at (frame);
1302 assert (fi != _map.end());
1304 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to bars in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame));
1309 /* find bar previous to 'frame' */
1311 if ((*fi).is_bar() && (*fi).frame == frame) {
1315 while (!(*fi).is_bar()) {
1316 if (fi == _map.begin()) {
1321 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1322 (*fi).bar, (*fi).beat, (*fi).frame));
1325 } else if (dir > 0) {
1327 /* find bar following 'frame' */
1329 if ((*fi).is_bar() && (*fi).frame == frame) {
1333 while (!(*fi).is_bar()) {
1335 if (fi == _map.end()) {
1341 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1342 (*fi).bar, (*fi).beat, (*fi).frame));
1347 /* true rounding: find nearest bar */
1349 BBTPointList::const_iterator prev = fi;
1350 BBTPointList::const_iterator next = fi;
1352 if ((*fi).frame == frame) {
1356 while ((*prev).beat != 1) {
1357 if (prev == _map.begin()) {
1363 while ((*next).beat != 1) {
1365 if (next == _map.end()) {
1371 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1372 return (*prev).frame;
1374 return (*next).frame;
1383 if ((*fi).frame >= frame) {
1384 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1387 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1388 (*fi).bar, (*fi).beat, (*fi).frame));
1390 } else if (dir > 0) {
1391 if ((*fi).frame <= frame) {
1392 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1395 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1396 (*fi).bar, (*fi).beat, (*fi).frame));
1399 /* find beat nearest to frame */
1400 if ((*fi).frame == frame) {
1404 BBTPointList::const_iterator prev = fi;
1405 BBTPointList::const_iterator next = fi;
1409 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1410 return (*prev).frame;
1412 return (*next).frame;
1424 TempoMap::map (TempoMap::BBTPointList::const_iterator& begin,
1425 TempoMap::BBTPointList::const_iterator& end,
1426 framepos_t lower, framepos_t upper)
1428 if (_map.empty() || upper >= _map.back().frame) {
1429 recompute_map (false, upper);
1432 begin = lower_bound (_map.begin(), _map.end(), lower);
1433 end = upper_bound (_map.begin(), _map.end(), upper);
1437 TempoMap::tempo_section_at (framepos_t frame) const
1439 Glib::RWLock::ReaderLock lm (lock);
1440 Metrics::const_iterator i;
1441 TempoSection* prev = 0;
1443 for (i = metrics->begin(); i != metrics->end(); ++i) {
1446 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1448 if ((*i)->frame() > frame) {
1464 TempoMap::tempo_at (framepos_t frame) const
1466 TempoMetric m (metric_at (frame));
1472 TempoMap::meter_at (framepos_t frame) const
1474 TempoMetric m (metric_at (frame));
1479 TempoMap::get_state ()
1481 Metrics::const_iterator i;
1482 XMLNode *root = new XMLNode ("TempoMap");
1485 Glib::RWLock::ReaderLock lm (lock);
1486 for (i = metrics->begin(); i != metrics->end(); ++i) {
1487 root->add_child_nocopy ((*i)->get_state());
1495 TempoMap::set_state (const XMLNode& node, int /*version*/)
1498 Glib::RWLock::WriterLock lm (lock);
1501 XMLNodeConstIterator niter;
1502 Metrics old_metrics (*metrics);
1503 MeterSection* last_meter = 0;
1507 nlist = node.children();
1509 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1510 XMLNode* child = *niter;
1512 if (child->name() == TempoSection::xml_state_node_name) {
1515 TempoSection* ts = new TempoSection (*child);
1516 metrics->push_back (ts);
1518 if (ts->bar_offset() < 0.0) {
1520 ts->update_bar_offset_from_bbt (*last_meter);
1525 catch (failed_constructor& err){
1526 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1527 *metrics = old_metrics;
1531 } else if (child->name() == MeterSection::xml_state_node_name) {
1534 MeterSection* ms = new MeterSection (*child);
1535 metrics->push_back (ms);
1539 catch (failed_constructor& err) {
1540 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1541 *metrics = old_metrics;
1547 if (niter == nlist.end()) {
1549 MetricSectionSorter cmp;
1550 metrics->sort (cmp);
1551 recompute_map (true);
1555 PropertyChanged (PropertyChange ());
1561 TempoMap::dump (std::ostream& o) const
1563 const MeterSection* m;
1564 const TempoSection* t;
1566 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1568 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1569 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? "
1570 << t->movable() << ')' << endl;
1571 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1572 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1573 << " (movable? " << m->movable() << ')' << endl;
1579 TempoMap::n_tempos() const
1581 Glib::RWLock::ReaderLock lm (lock);
1584 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1585 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1594 TempoMap::n_meters() const
1596 Glib::RWLock::ReaderLock lm (lock);
1599 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1600 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1609 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1611 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1612 if ((*i)->frame() >= where && (*i)->movable ()) {
1613 (*i)->set_frame ((*i)->frame() + amount);
1617 timestamp_metrics_from_audio_time ();
1619 PropertyChanged (PropertyChange ());
1622 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1623 * pos can be -ve, if required.
1626 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
1628 return framepos_plus_bbt (pos, BBT_Time (beats));
1631 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1633 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
1635 Metrics::const_iterator i;
1636 const TempoSection* tempo = 0;
1637 const TempoSection* t;
1639 /* Find the starting tempo */
1641 for (i = metrics->begin(); i != metrics->end(); ++i) {
1643 /* This is a bit of a hack, but pos could be -ve, and if it is,
1644 we consider the initial metric changes (at time 0) to actually
1645 be in effect at pos.
1647 framepos_t f = (*i)->frame ();
1648 if (pos < 0 && f == 0) {
1652 if ((*i)->frame() > pos) {
1656 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1661 bool no_more_tempos = false;
1663 /* Move i back to the tempo before "pos" */
1664 if (i != metrics->begin ()) {
1665 while (i != metrics->begin ()) {
1667 t = dynamic_cast<TempoSection*> (*i);
1673 no_more_tempos = true;
1678 tempo -> the Tempo for "pos"
1679 i -> the first metric before "pos", unless no_more_tempos is true
1684 /* Distance to the end of this section in frames */
1685 framecnt_t distance_frames = no_more_tempos ? max_framepos : (pos - (*i)->frame());
1687 /* Distance to the end in beats */
1688 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1690 /* Amount to subtract this time */
1691 double const sub = min (distance_beats, beats);
1695 pos -= sub * tempo->frames_per_beat (_frame_rate);
1697 /* Move i and tempo back, if there's anything to move to */
1698 if (i != metrics->begin ()) {
1699 while (i != metrics->begin ()) {
1701 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1707 no_more_tempos = true;
1714 /** Add the BBT interval op to pos and return the result */
1716 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
1718 BBT_Time op_copy (op);
1719 int additional_minutes = 1;
1720 BBTPointList::const_iterator i;
1724 i = bbt_before_or_at (pos);
1728 if ((*i).frame != pos) {
1729 /* we know that (*i).frame is before pos */
1730 cerr << "Need to account for offset of " << (pos - (*i).frame) << endl;
1733 while (i != _map.end() && (op.bars || op.beats)) {
1735 if ((*i).is_bar()) {
1746 if (i != _map.end()) {
1750 /* we hit the end of the map before finish the bbt walk.
1753 require_map_to (pos + (_frame_rate * 60 * additional_minutes));
1754 additional_minutes *= 2;
1756 /* go back and try again */
1757 cerr << "reached end of map with op now at " << op << " end = " << _map.back().frame << ' ' << _map.back().bar << '|' << _map.back().beat << ", trying to walk " << op_copy << " ... retry\n";
1762 llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1768 /** Count the number of beats that are equivalent to distance when going forward,
1772 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
1774 BBTPointList::const_iterator i = bbt_after_or_at (pos);
1775 Evoral::MusicalTime beats = 0;
1776 framepos_t end = pos + distance;
1778 require_map_to (end);
1780 /* if our starting BBTPoint is after pos, add a fractional beat
1781 to represent that distance.
1784 if ((*i).frame != pos) {
1785 beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1788 while (i != _map.end() && (*i).frame < end) {
1792 assert (i != _map.end());
1794 /* if our ending BBTPoint is after the end, subtract a fractional beat
1795 to represent that distance.
1798 if ((*i).frame > end) {
1799 beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1805 TempoMap::BBTPointList::const_iterator
1806 TempoMap::bbt_before_or_at (framepos_t pos)
1808 require_map_to (pos);
1809 BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), pos);
1810 assert (i != _map.end());
1811 if ((*i).frame > pos) {
1812 assert (i != _map.begin());
1818 TempoMap::BBTPointList::const_iterator
1819 TempoMap::bbt_after_or_at (framepos_t pos)
1821 require_map_to (pos);
1822 BBTPointList::const_iterator i = upper_bound (_map.begin(), _map.end(), pos);
1823 assert (i != _map.end());
1828 bool operator() (const BBT_Time& a, const BBT_Time& b) {
1833 TempoMap::BBTPointList::const_iterator
1834 TempoMap::bbt_point_for (const BBT_Time& bbt)
1837 int additional_minutes = 1;
1839 while (_map.empty() || _map.back().bar < (bbt.bars + 1)) {
1840 /* add some more distance, using bigger steps each time */
1841 require_map_to (_map.back().frame + (_frame_rate * 60 * additional_minutes));
1842 additional_minutes *= 2;
1845 BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
1846 assert (i != _map.end());
1851 /** Compare the time of this with that of another MetricSection.
1852 * @param with_bbt True to compare using start(), false to use frame().
1853 * @return -1 for less than, 0 for equal, 1 for greater than.
1857 MetricSection::compare (const MetricSection& other) const
1859 if (start() == other.start()) {
1861 } else if (start() < other.start()) {
1872 MetricSection::operator== (const MetricSection& other) const
1874 return compare (other) == 0;
1878 MetricSection::operator!= (const MetricSection& other) const
1880 return compare (other) != 0;
1884 operator<< (std::ostream& o, const Meter& m) {
1885 return o << m.divisions_per_bar() << '/' << m.note_divisor();
1889 operator<< (std::ostream& o, const Tempo& t) {
1890 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
1894 operator<< (std::ostream& o, const MetricSection& section) {
1896 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
1898 const TempoSection* ts;
1899 const MeterSection* ms;
1901 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
1902 o << *((Tempo*) ts);
1903 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
1904 o << *((Meter*) ms);