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;
81 if ((prop = node.property ("start")) != 0) {
82 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
86 /* legacy session - start used to be in bbt*/
92 error << _("TempoSection XML node has no \"start\" property") << endmsg;
96 if ((prop = node.property ("beat")) != 0) {
97 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
98 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
103 error << _("TempoSection XML node has no \"beat\" property") << endmsg;
107 if ((prop = node.property ("beats-per-minute")) == 0) {
108 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
109 throw failed_constructor();
112 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
113 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
114 throw failed_constructor();
117 if ((prop = node.property ("note-type")) == 0) {
118 /* older session, make note type be quarter by default */
121 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
122 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
123 throw failed_constructor();
127 if ((prop = node.property ("movable")) == 0) {
128 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
129 throw failed_constructor();
132 set_movable (string_is_affirmative (prop->value()));
134 if ((prop = node.property ("bar-offset")) == 0) {
137 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
138 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
139 throw failed_constructor();
143 if ((prop = node.property ("tempo-type")) == 0) {
144 _type = Type::Constant;
146 if (strstr(prop->value().c_str(),"Constant")) {
147 _type = Type::Constant;
155 TempoSection::get_state() const
157 XMLNode *root = new XMLNode (xml_state_node_name);
161 snprintf (buf, sizeof (buf), "%f", beat());
162 root->add_property ("beat", buf);
163 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
164 root->add_property ("beats-per-minute", buf);
165 snprintf (buf, sizeof (buf), "%f", _note_type);
166 root->add_property ("note-type", buf);
167 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
168 // root->add_property ("bar-offset", buf);
169 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
170 root->add_property ("movable", buf);
172 snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
173 root->add_property ("tempo-type", buf);
180 TempoSection::update_bar_offset_from_bbt (const Meter& m)
182 _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
183 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
185 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
189 TempoSection::set_type (Type type)
194 /** returns the tempo at the zero-based (relative to tempo section) frame.
197 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
200 if (_type == Constant) {
201 return beats_per_minute();
204 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;
207 /** returns the zero-based frame (relative to tempo section)
208 where the tempo occurs.
211 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
213 if (_type == Constant) {
217 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);
220 /** returns the zero-based tick (relative to tempo section)
221 where the zero-based frame (relative to tempo section)
225 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
227 if (_type == Constant) {
228 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
231 return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
234 /** returns the zero-based frame (relative to tempo section)
235 where the zero-based tick (relative to tempo section)
239 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
241 if (_type == Constant) {
242 return (framepos_t) floor ((tick / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
245 return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
248 /** returns the zero-based beat (relative to tempo section)
249 where the zero-based frame (relative to tempo section)
253 TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
255 return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
258 /** returns the zero-based frame (relative to tempo section start frame)
259 where the zero-based beat (relative to tempo section start)
264 TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
266 return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
270 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
272 return time * 60.0 * frame_rate;
276 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
278 return (frame / (double) frame_rate) / 60.0;
281 /* position function */
283 TempoSection::a_func (double end_tpm, double c_func) const
285 return log (end_tpm / ticks_per_minute()) / c_func;
288 /*function constant*/
290 TempoSection::c_func (double end_tpm, double end_time) const
292 return log (end_tpm / ticks_per_minute()) / end_time;
295 /* tempo in tpm at time in minutes */
297 TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
299 return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
302 /* time in minutes at tempo in tpm */
304 TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
306 return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
309 /* tick at time in minutes */
311 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
313 return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
316 /* time in minutes at tick */
318 TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
320 return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
323 /* beat at time in minutes */
325 TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
327 return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
330 /* time in munutes at beat */
332 TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
334 return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
338 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
342 if (_bar_offset < 0.0) {
349 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
350 new_beat = ticks / BBT_Time::ticks_per_beat;
352 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
353 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
358 /***********************************************************************/
360 const string MeterSection::xml_state_node_name = "Meter";
362 MeterSection::MeterSection (const XMLNode& node)
363 : MetricSection (0.0), Meter (TempoMap::default_meter())
365 XMLProperty const * prop;
368 const XMLProperty *prop;
371 pair<double, BBT_Time> start;
373 if ((prop = node.property ("start")) != 0) {
374 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
378 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
380 /* legacy session - start used to be in bbt*/
384 error << _("MeterSection XML node has no \"start\" property") << endmsg;
387 if ((prop = node.property ("beat")) != 0) {
388 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
389 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
392 error << _("MeterSection XML node has no \"beat\" property") << endmsg;
397 if ((prop = node.property ("bbt")) == 0) {
398 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
399 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
403 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
404 throw failed_constructor();
411 /* beats-per-bar is old; divisions-per-bar is new */
413 if ((prop = node.property ("divisions-per-bar")) == 0) {
414 if ((prop = node.property ("beats-per-bar")) == 0) {
415 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
416 throw failed_constructor();
420 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
421 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
422 throw failed_constructor();
425 if ((prop = node.property ("note-type")) == 0) {
426 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
427 throw failed_constructor();
430 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
431 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
432 throw failed_constructor();
435 if ((prop = node.property ("movable")) == 0) {
436 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
437 throw failed_constructor();
440 set_movable (string_is_affirmative (prop->value()));
444 MeterSection::get_state() const
446 XMLNode *root = new XMLNode (xml_state_node_name);
450 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
454 root->add_property ("bbt", buf);
455 snprintf (buf, sizeof (buf), "%lf", beat());
456 root->add_property ("beat", buf);
457 snprintf (buf, sizeof (buf), "%f", _note_type);
458 root->add_property ("note-type", buf);
459 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
460 root->add_property ("divisions-per-bar", buf);
461 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
462 root->add_property ("movable", buf);
467 /***********************************************************************/
469 struct MetricSectionSorter {
470 bool operator() (const MetricSection* a, const MetricSection* b) {
471 return a->beat() < b->beat();
475 struct MetricSectionFrameSorter {
476 bool operator() (const MetricSection* a, const MetricSection* b) {
477 return a->frame() < b->frame();
481 TempoMap::TempoMap (framecnt_t fr)
490 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Constant);
491 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
493 t->set_movable (false);
494 m->set_movable (false);
496 /* note: frame time is correct (zero) for both of these */
498 metrics.push_back (t);
499 metrics.push_back (m);
503 TempoMap::~TempoMap ()
508 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
510 bool removed = false;
513 Glib::Threads::RWLock::WriterLock lm (lock);
514 if ((removed = remove_tempo_locked (tempo))) {
515 if (complete_operation) {
516 recompute_map (true);
521 if (removed && complete_operation) {
522 PropertyChanged (PropertyChange ());
527 TempoMap::remove_tempo_locked (const TempoSection& tempo)
531 for (i = metrics.begin(); i != metrics.end(); ++i) {
532 if (dynamic_cast<TempoSection*> (*i) != 0) {
533 if (tempo.frame() == (*i)->frame()) {
534 if ((*i)->movable()) {
546 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
548 bool removed = false;
551 Glib::Threads::RWLock::WriterLock lm (lock);
552 if ((removed = remove_meter_locked (tempo))) {
553 if (complete_operation) {
554 recompute_map (true);
559 if (removed && complete_operation) {
560 PropertyChanged (PropertyChange ());
565 TempoMap::remove_meter_locked (const MeterSection& tempo)
569 for (i = metrics.begin(); i != metrics.end(); ++i) {
570 if (dynamic_cast<MeterSection*> (*i) != 0) {
571 if (tempo.frame() == (*i)->frame()) {
572 if ((*i)->movable()) {
584 TempoMap::do_insert (MetricSection* section)
586 bool need_add = true;
588 /* we only allow new meters to be inserted on beat 1 of an existing
592 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
593 assert (m->bbt().ticks == 0);
595 /* we need to (potentially) update the BBT times of tempo
596 sections based on this new meter.
599 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
601 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
602 corrected.second.beats = 1;
603 corrected.second.ticks = 0;
604 corrected.first = bbt_to_beats_unlocked (corrected.second);
605 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
606 m->bbt(), corrected.second) << endmsg;
607 m->set_beat (corrected);
613 /* Look for any existing MetricSection that is of the same type and
614 in the same bar as the new one, and remove it before adding
615 the new one. Note that this means that if we find a matching,
616 existing section, we can break out of the loop since we're
617 guaranteed that there is only one such match.
620 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
622 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
623 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
625 if (tempo && insert_tempo) {
629 if (tempo->beat() == insert_tempo->beat()) {
631 if (!tempo->movable()) {
633 /* can't (re)move this section, so overwrite
634 * its data content (but not its properties as
638 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
646 } else if (!tempo && !insert_tempo) {
649 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
650 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
651 if (meter->beat() == insert_meter->beat()) {
653 if (!meter->movable()) {
655 /* can't (re)move this section, so overwrite
656 * its data content (but not its properties as
660 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
670 /* non-matching types, so we don't care */
674 /* Add the given MetricSection, if we didn't just reset an existing
679 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
680 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
684 for (i = metrics.begin(); i != metrics.end(); ++i) {
685 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
687 if (meter && meter->beat() > insert_meter->beat()) {
691 } else if (insert_tempo) {
692 for (i = metrics.begin(); i != metrics.end(); ++i) {
693 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
696 if (tempo->beat() > insert_tempo->beat()) {
703 metrics.insert (i, section);
708 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
711 Glib::Threads::RWLock::WriterLock lm (lock);
712 TempoSection& first (first_tempo());
714 if (ts.beat() != first.beat()) {
715 remove_tempo_locked (ts);
716 add_tempo_locked (tempo, where, true, type);
718 first.set_type (type);
720 /* cannot move the first tempo section */
721 *static_cast<Tempo*>(&first) = tempo;
722 recompute_map (false);
727 PropertyChanged (PropertyChange ());
731 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
734 Glib::Threads::RWLock::WriterLock lm (lock);
736 /* currently this is always done in audio time */
737 //if (ts.position_lock_style() == MusicTime) {
740 ts.set_beat (beat_where);
741 MetricSectionSorter cmp;
745 ts.set_frame (frame);
747 MetricSectionFrameSorter fcmp;
750 Metrics::const_iterator i;
751 TempoSection* prev_ts = 0;
752 TempoSection* next_ts = 0;
754 for (i = metrics.begin(); i != metrics.end(); ++i) {
756 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
758 if (t->frame() >= frame) {
766 for (i = metrics.begin(); i != metrics.end(); ++i) {
768 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
770 if (t->frame() > frame) {
778 /* set the start beat */
779 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
780 double beats = beats_to_ts + prev_ts->beat();
783 if (next_ts->beat() < beats) {
784 /* with frame-based editing, it is possible to get in a
785 situation where if the tempo was placed at the mouse pointer frame,
786 the following music-based tempo would jump to an earlier frame,
787 changing the beat beat of the moved tempo.
788 in this case, we have to do some beat-based comparison TODO
790 } else if (prev_ts->beat() > beats) {
791 ts.set_beat (prev_ts->beat());
798 MetricSectionSorter cmp;
803 recompute_map (false);
806 MetricPositionChanged (); // Emit Signal
810 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
813 Glib::Threads::RWLock::WriterLock lm (lock);
814 add_tempo_locked (tempo, where, true, type);
818 PropertyChanged (PropertyChange ());
822 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
824 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
829 recompute_map (false);
834 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
837 Glib::Threads::RWLock::WriterLock lm (lock);
838 MeterSection& first (first_meter());
839 if (ms.beat() != first.beat()) {
840 remove_meter_locked (ms);
841 add_meter_locked (meter, bbt_to_beats_unlocked (where), where, true);
843 /* cannot move the first meter section */
844 *static_cast<Meter*>(&first) = meter;
845 recompute_map (true);
849 PropertyChanged (PropertyChange ());
853 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
856 Glib::Threads::RWLock::WriterLock lm (lock);
857 add_meter_locked (meter, beat, where, true);
862 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
867 PropertyChanged (PropertyChange ());
871 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
873 /* a new meter always starts a new bar on the first beat. so
874 round the start time appropriately. remember that
875 `where' is based on the existing tempo map, not
876 the result after we insert the new meter.
880 if (where.beats != 1) {
885 /* new meters *always* start on a beat. */
888 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
891 recompute_map (true);
897 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
899 Tempo newtempo (beats_per_minute, note_type);
902 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
903 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
905 Glib::Threads::RWLock::WriterLock lm (lock);
906 *((Tempo*) t) = newtempo;
907 recompute_map (false);
909 PropertyChanged (PropertyChange ());
916 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
918 Tempo newtempo (beats_per_minute, note_type);
924 /* find the TempoSection immediately preceding "where"
927 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
929 if ((*i)->frame() > where) {
935 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
945 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
955 Glib::Threads::RWLock::WriterLock lm (lock);
956 /* cannot move the first tempo section */
957 *((Tempo*)prev) = newtempo;
958 recompute_map (false);
961 PropertyChanged (PropertyChange ());
965 TempoMap::first_meter () const
967 const MeterSection *m = 0;
969 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
970 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
975 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
976 abort(); /*NOTREACHED*/
981 TempoMap::first_meter ()
985 /* CALLER MUST HOLD LOCK */
987 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
988 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
993 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
994 abort(); /*NOTREACHED*/
999 TempoMap::first_tempo () const
1001 const TempoSection *t = 0;
1003 /* CALLER MUST HOLD LOCK */
1005 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1006 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1011 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1012 abort(); /*NOTREACHED*/
1017 TempoMap::first_tempo ()
1019 TempoSection *t = 0;
1021 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1022 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1027 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1028 abort(); /*NOTREACHED*/
1033 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1035 /* CALLER MUST HOLD WRITE LOCK */
1039 /* we will actually stop once we hit
1046 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1049 /* silly call from Session::process() during startup
1054 Metrics::const_iterator i;
1056 TempoSection* prev_ts = 0;
1058 for (i = metrics.begin(); i != metrics.end(); ++i) {
1061 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1064 double const beats_relative_to_prev_ts = t->beat() - prev_ts->beat();
1065 double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
1067 /* assume (falsely) that the target tempo is constant */
1068 double const t_fpb = t->frames_per_beat (_frame_rate);
1069 double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
1070 /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
1071 double length_estimate = beats_relative_to_prev_ts * av_fpb;
1073 if (prev_ts->type() == TempoSection::Type::Constant) {
1074 length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
1077 double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
1078 double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
1080 while (fabs (tick_error) > system_precision_at_target_tempo) {
1082 double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
1083 (framepos_t) length_estimate, _frame_rate);
1084 tick_error = ticks_relative_to_prev_ts - actual_ticks;
1085 length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
1088 t->set_frame (length_estimate + prev_ts->frame());
1094 Metrics::const_iterator mi;
1095 MeterSection* meter = 0;
1097 for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1098 /* we can do this beacuse we have the tempo section frames set */
1099 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1100 meter->set_frame (frame_at_tick (meter->beat() * BBT_Time::ticks_per_beat));
1107 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1109 Glib::Threads::RWLock::ReaderLock lm (lock);
1110 TempoMetric m (first_meter(), first_tempo());
1112 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1113 at something, because we insert the default tempo and meter during
1114 TempoMap construction.
1116 now see if we can find better candidates.
1119 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1121 if ((*i)->frame() > frame) {
1134 /* XX meters only */
1136 TempoMap::metric_at (BBT_Time bbt) const
1138 Glib::Threads::RWLock::ReaderLock lm (lock);
1139 TempoMetric m (first_meter(), first_tempo());
1141 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1142 at something, because we insert the default tempo and meter during
1143 TempoMap construction.
1145 now see if we can find better candidates.
1148 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1150 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1151 BBT_Time section_start (mw->bbt());
1153 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1165 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1167 Glib::Threads::RWLock::ReaderLock lm (lock);
1173 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1176 bbt = beats_to_bbt_unlocked (beat_at_frame (frame));
1180 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1182 Glib::Threads::RWLock::ReaderLock lm (lock);
1183 return bbt_to_beats_unlocked (bbt);
1187 TempoMap::bbt_to_beats_unlocked (Timecode::BBT_Time bbt)
1189 /* CALLER HOLDS READ LOCK */
1191 double accumulated_beats = 0.0;
1192 double accumulated_bars = 0.0;
1193 MeterSection* prev_ms = 0;
1195 Metrics::const_iterator i;
1197 for (i = metrics.begin(); i != metrics.end(); ++i) {
1199 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1200 double bars_to_m = 0.0;
1202 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1204 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1208 accumulated_beats += m->beat() - prev_ms->beat();
1209 accumulated_bars += bars_to_m;
1215 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1216 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1217 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1222 TempoMap::beats_to_bbt (double beats)
1224 Glib::Threads::RWLock::ReaderLock lm (lock);
1225 return beats_to_bbt_unlocked (beats);
1229 TempoMap::beats_to_bbt_unlocked (double beats)
1231 /* CALLER HOLDS READ LOCK */
1233 MeterSection* prev_ms = 0;
1234 uint32_t accumulated_bars = 0;
1236 Metrics::const_iterator i;
1238 for (i = metrics.begin(); i != metrics.end(); ++i) {
1239 MeterSection* m = 0;
1241 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1243 if (beats < m->beat()) {
1244 /* this is the meter after the one our beat is on*/
1249 /* we need a whole number of bars. */
1250 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1257 double const beats_in_ms = beats - prev_ms->beat();
1258 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1259 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1260 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1261 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1265 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1266 ret.beats = (uint32_t) floor (remaining_beats);
1267 ret.bars = total_bars;
1269 /* 0 0 0 to 1 1 0 - based mapping*/
1273 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1275 ret.ticks -= BBT_Time::ticks_per_beat;
1278 if (ret.beats > prev_ms->divisions_per_bar()) {
1287 TempoMap::tick_at_frame (framecnt_t frame) const
1289 /* HOLD (at least) THE READER LOCK */
1291 Metrics::const_iterator i;
1292 TempoSection* prev_ts = 0;
1293 double accumulated_ticks = 0.0;
1295 for (i = metrics.begin(); i != metrics.end(); ++i) {
1298 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1300 if ((prev_ts) && frame < t->frame()) {
1301 /*the previous ts is the one containing the frame */
1303 framepos_t const time = frame - prev_ts->frame();
1304 framepos_t const last_frame = t->frame() - prev_ts->frame();
1305 double const last_beats_per_minute = t->beats_per_minute();
1307 return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
1310 if (prev_ts && t->frame() > prev_ts->frame()) {
1311 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1318 /* treated as constant for this ts */
1319 framecnt_t const frames_in_section = frame - prev_ts->frame();
1320 double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1322 return ticks_in_section + accumulated_ticks;
1327 TempoMap::frame_at_tick (double tick) const
1329 /* HOLD THE READER LOCK */
1331 double accumulated_ticks = 0.0;
1332 double accumulated_ticks_to_prev = 0.0;
1333 const TempoSection* prev_ts = 0;
1335 Metrics::const_iterator i;
1337 for (i = metrics.begin(); i != metrics.end(); ++i) {
1339 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1341 if (prev_ts && t->frame() > prev_ts->frame()) {
1342 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1345 if (prev_ts && tick < accumulated_ticks) {
1346 /* prev_ts is the one affecting us. */
1348 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1349 framepos_t const last_time = t->frame() - prev_ts->frame();
1350 double const last_beats_per_minute = t->beats_per_minute();
1352 return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + prev_ts->frame();
1354 accumulated_ticks_to_prev = accumulated_ticks;
1358 /* must be treated as constant, irrespective of _type */
1359 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1360 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1362 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1368 TempoMap::beat_at_frame (framecnt_t frame) const
1370 Glib::Threads::RWLock::ReaderLock lm (lock);
1372 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1376 TempoMap::frame_at_beat (double beat) const
1378 Glib::Threads::RWLock::ReaderLock lm (lock);
1380 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1384 TempoMap::frame_time (const BBT_Time& bbt)
1387 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1391 if (bbt.beats < 1) {
1392 throw std::logic_error ("beats are counted from one");
1394 Glib::Threads::RWLock::ReaderLock lm (lock);
1396 framepos_t const ret = frame_at_beat (bbt_to_beats_unlocked (bbt));
1403 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1406 Glib::Threads::RWLock::ReaderLock lm (lock);
1408 Metrics::const_iterator i;
1409 TempoSection* first = 0;
1410 TempoSection* second = 0;
1412 for (i = metrics.begin(); i != metrics.end(); ++i) {
1415 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1417 if ((*i)->frame() > pos) {
1425 if (first && second) {
1426 framepos_t const last_time = second->frame() - first->frame();
1427 double const last_beats_per_minute = second->beats_per_minute();
1429 framepos_t const time = pos - first->frame();
1430 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1431 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1433 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1435 return time_at_bbt - time;
1438 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1439 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1443 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1445 return round_to_type (fr, dir, Bar);
1449 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1451 return round_to_type (fr, dir, Beat);
1455 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1457 Glib::Threads::RWLock::ReaderLock lm (lock);
1459 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1460 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1461 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1463 ticks -= beats * BBT_Time::ticks_per_beat;
1466 /* round to next (or same iff dir == RoundUpMaybe) */
1468 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1470 if (mod == 0 && dir == RoundUpMaybe) {
1471 /* right on the subdivision, which is fine, so do nothing */
1473 } else if (mod == 0) {
1474 /* right on the subdivision, so the difference is just the subdivision ticks */
1475 ticks += ticks_one_subdivisions_worth;
1478 /* not on subdivision, compute distance to next subdivision */
1480 ticks += ticks_one_subdivisions_worth - mod;
1483 if (ticks >= BBT_Time::ticks_per_beat) {
1484 ticks -= BBT_Time::ticks_per_beat;
1486 } else if (dir < 0) {
1488 /* round to previous (or same iff dir == RoundDownMaybe) */
1490 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1492 if (difference == 0 && dir == RoundDownAlways) {
1493 /* right on the subdivision, but force-rounding down,
1494 so the difference is just the subdivision ticks */
1495 difference = ticks_one_subdivisions_worth;
1498 if (ticks < difference) {
1499 ticks = BBT_Time::ticks_per_beat - ticks;
1501 ticks -= difference;
1505 /* round to nearest */
1508 /* compute the distance to the previous and next subdivision */
1510 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1512 /* closer to the next subdivision, so shift forward */
1514 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1516 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1518 if (ticks > BBT_Time::ticks_per_beat) {
1520 ticks -= BBT_Time::ticks_per_beat;
1521 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1524 } else if (rem > 0) {
1526 /* closer to previous subdivision, so shift backward */
1530 /* can't go backwards past zero, so ... */
1533 /* step back to previous beat */
1535 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1536 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1538 ticks = lrint (ticks - rem);
1539 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1542 /* on the subdivision, do nothing */
1545 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1549 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1551 Glib::Threads::RWLock::ReaderLock lm (lock);
1553 double const beat_at_framepos = beat_at_frame (frame);
1555 BBT_Time bbt (beats_to_bbt_unlocked (beat_at_framepos));
1560 /* find bar previous to 'frame' */
1563 return frame_time (bbt);
1565 } else if (dir > 0) {
1566 /* find bar following 'frame' */
1570 return frame_time (bbt);
1572 /* true rounding: find nearest bar */
1573 framepos_t raw_ft = frame_time (bbt);
1576 framepos_t prev_ft = frame_time (bbt);
1578 framepos_t next_ft = frame_time (bbt);
1580 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1591 return frame_at_beat (floor (beat_at_framepos));
1592 } else if (dir > 0) {
1593 return frame_at_beat (ceil (beat_at_framepos));
1595 return frame_at_beat (floor (beat_at_framepos + 0.5));
1604 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1605 framepos_t lower, framepos_t upper)
1607 Glib::Threads::RWLock::ReaderLock lm (lock);
1608 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1609 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1611 while (cnt <= upper_beat) {
1612 framecnt_t const pos = frame_at_beat (cnt);
1613 MeterSection const meter = meter_section_at (pos);
1614 Tempo const tempo = tempo_at (pos);
1615 BBT_Time const bbt = beats_to_bbt_unlocked ((double) cnt);
1617 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1623 TempoMap::tempo_section_at (framepos_t frame) const
1625 Glib::Threads::RWLock::ReaderLock lm (lock);
1627 Metrics::const_iterator i;
1628 TempoSection* prev = 0;
1630 for (i = metrics.begin(); i != metrics.end(); ++i) {
1633 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1635 if ((*i)->frame() > frame) {
1645 abort(); /*NOTREACHED*/
1651 /* don't use this to calculate length (the tempo is only correct for this frame).
1652 do that stuff based on the beat_at_frame and frame_at_beat api
1655 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1657 Glib::Threads::RWLock::ReaderLock lm (lock);
1659 const TempoSection* ts_at = &tempo_section_at (frame);
1660 const TempoSection* ts_after = 0;
1661 Metrics::const_iterator i;
1663 for (i = metrics.begin(); i != metrics.end(); ++i) {
1666 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1668 if ((*i)->frame() > frame) {
1676 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
1678 /* must be treated as constant tempo */
1679 return ts_at->frames_per_beat (_frame_rate);
1683 TempoMap::tempo_at (framepos_t frame) const
1685 Glib::Threads::RWLock::ReaderLock lm (lock);
1687 TempoMetric m (metric_at (frame));
1688 TempoSection* prev_ts = 0;
1690 Metrics::const_iterator i;
1692 for (i = metrics.begin(); i != metrics.end(); ++i) {
1694 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1695 if ((prev_ts) && t->frame() > frame) {
1696 /* this is the one past frame */
1697 framepos_t const time = frame - prev_ts->frame();
1698 framepos_t const last_time = t->frame() - prev_ts->frame();
1699 double const last_beats_per_minute = t->beats_per_minute();
1700 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1701 Tempo const ret_tempo (ret, m.tempo().note_type ());
1713 TempoMap::meter_section_at (framepos_t frame) const
1715 Glib::Threads::RWLock::ReaderLock lm (lock);
1716 Metrics::const_iterator i;
1717 MeterSection* prev = 0;
1719 for (i = metrics.begin(); i != metrics.end(); ++i) {
1722 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1724 if ((*i)->frame() > frame) {
1734 abort(); /*NOTREACHED*/
1741 TempoMap::meter_at (framepos_t frame) const
1743 TempoMetric m (metric_at (frame));
1748 TempoMap::get_state ()
1750 Metrics::const_iterator i;
1751 XMLNode *root = new XMLNode ("TempoMap");
1754 Glib::Threads::RWLock::ReaderLock lm (lock);
1755 for (i = metrics.begin(); i != metrics.end(); ++i) {
1756 root->add_child_nocopy ((*i)->get_state());
1764 TempoMap::set_state (const XMLNode& node, int /*version*/)
1767 Glib::Threads::RWLock::WriterLock lm (lock);
1770 XMLNodeConstIterator niter;
1771 Metrics old_metrics (metrics);
1772 MeterSection* last_meter = 0;
1775 nlist = node.children();
1777 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1778 XMLNode* child = *niter;
1780 if (child->name() == TempoSection::xml_state_node_name) {
1783 TempoSection* ts = new TempoSection (*child);
1784 metrics.push_back (ts);
1786 if (ts->bar_offset() < 0.0) {
1788 //ts->update_bar_offset_from_bbt (*last_meter);
1793 catch (failed_constructor& err){
1794 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1795 metrics = old_metrics;
1799 } else if (child->name() == MeterSection::xml_state_node_name) {
1802 MeterSection* ms = new MeterSection (*child);
1803 metrics.push_back (ms);
1807 catch (failed_constructor& err) {
1808 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1809 metrics = old_metrics;
1815 if (niter == nlist.end()) {
1816 MetricSectionSorter cmp;
1819 /* check for legacy sessions where bbt was the base musical unit for tempo */
1820 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1821 MeterSection* prev_ms;
1822 TempoSection* prev_ts;
1823 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1824 if (prev_ms->beat() < 0.0) {
1825 /*XX we cannot possibly make this work??. */
1826 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());
1827 prev_ms->set_beat (start);
1829 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1830 if (prev_ts->beat() < 0.0) {
1831 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);
1832 prev_ts->set_beat (start);
1837 /* check for multiple tempo/meters at the same location, which
1838 ardour2 somehow allowed.
1841 Metrics::iterator prev = metrics.end();
1842 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1843 if (prev != metrics.end()) {
1845 MeterSection* prev_ms;
1847 TempoSection* prev_ts;
1848 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1849 if (prev_ms->beat() == ms->beat()) {
1850 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
1851 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
1854 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1855 if (prev_ts->beat() == ts->beat()) {
1856 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
1857 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
1865 recompute_map (true, -1);
1868 PropertyChanged (PropertyChange ());
1874 TempoMap::dump (std::ostream& o) const
1876 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1877 const MeterSection* m;
1878 const TempoSection* t;
1880 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1882 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1883 o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->beat() << " frame= " << t->frame() << " (movable? "
1884 << t->movable() << ')' << endl;
1885 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1886 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
1887 << " (movable? " << m->movable() << ')' << endl;
1893 TempoMap::n_tempos() const
1895 Glib::Threads::RWLock::ReaderLock lm (lock);
1898 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1899 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1908 TempoMap::n_meters() const
1910 Glib::Threads::RWLock::ReaderLock lm (lock);
1913 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1914 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1923 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1926 Glib::Threads::RWLock::WriterLock lm (lock);
1927 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1928 if ((*i)->frame() >= where && (*i)->movable ()) {
1929 (*i)->set_frame ((*i)->frame() + amount);
1933 /* now reset the BBT time of all metrics, based on their new
1934 * audio time. This is the only place where we do this reverse
1938 Metrics::iterator i;
1939 const MeterSection* meter;
1940 const TempoSection* tempo;
1944 meter = &first_meter ();
1945 tempo = &first_tempo ();
1950 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1953 MetricSection* prev = 0;
1955 for (i = metrics.begin(); i != metrics.end(); ++i) {
1958 //TempoMetric metric (*meter, *tempo);
1959 MeterSection* ms = const_cast<MeterSection*>(meter);
1960 TempoSection* ts = const_cast<TempoSection*>(tempo);
1963 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1964 ts->set_beat (t->beat());
1966 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
1967 ts->set_beat (m->beat());
1969 ts->set_frame (prev->frame());
1973 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
1974 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
1975 ms->set_beat (start);
1977 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1978 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_unlocked (t->beat()));
1979 ms->set_beat (start);
1981 ms->set_frame (prev->frame());
1985 // metric will be at frames=0 bbt=1|1|0 by default
1986 // which is correct for our purpose
1989 // cerr << bbt << endl;
1991 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1992 t->set_beat (beat_at_frame (m->frame()));
1994 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
1995 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1996 bbt_time (m->frame(), bbt);
1998 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2004 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2005 /* round up to next beat */
2011 if (bbt.beats != 1) {
2012 /* round up to next bar */
2017 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
2018 m->set_beat (start);
2020 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2022 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2023 abort(); /*NOTREACHED*/
2029 recompute_map (true);
2033 PropertyChanged (PropertyChange ());
2036 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2040 std::list<MetricSection*> metric_kill_list;
2042 TempoSection* last_tempo = NULL;
2043 MeterSection* last_meter = NULL;
2044 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2045 bool meter_after = false; // is there a meter marker likewise?
2047 Glib::Threads::RWLock::WriterLock lm (lock);
2048 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2049 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2050 metric_kill_list.push_back(*i);
2051 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2054 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2058 else if ((*i)->frame() >= where) {
2059 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2060 (*i)->set_frame ((*i)->frame() - amount);
2061 if ((*i)->frame() == where) {
2062 // marker was immediately after end of range
2063 tempo_after = dynamic_cast<TempoSection*> (*i);
2064 meter_after = dynamic_cast<MeterSection*> (*i);
2070 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2071 if (last_tempo && !tempo_after) {
2072 metric_kill_list.remove(last_tempo);
2073 last_tempo->set_frame(where);
2076 if (last_meter && !meter_after) {
2077 metric_kill_list.remove(last_meter);
2078 last_meter->set_frame(where);
2082 //remove all the remaining metrics
2083 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2089 recompute_map (true);
2092 PropertyChanged (PropertyChange ());
2096 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2097 * pos can be -ve, if required.
2100 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2102 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2105 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2107 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2109 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2112 /** Add the BBT interval op to pos and return the result */
2114 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2116 cerr << "framepos_plus_bbt - untested" << endl;
2117 Glib::Threads::RWLock::ReaderLock lm (lock);
2119 Metrics::const_iterator i;
2120 const MeterSection* meter;
2121 const MeterSection* m;
2122 const TempoSection* tempo;
2123 const TempoSection* next_tempo = 0;
2124 const TempoSection* t;
2125 double frames_per_beat;
2126 framepos_t effective_pos = max (pos, (framepos_t) 0);
2128 meter = &first_meter ();
2129 tempo = &first_tempo ();
2134 /* find the starting metrics for tempo & meter */
2136 for (i = metrics.begin(); i != metrics.end(); ++i) {
2138 if ((*i)->frame() > effective_pos) {
2142 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2144 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2149 for (i = metrics.begin(); i != metrics.end(); ++i) {
2150 if ((*i)->frame() > effective_pos) {
2151 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2160 meter -> the Meter for "pos"
2161 tempo -> the Tempo for "pos"
2162 next_tempo -> the Tempo after "pos" or 0
2163 i -> for first new metric after "pos", possibly metrics.end()
2166 /* now comes the complicated part. we have to add one beat a time,
2167 checking for a new metric on every beat.
2171 /* fpb is used for constant tempo */
2172 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2179 /* check if we need to use a new metric section: has adding frames moved us
2180 to or after the start of the next metric section? in which case, use it.
2183 if (i != metrics.end()) {
2184 if ((*i)->frame() <= pos) {
2186 /* about to change tempo or meter, so add the
2187 * number of frames for the bars we've just
2188 * traversed before we change the
2189 * frames_per_beat value.
2192 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2197 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2199 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2204 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2206 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2210 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2217 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2219 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2226 /* given the current meter, have we gone past the end of the bar ? */
2231 /* check if we need to use a new metric section: has adding frames moved us
2232 to or after the start of the next metric section? in which case, use it.
2235 if (i != metrics.end()) {
2236 if ((*i)->frame() <= pos) {
2238 /* about to change tempo or meter, so add the
2239 * number of frames for the beats we've just
2240 * traversed before we change the
2241 * frames_per_beat value.
2244 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2249 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2251 pos += llrint (beats * frames_per_beat);
2256 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2258 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2262 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2268 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2270 pos += llrint (beats * frames_per_beat);
2274 pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2281 /** Count the number of beats that are equivalent to distance when going forward,
2285 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2287 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2291 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2297 operator<< (std::ostream& o, const Meter& m) {
2298 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2302 operator<< (std::ostream& o, const Tempo& t) {
2303 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2307 operator<< (std::ostream& o, const MetricSection& section) {
2309 o << "MetricSection @ " << section.frame() << ' ';
2311 const TempoSection* ts;
2312 const MeterSection* ms;
2314 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2315 o << *((const Tempo*) ts);
2316 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2317 //o << *((const Meter*) ms);