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;
281 _map = new BBTPointList;
283 last_bbt_valid = false;
290 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
291 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
293 t->set_movable (false);
294 m->set_movable (false);
296 /* note: frame time is correct (zero) for both of these */
298 metrics->push_back (t);
299 metrics->push_back (m);
302 TempoMap::~TempoMap ()
309 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
311 bool removed = false;
314 Glib::RWLock::WriterLock lm (metrics_lock);
317 for (i = metrics->begin(); i != metrics->end(); ++i) {
318 if (dynamic_cast<TempoSection*> (*i) != 0) {
319 if (tempo.frame() == (*i)->frame()) {
320 if ((*i)->movable()) {
330 if (removed && complete_operation) {
331 recompute_map (false);
332 PropertyChanged (PropertyChange ());
337 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
339 bool removed = false;
342 Glib::RWLock::WriterLock lm (metrics_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()) {
358 if (removed && complete_operation) {
359 recompute_map (true);
360 PropertyChanged (PropertyChange ());
365 TempoMap::do_insert (MetricSection* section)
367 bool need_add = true;
369 assert (section->start().ticks == 0);
371 /* we only allow new meters to be inserted on beat 1 of an existing
375 if (dynamic_cast<MeterSection*>(section)) {
377 /* we need to (potentially) update the BBT times of tempo
378 sections based on this new meter.
381 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
383 BBT_Time corrected = section->start();
387 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
388 section->start(), corrected) << endmsg;
390 section->set_start (corrected);
396 /* Look for any existing MetricSection that is of the same type and
397 at the same time as the new one, and remove it before adding
401 Metrics::iterator to_remove = metrics->end ();
403 for (i = metrics->begin(); i != metrics->end(); ++i) {
405 int const c = (*i)->compare (*section);
408 /* this section is before the one to be added; go back round */
411 /* this section is after the one to be added; there can't be any at the same time */
415 /* hacky comparison of type */
416 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
417 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
419 if (iter_is_tempo == insert_is_tempo) {
421 if (!(*i)->movable()) {
423 /* can't (re)move this section, so overwrite it
426 if (!iter_is_tempo) {
427 *(dynamic_cast<MeterSection*>(*i)) = *(dynamic_cast<MeterSection*>(section));
429 *(dynamic_cast<TempoSection*>(*i)) = *(dynamic_cast<TempoSection*>(section));
440 if (to_remove != metrics->end()) {
441 /* remove the MetricSection at the same time as the one we are about to add */
442 metrics->erase (to_remove);
445 /* Add the given MetricSection */
448 for (i = metrics->begin(); i != metrics->end(); ++i) {
450 if ((*i)->compare (*section) < 0) {
454 metrics->insert (i, section);
458 if (i == metrics->end()) {
459 metrics->insert (metrics->end(), section);
465 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
467 const TempoSection& first (first_tempo());
470 remove_tempo (ts, false);
471 add_tempo (tempo, where);
474 Glib::RWLock::WriterLock lm (metrics_lock);
475 /* cannot move the first tempo section */
476 *((Tempo*)&first) = tempo;
479 recompute_map (false);
482 PropertyChanged (PropertyChange ());
486 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
489 Glib::RWLock::WriterLock lm (metrics_lock);
491 /* new tempos always start on a beat */
494 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
496 /* find the meter to use to set the bar offset of this
500 const Meter* meter = &first_meter();
502 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
503 at something, because we insert the default tempo and meter during
504 TempoMap construction.
506 now see if we can find better candidates.
509 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
511 const MeterSection* m;
513 if (where < (*i)->start()) {
517 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
522 ts->update_bar_offset_from_bbt (*meter);
529 recompute_map (false);
531 PropertyChanged (PropertyChange ());
535 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
537 const MeterSection& first (first_meter());
540 remove_meter (ms, false);
541 add_meter (meter, where);
544 Glib::RWLock::WriterLock lm (metrics_lock);
545 /* cannot move the first meter section */
546 *((Meter*)&first) = meter;
548 recompute_map (true);
551 PropertyChanged (PropertyChange ());
555 TempoMap::add_meter (const Meter& meter, BBT_Time where)
558 Glib::RWLock::WriterLock lm (metrics_lock);
560 /* a new meter always starts a new bar on the first beat. so
561 round the start time appropriately. remember that
562 `where' is based on the existing tempo map, not
563 the result after we insert the new meter.
567 if (where.beats != 1) {
572 /* new meters *always* start on a beat. */
575 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
578 recompute_map (true);
581 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
586 PropertyChanged (PropertyChange ());
590 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
592 Tempo newtempo (beats_per_minute, note_type);
595 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
596 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
598 Glib::RWLock::WriterLock lm (metrics_lock);
599 *((Tempo*) t) = newtempo;
601 recompute_map (false);
602 PropertyChanged (PropertyChange ());
609 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
611 Tempo newtempo (beats_per_minute, note_type);
617 /* find the TempoSection immediately preceding "where"
620 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
622 if ((*i)->frame() > where) {
628 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
638 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
648 Glib::RWLock::WriterLock lm (metrics_lock);
649 /* cannot move the first tempo section */
650 *((Tempo*)prev) = newtempo;
653 recompute_map (false);
654 PropertyChanged (PropertyChange ());
658 TempoMap::first_meter () const
660 const MeterSection *m = 0;
662 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
663 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
668 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
674 TempoMap::first_tempo () const
676 const TempoSection *t = 0;
678 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
679 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
684 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
690 TempoMap::timestamp_metrics_from_audio_time ()
693 const MeterSection* meter;
694 const TempoSection* tempo;
698 meter = &first_meter ();
699 tempo = &first_tempo ();
704 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
707 MetricSection* prev = 0;
709 for (i = metrics->begin(); i != metrics->end(); ++i) {
712 TempoMetric metric (*meter, *tempo);
715 metric.set_start (prev->start());
716 metric.set_frame (prev->frame());
718 // metric will be at frames=0 bbt=1|1|0 by default
719 // which is correct for our purpose
722 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
723 bbt_time_unlocked ((*i)->frame(), bbt, bi);
725 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
731 if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
732 /* round up to next beat */
738 if (bbt.beats != 1) {
739 /* round up to next bar */
745 // cerr << bbt << endl;
747 (*i)->set_start (bbt);
749 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
751 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
752 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
754 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
756 fatal << _("programming error: unhandled MetricSection type") << endmsg;
764 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
772 TempoMap::require_map_to (framepos_t pos)
777 Glib::RWLock::ReaderLock lm (map_lock);
778 revise_map = (_map->empty() || _map->back().frame < pos);
782 recompute_map (false, pos);
787 TempoMap::require_map_to (const BBT_Time& bbt)
792 Glib::RWLock::ReaderLock lm (map_lock);
793 revise_map = (_map->empty() || _map->back().bbt() < bbt);
797 recompute_map (false, 99);
802 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
808 double divisions_per_bar;
810 double current_frame;
812 Metrics::iterator next_metric;
813 BBTPointList* new_map = new BBTPointList;
817 Glib::RWLock::ReaderLock lm (map_lock);
820 /* compute 1 mins worth */
821 end = _frame_rate * 60;
823 end = _map->back().frame;
827 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
829 Glib::RWLock::ReaderLock lm (metrics_lock);
831 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
832 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
838 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
839 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
845 /* assumes that the first meter & tempo are at frame zero */
847 meter->set_frame (0);
848 tempo->set_frame (0);
850 /* assumes that the first meter & tempo are at 1|1|0 */
855 divisions_per_bar = meter->divisions_per_bar ();
856 beat_frames = meter->frames_per_division (*tempo,_frame_rate);
858 if (reassign_tempo_bbt) {
860 MeterSection* rmeter = meter;
862 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
864 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
866 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
868 /* reassign the BBT time of this tempo section
869 * based on its bar offset position.
872 ts->update_bbt_time_from_bar_offset (*rmeter);
874 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
877 fatal << _("programming error: unhandled MetricSection type") << endmsg;
883 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2 dpb %3 fpb %4\n",
884 *((Meter*)meter), *((Tempo*)tempo), divisions_per_bar, beat_frames));
886 next_metric = metrics->begin();
887 ++next_metric; // skip meter (or tempo)
888 ++next_metric; // skip tempo (or meter)
890 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
891 new_map->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
893 while (current_frame < end) {
896 current_frame += beat_frames;
898 if (current.beats > meter->divisions_per_bar()) {
903 if (next_metric != metrics->end()) {
905 /* no operator >= so invert operator < */
907 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
909 if (!(current < (*next_metric)->start())) {
912 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
916 /* new tempo section: if its on a beat,
917 * we don't have to do anything other
918 * than recompute various distances,
919 * done further below as we transition
920 * the next metric section.
922 * if its not on the beat, we have to
923 * compute the duration of the beat it
924 * is within, which will be different
925 * from the preceding following ones
926 * since it takes part of its duration
927 * from the preceding tempo and part
928 * from this new tempo.
931 if (tempo->start().ticks != 0) {
933 double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);
935 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
936 tempo->start(), current_frame, tempo->bar_offset()));
938 /* back up to previous beat */
939 current_frame -= beat_frames;
940 /* set tempo section location based on offset from last beat */
941 tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
942 /* advance to the location of the new (adjusted) beat */
943 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
944 /* next metric doesn't have to
945 * match this precisely to
948 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
952 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
953 tempo->start(), current_frame));
954 tempo->set_frame (current_frame);
957 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
961 /* new meter section: always defines the
965 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
966 meter->start(), current, current_frame));
968 assert (current.beats == 1);
970 meter->set_frame (current_frame);
973 divisions_per_bar = meter->divisions_per_bar ();
974 beat_frames = meter->frames_per_division (*tempo, _frame_rate);
976 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
977 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
981 if (next_metric != metrics->end() && ((*next_metric)->start() == current)) {
982 /* same position so go back and set this one up before advancing
989 if (current.beats == 1) {
990 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
991 new_map->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
993 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
994 new_map->push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
999 Glib::RWLock::WriterLock lm (map_lock);
1000 swap (_map, new_map);
1006 TempoMap::metric_at (framepos_t frame) const
1008 Glib::RWLock::ReaderLock lm (metrics_lock);
1009 TempoMetric m (first_meter(), first_tempo());
1013 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1014 at something, because we insert the default tempo and meter during
1015 TempoMap construction.
1017 now see if we can find better candidates.
1020 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1022 // cerr << "Looking at a metric section " << **i << endl;
1024 if ((*i)->frame() > frame) {
1028 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1029 m.set_tempo (*tempo);
1030 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1031 m.set_meter (*meter);
1034 m.set_frame ((*i)->frame ());
1035 m.set_start ((*i)->start ());
1038 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1043 TempoMap::metric_at (BBT_Time bbt) const
1045 Glib::RWLock::ReaderLock lm (metrics_lock);
1046 TempoMetric m (first_meter(), first_tempo());
1050 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1051 at something, because we insert the default tempo and meter during
1052 TempoMap construction.
1054 now see if we can find better candidates.
1057 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1059 BBT_Time section_start ((*i)->start());
1061 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1065 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1066 m.set_tempo (*tempo);
1067 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1068 m.set_meter (*meter);
1071 m.set_frame ((*i)->frame ());
1072 m.set_start (section_start);
1079 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1082 Glib::RWLock::ReaderLock lm (map_lock);
1083 BBTPointList::const_iterator i = bbt_before_or_at (frame);
1084 bbt_time_unlocked (frame, bbt, i);
1089 TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1091 bbt.bars = (*i).bar;
1092 bbt.beats = (*i).beat;
1094 if ((*i).frame == frame) {
1097 bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
1098 BBT_Time::ticks_per_bar_division);
1103 TempoMap::frame_time (const BBT_Time& bbt)
1105 Glib::RWLock::ReaderLock lm (map_lock);
1107 BBTPointList::const_iterator s = bbt_point_for (BBT_Time (1, 1, 0));
1108 BBTPointList::const_iterator e = bbt_point_for (BBT_Time (bbt.bars, bbt.beats, 0));
1110 if (bbt.ticks != 0) {
1111 return ((*e).frame - (*s).frame) +
1112 llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
1114 return ((*e).frame - (*s).frame);
1119 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1121 Glib::RWLock::ReaderLock lm (map_lock);
1122 framecnt_t frames = 0;
1125 bbt_time (pos, when);
1126 frames = bbt_duration_at_unlocked (when, bbt,dir);
1132 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1134 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1138 /* round back to the previous precise beat */
1139 BBTPointList::const_iterator wi = bbt_point_for (BBT_Time (when.bars, when.beats, 0));
1140 BBTPointList::const_iterator start (wi);
1141 double tick_frames = 0;
1143 assert (wi != _map->end());
1145 /* compute how much rounding we did because of non-zero ticks */
1147 if (when.ticks != 0) {
1148 tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
1154 while (wi != _map->end() && bars < bbt.bars) {
1156 if ((*wi).is_bar()) {
1160 assert (wi != _map->end());
1162 while (wi != _map->end() && beats < bbt.beats) {
1166 assert (wi != _map->end());
1168 /* add any additional frames related to ticks in the added value */
1170 if (bbt.ticks != 0) {
1171 tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
1174 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1178 TempoMap::round_to_bar (framepos_t fr, int dir)
1180 return round_to_type (fr, dir, Bar);
1184 TempoMap::round_to_beat (framepos_t fr, int dir)
1186 return round_to_type (fr, dir, Beat);
1190 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1192 Glib::RWLock::ReaderLock lm (map_lock);
1193 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1195 uint32_t ticks_one_subdivisions_worth;
1196 uint32_t difference;
1198 bbt_time_unlocked (fr, the_beat, i);
1200 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1201 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1203 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
1207 /* round to next (even if we're on a subdivision */
1209 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1212 /* right on the subdivision, so the difference is just the subdivision ticks */
1213 the_beat.ticks += ticks_one_subdivisions_worth;
1216 /* not on subdivision, compute distance to next subdivision */
1218 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1221 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1222 assert (i != _map->end());
1224 assert (i != _map->end());
1225 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1229 } else if (dir < 0) {
1231 /* round to previous (even if we're on a subdivision) */
1233 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1236 /* right on the subdivision, so the difference is just the subdivision ticks */
1237 difference = ticks_one_subdivisions_worth;
1239 /* not on subdivision, compute distance to previous subdivision, which
1240 is just the modulus.
1246 if (the_beat.ticks < difference) {
1247 if (i == _map->begin()) {
1248 /* can't go backwards from wherever pos is, so just return it */
1252 the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
1254 the_beat.ticks -= difference;
1258 /* round to nearest */
1262 /* compute the distance to the previous and next subdivision */
1264 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1266 /* closer to the next subdivision, so shift forward */
1268 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1270 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1272 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1273 assert (i != _map->end());
1275 assert (i != _map->end());
1276 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1277 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1280 } else if (rem > 0) {
1282 /* closer to previous subdivision, so shift backward */
1284 if (rem > the_beat.ticks) {
1285 if (i == _map->begin()) {
1286 /* can't go backwards past zero, so ... */
1289 /* step back to previous beat */
1291 the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
1292 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1294 the_beat.ticks = lrint (the_beat.ticks - rem);
1295 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1298 /* on the subdivision, do nothing */
1302 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
1303 (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
1307 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1309 Glib::RWLock::ReaderLock lm (map_lock);
1310 BBTPointList::const_iterator fi;
1313 fi = bbt_after_or_at (frame);
1315 fi = bbt_before_or_at (frame);
1318 assert (fi != _map->end());
1320 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));
1325 /* find bar previous to 'frame' */
1327 if ((*fi).is_bar() && (*fi).frame == frame) {
1331 while (!(*fi).is_bar()) {
1332 if (fi == _map->begin()) {
1337 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1338 (*fi).bar, (*fi).beat, (*fi).frame));
1341 } else if (dir > 0) {
1343 /* find bar following 'frame' */
1345 if ((*fi).is_bar() && (*fi).frame == frame) {
1349 while (!(*fi).is_bar()) {
1351 if (fi == _map->end()) {
1357 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1358 (*fi).bar, (*fi).beat, (*fi).frame));
1363 /* true rounding: find nearest bar */
1365 BBTPointList::const_iterator prev = fi;
1366 BBTPointList::const_iterator next = fi;
1368 if ((*fi).frame == frame) {
1372 while ((*prev).beat != 1) {
1373 if (prev == _map->begin()) {
1379 while ((*next).beat != 1) {
1381 if (next == _map->end()) {
1387 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1388 return (*prev).frame;
1390 return (*next).frame;
1399 if ((*fi).frame >= frame) {
1400 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1403 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1404 (*fi).bar, (*fi).beat, (*fi).frame));
1406 } else if (dir > 0) {
1407 if ((*fi).frame <= frame) {
1408 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1411 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1412 (*fi).bar, (*fi).beat, (*fi).frame));
1415 /* find beat nearest to frame */
1416 if ((*fi).frame == frame) {
1420 BBTPointList::const_iterator prev = fi;
1421 BBTPointList::const_iterator next = fi;
1425 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1426 return (*prev).frame;
1428 return (*next).frame;
1440 TempoMap::map (TempoMap::BBTPointList::const_iterator& begin,
1441 TempoMap::BBTPointList::const_iterator& end,
1442 framepos_t lower, framepos_t upper)
1444 require_map_to (upper);
1445 begin = lower_bound (_map->begin(), _map->end(), lower);
1446 end = upper_bound (_map->begin(), _map->end(), upper);
1450 TempoMap::tempo_section_at (framepos_t frame) const
1452 Glib::RWLock::ReaderLock lm (metrics_lock);
1453 Metrics::const_iterator i;
1454 TempoSection* prev = 0;
1456 for (i = metrics->begin(); i != metrics->end(); ++i) {
1459 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1461 if ((*i)->frame() > frame) {
1477 TempoMap::tempo_at (framepos_t frame) const
1479 TempoMetric m (metric_at (frame));
1485 TempoMap::meter_at (framepos_t frame) const
1487 TempoMetric m (metric_at (frame));
1492 TempoMap::get_state ()
1494 Metrics::const_iterator i;
1495 XMLNode *root = new XMLNode ("TempoMap");
1498 Glib::RWLock::ReaderLock lm (metrics_lock);
1499 for (i = metrics->begin(); i != metrics->end(); ++i) {
1500 root->add_child_nocopy ((*i)->get_state());
1508 TempoMap::set_state (const XMLNode& node, int /*version*/)
1511 Glib::RWLock::WriterLock lm (metrics_lock);
1514 XMLNodeConstIterator niter;
1515 Metrics old_metrics (*metrics);
1516 MeterSection* last_meter = 0;
1520 nlist = node.children();
1522 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1523 XMLNode* child = *niter;
1525 if (child->name() == TempoSection::xml_state_node_name) {
1528 TempoSection* ts = new TempoSection (*child);
1529 metrics->push_back (ts);
1531 if (ts->bar_offset() < 0.0) {
1533 ts->update_bar_offset_from_bbt (*last_meter);
1538 catch (failed_constructor& err){
1539 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1540 *metrics = old_metrics;
1544 } else if (child->name() == MeterSection::xml_state_node_name) {
1547 MeterSection* ms = new MeterSection (*child);
1548 metrics->push_back (ms);
1552 catch (failed_constructor& err) {
1553 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1554 *metrics = old_metrics;
1560 if (niter == nlist.end()) {
1561 MetricSectionSorter cmp;
1562 metrics->sort (cmp);
1566 recompute_map (true);
1567 PropertyChanged (PropertyChange ());
1573 TempoMap::dump (std::ostream& o) const
1575 Glib::RWLock::ReaderLock lm (metrics_lock);
1576 const MeterSection* m;
1577 const TempoSection* t;
1579 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1581 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1582 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? "
1583 << t->movable() << ')' << endl;
1584 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1585 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1586 << " (movable? " << m->movable() << ')' << endl;
1592 TempoMap::n_tempos() const
1594 Glib::RWLock::ReaderLock lm (metrics_lock);
1597 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1598 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1607 TempoMap::n_meters() const
1609 Glib::RWLock::ReaderLock lm (metrics_lock);
1612 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1613 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1622 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1624 Glib::RWLock::WriterLock lm (metrics_lock);
1625 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1626 if ((*i)->frame() >= where && (*i)->movable ()) {
1627 (*i)->set_frame ((*i)->frame() + amount);
1631 timestamp_metrics_from_audio_time ();
1633 PropertyChanged (PropertyChange ());
1636 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1637 * pos can be -ve, if required.
1640 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
1642 return framepos_plus_bbt (pos, BBT_Time (beats));
1645 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1647 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
1649 return framepos_minus_bbt (pos, BBT_Time (beats));
1653 TempoMap::framepos_minus_bbt (framepos_t pos, BBT_Time op)
1655 Glib::RWLock::ReaderLock lm (map_lock);
1656 BBTPointList::const_iterator i;
1657 framecnt_t extra_frames = 0;
1659 /* start from the bar|beat right before (or at) pos */
1661 i = bbt_before_or_at (pos);
1663 /* we know that (*i).frame is less than or equal to pos */
1664 extra_frames = pos - (*i).frame;
1666 /* walk backwards */
1668 while (i != _map->begin() && (op.bars || op.beats)) {
1670 if ((*i).is_bar()) {
1681 /* handle ticks (assumed to be less than
1682 * BBT_Time::ticks_per_bar_division, as always.
1686 frameoffset_t tick_frames = llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1687 framepos_t pre_tick_frames = (*i).frame + extra_frames;
1688 if (tick_frames < pre_tick_frames) {
1689 return pre_tick_frames - tick_frames;
1693 return (*i).frame + extra_frames;
1697 /** Add the BBT interval op to pos and return the result */
1699 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
1701 Glib::RWLock::ReaderLock lm (map_lock);
1702 BBT_Time op_copy (op);
1703 int additional_minutes = 1;
1704 BBTPointList::const_iterator i;
1705 framecnt_t backup_frames = 0;
1709 i = bbt_before_or_at (pos);
1713 /* we know that (*i).frame is before or equal to pos */
1714 backup_frames = pos - (*i).frame;
1716 while (i != _map->end() && (op.bars || op.beats)) {
1718 if ((*i).is_bar()) {
1729 if (i != _map->end()) {
1733 /* we hit the end of the map before finish the bbt walk.
1736 require_map_to (pos + (_frame_rate * 60 * additional_minutes));
1737 additional_minutes *= 2;
1739 /* go back and try again */
1740 warning << "reached end of map with op now at " << op << " end = "
1741 << _map->back().frame << ' ' << _map->back().bar << '|' << _map->back().beat << ", trying to walk "
1742 << op_copy << " ... retry"
1747 return (*i).frame - backup_frames +
1748 llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1750 return (*i).frame - backup_frames;
1754 /** Count the number of beats that are equivalent to distance when going forward,
1758 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
1760 Glib::RWLock::ReaderLock lm (map_lock);
1761 BBTPointList::const_iterator i = bbt_after_or_at (pos);
1762 Evoral::MusicalTime beats = 0;
1763 framepos_t end = pos + distance;
1765 require_map_to (end);
1767 /* if our starting BBTPoint is after pos, add a fractional beat
1768 to represent that distance.
1771 if ((*i).frame != pos) {
1772 beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1775 while (i != _map->end() && (*i).frame < end) {
1779 assert (i != _map->end());
1781 /* if our ending BBTPoint is after the end, subtract a fractional beat
1782 to represent that distance.
1785 if ((*i).frame > end) {
1786 beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1792 TempoMap::BBTPointList::const_iterator
1793 TempoMap::bbt_before_or_at (framepos_t pos)
1795 BBTPointList::const_iterator i;
1797 require_map_to (pos);
1799 Glib::RWLock::ReaderLock lm (map_lock);
1800 i = lower_bound (_map->begin(), _map->end(), pos);
1801 assert (i != _map->end());
1802 if ((*i).frame > pos) {
1803 assert (i != _map->begin());
1810 TempoMap::BBTPointList::const_iterator
1811 TempoMap::bbt_after_or_at (framepos_t pos)
1813 BBTPointList::const_iterator i;
1815 require_map_to (pos);
1817 Glib::RWLock::ReaderLock lm (map_lock);
1818 i = upper_bound (_map->begin(), _map->end(), pos);
1819 assert (i != _map->end());
1825 bool operator() (const BBT_Time& a, const BBT_Time& b) {
1830 TempoMap::BBTPointList::const_iterator
1831 TempoMap::bbt_point_for (const BBT_Time& bbt)
1833 BBTPointList::const_iterator i;
1835 int additional_minutes = 1;
1839 Glib::RWLock::ReaderLock lm (map_lock);
1840 if (!_map->empty() && _map->back().bar >= (bbt.bars + 1)) {
1844 /* add some more distance, using bigger steps each time */
1845 require_map_to (_map->back().frame + (_frame_rate * 60 * additional_minutes));
1846 additional_minutes *= 2;
1850 Glib::RWLock::ReaderLock lm (map_lock);
1851 i = lower_bound (_map->begin(), _map->end(), bbt, cmp);
1852 assert (i != _map->end());
1859 /** Compare the time of this with that of another MetricSection.
1860 * @param with_bbt True to compare using start(), false to use frame().
1861 * @return -1 for less than, 0 for equal, 1 for greater than.
1865 MetricSection::compare (const MetricSection& other) const
1867 if (start() == other.start()) {
1869 } else if (start() < other.start()) {
1880 MetricSection::operator== (const MetricSection& other) const
1882 return compare (other) == 0;
1886 MetricSection::operator!= (const MetricSection& other) const
1888 return compare (other) != 0;
1892 operator<< (std::ostream& o, const Meter& m) {
1893 return o << m.divisions_per_bar() << '/' << m.note_divisor();
1897 operator<< (std::ostream& o, const Tempo& t) {
1898 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
1902 operator<< (std::ostream& o, const MetricSection& section) {
1904 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
1906 const TempoSection* ts;
1907 const MeterSection* ms;
1909 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
1910 o << *((Tempo*) ts);
1911 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
1912 o << *((Meter*) ms);