2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
28 #include "evoral/Beats.hpp"
29 #include "ardour/debug.h"
30 #include "ardour/lmath.h"
31 #include "ardour/tempo.h"
37 using namespace ARDOUR;
40 using Timecode::BBT_Time;
42 /* _default tempo is 4/4 qtr=120 */
44 Meter TempoMap::_default_meter (4.0, 4.0);
45 Tempo TempoMap::_default_tempo (120.0);
47 /***********************************************************************/
50 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
52 /* This is tempo- and meter-sensitive. The number it returns
53 is based on the interval between any two lines in the
54 grid that is constructed from tempo and meter sections.
56 The return value IS NOT interpretable in terms of "beats".
59 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
65 return frames_per_grid (tempo, sr) * _divisions_per_bar;
69 /***********************************************************************/
71 const string TempoSection::xml_state_node_name = "Tempo";
73 TempoSection::TempoSection (const XMLNode& node)
74 : MetricSection (0.0), Tempo (TempoMap::default_tempo())
76 const XMLProperty *prop;
80 if ((prop = node.property ("start")) == 0) {
81 error << _("MeterSection XML node has no \"start\" property") << endmsg;
82 throw failed_constructor();
85 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
89 /* legacy session - start used to be in bbt*/
92 } else if (sscanf (prop->value().c_str(), "%lf", &start) != 1 || start < 0.0) {
93 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
98 if ((prop = node.property ("beats-per-minute")) == 0) {
99 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
100 throw failed_constructor();
103 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
104 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
105 throw failed_constructor();
108 if ((prop = node.property ("note-type")) == 0) {
109 /* older session, make note type be quarter by default */
112 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
113 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
114 throw failed_constructor();
118 if ((prop = node.property ("movable")) == 0) {
119 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
120 throw failed_constructor();
123 set_movable (string_is_affirmative (prop->value()));
125 if ((prop = node.property ("bar-offset")) == 0) {
128 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
129 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
130 throw failed_constructor();
134 if ((prop = node.property ("tempo-type")) == 0) {
135 _type = Type::Constant;
137 if (strstr(prop->value().c_str(),"Constant")) {
138 _type = Type::Constant;
146 TempoSection::get_state() const
148 XMLNode *root = new XMLNode (xml_state_node_name);
152 snprintf (buf, sizeof (buf), "%f", beat());
153 root->add_property ("start", buf);
154 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
155 root->add_property ("beats-per-minute", buf);
156 snprintf (buf, sizeof (buf), "%f", _note_type);
157 root->add_property ("note-type", buf);
158 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
159 // root->add_property ("bar-offset", buf);
160 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
161 root->add_property ("movable", buf);
163 snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
164 root->add_property ("tempo-type", buf);
171 TempoSection::update_bar_offset_from_bbt (const Meter& m)
173 _bar_offset = (start() * BBT_Time::ticks_per_beat) /
174 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
176 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
180 TempoSection::set_type (Type type)
185 /** returns the tempo at the zero-based (relative to tempo section) frame.
188 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
191 if (_type == Constant) {
192 return beats_per_minute();
195 return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat;
198 /** returns the zero-based frame (relative to tempo section)
199 where the tempo occurs.
202 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
204 if (_type == Constant) {
208 return minute_to_frame (time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
211 /** returns the zero-based tick (relative to tempo section)
212 where the zero-based frame (relative to tempo section)
216 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
218 if (_type == Constant) {
219 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
222 return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
225 /** returns the zero-based frame (relative to tempo section)
226 where the zero-based tick (relative to tempo section)
230 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
232 if (_type == Constant) {
233 return (framepos_t) floor ((tick / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
236 return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
239 /** returns the zero-based beat (relative to tempo section)
240 where the zero-based frame (relative to tempo section)
244 TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
246 return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
249 /** returns the zero-based frame (relative to tempo section start frame)
250 where the zero-based beat (relative to tempo section start)
255 TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
257 return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
261 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
263 return time * 60.0 * frame_rate;
267 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
269 return (frame / (double) frame_rate) / 60.0;
272 /* position function */
274 TempoSection::a_func (double end_tpm, double c_func) const
276 return log (end_tpm / ticks_per_minute()) / c_func;
279 /*function constant*/
281 TempoSection::c_func (double end_tpm, double end_time) const
283 return log (end_tpm / ticks_per_minute()) / end_time;
286 /* tempo in tpm at time in minutes */
288 TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
290 return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
293 /* time in minutes at tempo in tpm */
295 TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
297 return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
300 /* tick at time in minutes */
302 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
304 return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
307 /* time in minutes at tick */
309 TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
311 return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
314 /* beat at time in minutes */
316 TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
318 return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
321 /* time in munutes at beat */
323 TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
325 return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
329 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
333 if (_bar_offset < 0.0) {
340 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
341 new_start = ticks / BBT_Time::ticks_per_beat;
343 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
344 _bar_offset, meter.divisions_per_bar(), ticks, new_start, new_start));
346 set_start (new_start);
349 /***********************************************************************/
351 const string MeterSection::xml_state_node_name = "Meter";
353 MeterSection::MeterSection (const XMLNode& node)
354 : MetricSection (0.0), Meter (TempoMap::default_meter())
356 XMLProperty const * prop;
359 const XMLProperty *prop;
362 pair<double, BBT_Time> start;
364 if ((prop = node.property ("start")) == 0) {
365 error << _("MeterSection XML node has no \"start\" property") << endmsg;
366 throw failed_constructor();
368 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
372 /* legacy session - start used to be in bbt*/
374 } else if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
375 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
376 throw failed_constructor();
380 if ((prop = node.property ("bbt")) == 0) {
381 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
382 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
386 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
387 throw failed_constructor();
393 /* beats-per-bar is old; divisions-per-bar is new */
395 if ((prop = node.property ("divisions-per-bar")) == 0) {
396 if ((prop = node.property ("beats-per-bar")) == 0) {
397 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
398 throw failed_constructor();
402 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
403 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
404 throw failed_constructor();
407 if ((prop = node.property ("note-type")) == 0) {
408 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
409 throw failed_constructor();
412 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
413 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
414 throw failed_constructor();
417 if ((prop = node.property ("movable")) == 0) {
418 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
419 throw failed_constructor();
422 set_movable (string_is_affirmative (prop->value()));
426 MeterSection::get_state() const
428 XMLNode *root = new XMLNode (xml_state_node_name);
432 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
436 root->add_property ("bbt", buf);
437 snprintf (buf, sizeof (buf), "%lf", start());
438 root->add_property ("start", buf);
439 snprintf (buf, sizeof (buf), "%f", _note_type);
440 root->add_property ("note-type", buf);
441 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
442 root->add_property ("divisions-per-bar", buf);
443 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
444 root->add_property ("movable", buf);
449 /***********************************************************************/
451 struct MetricSectionSorter {
452 bool operator() (const MetricSection* a, const MetricSection* b) {
453 return a->start() < b->start();
457 struct MetricSectionFrameSorter {
458 bool operator() (const MetricSection* a, const MetricSection* b) {
459 return a->frame() < b->frame();
463 TempoMap::TempoMap (framecnt_t fr)
472 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Constant);
473 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
475 t->set_movable (false);
476 m->set_movable (false);
478 /* note: frame time is correct (zero) for both of these */
480 metrics.push_back (t);
481 metrics.push_back (m);
485 TempoMap::~TempoMap ()
490 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
492 bool removed = false;
495 Glib::Threads::RWLock::WriterLock lm (lock);
496 if ((removed = remove_tempo_locked (tempo))) {
497 if (complete_operation) {
498 recompute_map (true);
503 if (removed && complete_operation) {
504 PropertyChanged (PropertyChange ());
509 TempoMap::remove_tempo_locked (const TempoSection& tempo)
513 for (i = metrics.begin(); i != metrics.end(); ++i) {
514 if (dynamic_cast<TempoSection*> (*i) != 0) {
515 if (tempo.frame() == (*i)->frame()) {
516 if ((*i)->movable()) {
528 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
530 bool removed = false;
533 Glib::Threads::RWLock::WriterLock lm (lock);
534 if ((removed = remove_meter_locked (tempo))) {
535 if (complete_operation) {
536 recompute_map (true);
541 if (removed && complete_operation) {
542 PropertyChanged (PropertyChange ());
547 TempoMap::remove_meter_locked (const MeterSection& tempo)
551 for (i = metrics.begin(); i != metrics.end(); ++i) {
552 if (dynamic_cast<MeterSection*> (*i) != 0) {
553 if (tempo.frame() == (*i)->frame()) {
554 if ((*i)->movable()) {
566 TempoMap::do_insert (MetricSection* section)
568 bool need_add = true;
570 /* we only allow new meters to be inserted on beat 1 of an existing
574 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
575 assert (m->bbt().ticks == 0);
577 /* we need to (potentially) update the BBT times of tempo
578 sections based on this new meter.
581 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
583 pair<double, BBT_Time> corrected = make_pair (m->start(), m->bbt());
584 corrected.second.beats = 1;
585 corrected.second.ticks = 0;
586 corrected.first = bbt_to_beats (corrected.second);
587 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
588 m->bbt(), corrected.second) << endmsg;
589 m->set_start (corrected);
595 /* Look for any existing MetricSection that is of the same type and
596 in the same bar as the new one, and remove it before adding
597 the new one. Note that this means that if we find a matching,
598 existing section, we can break out of the loop since we're
599 guaranteed that there is only one such match.
602 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
604 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
605 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
607 if (tempo && insert_tempo) {
611 if (tempo->start() == insert_tempo->start()) {
613 if (!tempo->movable()) {
615 /* can't (re)move this section, so overwrite
616 * its data content (but not its properties as
620 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
628 } else if (!tempo && !insert_tempo) {
631 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
632 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
633 if (meter->start() == insert_meter->start()) {
635 if (!meter->movable()) {
637 /* can't (re)move this section, so overwrite
638 * its data content (but not its properties as
642 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
652 /* non-matching types, so we don't care */
656 /* Add the given MetricSection, if we didn't just reset an existing
661 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
662 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
666 for (i = metrics.begin(); i != metrics.end(); ++i) {
667 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
669 if (meter && meter->start() > insert_meter->start()) {
673 } else if (insert_tempo) {
674 for (i = metrics.begin(); i != metrics.end(); ++i) {
675 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
678 if (tempo->start() > insert_tempo->start()) {
685 metrics.insert (i, section);
690 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
693 Glib::Threads::RWLock::WriterLock lm (lock);
694 TempoSection& first (first_tempo());
696 if (ts.start() != first.start()) {
697 remove_tempo_locked (ts);
698 add_tempo_locked (tempo, where, true, type);
700 first.set_type (type);
702 /* cannot move the first tempo section */
703 *static_cast<Tempo*>(&first) = tempo;
704 recompute_map (false);
709 PropertyChanged (PropertyChange ());
713 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
716 Glib::Threads::RWLock::WriterLock lm (lock);
718 /* currently this is always done in audio time */
719 //if (ts.position_lock_style() == MusicTime) {
722 ts.set_start (beat_where);
723 MetricSectionSorter cmp;
727 ts.set_frame (frame);
729 MetricSectionFrameSorter fcmp;
732 Metrics::const_iterator i;
733 TempoSection* prev_ts = 0;
734 TempoSection* next_ts = 0;
736 for (i = metrics.begin(); i != metrics.end(); ++i) {
738 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
740 if (t->frame() >= frame) {
748 for (i = metrics.begin(); i != metrics.end(); ++i) {
750 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
752 if (t->frame() > frame) {
760 /* set the start beat */
761 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
762 double beats = beats_to_ts + prev_ts->start();
765 if (next_ts->start() < beats) {
766 /* with frame-based editing, it is possible to get in a
767 situation where if the tempo was placed at the mouse pointer frame,
768 the following music-based tempo would jump to an earlier frame,
769 changing the start beat of the moved tempo.
770 in this case, we have to do some beat-based comparison TODO
772 } else if (prev_ts->start() > beats) {
773 ts.set_start (prev_ts->start());
775 ts.set_start (beats);
778 ts.set_start (beats);
780 MetricSectionSorter cmp;
785 recompute_map (false);
788 MetricPositionChanged (); // Emit Signal
792 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
795 Glib::Threads::RWLock::WriterLock lm (lock);
796 add_tempo_locked (tempo, where, true, type);
800 PropertyChanged (PropertyChange ());
804 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
806 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
811 recompute_map (false);
816 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const double& start, const BBT_Time& where)
819 Glib::Threads::RWLock::WriterLock lm (lock);
820 MeterSection& first (first_meter());
821 if (ms.start() != first.start()) {
822 remove_meter_locked (ms);
823 add_meter_locked (meter, start, where, true);
825 /* cannot move the first meter section */
826 *static_cast<Meter*>(&first) = meter;
827 recompute_map (true);
831 PropertyChanged (PropertyChange ());
835 TempoMap::add_meter (const Meter& meter, double start, BBT_Time where)
838 Glib::Threads::RWLock::WriterLock lm (lock);
839 add_meter_locked (meter, start, where, true);
844 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
849 PropertyChanged (PropertyChange ());
853 TempoMap::add_meter_locked (const Meter& meter, double start, BBT_Time where, bool recompute)
855 /* a new meter always starts a new bar on the first beat. so
856 round the start time appropriately. remember that
857 `where' is based on the existing tempo map, not
858 the result after we insert the new meter.
862 if (where.beats != 1) {
867 /* new meters *always* start on a beat. */
870 do_insert (new MeterSection (start, where, meter.divisions_per_bar(), meter.note_divisor()));
873 recompute_map (true);
879 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
881 Tempo newtempo (beats_per_minute, note_type);
884 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
885 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
887 Glib::Threads::RWLock::WriterLock lm (lock);
888 *((Tempo*) t) = newtempo;
889 recompute_map (false);
891 PropertyChanged (PropertyChange ());
898 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
900 Tempo newtempo (beats_per_minute, note_type);
906 /* find the TempoSection immediately preceding "where"
909 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
911 if ((*i)->frame() > where) {
917 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
927 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
937 Glib::Threads::RWLock::WriterLock lm (lock);
938 /* cannot move the first tempo section */
939 *((Tempo*)prev) = newtempo;
940 recompute_map (false);
943 PropertyChanged (PropertyChange ());
947 TempoMap::first_meter () const
949 const MeterSection *m = 0;
951 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
952 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
957 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
958 abort(); /*NOTREACHED*/
963 TempoMap::first_meter ()
967 /* CALLER MUST HOLD LOCK */
969 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
970 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
975 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
976 abort(); /*NOTREACHED*/
981 TempoMap::first_tempo () const
983 const TempoSection *t = 0;
985 /* CALLER MUST HOLD LOCK */
987 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
988 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
993 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
994 abort(); /*NOTREACHED*/
999 TempoMap::first_tempo ()
1001 TempoSection *t = 0;
1003 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1004 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1009 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1010 abort(); /*NOTREACHED*/
1015 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1017 /* CALLER MUST HOLD WRITE LOCK */
1021 /* we will actually stop once we hit
1028 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1031 /* silly call from Session::process() during startup
1036 Metrics::const_iterator i;
1038 TempoSection* prev_ts = 0;
1040 for (i = metrics.begin(); i != metrics.end(); ++i) {
1043 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1046 double const beats_relative_to_prev_ts = t->start() - prev_ts->start();
1047 double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
1049 /* assume (falsely) that the target tempo is constant */
1050 double const t_fpb = t->frames_per_beat (_frame_rate);
1051 double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
1052 /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
1053 double length_estimate = beats_relative_to_prev_ts * av_fpb;
1055 if (prev_ts->type() == TempoSection::Type::Constant) {
1056 length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
1059 double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
1060 double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
1062 while (fabs (tick_error) > system_precision_at_target_tempo) {
1064 double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
1065 (framepos_t) length_estimate, _frame_rate);
1066 tick_error = ticks_relative_to_prev_ts - actual_ticks;
1067 length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
1070 t->set_frame (length_estimate + prev_ts->frame());
1076 Metrics::const_iterator mi;
1077 MeterSection* meter = 0;
1079 for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1080 /* we can do this beacuse we have the tempo section frames set */
1081 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1082 meter->set_frame (frame_at_tick (meter->start() * BBT_Time::ticks_per_beat));
1089 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1091 Glib::Threads::RWLock::ReaderLock lm (lock);
1092 TempoMetric m (first_meter(), first_tempo());
1094 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1095 at something, because we insert the default tempo and meter during
1096 TempoMap construction.
1098 now see if we can find better candidates.
1101 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1103 if ((*i)->frame() > frame) {
1116 /* XX meters only */
1118 TempoMap::metric_at (BBT_Time bbt) const
1120 Glib::Threads::RWLock::ReaderLock lm (lock);
1121 TempoMetric m (first_meter(), first_tempo());
1123 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1124 at something, because we insert the default tempo and meter during
1125 TempoMap construction.
1127 now see if we can find better candidates.
1130 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1132 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1133 BBT_Time section_start (mw->bbt());
1135 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1147 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1149 Glib::Threads::RWLock::ReaderLock lm (lock);
1155 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1158 bbt = beats_to_bbt (beat_at_frame (frame));
1162 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1164 /* CALLER HOLDS READ LOCK */
1166 double accumulated_beats = 0.0;
1167 double accumulated_bars = 0.0;
1168 MeterSection* prev_ms = 0;
1170 Metrics::const_iterator i;
1172 for (i = metrics.begin(); i != metrics.end(); ++i) {
1174 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1175 double bars_to_m = 0.0;
1177 bars_to_m = (m->start() - prev_ms->start()) / prev_ms->divisions_per_bar();
1179 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1183 accumulated_beats += m->start() - prev_ms->start();
1184 accumulated_bars += bars_to_m;
1190 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1191 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1192 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.ticks / BBT_Time::ticks_per_beat);
1198 TempoMap::beats_to_bbt (double beats)
1200 /* CALLER HOLDS READ LOCK */
1202 MeterSection* prev_ms = 0;
1203 uint32_t accumulated_bars = 0;
1205 Metrics::const_iterator i;
1207 for (i = metrics.begin(); i != metrics.end(); ++i) {
1208 MeterSection* m = 0;
1210 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1212 if (beats < m->start()) {
1213 /* this is the meter after the one our beat is on*/
1218 /* we need a whole number of bars. */
1219 accumulated_bars += ((m->start() - prev_ms->start()) + 1) / prev_ms->divisions_per_bar();
1226 double const beats_in_ms = beats - prev_ms->start();
1227 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1228 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1229 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1230 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1234 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1235 ret.beats = (uint32_t) floor (remaining_beats);
1236 ret.bars = total_bars;
1238 /* 0 0 0 to 1 1 0 - based mapping*/
1242 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1244 ret.ticks -= BBT_Time::ticks_per_beat;
1247 if (ret.beats > prev_ms->divisions_per_bar()) {
1256 TempoMap::tick_at_frame (framecnt_t frame) const
1258 /* HOLD (at least) THE READER LOCK */
1260 Metrics::const_iterator i;
1261 TempoSection* prev_ts = 0;
1262 double accumulated_ticks = 0.0;
1264 for (i = metrics.begin(); i != metrics.end(); ++i) {
1267 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1269 if ((prev_ts) && frame < t->frame()) {
1270 /*the previous ts is the one containing the frame */
1272 framepos_t const time = frame - prev_ts->frame();
1273 framepos_t const last_frame = t->frame() - prev_ts->frame();
1274 double const last_beats_per_minute = t->beats_per_minute();
1276 return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
1279 if (prev_ts && t->frame() > prev_ts->frame()) {
1280 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1287 /* treated as constant for this ts */
1288 framecnt_t const frames_in_section = frame - prev_ts->frame();
1289 double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1291 return ticks_in_section + accumulated_ticks;
1296 TempoMap::frame_at_tick (double tick) const
1298 /* HOLD THE READER LOCK */
1300 double accumulated_ticks = 0.0;
1301 double accumulated_ticks_to_prev = 0.0;
1302 const TempoSection* prev_ts = 0;
1304 Metrics::const_iterator i;
1306 for (i = metrics.begin(); i != metrics.end(); ++i) {
1308 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1310 if (prev_ts && t->frame() > prev_ts->frame()) {
1311 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1314 if (prev_ts && tick < accumulated_ticks) {
1315 /* prev_ts is the one affecting us. */
1317 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1318 framepos_t const last_time = t->frame() - prev_ts->frame();
1319 double const last_beats_per_minute = t->beats_per_minute();
1321 return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + prev_ts->frame();
1323 accumulated_ticks_to_prev = accumulated_ticks;
1327 /* must be treated as constant, irrespective of _type */
1328 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1329 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1331 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1337 TempoMap::beat_at_frame (framecnt_t frame) const
1339 Glib::Threads::RWLock::ReaderLock lm (lock);
1341 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1345 TempoMap::frame_at_beat (double beat) const
1347 Glib::Threads::RWLock::ReaderLock lm (lock);
1349 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1353 TempoMap::frame_time (const BBT_Time& bbt)
1356 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1360 if (bbt.beats < 1) {
1361 throw std::logic_error ("beats are counted from one");
1363 Glib::Threads::RWLock::ReaderLock lm (lock);
1365 framepos_t const ret = frame_at_beat (bbt_to_beats (bbt));
1372 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1375 Glib::Threads::RWLock::ReaderLock lm (lock);
1377 Metrics::const_iterator i;
1378 TempoSection* first = 0;
1379 TempoSection* second = 0;
1381 for (i = metrics.begin(); i != metrics.end(); ++i) {
1384 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1386 if ((*i)->frame() > pos) {
1394 if (first && second) {
1395 framepos_t const last_time = second->frame() - first->frame();
1396 double const last_beats_per_minute = second->beats_per_minute();
1398 framepos_t const time = pos - first->frame();
1399 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1400 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1402 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1404 return time_at_bbt - time;
1407 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1408 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1412 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1414 return round_to_type (fr, dir, Bar);
1418 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1420 return round_to_type (fr, dir, Beat);
1424 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1426 Glib::Threads::RWLock::ReaderLock lm (lock);
1428 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1429 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1430 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1432 ticks -= beats * BBT_Time::ticks_per_beat;
1435 /* round to next (or same iff dir == RoundUpMaybe) */
1437 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1439 if (mod == 0 && dir == RoundUpMaybe) {
1440 /* right on the subdivision, which is fine, so do nothing */
1442 } else if (mod == 0) {
1443 /* right on the subdivision, so the difference is just the subdivision ticks */
1444 ticks += ticks_one_subdivisions_worth;
1447 /* not on subdivision, compute distance to next subdivision */
1449 ticks += ticks_one_subdivisions_worth - mod;
1452 if (ticks >= BBT_Time::ticks_per_beat) {
1453 ticks -= BBT_Time::ticks_per_beat;
1455 } else if (dir < 0) {
1457 /* round to previous (or same iff dir == RoundDownMaybe) */
1459 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1461 if (difference == 0 && dir == RoundDownAlways) {
1462 /* right on the subdivision, but force-rounding down,
1463 so the difference is just the subdivision ticks */
1464 difference = ticks_one_subdivisions_worth;
1467 if (ticks < difference) {
1468 ticks = BBT_Time::ticks_per_beat - ticks;
1470 ticks -= difference;
1474 /* round to nearest */
1477 /* compute the distance to the previous and next subdivision */
1479 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1481 /* closer to the next subdivision, so shift forward */
1483 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1485 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1487 if (ticks > BBT_Time::ticks_per_beat) {
1489 ticks -= BBT_Time::ticks_per_beat;
1490 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1493 } else if (rem > 0) {
1495 /* closer to previous subdivision, so shift backward */
1499 /* can't go backwards past zero, so ... */
1502 /* step back to previous beat */
1504 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1505 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1507 ticks = lrint (ticks - rem);
1508 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1511 /* on the subdivision, do nothing */
1514 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1518 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1520 Glib::Threads::RWLock::ReaderLock lm (lock);
1522 double const beat_at_framepos = beat_at_frame (frame);
1524 BBT_Time bbt (beats_to_bbt (beat_at_framepos));
1529 /* find bar previous to 'frame' */
1532 return frame_time (bbt);
1534 } else if (dir > 0) {
1535 /* find bar following 'frame' */
1539 return frame_time (bbt);
1541 /* true rounding: find nearest bar */
1542 framepos_t raw_ft = frame_time (bbt);
1545 framepos_t prev_ft = frame_time (bbt);
1547 framepos_t next_ft = frame_time (bbt);
1549 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1560 return frame_at_beat (floor (beat_at_framepos));
1561 } else if (dir > 0) {
1562 return frame_at_beat (ceil (beat_at_framepos));
1564 return frame_at_beat (floor (beat_at_framepos + 0.5));
1573 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1574 framepos_t lower, framepos_t upper)
1576 Glib::Threads::RWLock::ReaderLock lm (lock);
1577 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1578 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1580 while (cnt <= upper_beat) {
1581 framecnt_t const pos = frame_at_beat (cnt);
1582 MeterSection const meter = meter_section_at (pos);
1583 Tempo const tempo = tempo_at (pos);
1584 BBT_Time const bbt = beats_to_bbt ((double) cnt);
1586 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1592 TempoMap::tempo_section_at (framepos_t frame) const
1594 Glib::Threads::RWLock::ReaderLock lm (lock);
1596 Metrics::const_iterator i;
1597 TempoSection* prev = 0;
1599 for (i = metrics.begin(); i != metrics.end(); ++i) {
1602 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1604 if ((*i)->frame() > frame) {
1614 abort(); /*NOTREACHED*/
1620 /* don't use this to calculate length (the tempo is only correct for this frame).
1621 do that stuff based on the beat_at_frame and frame_at_beat api
1624 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1626 Glib::Threads::RWLock::ReaderLock lm (lock);
1628 const TempoSection* ts_at = &tempo_section_at (frame);
1629 const TempoSection* ts_after = 0;
1630 Metrics::const_iterator i;
1632 for (i = metrics.begin(); i != metrics.end(); ++i) {
1635 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1637 if ((*i)->frame() > frame) {
1645 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
1647 /* must be treated as constant tempo */
1648 return ts_at->frames_per_beat (_frame_rate);
1652 TempoMap::tempo_at (framepos_t frame) const
1654 Glib::Threads::RWLock::ReaderLock lm (lock);
1656 TempoMetric m (metric_at (frame));
1657 TempoSection* prev_ts = 0;
1659 Metrics::const_iterator i;
1661 for (i = metrics.begin(); i != metrics.end(); ++i) {
1663 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1664 if ((prev_ts) && t->frame() > frame) {
1665 /* this is the one past frame */
1666 framepos_t const time = frame - prev_ts->frame();
1667 framepos_t const last_time = t->frame() - prev_ts->frame();
1668 double const last_beats_per_minute = t->beats_per_minute();
1669 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1670 Tempo const ret_tempo (ret, m.tempo().note_type ());
1682 TempoMap::meter_section_at (framepos_t frame) const
1684 Glib::Threads::RWLock::ReaderLock lm (lock);
1685 Metrics::const_iterator i;
1686 MeterSection* prev = 0;
1688 for (i = metrics.begin(); i != metrics.end(); ++i) {
1691 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1693 if ((*i)->frame() > frame) {
1703 abort(); /*NOTREACHED*/
1710 TempoMap::meter_at (framepos_t frame) const
1712 TempoMetric m (metric_at (frame));
1717 TempoMap::get_state ()
1719 Metrics::const_iterator i;
1720 XMLNode *root = new XMLNode ("TempoMap");
1723 Glib::Threads::RWLock::ReaderLock lm (lock);
1724 for (i = metrics.begin(); i != metrics.end(); ++i) {
1725 root->add_child_nocopy ((*i)->get_state());
1733 TempoMap::set_state (const XMLNode& node, int /*version*/)
1736 Glib::Threads::RWLock::WriterLock lm (lock);
1739 XMLNodeConstIterator niter;
1740 Metrics old_metrics (metrics);
1741 MeterSection* last_meter = 0;
1744 nlist = node.children();
1746 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1747 XMLNode* child = *niter;
1749 if (child->name() == TempoSection::xml_state_node_name) {
1752 TempoSection* ts = new TempoSection (*child);
1753 metrics.push_back (ts);
1755 if (ts->bar_offset() < 0.0) {
1757 //ts->update_bar_offset_from_bbt (*last_meter);
1762 catch (failed_constructor& err){
1763 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1764 metrics = old_metrics;
1768 } else if (child->name() == MeterSection::xml_state_node_name) {
1771 MeterSection* ms = new MeterSection (*child);
1772 metrics.push_back (ms);
1776 catch (failed_constructor& err) {
1777 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1778 metrics = old_metrics;
1784 if (niter == nlist.end()) {
1785 MetricSectionSorter cmp;
1788 /* check for legacy sessions where bbt was the base musical unit for tempo */
1789 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1790 MeterSection* prev_ms;
1791 TempoSection* prev_ts;
1792 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1793 if (prev_ms->start() < 0.0) {
1794 /*XX we cannot possibly make this work??. */
1795 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());
1796 prev_ms->set_start (start);
1798 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1799 if (prev_ts->start() < 0.0) {
1800 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);
1801 prev_ts->set_start (start);
1806 /* check for multiple tempo/meters at the same location, which
1807 ardour2 somehow allowed.
1810 Metrics::iterator prev = metrics.end();
1811 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1812 if (prev != metrics.end()) {
1814 MeterSection* prev_ms;
1816 TempoSection* prev_ts;
1817 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1818 if (prev_ms->start() == ms->start()) {
1819 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
1820 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
1823 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1824 if (prev_ts->start() == ts->start()) {
1825 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
1826 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
1834 recompute_map (true, -1);
1837 PropertyChanged (PropertyChange ());
1843 TempoMap::dump (std::ostream& o) const
1845 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1846 const MeterSection* m;
1847 const TempoSection* t;
1849 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1851 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1852 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? "
1853 << t->movable() << ')' << endl;
1854 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1855 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
1856 << " (movable? " << m->movable() << ')' << endl;
1862 TempoMap::n_tempos() const
1864 Glib::Threads::RWLock::ReaderLock lm (lock);
1867 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1868 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1877 TempoMap::n_meters() const
1879 Glib::Threads::RWLock::ReaderLock lm (lock);
1882 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1883 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1892 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1895 Glib::Threads::RWLock::WriterLock lm (lock);
1896 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1897 if ((*i)->frame() >= where && (*i)->movable ()) {
1898 (*i)->set_frame ((*i)->frame() + amount);
1902 /* now reset the BBT time of all metrics, based on their new
1903 * audio time. This is the only place where we do this reverse
1907 Metrics::iterator i;
1908 const MeterSection* meter;
1909 const TempoSection* tempo;
1913 meter = &first_meter ();
1914 tempo = &first_tempo ();
1919 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1922 MetricSection* prev = 0;
1924 for (i = metrics.begin(); i != metrics.end(); ++i) {
1927 //TempoMetric metric (*meter, *tempo);
1928 MeterSection* ms = const_cast<MeterSection*>(meter);
1929 TempoSection* ts = const_cast<TempoSection*>(tempo);
1932 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1933 ts->set_start (t->start());
1935 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
1936 ts->set_start (m->start());
1938 ts->set_frame (prev->frame());
1942 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
1943 pair<double, BBT_Time> start = make_pair (m->start(), m->bbt());
1944 ms->set_start (start);
1946 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1947 pair<double, BBT_Time> start = make_pair (t->start(), beats_to_bbt (t->start()));
1948 ms->set_start (start);
1950 ms->set_frame (prev->frame());
1954 // metric will be at frames=0 bbt=1|1|0 by default
1955 // which is correct for our purpose
1958 // cerr << bbt << endl;
1960 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1961 t->set_start (beat_at_frame (m->frame()));
1963 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1964 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1965 bbt_time (m->frame(), bbt);
1967 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1973 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1974 /* round up to next beat */
1980 if (bbt.beats != 1) {
1981 /* round up to next bar */
1986 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
1987 m->set_start (start);
1989 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1991 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1992 abort(); /*NOTREACHED*/
1998 recompute_map (true);
2002 PropertyChanged (PropertyChange ());
2005 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2009 std::list<MetricSection*> metric_kill_list;
2011 TempoSection* last_tempo = NULL;
2012 MeterSection* last_meter = NULL;
2013 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2014 bool meter_after = false; // is there a meter marker likewise?
2016 Glib::Threads::RWLock::WriterLock lm (lock);
2017 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2018 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2019 metric_kill_list.push_back(*i);
2020 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2023 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2027 else if ((*i)->frame() >= where) {
2028 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2029 (*i)->set_frame ((*i)->frame() - amount);
2030 if ((*i)->frame() == where) {
2031 // marker was immediately after end of range
2032 tempo_after = dynamic_cast<TempoSection*> (*i);
2033 meter_after = dynamic_cast<MeterSection*> (*i);
2039 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2040 if (last_tempo && !tempo_after) {
2041 metric_kill_list.remove(last_tempo);
2042 last_tempo->set_frame(where);
2045 if (last_meter && !meter_after) {
2046 metric_kill_list.remove(last_meter);
2047 last_meter->set_frame(where);
2051 //remove all the remaining metrics
2052 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2058 recompute_map (true);
2061 PropertyChanged (PropertyChange ());
2065 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2066 * pos can be -ve, if required.
2069 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2071 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2074 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2076 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2078 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2081 /** Add the BBT interval op to pos and return the result */
2083 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2085 cerr << "framepos_plus_bbt - untested" << endl;
2086 Glib::Threads::RWLock::ReaderLock lm (lock);
2088 Metrics::const_iterator i;
2089 const MeterSection* meter;
2090 const MeterSection* m;
2091 const TempoSection* tempo;
2092 const TempoSection* next_tempo = 0;
2093 const TempoSection* t;
2094 double frames_per_beat;
2095 framepos_t effective_pos = max (pos, (framepos_t) 0);
2097 meter = &first_meter ();
2098 tempo = &first_tempo ();
2103 /* find the starting metrics for tempo & meter */
2105 for (i = metrics.begin(); i != metrics.end(); ++i) {
2107 if ((*i)->frame() > effective_pos) {
2111 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2113 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2118 for (i = metrics.begin(); i != metrics.end(); ++i) {
2119 if ((*i)->frame() > effective_pos) {
2120 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2129 meter -> the Meter for "pos"
2130 tempo -> the Tempo for "pos"
2131 next_tempo -> the Tempo after "pos" or 0
2132 i -> for first new metric after "pos", possibly metrics.end()
2135 /* now comes the complicated part. we have to add one beat a time,
2136 checking for a new metric on every beat.
2140 /* fpb is used for constant tempo */
2141 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2148 /* check if we need to use a new metric section: has adding frames moved us
2149 to or after the start of the next metric section? in which case, use it.
2152 if (i != metrics.end()) {
2153 if ((*i)->frame() <= pos) {
2155 /* about to change tempo or meter, so add the
2156 * number of frames for the bars we've just
2157 * traversed before we change the
2158 * frames_per_beat value.
2161 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2166 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2168 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2173 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2175 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2179 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2186 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2188 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2195 /* given the current meter, have we gone past the end of the bar ? */
2200 /* check if we need to use a new metric section: has adding frames moved us
2201 to or after the start of the next metric section? in which case, use it.
2204 if (i != metrics.end()) {
2205 if ((*i)->frame() <= pos) {
2207 /* about to change tempo or meter, so add the
2208 * number of frames for the beats we've just
2209 * traversed before we change the
2210 * frames_per_beat value.
2213 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2218 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2220 pos += llrint (beats * frames_per_beat);
2225 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2227 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2231 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2237 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2239 pos += llrint (beats * frames_per_beat);
2243 pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2250 /** Count the number of beats that are equivalent to distance when going forward,
2254 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2256 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2260 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2266 operator<< (std::ostream& o, const Meter& m) {
2267 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2271 operator<< (std::ostream& o, const Tempo& t) {
2272 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2276 operator<< (std::ostream& o, const MetricSection& section) {
2278 o << "MetricSection @ " << section.frame() << ' ';
2280 const TempoSection* ts;
2281 const MeterSection* ms;
2283 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2284 o << *((const Tempo*) ts);
2285 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2286 //o << *((const Meter*) ms);