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;
85 if ((prop = node.property ("start")) != 0) {
86 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
90 /* legacy session - start used to be in bbt*/
96 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
100 if ((prop = node.property ("beat")) != 0) {
101 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
102 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
107 if ((prop = node.property ("frame")) != 0) {
108 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
109 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
115 if ((prop = node.property ("beats-per-minute")) == 0) {
116 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
117 throw failed_constructor();
120 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
121 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
122 throw failed_constructor();
125 if ((prop = node.property ("note-type")) == 0) {
126 /* older session, make note type be quarter by default */
129 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
130 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
131 throw failed_constructor();
135 if ((prop = node.property ("movable")) == 0) {
136 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
137 throw failed_constructor();
140 set_movable (string_is_affirmative (prop->value()));
142 if ((prop = node.property ("bar-offset")) == 0) {
145 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
146 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
147 throw failed_constructor();
151 if ((prop = node.property ("tempo-type")) == 0) {
154 _type = Type (string_2_enum (prop->value(), _type));
157 if ((prop = node.property ("lock-style")) == 0) {
158 set_position_lock_style (MusicTime);
160 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
165 TempoSection::get_state() const
167 XMLNode *root = new XMLNode (xml_state_node_name);
171 snprintf (buf, sizeof (buf), "%f", beat());
172 root->add_property ("beat", buf);
173 snprintf (buf, sizeof (buf), "%li", frame());
174 root->add_property ("frame", buf);
175 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
176 root->add_property ("beats-per-minute", buf);
177 snprintf (buf, sizeof (buf), "%f", _note_type);
178 root->add_property ("note-type", buf);
179 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
180 // root->add_property ("bar-offset", buf);
181 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
182 root->add_property ("movable", buf);
183 root->add_property ("tempo-type", enum_2_string (_type));
184 root->add_property ("lock-style", enum_2_string (position_lock_style()));
191 TempoSection::update_bar_offset_from_bbt (const Meter& m)
193 _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
194 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
196 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
200 TempoSection::set_type (Type type)
205 /** returns the tempo at the zero-based (relative to session) frame.
208 TempoSection::tempo_at_frame (framepos_t frm, framecnt_t frame_rate) const
211 if (_type == Constant) {
212 return beats_per_minute();
215 return tick_tempo_at_time (frame_to_minute (frm - frame(), frame_rate)) / BBT_Time::ticks_per_beat;
218 /** returns the zero-based frame (relative to session)
219 where the tempo occurs.
222 TempoSection::frame_at_tempo (double bpm, framecnt_t frame_rate) const
224 if (_type == Constant) {
228 return minute_to_frame (time_at_tick_tempo (bpm * BBT_Time::ticks_per_beat), frame_rate) + frame();
232 /** returns the zero-based tick (relative to session origin)
233 where the zero-based frame (relative to tempo section)
237 TempoSection::tick_at_frame (framepos_t frm, framecnt_t frame_rate) const
239 if (_type == Constant) {
240 return (((frm - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
243 return tick_at_time (frame_to_minute (frm - frame(), frame_rate)) + tick();
246 /** returns the zero-based frame (relative to session origin)
247 where the zero-based tick (relative to session)
251 TempoSection::frame_at_tick (double tck, framecnt_t frame_rate) const
253 if (_type == Constant) {
254 return (framepos_t) floor (((tck - tick()) / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate)) + frame();
257 return minute_to_frame (time_at_tick (tck - tick()), frame_rate) + frame();
260 /** returns the zero-based beat (relative to session origin)
261 where the zero-based frame (relative to session)
265 TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
267 return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
270 /** returns the zero-based frame (relative to session start frame)
271 where the zero-based beat (relative to session start)
276 TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
278 return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
286 Tt----|-----------------*|
287 Ta----|--------------|* |
293 _______________|___|____
294 time a t (next tempo)
297 Duration in beats at time a is the integral of some Tempo function.
298 In our case, the Tempo function (Tempo at time t) is
301 with function constant
306 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
307 b(t) = T0(e^(ct) - 1) / c
309 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
310 t(b) = log((cb / T0) + 1) / c
312 The time t at which Tempo T occurs is a as above:
313 t(T) = log(T / T0) / c
315 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
316 Our problem is that we usually don't know t.
317 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
318 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
319 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
321 By substituting our expanded t as a in the c function above, our problem is reduced to:
322 c = T0 (e^(log (Ta / T0)) - 1) / b
324 We can now store c for future time calculations.
325 If the following tempo section (the one that defines c in conjunction with this one)
326 is changed or moved, c is no longer valid.
328 The private methods' position parameters are all relative to this tempo section.
329 the public ones are session-relative
331 Most of this stuff is taken from this paper:
334 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
337 Zurich University of Arts
338 Institute for Computer Music and Sound Technology
340 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
344 /* set this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
346 TempoSection::set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
348 double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
349 _c_func = ticks_per_minute() * (exp (log_tempo_ratio) - 1) / ((end_beat - beat()) * BBT_Time::ticks_per_beat);
352 /* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from session origin */
354 TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
356 return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame - frame(), frame_rate));
360 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
362 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
366 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
368 return (frame / (double) frame_rate) / 60.0;
371 /* position function */
373 TempoSection::a_func (double end_tpm, double c_func) const
375 return log (end_tpm / ticks_per_minute()) / c_func;
378 /*function constant*/
380 TempoSection::c_func (double end_tpm, double end_time) const
382 return log (end_tpm / ticks_per_minute()) / end_time;
385 /* tempo in tpm at time in minutes */
387 TempoSection::tick_tempo_at_time (double time) const
389 return exp (_c_func * time) * ticks_per_minute();
392 /* time in minutes at tempo in tpm */
394 TempoSection::time_at_tick_tempo (double tick_tempo) const
396 return log (tick_tempo / ticks_per_minute()) / _c_func;
399 /* tick at time in minutes */
401 TempoSection::tick_at_time (double time) const
403 return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
406 /* time in minutes at tick */
408 TempoSection::time_at_tick (double tick) const
410 return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
413 /* beat at time in minutes */
415 TempoSection::beat_at_time (double time) const
417 return tick_at_time (time) / BBT_Time::ticks_per_beat;
420 /* time in munutes at beat */
422 TempoSection::time_at_beat (double beat) const
424 return time_at_tick (beat * BBT_Time::ticks_per_beat);
428 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
432 if (_bar_offset < 0.0) {
439 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
440 new_beat = ticks / BBT_Time::ticks_per_beat;
442 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
443 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
448 /***********************************************************************/
451 if a meter section is position locked to frames, then it can only be at 1|1|0.
452 thus we can have multiple 1|1|0s in the session tempo map.
456 BBT 1|1|0 2|1|0 3|1|0 1|1|0
459 all other meter sections are locked to beats.
461 the beat of a meter section is used to find its position rather than the stored bbt.
464 const string MeterSection::xml_state_node_name = "Meter";
466 MeterSection::MeterSection (const XMLNode& node)
467 : MetricSection (0.0), Meter (TempoMap::default_meter())
469 XMLProperty const * prop;
472 const XMLProperty *prop;
475 framepos_t frame = 0;
476 pair<double, BBT_Time> start;
478 if ((prop = node.property ("start")) != 0) {
479 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
483 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
485 /* legacy session - start used to be in bbt*/
489 error << _("MeterSection XML node has no \"start\" property") << endmsg;
492 if ((prop = node.property ("beat")) != 0) {
493 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
494 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
500 if ((prop = node.property ("bbt")) == 0) {
501 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
502 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
506 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
507 //throw failed_constructor();
514 if ((prop = node.property ("frame")) != 0) {
515 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
516 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
522 /* beats-per-bar is old; divisions-per-bar is new */
524 if ((prop = node.property ("divisions-per-bar")) == 0) {
525 if ((prop = node.property ("beats-per-bar")) == 0) {
526 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
527 throw failed_constructor();
530 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
531 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
532 throw failed_constructor();
535 if ((prop = node.property ("note-type")) == 0) {
536 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
537 throw failed_constructor();
539 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
540 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
541 throw failed_constructor();
544 if ((prop = node.property ("lock-style")) == 0) {
545 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
546 set_position_lock_style (PositionLockStyle::MusicTime);
548 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
551 if ((prop = node.property ("movable")) == 0) {
552 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
553 throw failed_constructor();
556 set_movable (string_is_affirmative (prop->value()));
560 MeterSection::get_state() const
562 XMLNode *root = new XMLNode (xml_state_node_name);
566 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
570 root->add_property ("bbt", buf);
571 snprintf (buf, sizeof (buf), "%lf", beat());
572 root->add_property ("beat", buf);
573 snprintf (buf, sizeof (buf), "%f", _note_type);
574 root->add_property ("note-type", buf);
575 snprintf (buf, sizeof (buf), "%li", frame());
576 root->add_property ("frame", buf);
577 root->add_property ("lock-style", enum_2_string (position_lock_style()));
578 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
579 root->add_property ("divisions-per-bar", buf);
580 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
581 root->add_property ("movable", buf);
586 /***********************************************************************/
588 struct MetricSectionSorter {
589 bool operator() (const MetricSection* a, const MetricSection* b) {
590 return a->beat() < b->beat();
594 struct MetricSectionFrameSorter {
595 bool operator() (const MetricSection* a, const MetricSection* b) {
596 return a->frame() < b->frame();
600 TempoMap::TempoMap (framecnt_t fr)
609 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
610 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
612 t->set_movable (false);
613 m->set_movable (false);
615 /* note: frame time is correct (zero) for both of these */
617 metrics.push_back (t);
618 metrics.push_back (m);
622 TempoMap::~TempoMap ()
627 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
629 bool removed = false;
632 Glib::Threads::RWLock::WriterLock lm (lock);
633 if ((removed = remove_tempo_locked (tempo))) {
634 if (complete_operation) {
635 recompute_map (true);
640 if (removed && complete_operation) {
641 PropertyChanged (PropertyChange ());
646 TempoMap::remove_tempo_locked (const TempoSection& tempo)
650 for (i = metrics.begin(); i != metrics.end(); ++i) {
651 if (dynamic_cast<TempoSection*> (*i) != 0) {
652 if (tempo.frame() == (*i)->frame()) {
653 if ((*i)->movable()) {
665 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
667 bool removed = false;
670 Glib::Threads::RWLock::WriterLock lm (lock);
671 if ((removed = remove_meter_locked (tempo))) {
672 if (complete_operation) {
673 recompute_map (true);
678 if (removed && complete_operation) {
679 PropertyChanged (PropertyChange ());
684 TempoMap::remove_meter_locked (const MeterSection& tempo)
688 for (i = metrics.begin(); i != metrics.end(); ++i) {
689 if (dynamic_cast<MeterSection*> (*i) != 0) {
690 if (tempo.frame() == (*i)->frame()) {
691 if ((*i)->movable()) {
703 TempoMap::do_insert (MetricSection* section)
705 bool need_add = true;
707 /* we only allow new meters to be inserted on beat 1 of an existing
711 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
712 assert (m->bbt().ticks == 0);
714 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
716 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
717 corrected.second.beats = 1;
718 corrected.second.ticks = 0;
719 corrected.first = bbt_to_beats_locked (corrected.second);
720 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
721 m->bbt(), corrected.second) << endmsg;
722 m->set_beat (corrected);
726 /* Look for any existing MetricSection that is of the same type and
727 in the same bar as the new one, and remove it before adding
728 the new one. Note that this means that if we find a matching,
729 existing section, we can break out of the loop since we're
730 guaranteed that there is only one such match.
733 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
735 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
736 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
738 if (tempo && insert_tempo) {
741 PositionLockStyle const ipl = insert_tempo->position_lock_style();
742 if ((ipl == MusicTime && tempo->beat() == insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() == insert_tempo->frame())) {
744 if (!tempo->movable()) {
746 /* can't (re)move this section, so overwrite
747 * its data content (but not its properties as
751 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
759 } else if (!tempo && !insert_tempo) {
762 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
763 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
764 PositionLockStyle const ipl = insert_meter->position_lock_style();
766 if ((ipl == MusicTime && meter->beat() == insert_meter->beat()) || (ipl == AudioTime && meter->frame() == insert_meter->frame())) {
768 if (!meter->movable()) {
770 /* can't (re)move this section, so overwrite
771 * its data content (but not its properties as
775 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
785 /* non-matching types, so we don't care */
789 /* Add the given MetricSection, if we didn't just reset an existing
794 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
795 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
799 for (i = metrics.begin(); i != metrics.end(); ++i) {
800 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
803 PositionLockStyle const ipl = insert_meter->position_lock_style();
804 if ((ipl == MusicTime && meter->beat() > insert_meter->beat()) || (ipl == AudioTime && meter->frame() > insert_meter->frame())) {
809 } else if (insert_tempo) {
810 for (i = metrics.begin(); i != metrics.end(); ++i) {
811 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
814 PositionLockStyle const ipl = insert_tempo->position_lock_style();
815 if ((ipl == MusicTime && tempo->beat() > insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() > insert_tempo->frame())) {
822 metrics.insert (i, section);
828 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
831 Glib::Threads::RWLock::WriterLock lm (lock);
832 TempoSection& first (first_tempo());
833 if (ts.beat() != first.beat()) {
834 remove_tempo_locked (ts);
835 add_tempo_locked (tempo, where, true, type);
837 first.set_type (type);
839 /* cannot move the first tempo section */
840 *static_cast<Tempo*>(&first) = tempo;
841 recompute_map (false);
846 PropertyChanged (PropertyChange ());
850 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
853 Glib::Threads::RWLock::WriterLock lm (lock);
854 TempoSection& first (first_tempo());
855 if (ts.beat() != first.beat()) {
856 remove_tempo_locked (ts);
857 add_tempo_locked (tempo, frame, true, type);
859 first.set_type (type);
861 /* cannot move the first tempo section */
862 *static_cast<Tempo*>(&first) = tempo;
863 recompute_map (false);
868 PropertyChanged (PropertyChange ());
872 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
875 Glib::Threads::RWLock::WriterLock lm (lock);
877 if (ts.position_lock_style() == MusicTime) {
878 std::cerr << "Music " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
880 ts.set_beat (beat_where);
881 Metrics::const_iterator i;
883 TempoSection* prev_ts = 0;
884 MetricSectionSorter cmp;
886 for (i = metrics.begin(); i != metrics.end(); ++i) {
888 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
890 if (t->beat() >= beat_where) {
898 prev_ts->set_c_func_from_tempo_and_beat (ts.beats_per_minute(), ts.beat(), _frame_rate);
899 ts.set_frame (prev_ts->frame_at_beat (ts.beat(), _frame_rate));
901 std::cerr << "Audio " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
904 ts.set_frame (frame);
905 MetricSectionFrameSorter fcmp;
908 Metrics::const_iterator i;
909 TempoSection* prev_ts = 0;
910 TempoSection* next_ts = 0;
912 for (i = metrics.begin(); i != metrics.end(); ++i) {
914 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
916 if (t->frame() >= frame) {
924 for (i = metrics.begin(); i != metrics.end(); ++i) {
926 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
928 if (t->frame() > frame) {
936 /* set the start beat - we need to reset the function constant before beat calculations make sense*/
937 prev_ts->set_c_func (prev_ts->compute_c_func (ts.beats_per_minute(), frame, _frame_rate));
938 double beats = prev_ts->beat_at_frame (frame, _frame_rate);
941 if (next_ts->beat() < beats) {
942 /* with frame-based editing, it is possible to get in a
943 situation where if the tempo was placed at the mouse pointer frame,
944 the following music-based tempo would jump to an earlier frame,
945 changing the beat beat of the moved tempo.
946 in this case, we have to do some beat-based comparison TODO
948 } else if (prev_ts->beat() > beats) {
949 ts.set_beat (prev_ts->beat());
958 MetricSectionSorter cmp;
963 recompute_map (false);
966 MetricPositionChanged (); // Emit Signal
970 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
973 Glib::Threads::RWLock::WriterLock lm (lock);
974 add_tempo_locked (tempo, where, true, type);
978 PropertyChanged (PropertyChange ());
982 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
985 Glib::Threads::RWLock::WriterLock lm (lock);
986 add_tempo_locked (tempo, frame, true, type);
990 PropertyChanged (PropertyChange ());
994 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
996 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
1001 recompute_map (false);
1006 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
1008 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
1013 recompute_map (false);
1018 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
1021 Glib::Threads::RWLock::WriterLock lm (lock);
1022 MeterSection& first (first_meter());
1023 if (ms.beat() != first.beat()) {
1024 remove_meter_locked (ms);
1025 add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
1027 /* cannot move the first meter section */
1028 *static_cast<Meter*>(&first) = meter;
1029 recompute_map (true);
1033 PropertyChanged (PropertyChange ());
1037 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1040 Glib::Threads::RWLock::WriterLock lm (lock);
1041 MeterSection& first (first_meter());
1042 if (ms.beat() != first.beat()) {
1043 remove_meter_locked (ms);
1044 add_meter_locked (meter, frame, true);
1046 /* cannot move the first meter section */
1047 *static_cast<Meter*>(&first) = meter;
1048 recompute_map (true);
1052 PropertyChanged (PropertyChange ());
1057 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1060 Glib::Threads::RWLock::WriterLock lm (lock);
1061 add_meter_locked (meter, beat, where, true);
1066 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1071 PropertyChanged (PropertyChange ());
1075 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1078 Glib::Threads::RWLock::WriterLock lm (lock);
1079 add_meter_locked (meter, frame, true);
1084 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1089 PropertyChanged (PropertyChange ());
1093 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1095 /* a new meter always starts a new bar on the first beat. so
1096 round the start time appropriately. remember that
1097 `where' is based on the existing tempo map, not
1098 the result after we insert the new meter.
1102 if (where.beats != 1) {
1107 /* new meters *always* start on a beat. */
1110 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
1113 recompute_map (true);
1119 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1122 /* MusicTime meters *always* start on 1|1|0. */
1123 MeterSection* ms = new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor());
1125 pair<double, BBT_Time> pr;
1129 /* just a dummy - the actual beat should be applied in recompute_map() as thins is AudioTime */
1136 recompute_map (true);
1142 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1144 Tempo newtempo (beats_per_minute, note_type);
1147 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1148 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1150 Glib::Threads::RWLock::WriterLock lm (lock);
1151 *((Tempo*) t) = newtempo;
1152 recompute_map (false);
1154 PropertyChanged (PropertyChange ());
1161 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1163 Tempo newtempo (beats_per_minute, note_type);
1166 TempoSection* first;
1167 Metrics::iterator i;
1169 /* find the TempoSection immediately preceding "where"
1172 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
1174 if ((*i)->frame() > where) {
1180 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1190 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1200 Glib::Threads::RWLock::WriterLock lm (lock);
1201 /* cannot move the first tempo section */
1202 *((Tempo*)prev) = newtempo;
1203 recompute_map (false);
1206 PropertyChanged (PropertyChange ());
1210 TempoMap::first_meter () const
1212 const MeterSection *m = 0;
1214 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1215 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1220 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1221 abort(); /*NOTREACHED*/
1226 TempoMap::first_meter ()
1228 MeterSection *m = 0;
1230 /* CALLER MUST HOLD LOCK */
1232 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1233 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1238 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1239 abort(); /*NOTREACHED*/
1244 TempoMap::first_tempo () const
1246 const TempoSection *t = 0;
1248 /* CALLER MUST HOLD LOCK */
1250 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1251 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1256 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1257 abort(); /*NOTREACHED*/
1262 TempoMap::first_tempo ()
1264 TempoSection *t = 0;
1266 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1267 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1272 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1273 abort(); /*NOTREACHED*/
1278 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1280 /* CALLER MUST HOLD WRITE LOCK */
1284 /* we will actually stop once we hit
1291 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1294 /* silly call from Session::process() during startup
1299 Metrics::const_iterator i;
1300 TempoSection* prev_ts = 0;
1302 for (i = metrics.begin(); i != metrics.end(); ++i) {
1305 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1307 if (t->position_lock_style() == AudioTime) {
1308 if (prev_ts->type() == TempoSection::Ramp) {
1309 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
1310 t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1312 prev_ts->set_c_func (0.0);
1313 t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1315 } else if (t->position_lock_style() == MusicTime) {
1316 if (prev_ts->type() == TempoSection::Ramp) {
1317 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
1318 t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1320 prev_ts->set_c_func (0.0);
1321 t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1329 MeterSection* meter = 0;
1331 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1332 /* We now have the tempo map set. use it to set meter positions.*/
1333 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1334 if (meter->position_lock_style() == AudioTime) {
1335 /* a frame based meter has to have a 1|1|0 bbt */
1336 pair<double, BBT_Time> pr;
1343 pr.first = beat_at_frame (meter->frame());
1345 meter->set_beat (pr);
1347 meter->set_frame (frame_at_tick (meter->tick()));
1355 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1357 Glib::Threads::RWLock::ReaderLock lm (lock);
1358 TempoMetric m (first_meter(), first_tempo());
1360 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1361 at something, because we insert the default tempo and meter during
1362 TempoMap construction.
1364 now see if we can find better candidates.
1367 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1369 if ((*i)->frame() > frame) {
1382 /* XX meters only */
1384 TempoMap::metric_at (BBT_Time bbt) const
1386 Glib::Threads::RWLock::ReaderLock lm (lock);
1387 TempoMetric m (first_meter(), first_tempo());
1389 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1390 at something, because we insert the default tempo and meter during
1391 TempoMap construction.
1393 now see if we can find better candidates.
1396 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1398 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1399 BBT_Time section_start (mw->bbt());
1401 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1413 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1415 Glib::Threads::RWLock::ReaderLock lm (lock);
1421 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1424 bbt = beats_to_bbt_locked (beat_at_frame (frame));
1428 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1430 Glib::Threads::RWLock::ReaderLock lm (lock);
1431 return bbt_to_beats_locked (bbt);
1435 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1437 /* CALLER HOLDS READ LOCK */
1439 double accumulated_beats = 0.0;
1440 double accumulated_bars = 0.0;
1441 MeterSection* prev_ms = 0;
1443 Metrics::const_iterator i;
1445 for (i = metrics.begin(); i != metrics.end(); ++i) {
1447 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1448 double bars_to_m = 0.0;
1450 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1452 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1456 accumulated_beats += m->beat() - prev_ms->beat();
1457 accumulated_bars += bars_to_m;
1463 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1464 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1465 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1470 TempoMap::beats_to_bbt (double beats)
1472 Glib::Threads::RWLock::ReaderLock lm (lock);
1473 return beats_to_bbt_locked (beats);
1477 TempoMap::beats_to_bbt_locked (double beats)
1479 /* CALLER HOLDS READ LOCK */
1481 MeterSection* prev_ms = 0;
1482 uint32_t accumulated_bars = 0;
1484 Metrics::const_iterator i;
1486 for (i = metrics.begin(); i != metrics.end(); ++i) {
1487 MeterSection* m = 0;
1489 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1491 if (beats < m->beat()) {
1492 /* this is the meter after the one our beat is on*/
1497 /* we need a whole number of bars. */
1498 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1505 double const beats_in_ms = beats - prev_ms->beat();
1506 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1507 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1508 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1509 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1513 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1514 ret.beats = (uint32_t) floor (remaining_beats);
1515 ret.bars = total_bars;
1517 /* 0 0 0 to 1 1 0 - based mapping*/
1521 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1523 ret.ticks -= BBT_Time::ticks_per_beat;
1526 if (ret.beats > prev_ms->divisions_per_bar()) {
1535 TempoMap::tick_at_frame (framecnt_t frame) const
1537 /* HOLD (at least) THE READER LOCK */
1539 Metrics::const_iterator i;
1540 TempoSection* prev_ts = 0;
1541 double accumulated_ticks = 0.0;
1543 for (i = metrics.begin(); i != metrics.end(); ++i) {
1546 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1548 if ((prev_ts) && frame < t->frame()) {
1549 /*the previous ts is the one containing the frame */
1550 return prev_ts->tick_at_frame (frame, _frame_rate);
1553 if (prev_ts && t->frame() > prev_ts->frame()) {
1554 accumulated_ticks = t->tick();
1561 /* treated as constant for this ts */
1562 double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1564 return ticks_in_section + accumulated_ticks;
1569 TempoMap::frame_at_tick (double tick) const
1571 /* HOLD THE READER LOCK */
1573 double accumulated_ticks = 0.0;
1574 double accumulated_ticks_to_prev = 0.0;
1575 const TempoSection* prev_ts = 0;
1577 Metrics::const_iterator i;
1579 for (i = metrics.begin(); i != metrics.end(); ++i) {
1581 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1583 if (prev_ts && t->frame() > prev_ts->frame()) {
1584 accumulated_ticks = t->tick();
1587 if (prev_ts && tick < accumulated_ticks) {
1588 /* prev_ts is the one affecting us. */
1589 return prev_ts->frame_at_tick (tick, _frame_rate);
1591 accumulated_ticks_to_prev = accumulated_ticks;
1595 /* must be treated as constant, irrespective of _type */
1596 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1597 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1599 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1605 TempoMap::beat_at_frame (framecnt_t frame) const
1607 Glib::Threads::RWLock::ReaderLock lm (lock);
1609 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1613 TempoMap::frame_at_beat (double beat) const
1615 Glib::Threads::RWLock::ReaderLock lm (lock);
1617 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1621 TempoMap::frame_time (const BBT_Time& bbt)
1624 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1628 if (bbt.beats < 1) {
1629 throw std::logic_error ("beats are counted from one");
1631 Glib::Threads::RWLock::ReaderLock lm (lock);
1633 framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
1640 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1643 Glib::Threads::RWLock::ReaderLock lm (lock);
1645 Metrics::const_iterator i;
1646 TempoSection* first = 0;
1647 TempoSection* second = 0;
1649 for (i = metrics.begin(); i != metrics.end(); ++i) {
1652 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1654 if ((*i)->frame() > pos) {
1662 if (first && second) {
1663 double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
1664 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1665 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1667 return time_at_bbt - pos;
1670 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1671 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1675 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1677 return round_to_type (fr, dir, Bar);
1681 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1683 return round_to_type (fr, dir, Beat);
1687 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1689 Glib::Threads::RWLock::ReaderLock lm (lock);
1691 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1692 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1693 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1695 ticks -= beats * BBT_Time::ticks_per_beat;
1698 /* round to next (or same iff dir == RoundUpMaybe) */
1700 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1702 if (mod == 0 && dir == RoundUpMaybe) {
1703 /* right on the subdivision, which is fine, so do nothing */
1705 } else if (mod == 0) {
1706 /* right on the subdivision, so the difference is just the subdivision ticks */
1707 ticks += ticks_one_subdivisions_worth;
1710 /* not on subdivision, compute distance to next subdivision */
1712 ticks += ticks_one_subdivisions_worth - mod;
1715 if (ticks >= BBT_Time::ticks_per_beat) {
1716 ticks -= BBT_Time::ticks_per_beat;
1718 } else if (dir < 0) {
1720 /* round to previous (or same iff dir == RoundDownMaybe) */
1722 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1724 if (difference == 0 && dir == RoundDownAlways) {
1725 /* right on the subdivision, but force-rounding down,
1726 so the difference is just the subdivision ticks */
1727 difference = ticks_one_subdivisions_worth;
1730 if (ticks < difference) {
1731 ticks = BBT_Time::ticks_per_beat - ticks;
1733 ticks -= difference;
1737 /* round to nearest */
1740 /* compute the distance to the previous and next subdivision */
1742 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1744 /* closer to the next subdivision, so shift forward */
1746 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1748 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1750 if (ticks > BBT_Time::ticks_per_beat) {
1752 ticks -= BBT_Time::ticks_per_beat;
1753 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1756 } else if (rem > 0) {
1758 /* closer to previous subdivision, so shift backward */
1762 /* can't go backwards past zero, so ... */
1765 /* step back to previous beat */
1767 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1768 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1770 ticks = lrint (ticks - rem);
1771 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1774 /* on the subdivision, do nothing */
1777 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1781 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1783 Glib::Threads::RWLock::ReaderLock lm (lock);
1785 double const beat_at_framepos = beat_at_frame (frame);
1787 BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1792 /* find bar previous to 'frame' */
1795 return frame_time (bbt);
1797 } else if (dir > 0) {
1798 /* find bar following 'frame' */
1802 return frame_time (bbt);
1804 /* true rounding: find nearest bar */
1805 framepos_t raw_ft = frame_time (bbt);
1808 framepos_t prev_ft = frame_time (bbt);
1810 framepos_t next_ft = frame_time (bbt);
1812 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1823 return frame_at_beat (floor (beat_at_framepos));
1824 } else if (dir > 0) {
1825 return frame_at_beat (ceil (beat_at_framepos));
1827 return frame_at_beat (floor (beat_at_framepos + 0.5));
1836 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1837 framepos_t lower, framepos_t upper)
1839 Glib::Threads::RWLock::ReaderLock lm (lock);
1840 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1841 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1843 while (cnt <= upper_beat) {
1844 framecnt_t const pos = frame_at_beat (cnt);
1845 MeterSection const meter = meter_section_at (pos);
1846 Tempo const tempo = tempo_at (pos);
1847 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
1849 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1855 TempoMap::tempo_section_at (framepos_t frame) const
1857 Glib::Threads::RWLock::ReaderLock lm (lock);
1859 Metrics::const_iterator i;
1860 TempoSection* prev = 0;
1862 for (i = metrics.begin(); i != metrics.end(); ++i) {
1865 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1867 if ((*i)->frame() > frame) {
1877 abort(); /*NOTREACHED*/
1883 /* don't use this to calculate length (the tempo is only correct for this frame).
1884 do that stuff based on the beat_at_frame and frame_at_beat api
1887 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1889 Glib::Threads::RWLock::ReaderLock lm (lock);
1891 const TempoSection* ts_at = &tempo_section_at (frame);
1892 const TempoSection* ts_after = 0;
1893 Metrics::const_iterator i;
1895 for (i = metrics.begin(); i != metrics.end(); ++i) {
1898 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1900 if ((*i)->frame() > frame) {
1908 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
1910 /* must be treated as constant tempo */
1911 return ts_at->frames_per_beat (_frame_rate);
1915 TempoMap::tempo_at (framepos_t frame) const
1917 Glib::Threads::RWLock::ReaderLock lm (lock);
1919 TempoMetric m (metric_at (frame));
1920 TempoSection* prev_ts = 0;
1922 Metrics::const_iterator i;
1924 for (i = metrics.begin(); i != metrics.end(); ++i) {
1926 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1927 if ((prev_ts) && t->frame() > frame) {
1928 /* this is the one past frame */
1929 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
1930 Tempo const ret_tempo (ret, m.tempo().note_type ());
1942 TempoMap::meter_section_at (framepos_t frame) const
1944 Glib::Threads::RWLock::ReaderLock lm (lock);
1945 Metrics::const_iterator i;
1946 MeterSection* prev = 0;
1948 for (i = metrics.begin(); i != metrics.end(); ++i) {
1951 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1953 if ((*i)->frame() > frame) {
1963 abort(); /*NOTREACHED*/
1970 TempoMap::meter_at (framepos_t frame) const
1972 TempoMetric m (metric_at (frame));
1977 TempoMap::get_state ()
1979 Metrics::const_iterator i;
1980 XMLNode *root = new XMLNode ("TempoMap");
1983 Glib::Threads::RWLock::ReaderLock lm (lock);
1984 for (i = metrics.begin(); i != metrics.end(); ++i) {
1985 root->add_child_nocopy ((*i)->get_state());
1993 TempoMap::set_state (const XMLNode& node, int /*version*/)
1996 Glib::Threads::RWLock::WriterLock lm (lock);
1999 XMLNodeConstIterator niter;
2000 Metrics old_metrics (metrics);
2001 MeterSection* last_meter = 0;
2004 nlist = node.children();
2006 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2007 XMLNode* child = *niter;
2009 if (child->name() == TempoSection::xml_state_node_name) {
2012 TempoSection* ts = new TempoSection (*child);
2013 metrics.push_back (ts);
2015 if (ts->bar_offset() < 0.0) {
2017 //ts->update_bar_offset_from_bbt (*last_meter);
2022 catch (failed_constructor& err){
2023 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2024 metrics = old_metrics;
2028 } else if (child->name() == MeterSection::xml_state_node_name) {
2031 MeterSection* ms = new MeterSection (*child);
2032 metrics.push_back (ms);
2036 catch (failed_constructor& err) {
2037 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2038 metrics = old_metrics;
2044 if (niter == nlist.end()) {
2045 MetricSectionSorter cmp;
2048 /* check for legacy sessions where bbt was the base musical unit for tempo */
2049 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2050 MeterSection* prev_ms;
2051 TempoSection* prev_ts;
2052 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2053 if (prev_ms->beat() < 0.0) {
2054 /*XX we cannot possibly make this work??. */
2055 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());
2056 prev_ms->set_beat (start);
2058 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2059 if (prev_ts->beat() < 0.0) {
2060 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);
2061 prev_ts->set_beat (start);
2066 /* check for multiple tempo/meters at the same location, which
2067 ardour2 somehow allowed.
2070 Metrics::iterator prev = metrics.end();
2071 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2072 if (prev != metrics.end()) {
2074 MeterSection* prev_ms;
2076 TempoSection* prev_ts;
2077 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2078 if (prev_ms->beat() == ms->beat()) {
2079 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2080 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2083 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2084 if (prev_ts->beat() == ts->beat()) {
2085 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2086 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2094 recompute_map (true, -1);
2097 PropertyChanged (PropertyChange ());
2103 TempoMap::dump (std::ostream& o) const
2105 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2106 const MeterSection* m;
2107 const TempoSection* t;
2109 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2111 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2112 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? "
2113 << t->movable() << ')' << endl;
2114 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2115 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2116 << " (movable? " << m->movable() << ')' << endl;
2122 TempoMap::n_tempos() const
2124 Glib::Threads::RWLock::ReaderLock lm (lock);
2127 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2128 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2137 TempoMap::n_meters() const
2139 Glib::Threads::RWLock::ReaderLock lm (lock);
2142 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2143 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2152 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2155 Glib::Threads::RWLock::WriterLock lm (lock);
2156 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2157 if ((*i)->frame() >= where && (*i)->movable ()) {
2158 (*i)->set_frame ((*i)->frame() + amount);
2162 /* now reset the BBT time of all metrics, based on their new
2163 * audio time. This is the only place where we do this reverse
2167 Metrics::iterator i;
2168 const MeterSection* meter;
2169 const TempoSection* tempo;
2173 meter = &first_meter ();
2174 tempo = &first_tempo ();
2179 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2182 MetricSection* prev = 0;
2184 for (i = metrics.begin(); i != metrics.end(); ++i) {
2187 //TempoMetric metric (*meter, *tempo);
2188 MeterSection* ms = const_cast<MeterSection*>(meter);
2189 TempoSection* ts = const_cast<TempoSection*>(tempo);
2192 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2193 ts->set_beat (t->beat());
2195 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2196 ts->set_beat (m->beat());
2198 ts->set_frame (prev->frame());
2202 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2203 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2204 ms->set_beat (start);
2206 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2207 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2208 ms->set_beat (start);
2210 ms->set_frame (prev->frame());
2214 // metric will be at frames=0 bbt=1|1|0 by default
2215 // which is correct for our purpose
2218 // cerr << bbt << endl;
2220 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2221 t->set_beat (beat_at_frame (m->frame()));
2223 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2224 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2225 bbt_time (m->frame(), bbt);
2227 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2233 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2234 /* round up to next beat */
2240 if (bbt.beats != 1) {
2241 /* round up to next bar */
2246 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
2247 m->set_beat (start);
2249 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2251 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2252 abort(); /*NOTREACHED*/
2258 recompute_map (true);
2262 PropertyChanged (PropertyChange ());
2265 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2269 std::list<MetricSection*> metric_kill_list;
2271 TempoSection* last_tempo = NULL;
2272 MeterSection* last_meter = NULL;
2273 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2274 bool meter_after = false; // is there a meter marker likewise?
2276 Glib::Threads::RWLock::WriterLock lm (lock);
2277 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2278 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2279 metric_kill_list.push_back(*i);
2280 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2283 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2287 else if ((*i)->frame() >= where) {
2288 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2289 (*i)->set_frame ((*i)->frame() - amount);
2290 if ((*i)->frame() == where) {
2291 // marker was immediately after end of range
2292 tempo_after = dynamic_cast<TempoSection*> (*i);
2293 meter_after = dynamic_cast<MeterSection*> (*i);
2299 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2300 if (last_tempo && !tempo_after) {
2301 metric_kill_list.remove(last_tempo);
2302 last_tempo->set_frame(where);
2305 if (last_meter && !meter_after) {
2306 metric_kill_list.remove(last_meter);
2307 last_meter->set_frame(where);
2311 //remove all the remaining metrics
2312 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2318 recompute_map (true);
2321 PropertyChanged (PropertyChange ());
2325 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2326 * pos can be -ve, if required.
2329 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2331 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2334 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2336 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2338 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2341 /** Add the BBT interval op to pos and return the result */
2343 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2345 cerr << "framepos_plus_bbt - untested" << endl;
2346 Glib::Threads::RWLock::ReaderLock lm (lock);
2348 Metrics::const_iterator i;
2349 const MeterSection* meter;
2350 const MeterSection* m;
2351 const TempoSection* tempo;
2352 const TempoSection* next_tempo = 0;
2353 const TempoSection* t;
2354 double frames_per_beat;
2355 framepos_t effective_pos = max (pos, (framepos_t) 0);
2357 meter = &first_meter ();
2358 tempo = &first_tempo ();
2363 /* find the starting metrics for tempo & meter */
2365 for (i = metrics.begin(); i != metrics.end(); ++i) {
2367 if ((*i)->frame() > effective_pos) {
2371 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2373 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2378 for (i = metrics.begin(); i != metrics.end(); ++i) {
2379 if ((*i)->frame() > effective_pos) {
2380 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2389 meter -> the Meter for "pos"
2390 tempo -> the Tempo for "pos"
2391 next_tempo -> the Tempo after "pos" or 0
2392 i -> for first new metric after "pos", possibly metrics.end()
2395 /* now comes the complicated part. we have to add one beat a time,
2396 checking for a new metric on every beat.
2400 /* fpb is used for constant tempo */
2401 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2408 /* check if we need to use a new metric section: has adding frames moved us
2409 to or after the start of the next metric section? in which case, use it.
2412 if (i != metrics.end()) {
2413 if ((*i)->frame() <= pos) {
2415 /* about to change tempo or meter, so add the
2416 * number of frames for the bars we've just
2417 * traversed before we change the
2418 * frames_per_beat value.
2421 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2426 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2428 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2433 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2435 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2439 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2446 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2448 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2455 /* given the current meter, have we gone past the end of the bar ? */
2460 /* check if we need to use a new metric section: has adding frames moved us
2461 to or after the start of the next metric section? in which case, use it.
2464 if (i != metrics.end()) {
2465 if ((*i)->frame() <= pos) {
2467 /* about to change tempo or meter, so add the
2468 * number of frames for the beats we've just
2469 * traversed before we change the
2470 * frames_per_beat value.
2473 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2478 pos += tempo->frame_at_beat (beats, _frame_rate);
2480 pos += llrint (beats * frames_per_beat);
2485 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2487 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2491 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2497 pos += tempo->frame_at_beat (beats, _frame_rate);
2499 pos += llrint (beats * frames_per_beat);
2503 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2510 /** Count the number of beats that are equivalent to distance when going forward,
2514 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2516 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2520 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2526 operator<< (std::ostream& o, const Meter& m) {
2527 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2531 operator<< (std::ostream& o, const Tempo& t) {
2532 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2536 operator<< (std::ostream& o, const MetricSection& section) {
2538 o << "MetricSection @ " << section.frame() << ' ';
2540 const TempoSection* ts;
2541 const MeterSection* ms;
2543 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2544 o << *((const Tempo*) ts);
2545 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2546 //o << *((const Meter*) ms);