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>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
72 /***********************************************************************/
74 const string TempoSection::xml_state_node_name = "Tempo";
76 TempoSection::TempoSection (const XMLNode& node)
77 : MetricSection (0.0), Tempo (TempoMap::default_tempo())
79 const XMLProperty *prop;
84 if ((prop = node.property ("start")) != 0) {
85 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
89 /* legacy session - start used to be in bbt*/
95 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
99 if ((prop = node.property ("beat")) != 0) {
100 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
101 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
106 error << _("TempoSection XML node has no \"beat\" property") << endmsg;
110 if ((prop = node.property ("beats-per-minute")) == 0) {
111 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
112 throw failed_constructor();
115 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
116 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
117 throw failed_constructor();
120 if ((prop = node.property ("note-type")) == 0) {
121 /* older session, make note type be quarter by default */
124 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
125 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
126 throw failed_constructor();
130 if ((prop = node.property ("movable")) == 0) {
131 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
132 throw failed_constructor();
135 set_movable (string_is_affirmative (prop->value()));
137 if ((prop = node.property ("bar-offset")) == 0) {
140 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
141 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
142 throw failed_constructor();
146 if ((prop = node.property ("tempo-type")) == 0) {
149 _type = Type (string_2_enum (prop->value(), _type));
154 TempoSection::get_state() const
156 XMLNode *root = new XMLNode (xml_state_node_name);
160 snprintf (buf, sizeof (buf), "%f", beat());
161 root->add_property ("beat", buf);
162 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
163 root->add_property ("beats-per-minute", buf);
164 snprintf (buf, sizeof (buf), "%f", _note_type);
165 root->add_property ("note-type", buf);
166 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
167 // root->add_property ("bar-offset", buf);
168 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
169 root->add_property ("movable", buf);
170 root->add_property ("tempo-type", enum_2_string (_type));
177 TempoSection::update_bar_offset_from_bbt (const Meter& m)
179 _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
180 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
182 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
186 TempoSection::set_type (Type type)
191 /** returns the tempo at the zero-based (relative to tempo section) frame.
194 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
197 if (_type == Constant) {
198 return beats_per_minute();
201 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;
204 /** returns the zero-based frame (relative to tempo section)
205 where the tempo occurs.
208 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
210 if (_type == Constant) {
214 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);
217 /** returns the zero-based tick (relative to tempo section)
218 where the zero-based frame (relative to tempo section)
222 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
224 if (_type == Constant) {
225 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
228 return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
231 /** returns the zero-based frame (relative to tempo section)
232 where the zero-based tick (relative to tempo section)
236 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
238 if (_type == Constant) {
239 return (framepos_t) floor ((tick / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
242 return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
245 /** returns the zero-based beat (relative to tempo section)
246 where the zero-based frame (relative to tempo section)
250 TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
252 return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
255 /** returns the zero-based frame (relative to tempo section start frame)
256 where the zero-based beat (relative to tempo section start)
261 TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
263 return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
267 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
269 return time * 60.0 * frame_rate;
273 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
275 return (frame / (double) frame_rate) / 60.0;
278 /* position function */
280 TempoSection::a_func (double end_tpm, double c_func) const
282 return log (end_tpm / ticks_per_minute()) / c_func;
285 /*function constant*/
287 TempoSection::c_func (double end_tpm, double end_time) const
289 return log (end_tpm / ticks_per_minute()) / end_time;
292 /* tempo in tpm at time in minutes */
294 TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
296 return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
299 /* time in minutes at tempo in tpm */
301 TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
303 return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
306 /* tick at time in minutes */
308 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
310 return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
313 /* time in minutes at tick */
315 TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
317 return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
320 /* beat at time in minutes */
322 TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
324 return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
327 /* time in munutes at beat */
329 TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
331 return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
335 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
339 if (_bar_offset < 0.0) {
346 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
347 new_beat = ticks / BBT_Time::ticks_per_beat;
349 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
350 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
355 /***********************************************************************/
357 const string MeterSection::xml_state_node_name = "Meter";
359 MeterSection::MeterSection (const XMLNode& node)
360 : MetricSection (0.0), Meter (TempoMap::default_meter())
362 XMLProperty const * prop;
365 const XMLProperty *prop;
368 pair<double, BBT_Time> start;
370 if ((prop = node.property ("start")) != 0) {
371 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
375 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
377 /* legacy session - start used to be in bbt*/
381 error << _("MeterSection XML node has no \"start\" property") << endmsg;
384 if ((prop = node.property ("beat")) != 0) {
385 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
386 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
389 error << _("MeterSection XML node has no \"beat\" property") << endmsg;
394 if ((prop = node.property ("bbt")) == 0) {
395 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
396 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
400 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
401 throw failed_constructor();
408 /* beats-per-bar is old; divisions-per-bar is new */
410 if ((prop = node.property ("divisions-per-bar")) == 0) {
411 if ((prop = node.property ("beats-per-bar")) == 0) {
412 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
413 throw failed_constructor();
417 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
418 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
419 throw failed_constructor();
422 if ((prop = node.property ("note-type")) == 0) {
423 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
424 throw failed_constructor();
427 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
428 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
429 throw failed_constructor();
432 if ((prop = node.property ("movable")) == 0) {
433 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
434 throw failed_constructor();
437 set_movable (string_is_affirmative (prop->value()));
441 MeterSection::get_state() const
443 XMLNode *root = new XMLNode (xml_state_node_name);
447 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
451 root->add_property ("bbt", buf);
452 snprintf (buf, sizeof (buf), "%lf", beat());
453 root->add_property ("beat", buf);
454 snprintf (buf, sizeof (buf), "%f", _note_type);
455 root->add_property ("note-type", buf);
456 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
457 root->add_property ("divisions-per-bar", buf);
458 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
459 root->add_property ("movable", buf);
464 /***********************************************************************/
466 struct MetricSectionSorter {
467 bool operator() (const MetricSection* a, const MetricSection* b) {
468 return a->beat() < b->beat();
472 struct MetricSectionFrameSorter {
473 bool operator() (const MetricSection* a, const MetricSection* b) {
474 return a->frame() < b->frame();
478 TempoMap::TempoMap (framecnt_t fr)
487 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
488 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
490 t->set_movable (false);
491 m->set_movable (false);
493 /* note: frame time is correct (zero) for both of these */
495 metrics.push_back (t);
496 metrics.push_back (m);
500 TempoMap::~TempoMap ()
505 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
507 bool removed = false;
510 Glib::Threads::RWLock::WriterLock lm (lock);
511 if ((removed = remove_tempo_locked (tempo))) {
512 if (complete_operation) {
513 recompute_map (true);
518 if (removed && complete_operation) {
519 PropertyChanged (PropertyChange ());
524 TempoMap::remove_tempo_locked (const TempoSection& tempo)
528 for (i = metrics.begin(); i != metrics.end(); ++i) {
529 if (dynamic_cast<TempoSection*> (*i) != 0) {
530 if (tempo.frame() == (*i)->frame()) {
531 if ((*i)->movable()) {
543 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
545 bool removed = false;
548 Glib::Threads::RWLock::WriterLock lm (lock);
549 if ((removed = remove_meter_locked (tempo))) {
550 if (complete_operation) {
551 recompute_map (true);
556 if (removed && complete_operation) {
557 PropertyChanged (PropertyChange ());
562 TempoMap::remove_meter_locked (const MeterSection& tempo)
566 for (i = metrics.begin(); i != metrics.end(); ++i) {
567 if (dynamic_cast<MeterSection*> (*i) != 0) {
568 if (tempo.frame() == (*i)->frame()) {
569 if ((*i)->movable()) {
581 TempoMap::do_insert (MetricSection* section)
583 bool need_add = true;
585 /* we only allow new meters to be inserted on beat 1 of an existing
589 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
590 assert (m->bbt().ticks == 0);
592 /* we need to (potentially) update the BBT times of tempo
593 sections based on this new meter.
596 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
598 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
599 corrected.second.beats = 1;
600 corrected.second.ticks = 0;
601 corrected.first = bbt_to_beats_locked (corrected.second);
602 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
603 m->bbt(), corrected.second) << endmsg;
604 m->set_beat (corrected);
610 /* Look for any existing MetricSection that is of the same type and
611 in the same bar as the new one, and remove it before adding
612 the new one. Note that this means that if we find a matching,
613 existing section, we can break out of the loop since we're
614 guaranteed that there is only one such match.
617 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
619 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
620 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
622 if (tempo && insert_tempo) {
626 if (tempo->beat() == insert_tempo->beat()) {
628 if (!tempo->movable()) {
630 /* can't (re)move this section, so overwrite
631 * its data content (but not its properties as
635 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
643 } else if (!tempo && !insert_tempo) {
646 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
647 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
648 if (meter->beat() == insert_meter->beat()) {
650 if (!meter->movable()) {
652 /* can't (re)move this section, so overwrite
653 * its data content (but not its properties as
657 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
667 /* non-matching types, so we don't care */
671 /* Add the given MetricSection, if we didn't just reset an existing
676 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
677 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
681 for (i = metrics.begin(); i != metrics.end(); ++i) {
682 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
684 if (meter && meter->beat() > insert_meter->beat()) {
688 } else if (insert_tempo) {
689 for (i = metrics.begin(); i != metrics.end(); ++i) {
690 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
693 if (tempo->beat() > insert_tempo->beat()) {
700 metrics.insert (i, section);
705 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
708 Glib::Threads::RWLock::WriterLock lm (lock);
709 TempoSection& first (first_tempo());
711 if (ts.beat() != first.beat()) {
712 remove_tempo_locked (ts);
713 add_tempo_locked (tempo, where, true, type);
715 first.set_type (type);
717 /* cannot move the first tempo section */
718 *static_cast<Tempo*>(&first) = tempo;
719 recompute_map (false);
724 PropertyChanged (PropertyChange ());
728 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
731 Glib::Threads::RWLock::WriterLock lm (lock);
733 /* currently this is always done in audio time */
734 //if (ts.position_lock_style() == MusicTime) {
737 ts.set_beat (beat_where);
738 MetricSectionSorter cmp;
742 ts.set_frame (frame);
744 MetricSectionFrameSorter fcmp;
747 Metrics::const_iterator i;
748 TempoSection* prev_ts = 0;
749 TempoSection* next_ts = 0;
751 for (i = metrics.begin(); i != metrics.end(); ++i) {
753 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
755 if (t->frame() >= frame) {
763 for (i = metrics.begin(); i != metrics.end(); ++i) {
765 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
767 if (t->frame() > frame) {
775 /* set the start beat */
776 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
777 double beats = beats_to_ts + prev_ts->beat();
780 if (next_ts->beat() < beats) {
781 /* with frame-based editing, it is possible to get in a
782 situation where if the tempo was placed at the mouse pointer frame,
783 the following music-based tempo would jump to an earlier frame,
784 changing the beat beat of the moved tempo.
785 in this case, we have to do some beat-based comparison TODO
787 } else if (prev_ts->beat() > beats) {
788 ts.set_beat (prev_ts->beat());
795 MetricSectionSorter cmp;
800 recompute_map (false);
803 MetricPositionChanged (); // Emit Signal
807 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
810 Glib::Threads::RWLock::WriterLock lm (lock);
811 add_tempo_locked (tempo, where, true, type);
815 PropertyChanged (PropertyChange ());
819 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
821 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
826 recompute_map (false);
831 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
834 Glib::Threads::RWLock::WriterLock lm (lock);
835 MeterSection& first (first_meter());
836 if (ms.beat() != first.beat()) {
837 remove_meter_locked (ms);
838 add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
840 /* cannot move the first meter section */
841 *static_cast<Meter*>(&first) = meter;
842 recompute_map (true);
846 PropertyChanged (PropertyChange ());
850 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
853 Glib::Threads::RWLock::WriterLock lm (lock);
854 add_meter_locked (meter, beat, where, true);
859 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
864 PropertyChanged (PropertyChange ());
868 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
870 /* a new meter always starts a new bar on the first beat. so
871 round the start time appropriately. remember that
872 `where' is based on the existing tempo map, not
873 the result after we insert the new meter.
877 if (where.beats != 1) {
882 /* new meters *always* start on a beat. */
885 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
888 recompute_map (true);
894 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
896 Tempo newtempo (beats_per_minute, note_type);
899 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
900 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
902 Glib::Threads::RWLock::WriterLock lm (lock);
903 *((Tempo*) t) = newtempo;
904 recompute_map (false);
906 PropertyChanged (PropertyChange ());
913 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
915 Tempo newtempo (beats_per_minute, note_type);
921 /* find the TempoSection immediately preceding "where"
924 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
926 if ((*i)->frame() > where) {
932 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
942 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
952 Glib::Threads::RWLock::WriterLock lm (lock);
953 /* cannot move the first tempo section */
954 *((Tempo*)prev) = newtempo;
955 recompute_map (false);
958 PropertyChanged (PropertyChange ());
962 TempoMap::first_meter () const
964 const MeterSection *m = 0;
966 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
967 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
972 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
973 abort(); /*NOTREACHED*/
978 TempoMap::first_meter ()
982 /* CALLER MUST HOLD LOCK */
984 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
985 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
990 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
991 abort(); /*NOTREACHED*/
996 TempoMap::first_tempo () const
998 const TempoSection *t = 0;
1000 /* CALLER MUST HOLD LOCK */
1002 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1003 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1008 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1009 abort(); /*NOTREACHED*/
1014 TempoMap::first_tempo ()
1016 TempoSection *t = 0;
1018 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1019 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1024 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1025 abort(); /*NOTREACHED*/
1030 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1032 /* CALLER MUST HOLD WRITE LOCK */
1036 /* we will actually stop once we hit
1043 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1046 /* silly call from Session::process() during startup
1051 Metrics::const_iterator i;
1053 TempoSection* prev_ts = 0;
1055 for (i = metrics.begin(); i != metrics.end(); ++i) {
1058 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1061 double const beats_relative_to_prev_ts = t->beat() - prev_ts->beat();
1062 double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
1064 /* assume (falsely) that the target tempo is constant */
1065 double const t_fpb = t->frames_per_beat (_frame_rate);
1066 double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
1067 /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
1068 double length_estimate = beats_relative_to_prev_ts * av_fpb;
1070 if (prev_ts->type() == TempoSection::Constant) {
1071 length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
1074 double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
1075 double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
1077 while (fabs (tick_error) > system_precision_at_target_tempo) {
1079 double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
1080 (framepos_t) length_estimate, _frame_rate);
1081 tick_error = ticks_relative_to_prev_ts - actual_ticks;
1082 length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
1085 t->set_frame (length_estimate + prev_ts->frame());
1091 Metrics::const_iterator mi;
1092 MeterSection* meter = 0;
1094 for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1095 /* we can do this beacuse we have the tempo section frames set */
1096 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1097 meter->set_frame (frame_at_tick (meter->beat() * BBT_Time::ticks_per_beat));
1104 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1106 Glib::Threads::RWLock::ReaderLock lm (lock);
1107 TempoMetric m (first_meter(), first_tempo());
1109 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1110 at something, because we insert the default tempo and meter during
1111 TempoMap construction.
1113 now see if we can find better candidates.
1116 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1118 if ((*i)->frame() > frame) {
1131 /* XX meters only */
1133 TempoMap::metric_at (BBT_Time bbt) const
1135 Glib::Threads::RWLock::ReaderLock lm (lock);
1136 TempoMetric m (first_meter(), first_tempo());
1138 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1139 at something, because we insert the default tempo and meter during
1140 TempoMap construction.
1142 now see if we can find better candidates.
1145 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1147 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1148 BBT_Time section_start (mw->bbt());
1150 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1162 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1164 Glib::Threads::RWLock::ReaderLock lm (lock);
1170 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1173 bbt = beats_to_bbt_locked (beat_at_frame (frame));
1177 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1179 Glib::Threads::RWLock::ReaderLock lm (lock);
1180 return bbt_to_beats_locked (bbt);
1184 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1186 /* CALLER HOLDS READ LOCK */
1188 double accumulated_beats = 0.0;
1189 double accumulated_bars = 0.0;
1190 MeterSection* prev_ms = 0;
1192 Metrics::const_iterator i;
1194 for (i = metrics.begin(); i != metrics.end(); ++i) {
1196 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1197 double bars_to_m = 0.0;
1199 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1201 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1205 accumulated_beats += m->beat() - prev_ms->beat();
1206 accumulated_bars += bars_to_m;
1212 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1213 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1214 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1219 TempoMap::beats_to_bbt (double beats)
1221 Glib::Threads::RWLock::ReaderLock lm (lock);
1222 return beats_to_bbt_locked (beats);
1226 TempoMap::beats_to_bbt_locked (double beats)
1228 /* CALLER HOLDS READ LOCK */
1230 MeterSection* prev_ms = 0;
1231 uint32_t accumulated_bars = 0;
1233 Metrics::const_iterator i;
1235 for (i = metrics.begin(); i != metrics.end(); ++i) {
1236 MeterSection* m = 0;
1238 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1240 if (beats < m->beat()) {
1241 /* this is the meter after the one our beat is on*/
1246 /* we need a whole number of bars. */
1247 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1254 double const beats_in_ms = beats - prev_ms->beat();
1255 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1256 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1257 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1258 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1262 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1263 ret.beats = (uint32_t) floor (remaining_beats);
1264 ret.bars = total_bars;
1266 /* 0 0 0 to 1 1 0 - based mapping*/
1270 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1272 ret.ticks -= BBT_Time::ticks_per_beat;
1275 if (ret.beats > prev_ms->divisions_per_bar()) {
1284 TempoMap::tick_at_frame (framecnt_t frame) const
1286 /* HOLD (at least) THE READER LOCK */
1288 Metrics::const_iterator i;
1289 TempoSection* prev_ts = 0;
1290 double accumulated_ticks = 0.0;
1292 for (i = metrics.begin(); i != metrics.end(); ++i) {
1295 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1297 if ((prev_ts) && frame < t->frame()) {
1298 /*the previous ts is the one containing the frame */
1300 framepos_t const time = frame - prev_ts->frame();
1301 framepos_t const last_frame = t->frame() - prev_ts->frame();
1302 double const last_beats_per_minute = t->beats_per_minute();
1304 return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
1307 if (prev_ts && t->frame() > prev_ts->frame()) {
1308 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1315 /* treated as constant for this ts */
1316 framecnt_t const frames_in_section = frame - prev_ts->frame();
1317 double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1319 return ticks_in_section + accumulated_ticks;
1324 TempoMap::frame_at_tick (double tick) const
1326 /* HOLD THE READER LOCK */
1328 double accumulated_ticks = 0.0;
1329 double accumulated_ticks_to_prev = 0.0;
1330 const TempoSection* prev_ts = 0;
1332 Metrics::const_iterator i;
1334 for (i = metrics.begin(); i != metrics.end(); ++i) {
1336 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1338 if (prev_ts && t->frame() > prev_ts->frame()) {
1339 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1342 if (prev_ts && tick < accumulated_ticks) {
1343 /* prev_ts is the one affecting us. */
1345 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1346 framepos_t const last_time = t->frame() - prev_ts->frame();
1347 double const last_beats_per_minute = t->beats_per_minute();
1349 return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + prev_ts->frame();
1351 accumulated_ticks_to_prev = accumulated_ticks;
1355 /* must be treated as constant, irrespective of _type */
1356 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1357 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1359 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1365 TempoMap::beat_at_frame (framecnt_t frame) const
1367 Glib::Threads::RWLock::ReaderLock lm (lock);
1369 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1373 TempoMap::frame_at_beat (double beat) const
1375 Glib::Threads::RWLock::ReaderLock lm (lock);
1377 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1381 TempoMap::frame_time (const BBT_Time& bbt)
1384 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1388 if (bbt.beats < 1) {
1389 throw std::logic_error ("beats are counted from one");
1391 Glib::Threads::RWLock::ReaderLock lm (lock);
1393 framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
1400 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1403 Glib::Threads::RWLock::ReaderLock lm (lock);
1405 Metrics::const_iterator i;
1406 TempoSection* first = 0;
1407 TempoSection* second = 0;
1409 for (i = metrics.begin(); i != metrics.end(); ++i) {
1412 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1414 if ((*i)->frame() > pos) {
1422 if (first && second) {
1423 framepos_t const last_time = second->frame() - first->frame();
1424 double const last_beats_per_minute = second->beats_per_minute();
1426 framepos_t const time = pos - first->frame();
1427 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1428 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1430 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1432 return time_at_bbt - time;
1435 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1436 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1440 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1442 return round_to_type (fr, dir, Bar);
1446 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1448 return round_to_type (fr, dir, Beat);
1452 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1454 Glib::Threads::RWLock::ReaderLock lm (lock);
1456 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1457 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1458 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1460 ticks -= beats * BBT_Time::ticks_per_beat;
1463 /* round to next (or same iff dir == RoundUpMaybe) */
1465 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1467 if (mod == 0 && dir == RoundUpMaybe) {
1468 /* right on the subdivision, which is fine, so do nothing */
1470 } else if (mod == 0) {
1471 /* right on the subdivision, so the difference is just the subdivision ticks */
1472 ticks += ticks_one_subdivisions_worth;
1475 /* not on subdivision, compute distance to next subdivision */
1477 ticks += ticks_one_subdivisions_worth - mod;
1480 if (ticks >= BBT_Time::ticks_per_beat) {
1481 ticks -= BBT_Time::ticks_per_beat;
1483 } else if (dir < 0) {
1485 /* round to previous (or same iff dir == RoundDownMaybe) */
1487 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1489 if (difference == 0 && dir == RoundDownAlways) {
1490 /* right on the subdivision, but force-rounding down,
1491 so the difference is just the subdivision ticks */
1492 difference = ticks_one_subdivisions_worth;
1495 if (ticks < difference) {
1496 ticks = BBT_Time::ticks_per_beat - ticks;
1498 ticks -= difference;
1502 /* round to nearest */
1505 /* compute the distance to the previous and next subdivision */
1507 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1509 /* closer to the next subdivision, so shift forward */
1511 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1513 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1515 if (ticks > BBT_Time::ticks_per_beat) {
1517 ticks -= BBT_Time::ticks_per_beat;
1518 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1521 } else if (rem > 0) {
1523 /* closer to previous subdivision, so shift backward */
1527 /* can't go backwards past zero, so ... */
1530 /* step back to previous beat */
1532 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1533 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1535 ticks = lrint (ticks - rem);
1536 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1539 /* on the subdivision, do nothing */
1542 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1546 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1548 Glib::Threads::RWLock::ReaderLock lm (lock);
1550 double const beat_at_framepos = beat_at_frame (frame);
1552 BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1557 /* find bar previous to 'frame' */
1560 return frame_time (bbt);
1562 } else if (dir > 0) {
1563 /* find bar following 'frame' */
1567 return frame_time (bbt);
1569 /* true rounding: find nearest bar */
1570 framepos_t raw_ft = frame_time (bbt);
1573 framepos_t prev_ft = frame_time (bbt);
1575 framepos_t next_ft = frame_time (bbt);
1577 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1588 return frame_at_beat (floor (beat_at_framepos));
1589 } else if (dir > 0) {
1590 return frame_at_beat (ceil (beat_at_framepos));
1592 return frame_at_beat (floor (beat_at_framepos + 0.5));
1601 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1602 framepos_t lower, framepos_t upper)
1604 Glib::Threads::RWLock::ReaderLock lm (lock);
1605 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1606 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1608 while (cnt <= upper_beat) {
1609 framecnt_t const pos = frame_at_beat (cnt);
1610 MeterSection const meter = meter_section_at (pos);
1611 Tempo const tempo = tempo_at (pos);
1612 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
1614 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1620 TempoMap::tempo_section_at (framepos_t frame) const
1622 Glib::Threads::RWLock::ReaderLock lm (lock);
1624 Metrics::const_iterator i;
1625 TempoSection* prev = 0;
1627 for (i = metrics.begin(); i != metrics.end(); ++i) {
1630 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1632 if ((*i)->frame() > frame) {
1642 abort(); /*NOTREACHED*/
1648 /* don't use this to calculate length (the tempo is only correct for this frame).
1649 do that stuff based on the beat_at_frame and frame_at_beat api
1652 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1654 Glib::Threads::RWLock::ReaderLock lm (lock);
1656 const TempoSection* ts_at = &tempo_section_at (frame);
1657 const TempoSection* ts_after = 0;
1658 Metrics::const_iterator i;
1660 for (i = metrics.begin(); i != metrics.end(); ++i) {
1663 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1665 if ((*i)->frame() > frame) {
1673 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
1675 /* must be treated as constant tempo */
1676 return ts_at->frames_per_beat (_frame_rate);
1680 TempoMap::tempo_at (framepos_t frame) const
1682 Glib::Threads::RWLock::ReaderLock lm (lock);
1684 TempoMetric m (metric_at (frame));
1685 TempoSection* prev_ts = 0;
1687 Metrics::const_iterator i;
1689 for (i = metrics.begin(); i != metrics.end(); ++i) {
1691 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1692 if ((prev_ts) && t->frame() > frame) {
1693 /* this is the one past frame */
1694 framepos_t const time = frame - prev_ts->frame();
1695 framepos_t const last_time = t->frame() - prev_ts->frame();
1696 double const last_beats_per_minute = t->beats_per_minute();
1697 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1698 Tempo const ret_tempo (ret, m.tempo().note_type ());
1710 TempoMap::meter_section_at (framepos_t frame) const
1712 Glib::Threads::RWLock::ReaderLock lm (lock);
1713 Metrics::const_iterator i;
1714 MeterSection* prev = 0;
1716 for (i = metrics.begin(); i != metrics.end(); ++i) {
1719 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1721 if ((*i)->frame() > frame) {
1731 abort(); /*NOTREACHED*/
1738 TempoMap::meter_at (framepos_t frame) const
1740 TempoMetric m (metric_at (frame));
1745 TempoMap::get_state ()
1747 Metrics::const_iterator i;
1748 XMLNode *root = new XMLNode ("TempoMap");
1751 Glib::Threads::RWLock::ReaderLock lm (lock);
1752 for (i = metrics.begin(); i != metrics.end(); ++i) {
1753 root->add_child_nocopy ((*i)->get_state());
1761 TempoMap::set_state (const XMLNode& node, int /*version*/)
1764 Glib::Threads::RWLock::WriterLock lm (lock);
1767 XMLNodeConstIterator niter;
1768 Metrics old_metrics (metrics);
1769 MeterSection* last_meter = 0;
1772 nlist = node.children();
1774 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1775 XMLNode* child = *niter;
1777 if (child->name() == TempoSection::xml_state_node_name) {
1780 TempoSection* ts = new TempoSection (*child);
1781 metrics.push_back (ts);
1783 if (ts->bar_offset() < 0.0) {
1785 //ts->update_bar_offset_from_bbt (*last_meter);
1790 catch (failed_constructor& err){
1791 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1792 metrics = old_metrics;
1796 } else if (child->name() == MeterSection::xml_state_node_name) {
1799 MeterSection* ms = new MeterSection (*child);
1800 metrics.push_back (ms);
1804 catch (failed_constructor& err) {
1805 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1806 metrics = old_metrics;
1812 if (niter == nlist.end()) {
1813 MetricSectionSorter cmp;
1816 /* check for legacy sessions where bbt was the base musical unit for tempo */
1817 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1818 MeterSection* prev_ms;
1819 TempoSection* prev_ts;
1820 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1821 if (prev_ms->beat() < 0.0) {
1822 /*XX we cannot possibly make this work??. */
1823 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());
1824 prev_ms->set_beat (start);
1826 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1827 if (prev_ts->beat() < 0.0) {
1828 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);
1829 prev_ts->set_beat (start);
1834 /* check for multiple tempo/meters at the same location, which
1835 ardour2 somehow allowed.
1838 Metrics::iterator prev = metrics.end();
1839 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1840 if (prev != metrics.end()) {
1842 MeterSection* prev_ms;
1844 TempoSection* prev_ts;
1845 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1846 if (prev_ms->beat() == ms->beat()) {
1847 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
1848 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
1851 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1852 if (prev_ts->beat() == ts->beat()) {
1853 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
1854 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
1862 recompute_map (true, -1);
1865 PropertyChanged (PropertyChange ());
1871 TempoMap::dump (std::ostream& o) const
1873 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1874 const MeterSection* m;
1875 const TempoSection* t;
1877 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1879 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1880 o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->beat() << " frame= " << t->frame() << " (movable? "
1881 << t->movable() << ')' << endl;
1882 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1883 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
1884 << " (movable? " << m->movable() << ')' << endl;
1890 TempoMap::n_tempos() const
1892 Glib::Threads::RWLock::ReaderLock lm (lock);
1895 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1896 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1905 TempoMap::n_meters() const
1907 Glib::Threads::RWLock::ReaderLock lm (lock);
1910 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1911 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1920 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1923 Glib::Threads::RWLock::WriterLock lm (lock);
1924 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1925 if ((*i)->frame() >= where && (*i)->movable ()) {
1926 (*i)->set_frame ((*i)->frame() + amount);
1930 /* now reset the BBT time of all metrics, based on their new
1931 * audio time. This is the only place where we do this reverse
1935 Metrics::iterator i;
1936 const MeterSection* meter;
1937 const TempoSection* tempo;
1941 meter = &first_meter ();
1942 tempo = &first_tempo ();
1947 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1950 MetricSection* prev = 0;
1952 for (i = metrics.begin(); i != metrics.end(); ++i) {
1955 //TempoMetric metric (*meter, *tempo);
1956 MeterSection* ms = const_cast<MeterSection*>(meter);
1957 TempoSection* ts = const_cast<TempoSection*>(tempo);
1960 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1961 ts->set_beat (t->beat());
1963 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
1964 ts->set_beat (m->beat());
1966 ts->set_frame (prev->frame());
1970 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
1971 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
1972 ms->set_beat (start);
1974 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1975 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
1976 ms->set_beat (start);
1978 ms->set_frame (prev->frame());
1982 // metric will be at frames=0 bbt=1|1|0 by default
1983 // which is correct for our purpose
1986 // cerr << bbt << endl;
1988 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1989 t->set_beat (beat_at_frame (m->frame()));
1991 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
1992 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1993 bbt_time (m->frame(), bbt);
1995 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2001 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2002 /* round up to next beat */
2008 if (bbt.beats != 1) {
2009 /* round up to next bar */
2014 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
2015 m->set_beat (start);
2017 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2019 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2020 abort(); /*NOTREACHED*/
2026 recompute_map (true);
2030 PropertyChanged (PropertyChange ());
2033 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2037 std::list<MetricSection*> metric_kill_list;
2039 TempoSection* last_tempo = NULL;
2040 MeterSection* last_meter = NULL;
2041 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2042 bool meter_after = false; // is there a meter marker likewise?
2044 Glib::Threads::RWLock::WriterLock lm (lock);
2045 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2046 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2047 metric_kill_list.push_back(*i);
2048 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2051 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2055 else if ((*i)->frame() >= where) {
2056 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2057 (*i)->set_frame ((*i)->frame() - amount);
2058 if ((*i)->frame() == where) {
2059 // marker was immediately after end of range
2060 tempo_after = dynamic_cast<TempoSection*> (*i);
2061 meter_after = dynamic_cast<MeterSection*> (*i);
2067 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2068 if (last_tempo && !tempo_after) {
2069 metric_kill_list.remove(last_tempo);
2070 last_tempo->set_frame(where);
2073 if (last_meter && !meter_after) {
2074 metric_kill_list.remove(last_meter);
2075 last_meter->set_frame(where);
2079 //remove all the remaining metrics
2080 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2086 recompute_map (true);
2089 PropertyChanged (PropertyChange ());
2093 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2094 * pos can be -ve, if required.
2097 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2099 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2102 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2104 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2106 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2109 /** Add the BBT interval op to pos and return the result */
2111 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2113 cerr << "framepos_plus_bbt - untested" << endl;
2114 Glib::Threads::RWLock::ReaderLock lm (lock);
2116 Metrics::const_iterator i;
2117 const MeterSection* meter;
2118 const MeterSection* m;
2119 const TempoSection* tempo;
2120 const TempoSection* next_tempo = 0;
2121 const TempoSection* t;
2122 double frames_per_beat;
2123 framepos_t effective_pos = max (pos, (framepos_t) 0);
2125 meter = &first_meter ();
2126 tempo = &first_tempo ();
2131 /* find the starting metrics for tempo & meter */
2133 for (i = metrics.begin(); i != metrics.end(); ++i) {
2135 if ((*i)->frame() > effective_pos) {
2139 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2141 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2146 for (i = metrics.begin(); i != metrics.end(); ++i) {
2147 if ((*i)->frame() > effective_pos) {
2148 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2157 meter -> the Meter for "pos"
2158 tempo -> the Tempo for "pos"
2159 next_tempo -> the Tempo after "pos" or 0
2160 i -> for first new metric after "pos", possibly metrics.end()
2163 /* now comes the complicated part. we have to add one beat a time,
2164 checking for a new metric on every beat.
2168 /* fpb is used for constant tempo */
2169 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2176 /* check if we need to use a new metric section: has adding frames moved us
2177 to or after the start of the next metric section? in which case, use it.
2180 if (i != metrics.end()) {
2181 if ((*i)->frame() <= pos) {
2183 /* about to change tempo or meter, so add the
2184 * number of frames for the bars we've just
2185 * traversed before we change the
2186 * frames_per_beat value.
2189 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2194 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2196 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2201 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2203 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2207 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2214 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2216 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2223 /* given the current meter, have we gone past the end of the bar ? */
2228 /* check if we need to use a new metric section: has adding frames moved us
2229 to or after the start of the next metric section? in which case, use it.
2232 if (i != metrics.end()) {
2233 if ((*i)->frame() <= pos) {
2235 /* about to change tempo or meter, so add the
2236 * number of frames for the beats we've just
2237 * traversed before we change the
2238 * frames_per_beat value.
2241 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2246 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2248 pos += llrint (beats * frames_per_beat);
2253 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2255 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2259 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2265 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2267 pos += llrint (beats * frames_per_beat);
2271 pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2278 /** Count the number of beats that are equivalent to distance when going forward,
2282 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2284 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2288 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2294 operator<< (std::ostream& o, const Meter& m) {
2295 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2299 operator<< (std::ostream& o, const Tempo& t) {
2300 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2304 operator<< (std::ostream& o, const MetricSection& section) {
2306 o << "MetricSection @ " << section.frame() << ' ';
2308 const TempoSection* ts;
2309 const MeterSection* ms;
2311 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2312 o << *((const Tempo*) ts);
2313 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2314 //o << *((const Meter*) ms);