2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
28 #include "evoral/Beats.hpp"
29 #include "ardour/debug.h"
30 #include "ardour/lmath.h"
31 #include "ardour/tempo.h"
37 using namespace ARDOUR;
40 using Timecode::BBT_Time;
42 /* _default tempo is 4/4 qtr=120 */
44 Meter TempoMap::_default_meter (4.0, 4.0);
45 Tempo TempoMap::_default_tempo (120.0);
47 /***********************************************************************/
50 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
52 /* This is tempo- and meter-sensitive. The number it returns
53 is based on the interval between any two lines in the
54 grid that is constructed from tempo and meter sections.
56 The return value IS NOT interpretable in terms of "beats".
59 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
65 return frames_per_grid (tempo, sr) * _divisions_per_bar;
69 /***********************************************************************/
71 const string TempoSection::xml_state_node_name = "Tempo";
73 TempoSection::TempoSection (const XMLNode& node)
74 : MetricSection (0.0), Tempo (TempoMap::default_tempo())
76 const XMLProperty *prop;
80 if ((prop = node.property ("start")) == 0) {
81 error << _("MeterSection XML node has no \"start\" property") << endmsg;
82 throw failed_constructor();
85 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
89 /* legacy session - start used to be in bbt*/
92 } else if (sscanf (prop->value().c_str(), "%lf", &start) != 1 || start < 0.0) {
93 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
98 if ((prop = node.property ("beats-per-minute")) == 0) {
99 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
100 throw failed_constructor();
103 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
104 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
105 throw failed_constructor();
108 if ((prop = node.property ("note-type")) == 0) {
109 /* older session, make note type be quarter by default */
112 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
113 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
114 throw failed_constructor();
118 if ((prop = node.property ("movable")) == 0) {
119 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
120 throw failed_constructor();
123 set_movable (string_is_affirmative (prop->value()));
125 if ((prop = node.property ("bar-offset")) == 0) {
128 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
129 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
130 throw failed_constructor();
134 if ((prop = node.property ("tempo-type")) == 0) {
135 _type = Type::Constant;
137 if (strstr(prop->value().c_str(),"Constant")) {
138 _type = Type::Constant;
146 TempoSection::get_state() const
148 XMLNode *root = new XMLNode (xml_state_node_name);
152 snprintf (buf, sizeof (buf), "%f", beat());
153 root->add_property ("start", buf);
154 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
155 root->add_property ("beats-per-minute", buf);
156 snprintf (buf, sizeof (buf), "%f", _note_type);
157 root->add_property ("note-type", buf);
158 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
159 // root->add_property ("bar-offset", buf);
160 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
161 root->add_property ("movable", buf);
163 snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
164 root->add_property ("tempo-type", buf);
171 TempoSection::update_bar_offset_from_bbt (const Meter& m)
173 _bar_offset = (start() * BBT_Time::ticks_per_beat) /
174 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
176 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
180 TempoSection::set_type (Type type)
185 /** returns the tempo at the zero-based (relative to tempo section) frame.
188 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
191 if (_type == Constant) {
192 return beats_per_minute();
195 return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat;
198 /** returns the zero-based frame (relative to tempo section)
199 where the tempo occurs.
202 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
204 if (_type == Constant) {
208 return minute_to_frame (time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
211 /** returns the zero-based tick (relative to tempo section)
212 where the zero-based frame (relative to tempo section)
216 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
218 if (_type == Constant) {
219 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
222 return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
225 /** returns the zero-based frame (relative to tempo section)
226 where the zero-based tick (relative to tempo section)
230 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
232 if (_type == Constant) {
233 return (framepos_t) floor ((tick / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
236 return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
239 /** returns the zero-based beat (relative to tempo section)
240 where the zero-based frame (relative to tempo section)
244 TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
246 return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
249 /** returns the zero-based frame (relative to tempo section start frame)
250 where the zero-based beat (relative to tempo section start)
255 TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
257 return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
261 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
263 return time * 60.0 * frame_rate;
267 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
269 return (frame / (double) frame_rate) / 60.0;
272 /* position function */
274 TempoSection::a_func (double end_tpm, double c_func) const
276 return log (end_tpm / ticks_per_minute()) / c_func;
279 /*function constant*/
281 TempoSection::c_func (double end_tpm, double end_time) const
283 return log (end_tpm / ticks_per_minute()) / end_time;
286 /* tempo in tpm at time in minutes */
288 TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
290 return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
293 /* time in minutes at tempo in tpm */
295 TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
297 return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
300 /* tick at time in minutes */
302 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
304 return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
307 /* time in minutes at tick */
309 TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
311 return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
314 /* beat at time in minutes */
316 TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
318 return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
321 /* time in munutes at beat */
323 TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
325 return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
329 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
333 if (_bar_offset < 0.0) {
340 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
341 new_start = ticks / BBT_Time::ticks_per_beat;
343 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
344 _bar_offset, meter.divisions_per_bar(), ticks, new_start, new_start));
346 set_start (new_start);
349 /***********************************************************************/
351 const string MeterSection::xml_state_node_name = "Meter";
353 MeterSection::MeterSection (const XMLNode& node)
354 : MetricSection (0.0), Meter (TempoMap::default_meter())
356 XMLProperty const * prop;
359 const XMLProperty *prop;
362 pair<double, BBT_Time> start;
364 if ((prop = node.property ("start")) == 0) {
365 error << _("MeterSection XML node has no \"start\" property") << endmsg;
366 throw failed_constructor();
368 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
372 /* legacy session - start used to be in bbt*/
374 } else if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
375 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
376 throw failed_constructor();
380 if ((prop = node.property ("bbt")) == 0) {
381 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
382 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
386 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
387 throw failed_constructor();
393 /* beats-per-bar is old; divisions-per-bar is new */
395 if ((prop = node.property ("divisions-per-bar")) == 0) {
396 if ((prop = node.property ("beats-per-bar")) == 0) {
397 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
398 throw failed_constructor();
402 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
403 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
404 throw failed_constructor();
407 if ((prop = node.property ("note-type")) == 0) {
408 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
409 throw failed_constructor();
412 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
413 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
414 throw failed_constructor();
417 if ((prop = node.property ("movable")) == 0) {
418 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
419 throw failed_constructor();
422 set_movable (string_is_affirmative (prop->value()));
426 MeterSection::get_state() const
428 XMLNode *root = new XMLNode (xml_state_node_name);
432 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
436 root->add_property ("bbt", buf);
437 snprintf (buf, sizeof (buf), "%f", start());
438 root->add_property ("start", buf);
439 snprintf (buf, sizeof (buf), "%f", _note_type);
440 root->add_property ("note-type", buf);
441 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
442 root->add_property ("divisions-per-bar", buf);
443 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
444 root->add_property ("movable", buf);
449 /***********************************************************************/
451 struct MetricSectionSorter {
452 bool operator() (const MetricSection* a, const MetricSection* b) {
453 return a->start() < b->start();
457 struct MetricSectionFrameSorter {
458 bool operator() (const MetricSection* a, const MetricSection* b) {
459 return a->frame() < b->frame();
463 TempoMap::TempoMap (framecnt_t fr)
472 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp);
473 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
475 t->set_movable (false);
476 m->set_movable (false);
478 /* note: frame time is correct (zero) for both of these */
480 metrics.push_back (t);
481 metrics.push_back (m);
485 TempoMap::~TempoMap ()
490 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
492 bool removed = false;
495 Glib::Threads::RWLock::WriterLock lm (lock);
496 if ((removed = remove_tempo_locked (tempo))) {
497 if (complete_operation) {
498 recompute_map (true);
503 if (removed && complete_operation) {
504 PropertyChanged (PropertyChange ());
509 TempoMap::remove_tempo_locked (const TempoSection& tempo)
513 for (i = metrics.begin(); i != metrics.end(); ++i) {
514 if (dynamic_cast<TempoSection*> (*i) != 0) {
515 if (tempo.frame() == (*i)->frame()) {
516 if ((*i)->movable()) {
528 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
530 bool removed = false;
533 Glib::Threads::RWLock::WriterLock lm (lock);
534 if ((removed = remove_meter_locked (tempo))) {
535 if (complete_operation) {
536 recompute_map (true);
541 if (removed && complete_operation) {
542 PropertyChanged (PropertyChange ());
547 TempoMap::remove_meter_locked (const MeterSection& tempo)
551 for (i = metrics.begin(); i != metrics.end(); ++i) {
552 if (dynamic_cast<MeterSection*> (*i) != 0) {
553 if (tempo.frame() == (*i)->frame()) {
554 if ((*i)->movable()) {
566 TempoMap::do_insert (MetricSection* section)
568 bool need_add = true;
570 /* we only allow new meters to be inserted on beat 1 of an existing
574 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
575 assert (m->bbt().ticks == 0);
577 /* we need to (potentially) update the BBT times of tempo
578 sections based on this new meter.
581 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
583 pair<double, BBT_Time> corrected = make_pair (m->start(), m->bbt());
584 corrected.second.beats = 1;
585 corrected.second.ticks = 0;
587 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
588 m->bbt(), corrected.second) << endmsg;
589 m->set_start (corrected);
595 /* Look for any existing MetricSection that is of the same type and
596 in the same bar as the new one, and remove it before adding
597 the new one. Note that this means that if we find a matching,
598 existing section, we can break out of the loop since we're
599 guaranteed that there is only one such match.
602 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
604 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
605 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
607 if (tempo && insert_tempo) {
611 if (tempo->start() == insert_tempo->start()) {
613 if (!tempo->movable()) {
615 /* can't (re)move this section, so overwrite
616 * its data content (but not its properties as
620 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
628 } else if (!tempo && !insert_tempo) {
631 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
632 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
633 if (meter->start() == insert_meter->start()) {
635 if (!meter->movable()) {
637 /* can't (re)move this section, so overwrite
638 * its data content (but not its properties as
642 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
652 /* non-matching types, so we don't care */
656 /* Add the given MetricSection, if we didn't just reset an existing
661 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
662 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
666 for (i = metrics.begin(); i != metrics.end(); ++i) {
667 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
669 if (meter && meter->start() > insert_meter->start()) {
673 } else if (insert_tempo) {
674 for (i = metrics.begin(); i != metrics.end(); ++i) {
675 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
678 if (tempo->start() > insert_tempo->start()) {
685 metrics.insert (i, section);
690 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
693 Glib::Threads::RWLock::WriterLock lm (lock);
694 TempoSection& first (first_tempo());
696 if (ts.start() != first.start()) {
697 remove_tempo_locked (ts);
698 add_tempo_locked (tempo, where, true, type);
700 first.set_type (type);
702 /* cannot move the first tempo section */
703 *static_cast<Tempo*>(&first) = tempo;
704 recompute_map (false);
709 PropertyChanged (PropertyChange ());
713 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
715 TempoSection& first (first_tempo());
716 if (ts.frame() != first.frame()) {
718 Glib::Threads::RWLock::WriterLock lm (lock);
719 /* currently this is always done in audio time */
721 //if (ts.position_lock_style() == AudioTime) {
723 ts.set_start (beat_where);
724 MetricSectionSorter cmp;
728 ts.set_frame (frame);
730 MetricSectionFrameSorter fcmp;
733 Metrics::const_iterator i;
734 TempoSection* prev_ts = 0;
735 TempoSection* next_ts = 0;
737 for (i = metrics.begin(); i != metrics.end(); ++i) {
739 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
741 if (t->frame() >= frame) {
749 for (i = metrics.begin(); i != metrics.end(); ++i) {
751 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
753 if (t->frame() > frame) {
761 /* set the start beat */
762 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
763 double beats = beats_to_ts + prev_ts->start();
766 if (next_ts->start() < beats) {
767 /* with frame-based editing, it is possible to get in a
768 situation where if the tempo was placed at the mouse pointer frame,
769 the following music-based tempo would jump to an earlier frame,
770 changing the start beat of the moved tempo.
771 in this case, we have to do some beat-based comparison TODO
774 ts.set_start (next_ts->start());
775 } else if (prev_ts->start() > beats) {
776 ts.set_start (prev_ts->start());
778 ts.set_start (beats);
781 ts.set_start (beats);
783 MetricSectionSorter cmp;
788 recompute_map (false);
792 MetricPositionChanged (); // Emit Signal
796 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
799 Glib::Threads::RWLock::WriterLock lm (lock);
800 add_tempo_locked (tempo, where, true, type);
804 PropertyChanged (PropertyChange ());
808 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
810 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
815 recompute_map (false);
820 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const double& start, const BBT_Time& where)
823 Glib::Threads::RWLock::WriterLock lm (lock);
824 MeterSection& first (first_meter());
825 if (ms.start() != first.start()) {
826 remove_meter_locked (ms);
827 add_meter_locked (meter, start, where, true);
829 /* cannot move the first meter section */
830 *static_cast<Meter*>(&first) = meter;
831 recompute_map (true);
835 PropertyChanged (PropertyChange ());
839 TempoMap::add_meter (const Meter& meter, double start, BBT_Time where)
842 Glib::Threads::RWLock::WriterLock lm (lock);
843 add_meter_locked (meter, start, where, true);
848 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
853 PropertyChanged (PropertyChange ());
857 TempoMap::add_meter_locked (const Meter& meter, double start, BBT_Time where, bool recompute)
859 /* a new meter always starts a new bar on the first beat. so
860 round the start time appropriately. remember that
861 `where' is based on the existing tempo map, not
862 the result after we insert the new meter.
866 if (where.beats != 1) {
871 /* new meters *always* start on a beat. */
874 do_insert (new MeterSection (start, where, meter.divisions_per_bar(), meter.note_divisor()));
877 recompute_map (true);
883 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
885 Tempo newtempo (beats_per_minute, note_type);
888 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
889 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
891 Glib::Threads::RWLock::WriterLock lm (lock);
892 *((Tempo*) t) = newtempo;
893 recompute_map (false);
895 PropertyChanged (PropertyChange ());
902 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
904 Tempo newtempo (beats_per_minute, note_type);
910 /* find the TempoSection immediately preceding "where"
913 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
915 if ((*i)->frame() > where) {
921 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
931 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
941 Glib::Threads::RWLock::WriterLock lm (lock);
942 /* cannot move the first tempo section */
943 *((Tempo*)prev) = newtempo;
944 recompute_map (false);
947 PropertyChanged (PropertyChange ());
951 TempoMap::first_meter () const
953 const MeterSection *m = 0;
955 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
956 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
961 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
962 abort(); /*NOTREACHED*/
967 TempoMap::first_meter ()
971 /* CALLER MUST HOLD LOCK */
973 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
974 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
979 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
980 abort(); /*NOTREACHED*/
985 TempoMap::first_tempo () const
987 const TempoSection *t = 0;
989 /* CALLER MUST HOLD LOCK */
991 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
992 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
997 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
998 abort(); /*NOTREACHED*/
1003 TempoMap::first_tempo ()
1005 TempoSection *t = 0;
1007 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1008 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1013 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1014 abort(); /*NOTREACHED*/
1019 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1021 /* CALLER MUST HOLD WRITE LOCK */
1023 MeterSection* meter = 0;
1024 TempoSection* tempo = 0;
1025 double current_frame;
1027 Metrics::iterator next_metric;
1031 /* we will actually stop once we hit
1038 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1040 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1043 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
1051 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1054 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
1062 /* assumes that the first meter & tempo are at frame zero */
1064 meter->set_frame (0);
1065 tempo->set_frame (0);
1067 /* assumes that the first meter & tempo are at 1|1|0 */
1072 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
1074 next_metric = metrics.begin();
1075 ++next_metric; // skip meter (or tempo)
1076 ++next_metric; // skip tempo (or meter)
1078 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
1081 /* silly call from Session::process() during startup
1086 _extend_map (tempo, meter, next_metric, current, current_frame, end);
1090 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
1091 Metrics::iterator next_metric,
1092 BBT_Time current, framepos_t current_frame, framepos_t end)
1094 /* CALLER MUST HOLD WRITE LOCK */
1096 Metrics::const_iterator i;
1097 Metrics::const_iterator mi;
1099 TempoSection* prev_ts = tempo;
1101 for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1102 MeterSection* m = 0;
1104 if ((m = dynamic_cast<MeterSection*> (*mi)) != 0) {
1106 for (i = metrics.begin(); i != metrics.end(); ++i) {
1109 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1111 if (t->start() > prev_ts->start()) {
1113 /*tempo section (t) lies in the previous meter */
1114 double const beats_relative_to_prev_ts = t->start() - prev_ts->start();
1115 double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
1117 /* assume (falsely) that the target tempo is constant */
1118 double const t_fpb = t->frames_per_beat (_frame_rate);
1119 double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
1120 /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
1121 double length_estimate = beats_relative_to_prev_ts * av_fpb;
1123 if (prev_ts->type() == TempoSection::Type::Constant) {
1124 length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
1127 double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
1128 double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
1130 while (fabs (tick_error) > system_precision_at_target_tempo) {
1132 double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
1133 (framepos_t) length_estimate, _frame_rate);
1134 tick_error = ticks_relative_to_prev_ts - actual_ticks;
1135 length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
1138 t->set_frame (length_estimate + prev_ts->frame());
1140 double const meter_start_beats = m->start();
1141 if (meter_start_beats < t->start() && meter_start_beats == prev_ts->start()) {
1143 m->set_frame (prev_ts->frame());
1144 } else if (meter_start_beats < t->start() && meter_start_beats > prev_ts->start()) {
1145 framepos_t new_frame = prev_ts->frame_at_beat (m->start() - prev_ts->start(),
1146 t->beats_per_minute(),
1147 t->frame() - prev_ts->frame(),
1148 _frame_rate) + prev_ts->frame();
1149 m->set_frame (new_frame);
1162 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1164 Glib::Threads::RWLock::ReaderLock lm (lock);
1165 TempoMetric m (first_meter(), first_tempo());
1167 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1168 at something, because we insert the default tempo and meter during
1169 TempoMap construction.
1171 now see if we can find better candidates.
1174 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1176 if ((*i)->frame() > frame) {
1189 /* XX meters only */
1191 TempoMap::metric_at (BBT_Time bbt) const
1193 Glib::Threads::RWLock::ReaderLock lm (lock);
1194 TempoMetric m (first_meter(), first_tempo());
1196 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1197 at something, because we insert the default tempo and meter during
1198 TempoMap construction.
1200 now see if we can find better candidates.
1203 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1205 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1206 BBT_Time section_start (mw->bbt());
1208 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1220 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1222 Glib::Threads::RWLock::ReaderLock lm (lock);
1228 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1231 bbt = beats_to_bbt (beat_at_frame (frame));
1235 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1237 /* CALLER HOLDS READ LOCK */
1239 double accumulated_beats = 0.0;
1240 double accumulated_bars = 0.0;
1241 MeterSection* prev_ms = 0;
1243 Metrics::const_iterator i;
1245 for (i = metrics.begin(); i != metrics.end(); ++i) {
1247 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1248 double bars_to_m = 0.0;
1250 bars_to_m = (m->start() - prev_ms->start()) / prev_ms->divisions_per_bar();
1252 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1256 accumulated_beats += m->start() - prev_ms->start();
1257 accumulated_bars += bars_to_m;
1263 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1264 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1265 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.ticks / BBT_Time::ticks_per_beat);
1271 TempoMap::beats_to_bbt (double beats)
1273 /* CALLER HOLDS READ LOCK */
1275 MeterSection* prev_ms = 0;
1276 uint32_t accumulated_bars = 0;
1278 Metrics::const_iterator i;
1280 for (i = metrics.begin(); i != metrics.end(); ++i) {
1281 MeterSection* m = 0;
1283 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1285 if (beats < m->start()) {
1286 /* this is the meter after the one our beat is on*/
1291 /* we need a whole number of bars. */
1292 accumulated_bars += ((m->start() - prev_ms->start()) + 1) / prev_ms->divisions_per_bar();
1299 double const beats_in_ms = beats - prev_ms->start();
1300 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1301 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1302 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1303 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1307 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1308 ret.beats = (uint32_t) floor (remaining_beats);
1309 ret.bars = total_bars;
1311 /* 0 0 0 to 1 1 0 - based mapping*/
1315 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1317 ret.ticks -= BBT_Time::ticks_per_beat;
1320 if (ret.beats > prev_ms->divisions_per_bar()) {
1329 TempoMap::tick_at_frame (framecnt_t frame) const
1332 Metrics::const_iterator i;
1333 const TempoSection* prev_ts = 0;
1334 double accumulated_ticks = 0.0;
1336 for (i = metrics.begin(); i != metrics.end(); ++i) {
1339 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1341 if ((prev_ts) && frame < t->frame()) {
1342 /*the previous ts is the one containing the frame */
1344 framepos_t time = frame - prev_ts->frame();
1345 framepos_t last_frame = t->frame() - prev_ts->frame();
1346 double last_beats_per_minute = t->beats_per_minute();
1348 return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
1351 if (prev_ts && t->frame() > prev_ts->frame()) {
1352 framepos_t time = t->frame() - prev_ts->frame();
1353 framepos_t last_frame = t->frame() - prev_ts->frame();
1354 double last_beats_per_minute = t->beats_per_minute();
1355 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate);
1362 /* treated s linear for this ts */
1363 framecnt_t frames_in_section = frame - prev_ts->frame();
1364 double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1366 return ticks_in_section + accumulated_ticks;
1371 TempoMap::frame_at_tick (double tick) const
1373 /* HOLD THE READER LOCK */
1375 double accumulated_ticks = 0.0;
1376 double accumulated_ticks_to_prev = 0.0;
1377 const TempoSection* prev_ts = 0;
1379 Metrics::const_iterator i;
1381 for (i = metrics.begin(); i != metrics.end(); ++i) {
1383 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1385 if (prev_ts && t->frame() > prev_ts->frame()) {
1386 framepos_t const time = t->frame() - prev_ts->frame();
1387 framepos_t const last_time = t->frame() - prev_ts->frame();
1388 double const last_beats_per_minute = t->beats_per_minute();
1389 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1392 if (prev_ts && tick < accumulated_ticks) {
1393 /* prev_ts is the one affecting us. */
1395 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1396 framepos_t const section_start = prev_ts->frame();
1397 framepos_t const last_time = t->frame() - prev_ts->frame();
1398 double const last_beats_per_minute = t->beats_per_minute();
1400 return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start;
1402 accumulated_ticks_to_prev = accumulated_ticks;
1406 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1407 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
1408 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1414 TempoMap::beat_at_frame (framecnt_t frame) const
1416 Glib::Threads::RWLock::ReaderLock lm (lock);
1418 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1422 TempoMap::frame_at_beat (double beat) const
1424 Glib::Threads::RWLock::ReaderLock lm (lock);
1426 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1430 TempoMap::frame_time (const BBT_Time& bbt)
1433 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1437 if (bbt.beats < 1) {
1438 throw std::logic_error ("beats are counted from one");
1440 Glib::Threads::RWLock::ReaderLock lm (lock);
1442 framepos_t const ret = frame_at_beat (bbt_to_beats (bbt));
1449 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1452 Glib::Threads::RWLock::ReaderLock lm (lock);
1454 Metrics::const_iterator i;
1455 TempoSection* first = 0;
1456 TempoSection* second = 0;
1458 for (i = metrics.begin(); i != metrics.end(); ++i) {
1461 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1463 if ((*i)->frame() > pos) {
1471 if (first && second) {
1472 framepos_t const last_time = second->frame() - first->frame();
1473 double const last_beats_per_minute = second->beats_per_minute();
1475 framepos_t const time = pos - first->frame();
1476 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1477 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1479 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1481 return time_at_bbt - time;
1484 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1485 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1489 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1491 return round_to_type (fr, dir, Bar);
1495 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1497 return round_to_type (fr, dir, Beat);
1501 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1503 Glib::Threads::RWLock::ReaderLock lm (lock);
1505 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1506 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1507 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1509 ticks -= beats * BBT_Time::ticks_per_beat;
1512 /* round to next (or same iff dir == RoundUpMaybe) */
1514 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1516 if (mod == 0 && dir == RoundUpMaybe) {
1517 /* right on the subdivision, which is fine, so do nothing */
1519 } else if (mod == 0) {
1520 /* right on the subdivision, so the difference is just the subdivision ticks */
1521 ticks += ticks_one_subdivisions_worth;
1524 /* not on subdivision, compute distance to next subdivision */
1526 ticks += ticks_one_subdivisions_worth - mod;
1529 if (ticks >= BBT_Time::ticks_per_beat) {
1530 ticks -= BBT_Time::ticks_per_beat;
1532 } else if (dir < 0) {
1534 /* round to previous (or same iff dir == RoundDownMaybe) */
1536 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1538 if (difference == 0 && dir == RoundDownAlways) {
1539 /* right on the subdivision, but force-rounding down,
1540 so the difference is just the subdivision ticks */
1541 difference = ticks_one_subdivisions_worth;
1544 if (ticks < difference) {
1545 ticks = BBT_Time::ticks_per_beat - ticks;
1547 ticks -= difference;
1551 /* round to nearest */
1554 /* compute the distance to the previous and next subdivision */
1556 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1558 /* closer to the next subdivision, so shift forward */
1560 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1562 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1564 if (ticks > BBT_Time::ticks_per_beat) {
1566 ticks -= BBT_Time::ticks_per_beat;
1567 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1570 } else if (rem > 0) {
1572 /* closer to previous subdivision, so shift backward */
1576 /* can't go backwards past zero, so ... */
1579 /* step back to previous beat */
1581 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1582 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1584 ticks = lrint (ticks - rem);
1585 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1588 /* on the subdivision, do nothing */
1591 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1595 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1597 Glib::Threads::RWLock::ReaderLock lm (lock);
1599 double const beat_at_framepos = beat_at_frame (frame);
1601 BBT_Time bbt (beats_to_bbt (beat_at_framepos));
1606 /* find bar previous to 'frame' */
1609 return frame_time (bbt);
1611 } else if (dir > 0) {
1612 /* find bar following 'frame' */
1616 return frame_time (bbt);
1618 /* true rounding: find nearest bar */
1619 framepos_t raw_ft = frame_time (bbt);
1622 framepos_t prev_ft = frame_time (bbt);
1624 framepos_t next_ft = frame_time (bbt);
1626 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1637 return frame_at_beat (floor (beat_at_framepos));
1638 } else if (dir > 0) {
1639 return frame_at_beat (ceil (beat_at_framepos));
1641 return frame_at_beat (floor (beat_at_framepos + 0.5));
1650 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1651 framepos_t lower, framepos_t upper)
1653 Glib::Threads::RWLock::ReaderLock lm (lock);
1654 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1655 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1657 while (cnt <= upper_beat) {
1658 framecnt_t const pos = frame_at_beat (cnt);
1659 MeterSection const meter = meter_section_at (pos);
1660 Tempo const tempo = tempo_at (pos);
1661 BBT_Time const bbt = beats_to_bbt ((double) cnt);
1663 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1669 TempoMap::tempo_section_at (framepos_t frame) const
1671 Glib::Threads::RWLock::ReaderLock lm (lock);
1673 Metrics::const_iterator i;
1674 TempoSection* prev = 0;
1676 for (i = metrics.begin(); i != metrics.end(); ++i) {
1679 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1681 if ((*i)->frame() > frame) {
1691 abort(); /*NOTREACHED*/
1697 /* don't use this to calculate length (the tempo is only correct for this frame).
1698 do that stuff based on the beat_at_frame and frame_at_beat api
1701 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1703 Glib::Threads::RWLock::ReaderLock lm (lock);
1705 const TempoSection* ts_at = &tempo_section_at (frame);
1706 const TempoSection* ts_after = 0;
1707 Metrics::const_iterator i;
1709 for (i = metrics.begin(); i != metrics.end(); ++i) {
1712 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1714 if ((*i)->frame() > frame) {
1722 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
1724 /* must be treated as constant tempo */
1725 return ts_at->frames_per_beat (_frame_rate);
1729 TempoMap::tempo_at (framepos_t frame) const
1731 Glib::Threads::RWLock::ReaderLock lm (lock);
1733 TempoMetric m (metric_at (frame));
1734 TempoSection* prev_ts = 0;
1736 Metrics::const_iterator i;
1738 for (i = metrics.begin(); i != metrics.end(); ++i) {
1740 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1741 if ((prev_ts) && t->frame() > frame) {
1742 /* this is the one past frame */
1743 framepos_t const time = frame - prev_ts->frame();
1744 framepos_t const last_time = t->frame() - prev_ts->frame();
1745 double const last_beats_per_minute = t->beats_per_minute();
1746 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1747 Tempo const ret_tempo (ret, m.tempo().note_type ());
1759 TempoMap::meter_section_at (framepos_t frame) const
1761 Glib::Threads::RWLock::ReaderLock lm (lock);
1762 Metrics::const_iterator i;
1763 MeterSection* prev = 0;
1765 for (i = metrics.begin(); i != metrics.end(); ++i) {
1768 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1770 if ((*i)->frame() > frame) {
1780 abort(); /*NOTREACHED*/
1787 TempoMap::meter_at (framepos_t frame) const
1789 TempoMetric m (metric_at (frame));
1794 TempoMap::get_state ()
1796 Metrics::const_iterator i;
1797 XMLNode *root = new XMLNode ("TempoMap");
1800 Glib::Threads::RWLock::ReaderLock lm (lock);
1801 for (i = metrics.begin(); i != metrics.end(); ++i) {
1802 root->add_child_nocopy ((*i)->get_state());
1810 TempoMap::set_state (const XMLNode& node, int /*version*/)
1813 Glib::Threads::RWLock::WriterLock lm (lock);
1816 XMLNodeConstIterator niter;
1817 Metrics old_metrics (metrics);
1818 MeterSection* last_meter = 0;
1821 nlist = node.children();
1823 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1824 XMLNode* child = *niter;
1826 if (child->name() == TempoSection::xml_state_node_name) {
1829 TempoSection* ts = new TempoSection (*child);
1830 metrics.push_back (ts);
1832 if (ts->bar_offset() < 0.0) {
1834 //ts->update_bar_offset_from_bbt (*last_meter);
1839 catch (failed_constructor& err){
1840 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1841 metrics = old_metrics;
1845 } else if (child->name() == MeterSection::xml_state_node_name) {
1848 MeterSection* ms = new MeterSection (*child);
1849 metrics.push_back (ms);
1853 catch (failed_constructor& err) {
1854 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1855 metrics = old_metrics;
1861 if (niter == nlist.end()) {
1862 MetricSectionSorter cmp;
1865 /* check for legacy sessions where bbt was the base musical unit for tempo */
1866 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1867 MeterSection* prev_ms;
1868 TempoSection* prev_ts;
1869 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1870 if (prev_ms->start() < 0.0) {
1871 /*XX we cannot possibly make this work??. */
1872 pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
1873 prev_ms->set_start (start);
1875 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1876 if (prev_ts->start() < 0.0) {
1877 double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
1878 prev_ts->set_start (start);
1883 /* check for multiple tempo/meters at the same location, which
1884 ardour2 somehow allowed.
1887 Metrics::iterator prev = metrics.end();
1888 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1889 if (prev != metrics.end()) {
1891 MeterSection* prev_ms;
1893 TempoSection* prev_ts;
1894 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1895 if (prev_ms->start() == ms->start()) {
1896 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
1897 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
1900 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1901 if (prev_ts->start() == ts->start()) {
1902 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
1903 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
1911 recompute_map (true, -1);
1914 PropertyChanged (PropertyChange ());
1920 TempoMap::dump (std::ostream& o) const
1922 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1923 const MeterSection* m;
1924 const TempoSection* t;
1926 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1928 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1929 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? "
1930 << t->movable() << ')' << endl;
1931 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1932 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
1933 << " (movable? " << m->movable() << ')' << endl;
1939 TempoMap::n_tempos() const
1941 Glib::Threads::RWLock::ReaderLock lm (lock);
1944 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1945 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1954 TempoMap::n_meters() const
1956 Glib::Threads::RWLock::ReaderLock lm (lock);
1959 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1960 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1969 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1972 Glib::Threads::RWLock::WriterLock lm (lock);
1973 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1974 if ((*i)->frame() >= where && (*i)->movable ()) {
1975 (*i)->set_frame ((*i)->frame() + amount);
1979 /* now reset the BBT time of all metrics, based on their new
1980 * audio time. This is the only place where we do this reverse
1984 Metrics::iterator i;
1985 const MeterSection* meter;
1986 const TempoSection* tempo;
1990 meter = &first_meter ();
1991 tempo = &first_tempo ();
1996 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1999 MetricSection* prev = 0;
2001 for (i = metrics.begin(); i != metrics.end(); ++i) {
2004 //TempoMetric metric (*meter, *tempo);
2005 MeterSection* ms = const_cast<MeterSection*>(meter);
2006 TempoSection* ts = const_cast<TempoSection*>(tempo);
2009 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2010 ts->set_start (t->start());
2012 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2013 ts->set_start (m->start());
2015 ts->set_frame (prev->frame());
2019 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2020 pair<double, BBT_Time> start = make_pair (m->start(), m->bbt());
2021 ms->set_start (start);
2023 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2024 pair<double, BBT_Time> start = make_pair (t->start(), beats_to_bbt (t->start()));
2025 ms->set_start (start);
2027 ms->set_frame (prev->frame());
2031 // metric will be at frames=0 bbt=1|1|0 by default
2032 // which is correct for our purpose
2035 // cerr << bbt << endl;
2037 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2038 t->set_start (beat_at_frame (m->frame()));
2040 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2041 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2042 bbt_time (m->frame(), bbt);
2044 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2050 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2051 /* round up to next beat */
2057 if (bbt.beats != 1) {
2058 /* round up to next bar */
2063 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
2064 m->set_start (start);
2066 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2068 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2069 abort(); /*NOTREACHED*/
2075 recompute_map (true);
2079 PropertyChanged (PropertyChange ());
2082 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2086 std::list<MetricSection*> metric_kill_list;
2088 TempoSection* last_tempo = NULL;
2089 MeterSection* last_meter = NULL;
2090 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2091 bool meter_after = false; // is there a meter marker likewise?
2093 Glib::Threads::RWLock::WriterLock lm (lock);
2094 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2095 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2096 metric_kill_list.push_back(*i);
2097 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2100 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2104 else if ((*i)->frame() >= where) {
2105 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2106 (*i)->set_frame ((*i)->frame() - amount);
2107 if ((*i)->frame() == where) {
2108 // marker was immediately after end of range
2109 tempo_after = dynamic_cast<TempoSection*> (*i);
2110 meter_after = dynamic_cast<MeterSection*> (*i);
2116 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2117 if (last_tempo && !tempo_after) {
2118 metric_kill_list.remove(last_tempo);
2119 last_tempo->set_frame(where);
2122 if (last_meter && !meter_after) {
2123 metric_kill_list.remove(last_meter);
2124 last_meter->set_frame(where);
2128 //remove all the remaining metrics
2129 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2135 recompute_map (true);
2138 PropertyChanged (PropertyChange ());
2142 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2143 * pos can be -ve, if required.
2146 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2148 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2151 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2153 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2155 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2158 /** Add the BBT interval op to pos and return the result */
2160 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2162 cerr << "framepos_plus_bbt - untested" << endl;
2163 Glib::Threads::RWLock::ReaderLock lm (lock);
2165 Metrics::const_iterator i;
2166 const MeterSection* meter;
2167 const MeterSection* m;
2168 const TempoSection* tempo;
2169 const TempoSection* next_tempo = 0;
2170 const TempoSection* t;
2171 double frames_per_beat;
2172 framepos_t effective_pos = max (pos, (framepos_t) 0);
2174 meter = &first_meter ();
2175 tempo = &first_tempo ();
2180 /* find the starting metrics for tempo & meter */
2182 for (i = metrics.begin(); i != metrics.end(); ++i) {
2184 if ((*i)->frame() > effective_pos) {
2188 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2190 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2195 for (i = metrics.begin(); i != metrics.end(); ++i) {
2196 if ((*i)->frame() > effective_pos) {
2197 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2206 meter -> the Meter for "pos"
2207 tempo -> the Tempo for "pos"
2208 next_tempo -> the Tempo after "pos" or 0
2209 i -> for first new metric after "pos", possibly metrics.end()
2212 /* now comes the complicated part. we have to add one beat a time,
2213 checking for a new metric on every beat.
2217 /* fpb is used for constant tempo */
2218 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2225 /* check if we need to use a new metric section: has adding frames moved us
2226 to or after the start of the next metric section? in which case, use it.
2229 if (i != metrics.end()) {
2230 if ((*i)->frame() <= pos) {
2232 /* about to change tempo or meter, so add the
2233 * number of frames for the bars we've just
2234 * traversed before we change the
2235 * frames_per_beat value.
2238 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2243 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2245 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2250 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2252 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2256 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2263 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2265 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2272 /* given the current meter, have we gone past the end of the bar ? */
2277 /* check if we need to use a new metric section: has adding frames moved us
2278 to or after the start of the next metric section? in which case, use it.
2281 if (i != metrics.end()) {
2282 if ((*i)->frame() <= pos) {
2284 /* about to change tempo or meter, so add the
2285 * number of frames for the beats we've just
2286 * traversed before we change the
2287 * frames_per_beat value.
2290 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2295 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2297 pos += llrint (beats * frames_per_beat);
2302 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2304 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2308 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2314 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2316 pos += llrint (beats * frames_per_beat);
2320 pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2327 /** Count the number of beats that are equivalent to distance when going forward,
2331 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2333 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2337 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2343 operator<< (std::ostream& o, const Meter& m) {
2344 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2348 operator<< (std::ostream& o, const Tempo& t) {
2349 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2353 operator<< (std::ostream& o, const MetricSection& section) {
2355 o << "MetricSection @ " << section.frame() << ' ';
2357 const TempoSection* ts;
2358 const MeterSection* ms;
2360 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2361 o << *((const Tempo*) ts);
2362 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2363 //o << *((const Meter*) ms);