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 (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()) {
329 if (removed && complete_operation) {
330 recompute_map (false, false);
334 if (removed && complete_operation) {
335 PropertyChanged (PropertyChange ());
340 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
342 bool removed = false;
345 Glib::RWLock::WriterLock lm (lock);
348 for (i = metrics->begin(); i != metrics->end(); ++i) {
349 if (dynamic_cast<MeterSection*> (*i) != 0) {
350 if (tempo.frame() == (*i)->frame()) {
351 if ((*i)->movable()) {
360 if (removed && complete_operation) {
361 recompute_map (true, false);
365 if (removed && complete_operation) {
366 PropertyChanged (PropertyChange ());
371 TempoMap::do_insert (MetricSection* section)
373 bool need_add = true;
375 assert (section->start().ticks == 0);
377 /* we only allow new meters to be inserted on beat 1 of an existing
381 if (dynamic_cast<MeterSection*>(section)) {
383 /* we need to (potentially) update the BBT times of tempo
384 sections based on this new meter.
387 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
389 BBT_Time corrected = section->start();
393 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
394 section->start(), corrected) << endmsg;
396 section->set_start (corrected);
402 /* Look for any existing MetricSection that is of the same type and
403 at the same time as the new one, and remove it before adding
407 Metrics::iterator to_remove = metrics->end ();
409 for (i = metrics->begin(); i != metrics->end(); ++i) {
411 int const c = (*i)->compare (*section);
414 /* this section is before the one to be added; go back round */
417 /* this section is after the one to be added; there can't be any at the same time */
421 /* hacky comparison of type */
422 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
423 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
425 if (iter_is_tempo == insert_is_tempo) {
427 if (!(*i)->movable()) {
429 /* can't (re)move this section, so overwrite it
432 if (!iter_is_tempo) {
433 *(dynamic_cast<MeterSection*>(*i)) = *(dynamic_cast<MeterSection*>(section));
435 *(dynamic_cast<TempoSection*>(*i)) = *(dynamic_cast<TempoSection*>(section));
446 if (to_remove != metrics->end()) {
447 /* remove the MetricSection at the same time as the one we are about to add */
448 metrics->erase (to_remove);
451 /* Add the given MetricSection */
454 for (i = metrics->begin(); i != metrics->end(); ++i) {
456 if ((*i)->compare (*section) < 0) {
460 metrics->insert (i, section);
464 if (i == metrics->end()) {
465 metrics->insert (metrics->end(), section);
471 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
473 const TempoSection& first (first_tempo());
476 remove_tempo (ts, false);
477 add_tempo (tempo, where);
480 Glib::RWLock::WriterLock lm (lock);
481 /* cannot move the first tempo section */
482 *((Tempo*)&first) = tempo;
483 recompute_map (false, false);
487 PropertyChanged (PropertyChange ());
491 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
494 Glib::RWLock::WriterLock lm (lock);
496 /* new tempos always start on a beat */
499 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
501 /* find the meter to use to set the bar offset of this
505 const Meter* meter = &first_meter();
507 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
508 at something, because we insert the default tempo and meter during
509 TempoMap construction.
511 now see if we can find better candidates.
514 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
516 const MeterSection* m;
518 if (where < (*i)->start()) {
522 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
527 ts->update_bar_offset_from_bbt (*meter);
533 recompute_map (false, false);
537 PropertyChanged (PropertyChange ());
541 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
543 const MeterSection& first (first_meter());
546 remove_meter (ms, false);
547 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, false);
557 PropertyChanged (PropertyChange ());
561 TempoMap::add_meter (const Meter& meter, BBT_Time where)
564 Glib::RWLock::WriterLock lm (lock);
566 /* a new meter always starts a new bar on the first beat. so
567 round the start time appropriately. remember that
568 `where' is based on the existing tempo map, not
569 the result after we insert the new meter.
573 if (where.beats != 1) {
578 /* new meters *always* start on a beat. */
581 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
582 recompute_map (true, false);
587 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
592 PropertyChanged (PropertyChange ());
596 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
598 Tempo newtempo (beats_per_minute, note_type);
601 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
602 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
604 Glib::RWLock::WriterLock lm (lock);
605 *((Tempo*) t) = newtempo;
606 recompute_map (false, false);
608 PropertyChanged (PropertyChange ());
615 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
617 Tempo newtempo (beats_per_minute, note_type);
623 /* find the TempoSection immediately preceding "where"
626 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
628 if ((*i)->frame() > where) {
634 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
644 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
654 Glib::RWLock::WriterLock lm (lock);
655 /* cannot move the first tempo section */
656 *((Tempo*)prev) = newtempo;
657 recompute_map (false, false);
660 PropertyChanged (PropertyChange ());
664 TempoMap::first_meter () const
666 const MeterSection *m = 0;
668 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
669 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
674 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
680 TempoMap::first_tempo () const
682 const TempoSection *t = 0;
684 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
685 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
690 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
696 TempoMap::require_map_to (framepos_t pos)
698 /* CALLER MUST HOLD READ LOCK AND MAY NOT HOLD WRITE LOCK */
700 if (_map->empty() || _map->back().frame < pos) {
701 recompute_map (false, true, pos);
706 TempoMap::require_map_to (const BBT_Time& bbt)
708 /* CALLER MUST HOLD READ LOCK AND MAY NOT HOLD WRITE LOCK */
710 if (_map->empty() || _map->back().bbt() < bbt) {
711 recompute_map (false, true, 99);
716 TempoMap::recompute_map (bool reassign_tempo_bbt, bool use_write_lock, framepos_t end)
718 /* CALLER MUST HOLD READ LOCK AND MAY HOLD WRITE LOCK */
724 double divisions_per_bar;
726 double current_frame;
728 Metrics::iterator next_metric;
729 BBTPointList* new_map = new BBTPointList;
734 /* compute 1 mins worth */
735 end = _frame_rate * 60;
737 end = _map->back().frame;
741 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
743 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
744 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
750 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
751 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
757 /* assumes that the first meter & tempo are at frame zero */
759 meter->set_frame (0);
760 tempo->set_frame (0);
762 /* assumes that the first meter & tempo are at 1|1|0 */
767 divisions_per_bar = meter->divisions_per_bar ();
768 beat_frames = meter->frames_per_division (*tempo,_frame_rate);
770 if (reassign_tempo_bbt) {
772 MeterSection* rmeter = meter;
774 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
776 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
778 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
780 /* reassign the BBT time of this tempo section
781 * based on its bar offset position.
784 ts->update_bbt_time_from_bar_offset (*rmeter);
786 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
789 fatal << _("programming error: unhandled MetricSection type") << endmsg;
795 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2 dpb %3 fpb %4\n",
796 *((Meter*)meter), *((Tempo*)tempo), divisions_per_bar, beat_frames));
798 next_metric = metrics->begin();
799 ++next_metric; // skip meter (or tempo)
800 ++next_metric; // skip tempo (or meter)
802 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
803 new_map->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
805 while (current_frame < end) {
808 current_frame += beat_frames;
810 if (current.beats > meter->divisions_per_bar()) {
815 if (next_metric != metrics->end()) {
817 /* no operator >= so invert operator < */
819 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
821 if (!(current < (*next_metric)->start())) {
824 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
828 /* new tempo section: if its on a beat,
829 * we don't have to do anything other
830 * than recompute various distances,
831 * done further below as we transition
832 * the next metric section.
834 * if its not on the beat, we have to
835 * compute the duration of the beat it
836 * is within, which will be different
837 * from the preceding following ones
838 * since it takes part of its duration
839 * from the preceding tempo and part
840 * from this new tempo.
843 if (tempo->start().ticks != 0) {
845 double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);
847 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
848 tempo->start(), current_frame, tempo->bar_offset()));
850 /* back up to previous beat */
851 current_frame -= beat_frames;
852 /* set tempo section location based on offset from last beat */
853 tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
854 /* advance to the location of the new (adjusted) beat */
855 current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
856 /* next metric doesn't have to
857 * match this precisely to
860 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
864 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
865 tempo->start(), current_frame));
866 tempo->set_frame (current_frame);
869 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
873 /* new meter section: always defines the
877 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
878 meter->start(), current, current_frame));
880 assert (current.beats == 1);
882 meter->set_frame (current_frame);
885 divisions_per_bar = meter->divisions_per_bar ();
886 beat_frames = meter->frames_per_division (*tempo, _frame_rate);
888 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
889 beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
893 if (next_metric != metrics->end() && ((*next_metric)->start() == current)) {
894 /* same position so go back and set this one up before advancing
901 if (current.beats == 1) {
902 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
903 new_map->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
905 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
906 new_map->push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
910 if (use_write_lock) {
911 Glib::RWLock::WriterLock lm (lock);
912 swap (_map, new_map);
914 swap (_map, new_map);
921 TempoMap::metric_at (framepos_t frame) const
923 Glib::RWLock::ReaderLock lm (lock);
924 TempoMetric m (first_meter(), first_tempo());
928 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
929 at something, because we insert the default tempo and meter during
930 TempoMap construction.
932 now see if we can find better candidates.
935 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
937 // cerr << "Looking at a metric section " << **i << endl;
939 if ((*i)->frame() > frame) {
943 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
944 m.set_tempo (*tempo);
945 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
946 m.set_meter (*meter);
949 m.set_frame ((*i)->frame ());
950 m.set_start ((*i)->start ());
953 // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
958 TempoMap::metric_at (BBT_Time bbt) const
960 Glib::RWLock::ReaderLock lm (lock);
961 TempoMetric m (first_meter(), first_tempo());
965 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
966 at something, because we insert the default tempo and meter during
967 TempoMap construction.
969 now see if we can find better candidates.
972 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
974 BBT_Time section_start ((*i)->start());
976 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
980 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
981 m.set_tempo (*tempo);
982 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
983 m.set_meter (*meter);
986 m.set_frame ((*i)->frame ());
987 m.set_start (section_start);
994 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
996 Glib::RWLock::ReaderLock lm (lock);
998 const BBTPointList::const_iterator& i = bbt_before_or_at (frame);
1000 bbt.bars = (*i).bar;
1001 bbt.beats = (*i).beat;
1003 if ((*i).frame == frame) {
1006 bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
1007 BBT_Time::ticks_per_bar_division);
1012 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1014 /* CALLER MUST HOLD READ LOCK */
1016 bbt.bars = (*i).bar;
1017 bbt.beats = (*i).beat;
1019 if ((*i).frame == frame) {
1022 bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
1023 BBT_Time::ticks_per_bar_division);
1028 TempoMap::frame_time (const BBT_Time& bbt)
1030 Glib::RWLock::ReaderLock lm (lock);
1032 BBTPointList::const_iterator s = bbt_point_for (BBT_Time (1, 1, 0));
1033 BBTPointList::const_iterator e = bbt_point_for (BBT_Time (bbt.bars, bbt.beats, 0));
1035 if (bbt.ticks != 0) {
1036 return ((*e).frame - (*s).frame) +
1037 llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
1039 return ((*e).frame - (*s).frame);
1044 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1046 Glib::RWLock::ReaderLock lm (lock);
1047 framecnt_t frames = 0;
1050 bbt_time (pos, when);
1051 frames = bbt_duration_at_unlocked (when, bbt,dir);
1057 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir)
1059 if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1063 /* round back to the previous precise beat */
1064 BBTPointList::const_iterator wi = bbt_point_for (BBT_Time (when.bars, when.beats, 0));
1065 BBTPointList::const_iterator start (wi);
1066 double tick_frames = 0;
1068 assert (wi != _map->end());
1070 /* compute how much rounding we did because of non-zero ticks */
1072 if (when.ticks != 0) {
1073 tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
1079 while (wi != _map->end() && bars < bbt.bars) {
1081 if ((*wi).is_bar()) {
1085 assert (wi != _map->end());
1087 while (wi != _map->end() && beats < bbt.beats) {
1091 assert (wi != _map->end());
1093 /* add any additional frames related to ticks in the added value */
1095 if (bbt.ticks != 0) {
1096 tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
1099 return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1103 TempoMap::round_to_bar (framepos_t fr, int dir)
1105 return round_to_type (fr, dir, Bar);
1109 TempoMap::round_to_beat (framepos_t fr, int dir)
1111 return round_to_type (fr, dir, Beat);
1115 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1117 Glib::RWLock::ReaderLock lm (lock);
1118 BBTPointList::const_iterator i = bbt_before_or_at (fr);
1120 uint32_t ticks_one_subdivisions_worth;
1121 uint32_t difference;
1123 bbt_time (fr, the_beat, i);
1125 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1126 fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1128 ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
1132 /* round to next (even if we're on a subdivision */
1134 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1137 /* right on the subdivision, so the difference is just the subdivision ticks */
1138 the_beat.ticks += ticks_one_subdivisions_worth;
1141 /* not on subdivision, compute distance to next subdivision */
1143 the_beat.ticks += ticks_one_subdivisions_worth - mod;
1146 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1147 assert (i != _map->end());
1149 assert (i != _map->end());
1150 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1154 } else if (dir < 0) {
1156 /* round to previous (even if we're on a subdivision) */
1158 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1161 /* right on the subdivision, so the difference is just the subdivision ticks */
1162 difference = ticks_one_subdivisions_worth;
1164 /* not on subdivision, compute distance to previous subdivision, which
1165 is just the modulus.
1171 if (the_beat.ticks < difference) {
1172 if (i == _map->begin()) {
1173 /* can't go backwards from wherever pos is, so just return it */
1177 the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
1179 the_beat.ticks -= difference;
1183 /* round to nearest */
1187 /* compute the distance to the previous and next subdivision */
1189 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1191 /* closer to the next subdivision, so shift forward */
1193 the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1195 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1197 if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
1198 assert (i != _map->end());
1200 assert (i != _map->end());
1201 the_beat.ticks -= BBT_Time::ticks_per_bar_division;
1202 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1205 } else if (rem > 0) {
1207 /* closer to previous subdivision, so shift backward */
1209 if (rem > the_beat.ticks) {
1210 if (i == _map->begin()) {
1211 /* can't go backwards past zero, so ... */
1214 /* step back to previous beat */
1216 the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
1217 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1219 the_beat.ticks = lrint (the_beat.ticks - rem);
1220 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1223 /* on the subdivision, do nothing */
1227 return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
1228 (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
1232 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1234 Glib::RWLock::ReaderLock lm (lock);
1235 BBTPointList::const_iterator fi;
1238 fi = bbt_after_or_at (frame);
1240 fi = bbt_before_or_at (frame);
1243 assert (fi != _map->end());
1245 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));
1250 /* find bar previous to 'frame' */
1252 if ((*fi).is_bar() && (*fi).frame == frame) {
1256 while (!(*fi).is_bar()) {
1257 if (fi == _map->begin()) {
1262 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1263 (*fi).bar, (*fi).beat, (*fi).frame));
1266 } else if (dir > 0) {
1268 /* find bar following 'frame' */
1270 if ((*fi).is_bar() && (*fi).frame == frame) {
1274 while (!(*fi).is_bar()) {
1276 if (fi == _map->end()) {
1282 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n",
1283 (*fi).bar, (*fi).beat, (*fi).frame));
1288 /* true rounding: find nearest bar */
1290 BBTPointList::const_iterator prev = fi;
1291 BBTPointList::const_iterator next = fi;
1293 if ((*fi).frame == frame) {
1297 while ((*prev).beat != 1) {
1298 if (prev == _map->begin()) {
1304 while ((*next).beat != 1) {
1306 if (next == _map->end()) {
1312 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1313 return (*prev).frame;
1315 return (*next).frame;
1324 if ((*fi).frame >= frame) {
1325 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1328 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1329 (*fi).bar, (*fi).beat, (*fi).frame));
1331 } else if (dir > 0) {
1332 if ((*fi).frame <= frame) {
1333 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1336 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n",
1337 (*fi).bar, (*fi).beat, (*fi).frame));
1340 /* find beat nearest to frame */
1341 if ((*fi).frame == frame) {
1345 BBTPointList::const_iterator prev = fi;
1346 BBTPointList::const_iterator next = fi;
1350 if ((frame - (*prev).frame) < ((*next).frame - frame)) {
1351 return (*prev).frame;
1353 return (*next).frame;
1365 TempoMap::map (TempoMap::BBTPointList::const_iterator& begin,
1366 TempoMap::BBTPointList::const_iterator& end,
1367 framepos_t lower, framepos_t upper)
1369 Glib::RWLock::ReaderLock lm (lock);
1370 require_map_to (upper);
1371 begin = lower_bound (_map->begin(), _map->end(), lower);
1372 end = upper_bound (_map->begin(), _map->end(), upper);
1376 TempoMap::tempo_section_at (framepos_t frame) const
1378 Glib::RWLock::ReaderLock lm (lock);
1379 Metrics::const_iterator i;
1380 TempoSection* prev = 0;
1382 for (i = metrics->begin(); i != metrics->end(); ++i) {
1385 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1387 if ((*i)->frame() > frame) {
1403 TempoMap::tempo_at (framepos_t frame) const
1405 TempoMetric m (metric_at (frame));
1411 TempoMap::meter_at (framepos_t frame) const
1413 TempoMetric m (metric_at (frame));
1418 TempoMap::get_state ()
1420 Metrics::const_iterator i;
1421 XMLNode *root = new XMLNode ("TempoMap");
1424 Glib::RWLock::ReaderLock lm (lock);
1425 for (i = metrics->begin(); i != metrics->end(); ++i) {
1426 root->add_child_nocopy ((*i)->get_state());
1434 TempoMap::set_state (const XMLNode& node, int /*version*/)
1437 Glib::RWLock::WriterLock lm (lock);
1440 XMLNodeConstIterator niter;
1441 Metrics old_metrics (*metrics);
1442 MeterSection* last_meter = 0;
1446 nlist = node.children();
1448 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1449 XMLNode* child = *niter;
1451 if (child->name() == TempoSection::xml_state_node_name) {
1454 TempoSection* ts = new TempoSection (*child);
1455 metrics->push_back (ts);
1457 if (ts->bar_offset() < 0.0) {
1459 ts->update_bar_offset_from_bbt (*last_meter);
1464 catch (failed_constructor& err){
1465 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1466 *metrics = old_metrics;
1470 } else if (child->name() == MeterSection::xml_state_node_name) {
1473 MeterSection* ms = new MeterSection (*child);
1474 metrics->push_back (ms);
1478 catch (failed_constructor& err) {
1479 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1480 *metrics = old_metrics;
1486 if (niter == nlist.end()) {
1487 MetricSectionSorter cmp;
1488 metrics->sort (cmp);
1491 recompute_map (true, false);
1494 PropertyChanged (PropertyChange ());
1500 TempoMap::dump (std::ostream& o) const
1502 Glib::RWLock::ReaderLock lm (lock);
1503 const MeterSection* m;
1504 const TempoSection* t;
1506 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1508 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1509 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? "
1510 << t->movable() << ')' << endl;
1511 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1512 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1513 << " (movable? " << m->movable() << ')' << endl;
1519 TempoMap::n_tempos() const
1521 Glib::RWLock::ReaderLock lm (lock);
1524 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1525 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1534 TempoMap::n_meters() const
1536 Glib::RWLock::ReaderLock lm (lock);
1539 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1540 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1549 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1552 Glib::RWLock::WriterLock lm (lock);
1553 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1554 if ((*i)->frame() >= where && (*i)->movable ()) {
1555 (*i)->set_frame ((*i)->frame() + amount);
1559 /* now reset the BBT time of all metrics, based on their new
1560 * audio time. This is the only place where we do this reverse
1564 Metrics::iterator i;
1565 const MeterSection* meter;
1566 const TempoSection* tempo;
1570 meter = &first_meter ();
1571 tempo = &first_tempo ();
1576 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1579 MetricSection* prev = 0;
1581 for (i = metrics->begin(); i != metrics->end(); ++i) {
1584 TempoMetric metric (*meter, *tempo);
1587 metric.set_start (prev->start());
1588 metric.set_frame (prev->frame());
1590 // metric will be at frames=0 bbt=1|1|0 by default
1591 // which is correct for our purpose
1594 BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1595 bbt_time ((*i)->frame(), bbt, bi);
1597 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1603 if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
1604 /* round up to next beat */
1610 if (bbt.beats != 1) {
1611 /* round up to next bar */
1617 // cerr << bbt << endl;
1619 (*i)->set_start (bbt);
1621 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1623 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1624 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1626 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1628 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1635 recompute_map (true, false);
1639 PropertyChanged (PropertyChange ());
1642 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1643 * pos can be -ve, if required.
1646 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
1648 return framepos_plus_bbt (pos, BBT_Time (beats));
1651 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1653 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
1655 return framepos_minus_bbt (pos, BBT_Time (beats));
1659 TempoMap::framepos_minus_bbt (framepos_t pos, BBT_Time op)
1661 Glib::RWLock::ReaderLock lm (lock);
1662 BBTPointList::const_iterator i;
1663 framecnt_t extra_frames = 0;
1664 bool had_bars = (op.bars != 0);
1666 /* start from the bar|beat right before (or at) pos */
1668 i = bbt_before_or_at (pos);
1670 /* we know that (*i).frame is less than or equal to pos */
1671 extra_frames = pos - (*i).frame;
1673 /* walk backwards */
1675 while (i != _map->begin() && (op.bars || op.beats)) {
1679 if ((*i).is_bar()) {
1686 if ((had_bars && op.bars == 0) || !had_bars) {
1687 /* finished counting bars, or none to count,
1688 so decrement beat count
1696 /* handle ticks (assumed to be less than
1697 * BBT_Time::ticks_per_bar_division, as always.
1701 frameoffset_t tick_frames = llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1702 framepos_t pre_tick_frames = (*i).frame + extra_frames;
1703 if (tick_frames < pre_tick_frames) {
1704 return pre_tick_frames - tick_frames;
1708 return (*i).frame + extra_frames;
1712 /** Add the BBT interval op to pos and return the result */
1714 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op)
1716 Glib::RWLock::ReaderLock lm (lock);
1717 BBT_Time op_copy (op);
1718 int additional_minutes = 1;
1719 BBTPointList::const_iterator i;
1720 framecnt_t backup_frames = 0;
1721 bool had_bars = (op.bars != 0);
1725 i = bbt_before_or_at (pos);
1729 /* we know that (*i).frame is before or equal to pos */
1730 backup_frames = pos - (*i).frame;
1732 while (i != _map->end() && (op.bars || op.beats)) {
1737 if ((*i).is_bar()) {
1744 if ((had_bars && op.bars == 0) || !had_bars) {
1745 /* finished counting bars, or none to count,
1746 so decrement beat count
1755 if (i != _map->end()) {
1759 /* we hit the end of the map before finish the bbt walk.
1762 require_map_to (pos + (_frame_rate * 60 * additional_minutes));
1763 additional_minutes *= 2;
1765 /* go back and try again */
1766 warning << "reached end of map with op now at " << op << " end = "
1767 << _map->back().frame << ' ' << _map->back().bar << '|' << _map->back().beat << ", trying to walk "
1768 << op_copy << " ... retry"
1773 return (*i).frame - backup_frames +
1774 llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
1776 return (*i).frame - backup_frames;
1780 /** Count the number of beats that are equivalent to distance when going forward,
1784 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
1786 Glib::RWLock::ReaderLock lm (lock);
1787 BBTPointList::const_iterator i = bbt_after_or_at (pos);
1788 Evoral::MusicalTime beats = 0;
1789 framepos_t end = pos + distance;
1791 require_map_to (end);
1793 /* if our starting BBTPoint is after pos, add a fractional beat
1794 to represent that distance.
1797 if ((*i).frame != pos) {
1798 beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1801 while (i != _map->end() && (*i).frame < end) {
1805 assert (i != _map->end());
1807 /* if our ending BBTPoint is after the end, subtract a fractional beat
1808 to represent that distance.
1811 if ((*i).frame > end) {
1812 beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
1818 TempoMap::BBTPointList::const_iterator
1819 TempoMap::bbt_before_or_at (framepos_t pos)
1821 /* CALLER MUST HOLD READ LOCK */
1822 BBTPointList::const_iterator i;
1824 require_map_to (pos);
1825 i = lower_bound (_map->begin(), _map->end(), pos);
1826 assert (i != _map->end());
1827 if ((*i).frame > pos) {
1828 assert (i != _map->begin());
1834 TempoMap::BBTPointList::const_iterator
1835 TempoMap::bbt_after_or_at (framepos_t pos)
1837 /* CALLER MUST HOLD READ LOCK */
1839 BBTPointList::const_iterator i;
1841 require_map_to (pos);
1842 i = upper_bound (_map->begin(), _map->end(), pos);
1843 assert (i != _map->end());
1848 bool operator() (const BBT_Time& a, const BBT_Time& b) {
1853 TempoMap::BBTPointList::const_iterator
1854 TempoMap::bbt_point_for (const BBT_Time& bbt)
1856 /* CALLER MUST HOLD READ LOCK */
1858 BBTPointList::const_iterator i;
1860 int additional_minutes = 1;
1864 if (!_map->empty() && _map->back().bar >= (bbt.bars + 1)) {
1868 /* add some more distance, using bigger steps each time */
1869 require_map_to (_map->back().frame + (_frame_rate * 60 * additional_minutes));
1870 additional_minutes *= 2;
1873 i = lower_bound (_map->begin(), _map->end(), bbt, cmp);
1874 assert (i != _map->end());
1880 /** Compare the time of this with that of another MetricSection.
1881 * @param with_bbt True to compare using start(), false to use frame().
1882 * @return -1 for less than, 0 for equal, 1 for greater than.
1886 MetricSection::compare (const MetricSection& other) const
1888 if (start() == other.start()) {
1890 } else if (start() < other.start()) {
1901 MetricSection::operator== (const MetricSection& other) const
1903 return compare (other) == 0;
1907 MetricSection::operator!= (const MetricSection& other) const
1909 return compare (other) != 0;
1913 operator<< (std::ostream& o, const Meter& m) {
1914 return o << m.divisions_per_bar() << '/' << m.note_divisor();
1918 operator<< (std::ostream& o, const Tempo& t) {
1919 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
1923 operator<< (std::ostream& o, const MetricSection& section) {
1925 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
1927 const TempoSection* ts;
1928 const MeterSection* ms;
1930 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
1931 o << *((Tempo*) ts);
1932 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
1933 o << *((Meter*) ms);