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)
186 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
189 if (_type == Constant) {
190 return beats_per_minute();
193 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;
197 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
199 if (_type == Constant) {
203 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);
207 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
209 if (_type == Constant) {
210 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
213 return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
217 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
219 if (_type == Constant) {
220 return (framepos_t) floor ((tick / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
223 return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
226 double TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
228 return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
231 framepos_t TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
233 return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
237 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
239 return time * 60.0 * frame_rate;
243 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
245 return (frame / (double) frame_rate) / 60.0;
248 /* constant for exp */
250 TempoSection::a_func (double begin_tpm, double end_tpm, double end_time) const
252 return log (end_tpm / ticks_per_minute()) / c_func (end_tpm, end_time);
255 TempoSection::c_func (double end_tpm, double end_time) const
257 return log (end_tpm / ticks_per_minute()) / end_time;
260 /* tempo in tpm at time in minutes */
262 TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
264 return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
267 /* time in minutes at tempo in tpm */
269 TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
271 return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
274 /* tempo in bpm at time in minutes */
276 TempoSection::tempo_at_time (double time, double end_bpm, double end_time) const
278 return tick_tempo_at_time (time, end_bpm * BBT_Time::ticks_per_beat, end_time) / BBT_Time::ticks_per_beat;
281 /* time in minutes at tempo in bpm */
283 TempoSection::time_at_tempo (double tempo, double end_bpm, double end_time) const
285 return time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, end_time);
288 /* tick at time in minutes */
290 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
292 return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
295 /* time in minutes at tick */
297 TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
299 return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
302 /* beat at time in minutes */
304 TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
306 return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
309 /* time in munutes at beat */
311 TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
313 return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
317 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
321 if (_bar_offset < 0.0) {
328 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
329 new_start = ticks / BBT_Time::ticks_per_beat;
331 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
332 _bar_offset, meter.divisions_per_bar(), ticks, new_start, new_start));
334 set_start (new_start);
337 /***********************************************************************/
339 const string MeterSection::xml_state_node_name = "Meter";
341 MeterSection::MeterSection (const XMLNode& node)
342 : MetricSection (0.0), Meter (TempoMap::default_meter())
344 XMLProperty const * prop;
347 const XMLProperty *prop;
350 pair<double, BBT_Time> start;
352 if ((prop = node.property ("start")) == 0) {
353 error << _("MeterSection XML node has no \"start\" property") << endmsg;
354 throw failed_constructor();
356 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
360 /* legacy session - start used to be in bbt*/
362 } else if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
363 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
364 throw failed_constructor();
368 if ((prop = node.property ("bbt")) == 0) {
369 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
370 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
374 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
375 throw failed_constructor();
381 /* beats-per-bar is old; divisions-per-bar is new */
383 if ((prop = node.property ("divisions-per-bar")) == 0) {
384 if ((prop = node.property ("beats-per-bar")) == 0) {
385 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
386 throw failed_constructor();
390 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
391 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
392 throw failed_constructor();
395 if ((prop = node.property ("note-type")) == 0) {
396 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
397 throw failed_constructor();
400 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
401 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
402 throw failed_constructor();
405 if ((prop = node.property ("movable")) == 0) {
406 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
407 throw failed_constructor();
410 set_movable (string_is_affirmative (prop->value()));
414 MeterSection::get_state() const
416 XMLNode *root = new XMLNode (xml_state_node_name);
420 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
424 root->add_property ("bbt", buf);
425 snprintf (buf, sizeof (buf), "%f", start());
426 root->add_property ("start", buf);
427 snprintf (buf, sizeof (buf), "%f", _note_type);
428 root->add_property ("note-type", buf);
429 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
430 root->add_property ("divisions-per-bar", buf);
431 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
432 root->add_property ("movable", buf);
437 /***********************************************************************/
439 struct MetricSectionSorter {
440 bool operator() (const MetricSection* a, const MetricSection* b) {
441 return a->start() < b->start();
445 struct MetricSectionFrameSorter {
446 bool operator() (const MetricSection* a, const MetricSection* b) {
447 return a->frame() < b->frame();
451 TempoMap::TempoMap (framecnt_t fr)
460 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp);
461 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
463 t->set_movable (false);
464 m->set_movable (false);
466 /* note: frame time is correct (zero) for both of these */
468 metrics.push_back (t);
469 metrics.push_back (m);
473 TempoMap::~TempoMap ()
478 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
480 bool removed = false;
483 Glib::Threads::RWLock::WriterLock lm (lock);
484 if ((removed = remove_tempo_locked (tempo))) {
485 if (complete_operation) {
486 recompute_map (true);
491 if (removed && complete_operation) {
492 PropertyChanged (PropertyChange ());
497 TempoMap::remove_tempo_locked (const TempoSection& tempo)
501 for (i = metrics.begin(); i != metrics.end(); ++i) {
502 if (dynamic_cast<TempoSection*> (*i) != 0) {
503 if (tempo.frame() == (*i)->frame()) {
504 if ((*i)->movable()) {
516 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
518 bool removed = false;
521 Glib::Threads::RWLock::WriterLock lm (lock);
522 if ((removed = remove_meter_locked (tempo))) {
523 if (complete_operation) {
524 recompute_map (true);
529 if (removed && complete_operation) {
530 PropertyChanged (PropertyChange ());
535 TempoMap::remove_meter_locked (const MeterSection& tempo)
539 for (i = metrics.begin(); i != metrics.end(); ++i) {
540 if (dynamic_cast<MeterSection*> (*i) != 0) {
541 if (tempo.frame() == (*i)->frame()) {
542 if ((*i)->movable()) {
554 TempoMap::do_insert (MetricSection* section)
556 bool need_add = true;
558 /* we only allow new meters to be inserted on beat 1 of an existing
562 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
563 assert (m->bbt().ticks == 0);
565 /* we need to (potentially) update the BBT times of tempo
566 sections based on this new meter.
569 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
571 pair<double, BBT_Time> corrected = make_pair (m->start(), m->bbt());
572 corrected.second.beats = 1;
573 corrected.second.ticks = 0;
575 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
576 m->bbt(), corrected.second) << endmsg;
577 m->set_start (corrected);
583 /* Look for any existing MetricSection that is of the same type and
584 in the same bar as the new one, and remove it before adding
585 the new one. Note that this means that if we find a matching,
586 existing section, we can break out of the loop since we're
587 guaranteed that there is only one such match.
590 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
592 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
593 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
595 if (tempo && insert_tempo) {
599 if (tempo->start() == insert_tempo->start()) {
601 if (!tempo->movable()) {
603 /* can't (re)move this section, so overwrite
604 * its data content (but not its properties as
608 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
616 } else if (!tempo && !insert_tempo) {
619 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
620 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
621 if (meter->start() == insert_meter->start()) {
623 if (!meter->movable()) {
625 /* can't (re)move this section, so overwrite
626 * its data content (but not its properties as
630 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
640 /* non-matching types, so we don't care */
644 /* Add the given MetricSection, if we didn't just reset an existing
649 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
650 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
654 for (i = metrics.begin(); i != metrics.end(); ++i) {
655 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
657 if (meter && meter->start() > insert_meter->start()) {
661 } else if (insert_tempo) {
662 for (i = metrics.begin(); i != metrics.end(); ++i) {
663 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
666 if (tempo->start() > insert_tempo->start()) {
673 metrics.insert (i, section);
678 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
681 Glib::Threads::RWLock::WriterLock lm (lock);
682 TempoSection& first (first_tempo());
684 if (ts.start() != first.start()) {
685 remove_tempo_locked (ts);
686 add_tempo_locked (tempo, where, true, type);
688 first.set_type (type);
690 /* cannot move the first tempo section */
691 *static_cast<Tempo*>(&first) = tempo;
692 recompute_map (false);
697 PropertyChanged (PropertyChange ());
701 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
703 TempoSection& first (first_tempo());
704 if (ts.frame() != first.frame()) {
706 Glib::Threads::RWLock::WriterLock lm (lock);
707 /* currently this is always done in audio time */
709 //if (ts.position_lock_style() == AudioTime) {
711 ts.set_start (beat_where);
712 MetricSectionSorter cmp;
716 ts.set_frame (frame);
718 MetricSectionFrameSorter fcmp;
721 Metrics::const_iterator i;
722 TempoSection* prev_ts = 0;
723 TempoSection* next_ts = 0;
725 for (i = metrics.begin(); i != metrics.end(); ++i) {
727 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
729 if (t->frame() >= frame) {
737 for (i = metrics.begin(); i != metrics.end(); ++i) {
739 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
741 if (t->frame() > frame) {
749 /* set the start beat */
750 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
751 double beats = beats_to_ts + prev_ts->start();
754 if (next_ts->start() < beats) {
755 /* with frame-based editing, it is possible to get in a
756 situation where if the tempo was placed at the mouse pointer frame,
757 the following music-based tempo would jump to an earlier frame,
758 changing the start beat of the moved tempo.
759 in this case, we have to do some beat-based comparison TODO
762 ts.set_start (next_ts->start());
763 } else if (prev_ts->start() > beats) {
764 ts.set_start (prev_ts->start());
766 ts.set_start (beats);
769 ts.set_start (beats);
771 MetricSectionSorter cmp;
776 recompute_map (false);
780 MetricPositionChanged (); // Emit Signal
784 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
787 Glib::Threads::RWLock::WriterLock lm (lock);
788 add_tempo_locked (tempo, where, true, type);
792 PropertyChanged (PropertyChange ());
796 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
798 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
803 recompute_map (false);
808 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const double& start, const BBT_Time& where)
811 Glib::Threads::RWLock::WriterLock lm (lock);
812 MeterSection& first (first_meter());
813 if (ms.start() != first.start()) {
814 remove_meter_locked (ms);
815 add_meter_locked (meter, start, where, true);
817 /* cannot move the first meter section */
818 *static_cast<Meter*>(&first) = meter;
819 recompute_map (true);
823 PropertyChanged (PropertyChange ());
827 TempoMap::add_meter (const Meter& meter, double start, BBT_Time where)
830 Glib::Threads::RWLock::WriterLock lm (lock);
831 add_meter_locked (meter, start, where, true);
836 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
841 PropertyChanged (PropertyChange ());
845 TempoMap::add_meter_locked (const Meter& meter, double start, BBT_Time where, bool recompute)
847 /* a new meter always starts a new bar on the first beat. so
848 round the start time appropriately. remember that
849 `where' is based on the existing tempo map, not
850 the result after we insert the new meter.
854 if (where.beats != 1) {
859 /* new meters *always* start on a beat. */
862 do_insert (new MeterSection (start, where, meter.divisions_per_bar(), meter.note_divisor()));
865 recompute_map (true);
871 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
873 Tempo newtempo (beats_per_minute, note_type);
876 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
877 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
879 Glib::Threads::RWLock::WriterLock lm (lock);
880 *((Tempo*) t) = newtempo;
881 recompute_map (false);
883 PropertyChanged (PropertyChange ());
890 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
892 Tempo newtempo (beats_per_minute, note_type);
898 /* find the TempoSection immediately preceding "where"
901 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
903 if ((*i)->frame() > where) {
909 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
919 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
929 Glib::Threads::RWLock::WriterLock lm (lock);
930 /* cannot move the first tempo section */
931 *((Tempo*)prev) = newtempo;
932 recompute_map (false);
935 PropertyChanged (PropertyChange ());
939 TempoMap::first_meter () const
941 const MeterSection *m = 0;
943 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
944 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
949 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
950 abort(); /*NOTREACHED*/
955 TempoMap::first_meter ()
959 /* CALLER MUST HOLD LOCK */
961 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
962 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
967 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
968 abort(); /*NOTREACHED*/
973 TempoMap::first_tempo () const
975 const TempoSection *t = 0;
977 /* CALLER MUST HOLD LOCK */
979 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
980 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
985 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
986 abort(); /*NOTREACHED*/
991 TempoMap::first_tempo ()
995 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
996 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1001 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1002 abort(); /*NOTREACHED*/
1007 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1009 /* CALLER MUST HOLD WRITE LOCK */
1011 MeterSection* meter = 0;
1012 TempoSection* tempo = 0;
1013 double current_frame;
1015 Metrics::iterator next_metric;
1019 /* we will actually stop once we hit
1026 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1028 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1031 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
1039 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1042 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
1050 /* assumes that the first meter & tempo are at frame zero */
1052 meter->set_frame (0);
1053 tempo->set_frame (0);
1055 /* assumes that the first meter & tempo are at 1|1|0 */
1060 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
1062 next_metric = metrics.begin();
1063 ++next_metric; // skip meter (or tempo)
1064 ++next_metric; // skip tempo (or meter)
1066 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
1069 /* silly call from Session::process() during startup
1074 _extend_map (tempo, meter, next_metric, current, current_frame, end);
1078 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
1079 Metrics::iterator next_metric,
1080 BBT_Time current, framepos_t current_frame, framepos_t end)
1082 /* CALLER MUST HOLD WRITE LOCK */
1084 Metrics::const_iterator i;
1085 Metrics::const_iterator mi;
1087 TempoSection* prev_ts = tempo;
1089 for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1090 MeterSection* m = 0;
1092 if ((m = dynamic_cast<MeterSection*> (*mi)) != 0) {
1094 for (i = metrics.begin(); i != metrics.end(); ++i) {
1097 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1099 if (t->start() > prev_ts->start()) {
1101 /*tempo section (t) lies in the previous meter */
1102 double const beats_relative_to_prev_ts = t->start() - prev_ts->start();
1103 double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
1105 /* assume (falsely) that the target tempo is constant */
1106 double const t_fpb = t->frames_per_beat (_frame_rate);
1107 double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
1108 /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
1109 double length_estimate = beats_relative_to_prev_ts * av_fpb;
1111 if (prev_ts->type() == TempoSection::Type::Constant) {
1112 length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
1115 double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
1116 double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
1118 while (fabs (tick_error) > system_precision_at_target_tempo) {
1120 double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
1121 (framepos_t) length_estimate, _frame_rate);
1122 tick_error = ticks_relative_to_prev_ts - actual_ticks;
1123 length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
1126 t->set_frame (length_estimate + prev_ts->frame());
1128 double const meter_start_beats = m->start();
1129 if (meter_start_beats < t->start() && meter_start_beats == prev_ts->start()) {
1131 m->set_frame (prev_ts->frame());
1132 } else if (meter_start_beats < t->start() && meter_start_beats > prev_ts->start()) {
1133 framepos_t new_frame = prev_ts->frame_at_beat (m->start() - prev_ts->start(),
1134 t->beats_per_minute(),
1135 t->frame() - prev_ts->frame(),
1136 _frame_rate) + prev_ts->frame();
1137 m->set_frame (new_frame);
1150 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1152 Glib::Threads::RWLock::ReaderLock lm (lock);
1153 TempoMetric m (first_meter(), first_tempo());
1155 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1156 at something, because we insert the default tempo and meter during
1157 TempoMap construction.
1159 now see if we can find better candidates.
1162 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1164 if ((*i)->frame() > frame) {
1177 /* XX meters only */
1179 TempoMap::metric_at (BBT_Time bbt) const
1181 Glib::Threads::RWLock::ReaderLock lm (lock);
1182 TempoMetric m (first_meter(), first_tempo());
1184 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1185 at something, because we insert the default tempo and meter during
1186 TempoMap construction.
1188 now see if we can find better candidates.
1191 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1193 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1194 BBT_Time section_start (mw->bbt());
1196 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1208 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1210 Glib::Threads::RWLock::ReaderLock lm (lock);
1216 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1219 bbt = beats_to_bbt (beat_at_frame (frame));
1223 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1225 /* CALLER HOLDS READ LOCK */
1227 double accumulated_beats = 0.0;
1228 double accumulated_bars = 0.0;
1229 MeterSection* prev_ms = 0;
1231 Metrics::const_iterator i;
1233 for (i = metrics.begin(); i != metrics.end(); ++i) {
1235 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1236 double bars_to_m = 0.0;
1238 bars_to_m = (m->start() - prev_ms->start()) / prev_ms->divisions_per_bar();
1240 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1244 accumulated_beats += m->start() - prev_ms->start();
1245 accumulated_bars += bars_to_m;
1251 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1252 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1253 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.ticks / BBT_Time::ticks_per_beat);
1259 TempoMap::beats_to_bbt (double beats)
1261 /* CALLER HOLDS READ LOCK */
1263 MeterSection* prev_ms = 0;
1264 uint32_t accumulated_bars = 0;
1266 Metrics::const_iterator i;
1268 for (i = metrics.begin(); i != metrics.end(); ++i) {
1269 MeterSection* m = 0;
1271 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1273 if (beats < m->start()) {
1274 /* this is the meter after the one our beat is on*/
1279 /* we need a whole number of bars. */
1280 accumulated_bars += ((m->start() - prev_ms->start()) + 1) / prev_ms->divisions_per_bar();
1287 double const beats_in_ms = beats - prev_ms->start();
1288 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1289 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1290 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1291 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1295 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1296 ret.beats = (uint32_t) floor (remaining_beats);
1297 ret.bars = total_bars;
1299 /* 0 0 0 to 1 1 0 - based mapping*/
1303 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1305 ret.ticks -= BBT_Time::ticks_per_beat;
1308 if (ret.beats > prev_ms->divisions_per_bar()) {
1317 TempoMap::tick_at_frame (framecnt_t frame) const
1320 Metrics::const_iterator i;
1321 const TempoSection* prev_ts = 0;
1322 double accumulated_ticks = 0.0;
1324 for (i = metrics.begin(); i != metrics.end(); ++i) {
1327 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1329 if ((prev_ts) && frame < t->frame()) {
1330 /*the previous ts is the one containing the frame */
1332 framepos_t time = frame - prev_ts->frame();
1333 framepos_t last_frame = t->frame() - prev_ts->frame();
1334 double last_beats_per_minute = t->beats_per_minute();
1336 return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
1339 if (prev_ts && t->frame() > prev_ts->frame()) {
1340 framepos_t time = t->frame() - prev_ts->frame();
1341 framepos_t last_frame = t->frame() - prev_ts->frame();
1342 double last_beats_per_minute = t->beats_per_minute();
1343 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate);
1350 /* treated s linear for this ts */
1351 framecnt_t frames_in_section = frame - prev_ts->frame();
1352 double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1354 return ticks_in_section + accumulated_ticks;
1359 TempoMap::frame_at_tick (double tick) const
1361 /* HOLD THE READER LOCK */
1363 double accumulated_ticks = 0.0;
1364 double accumulated_ticks_to_prev = 0.0;
1365 const TempoSection* prev_ts = 0;
1367 Metrics::const_iterator i;
1369 for (i = metrics.begin(); i != metrics.end(); ++i) {
1371 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1373 if (prev_ts && t->frame() > prev_ts->frame()) {
1374 framepos_t const time = t->frame() - prev_ts->frame();
1375 framepos_t const last_time = t->frame() - prev_ts->frame();
1376 double const last_beats_per_minute = t->beats_per_minute();
1377 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1380 if (prev_ts && tick < accumulated_ticks) {
1381 /* prev_ts is the one affecting us. */
1383 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1384 framepos_t const section_start = prev_ts->frame();
1385 framepos_t const last_time = t->frame() - prev_ts->frame();
1386 double const last_beats_per_minute = t->beats_per_minute();
1388 return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start;
1390 accumulated_ticks_to_prev = accumulated_ticks;
1394 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1395 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
1396 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1402 TempoMap::beat_at_frame (framecnt_t frame) const
1404 Glib::Threads::RWLock::ReaderLock lm (lock);
1406 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1410 TempoMap::frame_at_beat (double beat) const
1412 Glib::Threads::RWLock::ReaderLock lm (lock);
1414 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1418 TempoMap::frame_time (const BBT_Time& bbt)
1421 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1425 if (bbt.beats < 1) {
1426 throw std::logic_error ("beats are counted from one");
1428 Glib::Threads::RWLock::ReaderLock lm (lock);
1430 framepos_t const ret = frame_at_beat (bbt_to_beats (bbt));
1437 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1440 Glib::Threads::RWLock::ReaderLock lm (lock);
1442 Metrics::const_iterator i;
1443 TempoSection* first = 0;
1444 TempoSection* second = 0;
1446 for (i = metrics.begin(); i != metrics.end(); ++i) {
1449 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1451 if ((*i)->frame() > pos) {
1459 if (first && second) {
1460 framepos_t const last_time = second->frame() - first->frame();
1461 double const last_beats_per_minute = second->beats_per_minute();
1463 framepos_t const time = pos - first->frame();
1464 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1465 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1467 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1469 return time_at_bbt - time;
1472 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1473 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1477 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1479 return round_to_type (fr, dir, Bar);
1483 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1485 return round_to_type (fr, dir, Beat);
1489 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1491 Glib::Threads::RWLock::ReaderLock lm (lock);
1493 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1494 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1495 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1497 ticks -= beats * BBT_Time::ticks_per_beat;
1500 /* round to next (or same iff dir == RoundUpMaybe) */
1502 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1504 if (mod == 0 && dir == RoundUpMaybe) {
1505 /* right on the subdivision, which is fine, so do nothing */
1507 } else if (mod == 0) {
1508 /* right on the subdivision, so the difference is just the subdivision ticks */
1509 ticks += ticks_one_subdivisions_worth;
1512 /* not on subdivision, compute distance to next subdivision */
1514 ticks += ticks_one_subdivisions_worth - mod;
1517 if (ticks >= BBT_Time::ticks_per_beat) {
1518 ticks -= BBT_Time::ticks_per_beat;
1520 } else if (dir < 0) {
1522 /* round to previous (or same iff dir == RoundDownMaybe) */
1524 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1526 if (difference == 0 && dir == RoundDownAlways) {
1527 /* right on the subdivision, but force-rounding down,
1528 so the difference is just the subdivision ticks */
1529 difference = ticks_one_subdivisions_worth;
1532 if (ticks < difference) {
1533 ticks = BBT_Time::ticks_per_beat - ticks;
1535 ticks -= difference;
1539 /* round to nearest */
1542 /* compute the distance to the previous and next subdivision */
1544 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1546 /* closer to the next subdivision, so shift forward */
1548 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1550 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1552 if (ticks > BBT_Time::ticks_per_beat) {
1554 ticks -= BBT_Time::ticks_per_beat;
1555 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1558 } else if (rem > 0) {
1560 /* closer to previous subdivision, so shift backward */
1564 /* can't go backwards past zero, so ... */
1567 /* step back to previous beat */
1569 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1570 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1572 ticks = lrint (ticks - rem);
1573 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1576 /* on the subdivision, do nothing */
1579 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1583 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1585 Glib::Threads::RWLock::ReaderLock lm (lock);
1587 double const beat_at_framepos = beat_at_frame (frame);
1589 BBT_Time bbt (beats_to_bbt (beat_at_framepos));
1594 /* find bar previous to 'frame' */
1597 return frame_time (bbt);
1599 } else if (dir > 0) {
1600 /* find bar following 'frame' */
1604 return frame_time (bbt);
1606 /* true rounding: find nearest bar */
1607 framepos_t raw_ft = frame_time (bbt);
1610 framepos_t prev_ft = frame_time (bbt);
1612 framepos_t next_ft = frame_time (bbt);
1614 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1625 return frame_at_beat (floor (beat_at_framepos));
1626 } else if (dir > 0) {
1627 return frame_at_beat (ceil (beat_at_framepos));
1629 return frame_at_beat (floor (beat_at_framepos + 0.5));
1638 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1639 framepos_t lower, framepos_t upper)
1641 Glib::Threads::RWLock::ReaderLock lm (lock);
1642 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1643 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1645 while (cnt <= upper_beat) {
1646 framecnt_t const pos = frame_at_beat (cnt);
1647 MeterSection const meter = meter_section_at (pos);
1648 Tempo const tempo = tempo_at (pos);
1649 BBT_Time const bbt = beats_to_bbt ((double) cnt);
1651 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1657 TempoMap::tempo_section_at (framepos_t frame) const
1659 Glib::Threads::RWLock::ReaderLock lm (lock);
1661 Metrics::const_iterator i;
1662 TempoSection* prev = 0;
1664 for (i = metrics.begin(); i != metrics.end(); ++i) {
1667 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1669 if ((*i)->frame() > frame) {
1679 abort(); /*NOTREACHED*/
1685 /* don't use this to calculate length (the tempo is only correct for this frame).
1686 do that stuff based on the beat_at_frame and frame_at_beat api
1689 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1691 Glib::Threads::RWLock::ReaderLock lm (lock);
1693 const TempoSection* ts_at = &tempo_section_at (frame);
1694 const TempoSection* ts_after = 0;
1695 Metrics::const_iterator i;
1697 for (i = metrics.begin(); i != metrics.end(); ++i) {
1700 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1702 if ((*i)->frame() > frame) {
1710 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
1712 /* must be treated as constant tempo */
1713 return ts_at->frames_per_beat (_frame_rate);
1717 TempoMap::tempo_at (framepos_t frame) const
1719 Glib::Threads::RWLock::ReaderLock lm (lock);
1721 TempoMetric m (metric_at (frame));
1722 TempoSection* prev_ts = 0;
1724 Metrics::const_iterator i;
1726 for (i = metrics.begin(); i != metrics.end(); ++i) {
1728 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1729 if ((prev_ts) && t->frame() > frame) {
1730 /* this is the one past frame */
1731 framepos_t const time = frame - prev_ts->frame();
1732 framepos_t const last_time = t->frame() - prev_ts->frame();
1733 double const last_beats_per_minute = t->beats_per_minute();
1734 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1735 Tempo const ret_tempo (ret, m.tempo().note_type ());
1747 TempoMap::meter_section_at (framepos_t frame) const
1749 Glib::Threads::RWLock::ReaderLock lm (lock);
1750 Metrics::const_iterator i;
1751 MeterSection* prev = 0;
1753 for (i = metrics.begin(); i != metrics.end(); ++i) {
1756 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1758 if ((*i)->frame() > frame) {
1768 abort(); /*NOTREACHED*/
1775 TempoMap::meter_at (framepos_t frame) const
1777 TempoMetric m (metric_at (frame));
1782 TempoMap::get_state ()
1784 Metrics::const_iterator i;
1785 XMLNode *root = new XMLNode ("TempoMap");
1788 Glib::Threads::RWLock::ReaderLock lm (lock);
1789 for (i = metrics.begin(); i != metrics.end(); ++i) {
1790 root->add_child_nocopy ((*i)->get_state());
1798 TempoMap::set_state (const XMLNode& node, int /*version*/)
1801 Glib::Threads::RWLock::WriterLock lm (lock);
1804 XMLNodeConstIterator niter;
1805 Metrics old_metrics (metrics);
1806 MeterSection* last_meter = 0;
1809 nlist = node.children();
1811 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1812 XMLNode* child = *niter;
1814 if (child->name() == TempoSection::xml_state_node_name) {
1817 TempoSection* ts = new TempoSection (*child);
1818 metrics.push_back (ts);
1820 if (ts->bar_offset() < 0.0) {
1822 //ts->update_bar_offset_from_bbt (*last_meter);
1827 catch (failed_constructor& err){
1828 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1829 metrics = old_metrics;
1833 } else if (child->name() == MeterSection::xml_state_node_name) {
1836 MeterSection* ms = new MeterSection (*child);
1837 metrics.push_back (ms);
1841 catch (failed_constructor& err) {
1842 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1843 metrics = old_metrics;
1849 if (niter == nlist.end()) {
1850 MetricSectionSorter cmp;
1853 /* check for legacy sessions where bbt was the base musical unit for tempo */
1854 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1855 MeterSection* prev_ms;
1856 TempoSection* prev_ts;
1857 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1858 if (prev_ms->start() < 0.0) {
1859 /*XX we cannot possibly make this work??. */
1860 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());
1861 prev_ms->set_start (start);
1863 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1864 if (prev_ts->start() < 0.0) {
1865 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);
1866 prev_ts->set_start (start);
1871 /* check for multiple tempo/meters at the same location, which
1872 ardour2 somehow allowed.
1875 Metrics::iterator prev = metrics.end();
1876 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1877 if (prev != metrics.end()) {
1879 MeterSection* prev_ms;
1881 TempoSection* prev_ts;
1882 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1883 if (prev_ms->start() == ms->start()) {
1884 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
1885 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
1888 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1889 if (prev_ts->start() == ts->start()) {
1890 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
1891 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
1899 recompute_map (true, -1);
1902 PropertyChanged (PropertyChange ());
1908 TempoMap::dump (std::ostream& o) const
1910 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1911 const MeterSection* m;
1912 const TempoSection* t;
1914 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1916 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1917 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? "
1918 << t->movable() << ')' << endl;
1919 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1920 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
1921 << " (movable? " << m->movable() << ')' << endl;
1927 TempoMap::n_tempos() const
1929 Glib::Threads::RWLock::ReaderLock lm (lock);
1932 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1933 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1942 TempoMap::n_meters() const
1944 Glib::Threads::RWLock::ReaderLock lm (lock);
1947 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1948 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1957 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1960 Glib::Threads::RWLock::WriterLock lm (lock);
1961 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1962 if ((*i)->frame() >= where && (*i)->movable ()) {
1963 (*i)->set_frame ((*i)->frame() + amount);
1967 /* now reset the BBT time of all metrics, based on their new
1968 * audio time. This is the only place where we do this reverse
1972 Metrics::iterator i;
1973 const MeterSection* meter;
1974 const TempoSection* tempo;
1978 meter = &first_meter ();
1979 tempo = &first_tempo ();
1984 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1987 MetricSection* prev = 0;
1989 for (i = metrics.begin(); i != metrics.end(); ++i) {
1992 //TempoMetric metric (*meter, *tempo);
1993 MeterSection* ms = const_cast<MeterSection*>(meter);
1994 TempoSection* ts = const_cast<TempoSection*>(tempo);
1997 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1998 ts->set_start (t->start());
2000 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2001 ts->set_start (m->start());
2003 ts->set_frame (prev->frame());
2007 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2008 pair<double, BBT_Time> start = make_pair (m->start(), m->bbt());
2009 ms->set_start (start);
2011 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2012 pair<double, BBT_Time> start = make_pair (t->start(), beats_to_bbt (t->start()));
2013 ms->set_start (start);
2015 ms->set_frame (prev->frame());
2019 // metric will be at frames=0 bbt=1|1|0 by default
2020 // which is correct for our purpose
2023 // cerr << bbt << endl;
2025 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2026 t->set_start (beat_at_frame (m->frame()));
2028 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2029 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2030 bbt_time (m->frame(), bbt);
2032 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2038 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2039 /* round up to next beat */
2045 if (bbt.beats != 1) {
2046 /* round up to next bar */
2051 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
2052 m->set_start (start);
2054 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2056 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2057 abort(); /*NOTREACHED*/
2063 recompute_map (true);
2067 PropertyChanged (PropertyChange ());
2070 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2074 std::list<MetricSection*> metric_kill_list;
2076 TempoSection* last_tempo = NULL;
2077 MeterSection* last_meter = NULL;
2078 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2079 bool meter_after = false; // is there a meter marker likewise?
2081 Glib::Threads::RWLock::WriterLock lm (lock);
2082 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2083 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2084 metric_kill_list.push_back(*i);
2085 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2088 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2092 else if ((*i)->frame() >= where) {
2093 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2094 (*i)->set_frame ((*i)->frame() - amount);
2095 if ((*i)->frame() == where) {
2096 // marker was immediately after end of range
2097 tempo_after = dynamic_cast<TempoSection*> (*i);
2098 meter_after = dynamic_cast<MeterSection*> (*i);
2104 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2105 if (last_tempo && !tempo_after) {
2106 metric_kill_list.remove(last_tempo);
2107 last_tempo->set_frame(where);
2110 if (last_meter && !meter_after) {
2111 metric_kill_list.remove(last_meter);
2112 last_meter->set_frame(where);
2116 //remove all the remaining metrics
2117 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2123 recompute_map (true);
2126 PropertyChanged (PropertyChange ());
2130 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2131 * pos can be -ve, if required.
2134 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2136 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2139 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2141 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2143 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2146 /** Add the BBT interval op to pos and return the result */
2148 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2150 cerr << "framepos_plus_bbt - untested" << endl;
2151 Glib::Threads::RWLock::ReaderLock lm (lock);
2153 Metrics::const_iterator i;
2154 const MeterSection* meter;
2155 const MeterSection* m;
2156 const TempoSection* tempo;
2157 const TempoSection* next_tempo = 0;
2158 const TempoSection* t;
2159 double frames_per_beat;
2160 framepos_t effective_pos = max (pos, (framepos_t) 0);
2162 meter = &first_meter ();
2163 tempo = &first_tempo ();
2168 /* find the starting metrics for tempo & meter */
2170 for (i = metrics.begin(); i != metrics.end(); ++i) {
2172 if ((*i)->frame() > effective_pos) {
2176 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2178 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2183 for (i = metrics.begin(); i != metrics.end(); ++i) {
2184 if ((*i)->frame() > effective_pos) {
2185 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2194 meter -> the Meter for "pos"
2195 tempo -> the Tempo for "pos"
2196 next_tempo -> the Tempo after "pos" or 0
2197 i -> for first new metric after "pos", possibly metrics.end()
2200 /* now comes the complicated part. we have to add one beat a time,
2201 checking for a new metric on every beat.
2205 /* fpb is used for constant tempo */
2206 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2213 /* check if we need to use a new metric section: has adding frames moved us
2214 to or after the start of the next metric section? in which case, use it.
2217 if (i != metrics.end()) {
2218 if ((*i)->frame() <= pos) {
2220 /* about to change tempo or meter, so add the
2221 * number of frames for the bars we've just
2222 * traversed before we change the
2223 * frames_per_beat value.
2226 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2231 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2233 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2238 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2240 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2244 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2251 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2253 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2260 /* given the current meter, have we gone past the end of the bar ? */
2265 /* check if we need to use a new metric section: has adding frames moved us
2266 to or after the start of the next metric section? in which case, use it.
2269 if (i != metrics.end()) {
2270 if ((*i)->frame() <= pos) {
2272 /* about to change tempo or meter, so add the
2273 * number of frames for the beats we've just
2274 * traversed before we change the
2275 * frames_per_beat value.
2278 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2283 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2285 pos += llrint (beats * frames_per_beat);
2290 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2292 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2296 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2302 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2304 pos += llrint (beats * frames_per_beat);
2308 pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2315 /** Count the number of beats that are equivalent to distance when going forward,
2319 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2321 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2325 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2331 operator<< (std::ostream& o, const Meter& m) {
2332 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2336 operator<< (std::ostream& o, const Tempo& t) {
2337 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2341 operator<< (std::ostream& o, const MetricSection& section) {
2343 o << "MetricSection @ " << section.frame() << ' ';
2345 const TempoSection* ts;
2346 const MeterSection* ms;
2348 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2349 o << *((const Tempo*) ts);
2350 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2351 //o << *((const Meter*) ms);