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 tempo section) frame.
208 TempoSection::tempo_at_frame (framepos_t frame, framecnt_t frame_rate) const
211 if (_type == Constant) {
212 return beats_per_minute();
215 return tick_tempo_at_time (frame_to_minute (frame, frame_rate)) / BBT_Time::ticks_per_beat;
218 /** returns the zero-based frame (relative to tempo section)
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);
231 /** returns the zero-based tick (relative to tempo section)
232 where the zero-based frame (relative to tempo section)
236 TempoSection::tick_at_frame (framepos_t frame, framecnt_t frame_rate) const
238 if (_type == Constant) {
239 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
242 return tick_at_time (frame_to_minute (frame, frame_rate));
245 /** returns the zero-based frame (relative to tempo section)
246 where the zero-based tick (relative to tempo section)
250 TempoSection::frame_at_tick (double tick, framecnt_t frame_rate) const
252 if (_type == Constant) {
253 return (framepos_t) floor ((tick / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
256 return minute_to_frame (time_at_tick (tick), frame_rate);
259 /** returns the zero-based beat (relative to tempo section)
260 where the zero-based frame (relative to tempo section)
264 TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
266 return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
269 /** returns the zero-based frame (relative to tempo section start frame)
270 where the zero-based beat (relative to tempo section start)
275 TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
277 return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
285 Tt----|-----------------*|
286 Ta----|--------------|* |
292 _______________|___|____
293 time a t (next tempo)
296 Duration in beats at time a is the integral of some Tempo function.
297 In our case, the Tempo function (Tempo at time t) is
300 with function constant
305 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
306 b(t) = T0(e^(ct) - 1) / c
308 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:
309 t(b) = log((cb / T0) + 1) / c
311 The time t at which Tempo T occurs is a as above:
312 t(T) = log(T / T0) / c
314 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
315 Our problem is that we usually don't know t.
316 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.
317 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
318 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
320 By substituting our expanded t as a in the c function above, our problem is reduced to:
321 c = T0 (e^(log (Ta / T0)) - 1) / b
323 We can now store c for future time calculations.
324 If the following tempo section (the one that defines c in conjunction with this one)
325 is changed or moved, c is no longer valid.
327 Most of this stuff is taken from this paper:
330 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
333 Zurich University of Arts
334 Institute for Computer Music and Sound Technology
336 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
340 /* set this ramp's function constant using the end tempo and duration in beats of some later tempo section*/
342 TempoSection::set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
344 double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
345 _c_func = ticks_per_minute() * (exp (log_tempo_ratio) - 1) / (end_beat * BBT_Time::ticks_per_beat);
348 /* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from this tempo section */
350 TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
352 return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
356 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
358 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
362 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
364 return (frame / (double) frame_rate) / 60.0;
367 /* position function */
369 TempoSection::a_func (double end_tpm, double c_func) const
371 return log (end_tpm / ticks_per_minute()) / c_func;
374 /*function constant*/
376 TempoSection::c_func (double end_tpm, double end_time) const
378 return log (end_tpm / ticks_per_minute()) / end_time;
381 /* tempo in tpm at time in minutes */
383 TempoSection::tick_tempo_at_time (double time) const
385 return exp (_c_func * time) * ticks_per_minute();
388 /* time in minutes at tempo in tpm */
390 TempoSection::time_at_tick_tempo (double tick_tempo) const
392 return log (tick_tempo / ticks_per_minute()) / _c_func;
395 /* tick at time in minutes */
397 TempoSection::tick_at_time (double time) const
399 return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
402 /* time in minutes at tick */
404 TempoSection::time_at_tick (double tick) const
406 return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
409 /* beat at time in minutes */
411 TempoSection::beat_at_time (double time) const
413 return tick_at_time (time) / BBT_Time::ticks_per_beat;
416 /* time in munutes at beat */
418 TempoSection::time_at_beat (double beat) const
420 return time_at_tick (beat * BBT_Time::ticks_per_beat);
424 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
428 if (_bar_offset < 0.0) {
435 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
436 new_beat = ticks / BBT_Time::ticks_per_beat;
438 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
439 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
444 /***********************************************************************/
446 const string MeterSection::xml_state_node_name = "Meter";
448 MeterSection::MeterSection (const XMLNode& node)
449 : MetricSection (0.0), Meter (TempoMap::default_meter())
451 XMLProperty const * prop;
454 const XMLProperty *prop;
457 framepos_t frame = 0;
458 pair<double, BBT_Time> start;
460 if ((prop = node.property ("start")) != 0) {
461 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
465 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
467 /* legacy session - start used to be in bbt*/
471 error << _("MeterSection XML node has no \"start\" property") << endmsg;
474 if ((prop = node.property ("beat")) != 0) {
475 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
476 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
482 if ((prop = node.property ("bbt")) == 0) {
483 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
484 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
488 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
489 throw failed_constructor();
496 if ((prop = node.property ("frame")) != 0) {
497 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
498 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
504 /* beats-per-bar is old; divisions-per-bar is new */
506 if ((prop = node.property ("divisions-per-bar")) == 0) {
507 if ((prop = node.property ("beats-per-bar")) == 0) {
508 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
509 throw failed_constructor();
513 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
514 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
515 throw failed_constructor();
518 if ((prop = node.property ("note-type")) == 0) {
519 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
520 throw failed_constructor();
523 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
524 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
525 throw failed_constructor();
528 if ((prop = node.property ("lock-style")) == 0) {
529 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
530 //throw failed_constructor();
531 set_position_lock_style (PositionLockStyle::MusicTime);
533 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
536 if ((prop = node.property ("movable")) == 0) {
537 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
538 throw failed_constructor();
541 set_movable (string_is_affirmative (prop->value()));
545 MeterSection::get_state() const
547 XMLNode *root = new XMLNode (xml_state_node_name);
551 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
555 root->add_property ("bbt", buf);
556 snprintf (buf, sizeof (buf), "%lf", beat());
557 root->add_property ("beat", buf);
558 snprintf (buf, sizeof (buf), "%f", _note_type);
559 root->add_property ("frame", buf);
560 snprintf (buf, sizeof (buf), "%li", frame());
561 root->add_property ("note-type", buf);
562 root->add_property ("lock-style", enum_2_string (position_lock_style()));
563 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
564 root->add_property ("divisions-per-bar", buf);
565 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
566 root->add_property ("movable", buf);
571 /***********************************************************************/
573 struct MetricSectionSorter {
574 bool operator() (const MetricSection* a, const MetricSection* b) {
575 return a->beat() < b->beat();
579 struct MetricSectionFrameSorter {
580 bool operator() (const MetricSection* a, const MetricSection* b) {
581 return a->frame() < b->frame();
585 TempoMap::TempoMap (framecnt_t fr)
594 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
595 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
597 t->set_movable (false);
598 m->set_movable (false);
600 /* note: frame time is correct (zero) for both of these */
602 metrics.push_back (t);
603 metrics.push_back (m);
607 TempoMap::~TempoMap ()
612 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
614 bool removed = false;
617 Glib::Threads::RWLock::WriterLock lm (lock);
618 if ((removed = remove_tempo_locked (tempo))) {
619 if (complete_operation) {
620 recompute_map (true);
625 if (removed && complete_operation) {
626 PropertyChanged (PropertyChange ());
631 TempoMap::remove_tempo_locked (const TempoSection& tempo)
635 for (i = metrics.begin(); i != metrics.end(); ++i) {
636 if (dynamic_cast<TempoSection*> (*i) != 0) {
637 if (tempo.frame() == (*i)->frame()) {
638 if ((*i)->movable()) {
650 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
652 bool removed = false;
655 Glib::Threads::RWLock::WriterLock lm (lock);
656 if ((removed = remove_meter_locked (tempo))) {
657 if (complete_operation) {
658 recompute_map (true);
663 if (removed && complete_operation) {
664 PropertyChanged (PropertyChange ());
669 TempoMap::remove_meter_locked (const MeterSection& tempo)
673 for (i = metrics.begin(); i != metrics.end(); ++i) {
674 if (dynamic_cast<MeterSection*> (*i) != 0) {
675 if (tempo.frame() == (*i)->frame()) {
676 if ((*i)->movable()) {
688 TempoMap::do_insert (MetricSection* section)
690 bool need_add = true;
692 /* we only allow new meters to be inserted on beat 1 of an existing
696 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
697 assert (m->bbt().ticks == 0);
699 /* we need to (potentially) update the BBT times of tempo
700 sections based on this new meter.
703 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
705 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
706 corrected.second.beats = 1;
707 corrected.second.ticks = 0;
708 corrected.first = bbt_to_beats_locked (corrected.second);
709 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
710 m->bbt(), corrected.second) << endmsg;
711 m->set_beat (corrected);
717 /* Look for any existing MetricSection that is of the same type and
718 in the same bar as the new one, and remove it before adding
719 the new one. Note that this means that if we find a matching,
720 existing section, we can break out of the loop since we're
721 guaranteed that there is only one such match.
724 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
726 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
727 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
729 if (tempo && insert_tempo) {
732 PositionLockStyle const tpl = tempo->position_lock_style();
733 PositionLockStyle const ipl = insert_tempo->position_lock_style();
734 if (tpl == ipl && ((ipl == MusicTime && tempo->beat() == insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() == insert_tempo->frame()))) {
736 if (!tempo->movable()) {
738 /* can't (re)move this section, so overwrite
739 * its data content (but not its properties as
743 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
751 } else if (!tempo && !insert_tempo) {
754 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
755 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
756 PositionLockStyle const mpl = meter->position_lock_style();
757 PositionLockStyle const ipl = insert_meter->position_lock_style();
759 if (mpl == ipl && ((ipl == MusicTime && meter->beat() == insert_meter->beat()) || (ipl == AudioTime && meter->frame() == insert_meter->frame()))) {
761 if (!meter->movable()) {
763 /* can't (re)move this section, so overwrite
764 * its data content (but not its properties as
768 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
778 /* non-matching types, so we don't care */
782 /* Add the given MetricSection, if we didn't just reset an existing
787 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
788 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
792 for (i = metrics.begin(); i != metrics.end(); ++i) {
793 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
796 PositionLockStyle const ipl = insert_meter->position_lock_style();
797 if (ipl == MusicTime && meter->beat() > insert_meter->beat()) {
800 if (ipl == AudioTime && meter->frame() > insert_meter->frame()) {
805 } else if (insert_tempo) {
806 for (i = metrics.begin(); i != metrics.end(); ++i) {
807 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
810 PositionLockStyle const ipl = insert_tempo->position_lock_style();
811 if ((ipl == MusicTime && tempo->beat() > insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() > insert_tempo->frame())) {
818 metrics.insert (i, section);
823 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
826 Glib::Threads::RWLock::WriterLock lm (lock);
827 TempoSection& first (first_tempo());
828 if (ts.beat() != first.beat()) {
829 remove_tempo_locked (ts);
830 add_tempo_locked (tempo, where, true, type);
832 first.set_type (type);
834 /* cannot move the first tempo section */
835 *static_cast<Tempo*>(&first) = tempo;
836 recompute_map (false);
841 PropertyChanged (PropertyChange ());
845 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
848 Glib::Threads::RWLock::WriterLock lm (lock);
849 TempoSection& first (first_tempo());
850 if (ts.beat() != first.beat()) {
851 remove_tempo_locked (ts);
852 add_tempo_locked (tempo, frame, true, type);
854 first.set_type (type);
856 /* cannot move the first tempo section */
857 *static_cast<Tempo*>(&first) = tempo;
858 recompute_map (false);
863 PropertyChanged (PropertyChange ());
867 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
870 Glib::Threads::RWLock::WriterLock lm (lock);
872 if (ts.position_lock_style() == MusicTime) {
873 std::cerr << "Music " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
875 ts.set_beat (beat_where);
876 Metrics::const_iterator i;
878 TempoSection* prev_ts = 0;
879 MetricSectionSorter cmp;
881 for (i = metrics.begin(); i != metrics.end(); ++i) {
883 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
885 if (t->beat() >= beat_where) {
893 prev_ts->set_c_func_from_tempo_and_beat (ts.beats_per_minute(), ts.beat() - prev_ts->beat(), _frame_rate);
894 ts.set_frame (prev_ts->frame_at_beat (ts.beat() - prev_ts->beat(), _frame_rate));
896 std::cerr << "Audio " << " beat where : " << beat_where << " frame : " << frame <<std::endl;
899 ts.set_frame (frame);
900 MetricSectionFrameSorter fcmp;
903 Metrics::const_iterator i;
904 TempoSection* prev_ts = 0;
905 TempoSection* next_ts = 0;
907 for (i = metrics.begin(); i != metrics.end(); ++i) {
909 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
911 if (t->frame() >= frame) {
919 for (i = metrics.begin(); i != metrics.end(); ++i) {
921 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
923 if (t->frame() > frame) {
931 /* set the start beat - we need to reset the function constant before beat calculations make sense*/
932 prev_ts->set_c_func (prev_ts->compute_c_func (ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate));
934 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), _frame_rate);
935 double beats = beats_to_ts + prev_ts->beat();
938 if (next_ts->beat() < beats) {
939 /* with frame-based editing, it is possible to get in a
940 situation where if the tempo was placed at the mouse pointer frame,
941 the following music-based tempo would jump to an earlier frame,
942 changing the beat beat of the moved tempo.
943 in this case, we have to do some beat-based comparison TODO
945 } else if (prev_ts->beat() > beats) {
946 ts.set_beat (prev_ts->beat());
955 MetricSectionSorter cmp;
960 recompute_map (false);
963 MetricPositionChanged (); // Emit Signal
967 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
970 Glib::Threads::RWLock::WriterLock lm (lock);
971 add_tempo_locked (tempo, where, true, type);
975 PropertyChanged (PropertyChange ());
979 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
982 Glib::Threads::RWLock::WriterLock lm (lock);
983 add_tempo_locked (tempo, frame, true, type);
987 PropertyChanged (PropertyChange ());
991 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
994 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
999 recompute_map (false);
1004 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
1006 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
1007 std::cerr << "add tempo locked frame = " << ts->frame() << " pos lock : " << ts->position_lock_style() << std::endl;
1012 recompute_map (false);
1017 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
1020 Glib::Threads::RWLock::WriterLock lm (lock);
1021 MeterSection& first (first_meter());
1022 if (ms.beat() != first.beat()) {
1023 remove_meter_locked (ms);
1024 add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
1026 /* cannot move the first meter section */
1027 *static_cast<Meter*>(&first) = meter;
1028 recompute_map (true);
1032 PropertyChanged (PropertyChange ());
1036 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1039 Glib::Threads::RWLock::WriterLock lm (lock);
1040 MeterSection& first (first_meter());
1041 if (ms.beat() != first.beat()) {
1042 remove_meter_locked (ms);
1043 add_meter_locked (meter, frame, true);
1045 /* cannot move the first meter section */
1046 *static_cast<Meter*>(&first) = meter;
1047 recompute_map (true);
1051 PropertyChanged (PropertyChange ());
1056 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1059 Glib::Threads::RWLock::WriterLock lm (lock);
1060 add_meter_locked (meter, beat, where, true);
1065 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1070 PropertyChanged (PropertyChange ());
1074 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1077 Glib::Threads::RWLock::WriterLock lm (lock);
1078 add_meter_locked (meter, frame, true);
1083 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1088 PropertyChanged (PropertyChange ());
1092 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1094 /* a new meter always starts a new bar on the first beat. so
1095 round the start time appropriately. remember that
1096 `where' is based on the existing tempo map, not
1097 the result after we insert the new meter.
1101 if (where.beats != 1) {
1106 /* new meters *always* start on a beat. */
1109 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
1112 recompute_map (true);
1118 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1121 /* MusicTime meters *always* start on 1|1|0. */
1122 MeterSection* ms = new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor());
1124 pair<double, BBT_Time> pr;
1128 /* just a dummy - the actual beat should be applied in recompute_map() as thins is AudioTime */
1135 recompute_map (true);
1141 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1143 Tempo newtempo (beats_per_minute, note_type);
1146 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1147 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1149 Glib::Threads::RWLock::WriterLock lm (lock);
1150 *((Tempo*) t) = newtempo;
1151 recompute_map (false);
1153 PropertyChanged (PropertyChange ());
1160 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1162 Tempo newtempo (beats_per_minute, note_type);
1165 TempoSection* first;
1166 Metrics::iterator i;
1168 /* find the TempoSection immediately preceding "where"
1171 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
1173 if ((*i)->frame() > where) {
1179 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1189 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1199 Glib::Threads::RWLock::WriterLock lm (lock);
1200 /* cannot move the first tempo section */
1201 *((Tempo*)prev) = newtempo;
1202 recompute_map (false);
1205 PropertyChanged (PropertyChange ());
1209 TempoMap::first_meter () const
1211 const MeterSection *m = 0;
1213 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1214 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1219 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1220 abort(); /*NOTREACHED*/
1225 TempoMap::first_meter ()
1227 MeterSection *m = 0;
1229 /* CALLER MUST HOLD LOCK */
1231 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1232 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1237 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1238 abort(); /*NOTREACHED*/
1243 TempoMap::first_tempo () const
1245 const TempoSection *t = 0;
1247 /* CALLER MUST HOLD LOCK */
1249 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1250 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1255 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1256 abort(); /*NOTREACHED*/
1261 TempoMap::first_tempo ()
1263 TempoSection *t = 0;
1265 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1266 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1271 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1272 abort(); /*NOTREACHED*/
1277 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1279 /* CALLER MUST HOLD WRITE LOCK */
1283 /* we will actually stop once we hit
1290 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1293 /* silly call from Session::process() during startup
1298 Metrics::const_iterator i;
1299 TempoSection* prev_ts = 0;
1301 for (i = metrics.begin(); i != metrics.end(); ++i) {
1304 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1306 if (t->position_lock_style() == AudioTime) {
1307 if (prev_ts->type() == TempoSection::Ramp) {
1308 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame() - prev_ts->frame(), _frame_rate));
1309 t->set_beat (prev_ts->beat_at_frame (t->frame() - prev_ts->frame(), _frame_rate) + prev_ts->beat());
1311 prev_ts->set_c_func (0.0);
1312 t->set_beat (prev_ts->beat_at_frame (t->frame() - prev_ts->frame(), _frame_rate) + prev_ts->beat());
1314 } else if (t->position_lock_style() == MusicTime) {
1315 if (prev_ts->type() == TempoSection::Ramp) {
1316 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat() - prev_ts->beat(), _frame_rate);
1317 t->set_frame (prev_ts->frame_at_beat (t->beat() - prev_ts->beat(), _frame_rate) + prev_ts->frame());
1319 double const ticks_relative_to_prev = (t->beat() - prev_ts->beat()) * BBT_Time::ticks_per_beat;
1320 framecnt_t const duration = (framecnt_t) floor (ticks_relative_to_prev * prev_ts->frames_per_beat (_frame_rate)
1321 * BBT_Time::ticks_per_beat);
1322 prev_ts->set_c_func (0.0);
1323 t->set_frame (duration + prev_ts->frame());
1331 MeterSection* meter = 0;
1333 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1334 /* We now have the tempo map set. use it to set meter positions.*/
1335 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1336 if (meter->position_lock_style() == AudioTime) {
1337 /* a frame based meter has to have a 1|1|0 bbt */
1338 pair<double, BBT_Time> pr;
1345 pr.first = tick_at_frame (meter->frame()) / BBT_Time::ticks_per_beat;
1347 meter->set_beat (pr);
1348 } else if (meter->position_lock_style() == MusicTime) {
1349 meter->set_frame (frame_at_tick (meter->beat() * BBT_Time::ticks_per_beat));
1357 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1359 Glib::Threads::RWLock::ReaderLock lm (lock);
1360 TempoMetric m (first_meter(), first_tempo());
1362 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1363 at something, because we insert the default tempo and meter during
1364 TempoMap construction.
1366 now see if we can find better candidates.
1369 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1371 if ((*i)->frame() > frame) {
1384 /* XX meters only */
1386 TempoMap::metric_at (BBT_Time bbt) const
1388 Glib::Threads::RWLock::ReaderLock lm (lock);
1389 TempoMetric m (first_meter(), first_tempo());
1391 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1392 at something, because we insert the default tempo and meter during
1393 TempoMap construction.
1395 now see if we can find better candidates.
1398 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1400 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1401 BBT_Time section_start (mw->bbt());
1403 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1415 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1417 Glib::Threads::RWLock::ReaderLock lm (lock);
1423 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1426 bbt = beats_to_bbt_locked (beat_at_frame (frame));
1430 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1432 Glib::Threads::RWLock::ReaderLock lm (lock);
1433 return bbt_to_beats_locked (bbt);
1437 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1439 /* CALLER HOLDS READ LOCK */
1441 double accumulated_beats = 0.0;
1442 double accumulated_bars = 0.0;
1443 MeterSection* prev_ms = 0;
1445 Metrics::const_iterator i;
1447 for (i = metrics.begin(); i != metrics.end(); ++i) {
1449 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1450 double bars_to_m = 0.0;
1452 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1454 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1458 accumulated_beats += m->beat() - prev_ms->beat();
1459 accumulated_bars += bars_to_m;
1465 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1466 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1467 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1472 TempoMap::beats_to_bbt (double beats)
1474 Glib::Threads::RWLock::ReaderLock lm (lock);
1475 return beats_to_bbt_locked (beats);
1479 TempoMap::beats_to_bbt_locked (double beats)
1481 /* CALLER HOLDS READ LOCK */
1483 MeterSection* prev_ms = 0;
1484 uint32_t accumulated_bars = 0;
1486 Metrics::const_iterator i;
1488 for (i = metrics.begin(); i != metrics.end(); ++i) {
1489 MeterSection* m = 0;
1491 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1493 if (beats < m->beat()) {
1494 /* this is the meter after the one our beat is on*/
1499 /* we need a whole number of bars. */
1500 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1507 double const beats_in_ms = beats - prev_ms->beat();
1508 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1509 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1510 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1511 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1515 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1516 ret.beats = (uint32_t) floor (remaining_beats);
1517 ret.bars = total_bars;
1519 /* 0 0 0 to 1 1 0 - based mapping*/
1523 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1525 ret.ticks -= BBT_Time::ticks_per_beat;
1528 if (ret.beats > prev_ms->divisions_per_bar()) {
1537 TempoMap::tick_at_frame (framecnt_t frame) const
1539 /* HOLD (at least) THE READER LOCK */
1541 Metrics::const_iterator i;
1542 TempoSection* prev_ts = 0;
1543 double accumulated_ticks = 0.0;
1545 for (i = metrics.begin(); i != metrics.end(); ++i) {
1548 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1550 if ((prev_ts) && frame < t->frame()) {
1551 /*the previous ts is the one containing the frame */
1553 framepos_t const time = frame - prev_ts->frame();
1555 return prev_ts->tick_at_frame (time, _frame_rate) + accumulated_ticks;
1558 if (prev_ts && t->frame() > prev_ts->frame()) {
1559 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1566 /* treated as constant for this ts */
1567 framecnt_t const frames_in_section = frame - prev_ts->frame();
1568 double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1570 return ticks_in_section + accumulated_ticks;
1575 TempoMap::frame_at_tick (double tick) const
1577 /* HOLD THE READER LOCK */
1579 double accumulated_ticks = 0.0;
1580 double accumulated_ticks_to_prev = 0.0;
1581 const TempoSection* prev_ts = 0;
1583 Metrics::const_iterator i;
1585 for (i = metrics.begin(); i != metrics.end(); ++i) {
1587 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1589 if (prev_ts && t->frame() > prev_ts->frame()) {
1590 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1593 if (prev_ts && tick < accumulated_ticks) {
1594 /* prev_ts is the one affecting us. */
1596 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1598 return prev_ts->frame_at_tick (ticks_in_section, _frame_rate) + prev_ts->frame();
1600 accumulated_ticks_to_prev = accumulated_ticks;
1604 /* must be treated as constant, irrespective of _type */
1605 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1606 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1608 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1614 TempoMap::beat_at_frame (framecnt_t frame) const
1616 Glib::Threads::RWLock::ReaderLock lm (lock);
1618 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1622 TempoMap::frame_at_beat (double beat) const
1624 Glib::Threads::RWLock::ReaderLock lm (lock);
1626 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1630 TempoMap::frame_time (const BBT_Time& bbt)
1633 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1637 if (bbt.beats < 1) {
1638 throw std::logic_error ("beats are counted from one");
1640 Glib::Threads::RWLock::ReaderLock lm (lock);
1642 framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
1649 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1652 Glib::Threads::RWLock::ReaderLock lm (lock);
1654 Metrics::const_iterator i;
1655 TempoSection* first = 0;
1656 TempoSection* second = 0;
1658 for (i = metrics.begin(); i != metrics.end(); ++i) {
1661 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1663 if ((*i)->frame() > pos) {
1671 if (first && second) {
1672 framepos_t const time = pos - first->frame();
1673 double const tick_at_time = first->tick_at_frame (time, _frame_rate);
1674 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1675 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1677 return time_at_bbt - time;
1680 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1681 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1685 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1687 return round_to_type (fr, dir, Bar);
1691 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1693 return round_to_type (fr, dir, Beat);
1697 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1699 Glib::Threads::RWLock::ReaderLock lm (lock);
1701 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1702 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1703 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1705 ticks -= beats * BBT_Time::ticks_per_beat;
1708 /* round to next (or same iff dir == RoundUpMaybe) */
1710 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1712 if (mod == 0 && dir == RoundUpMaybe) {
1713 /* right on the subdivision, which is fine, so do nothing */
1715 } else if (mod == 0) {
1716 /* right on the subdivision, so the difference is just the subdivision ticks */
1717 ticks += ticks_one_subdivisions_worth;
1720 /* not on subdivision, compute distance to next subdivision */
1722 ticks += ticks_one_subdivisions_worth - mod;
1725 if (ticks >= BBT_Time::ticks_per_beat) {
1726 ticks -= BBT_Time::ticks_per_beat;
1728 } else if (dir < 0) {
1730 /* round to previous (or same iff dir == RoundDownMaybe) */
1732 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1734 if (difference == 0 && dir == RoundDownAlways) {
1735 /* right on the subdivision, but force-rounding down,
1736 so the difference is just the subdivision ticks */
1737 difference = ticks_one_subdivisions_worth;
1740 if (ticks < difference) {
1741 ticks = BBT_Time::ticks_per_beat - ticks;
1743 ticks -= difference;
1747 /* round to nearest */
1750 /* compute the distance to the previous and next subdivision */
1752 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1754 /* closer to the next subdivision, so shift forward */
1756 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1758 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1760 if (ticks > BBT_Time::ticks_per_beat) {
1762 ticks -= BBT_Time::ticks_per_beat;
1763 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1766 } else if (rem > 0) {
1768 /* closer to previous subdivision, so shift backward */
1772 /* can't go backwards past zero, so ... */
1775 /* step back to previous beat */
1777 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1778 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1780 ticks = lrint (ticks - rem);
1781 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1784 /* on the subdivision, do nothing */
1787 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1791 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1793 Glib::Threads::RWLock::ReaderLock lm (lock);
1795 double const beat_at_framepos = beat_at_frame (frame);
1797 BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1802 /* find bar previous to 'frame' */
1805 return frame_time (bbt);
1807 } else if (dir > 0) {
1808 /* find bar following 'frame' */
1812 return frame_time (bbt);
1814 /* true rounding: find nearest bar */
1815 framepos_t raw_ft = frame_time (bbt);
1818 framepos_t prev_ft = frame_time (bbt);
1820 framepos_t next_ft = frame_time (bbt);
1822 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1833 return frame_at_beat (floor (beat_at_framepos));
1834 } else if (dir > 0) {
1835 return frame_at_beat (ceil (beat_at_framepos));
1837 return frame_at_beat (floor (beat_at_framepos + 0.5));
1846 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1847 framepos_t lower, framepos_t upper)
1849 Glib::Threads::RWLock::ReaderLock lm (lock);
1850 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1851 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1853 while (cnt <= upper_beat) {
1854 framecnt_t const pos = frame_at_beat (cnt);
1855 MeterSection const meter = meter_section_at (pos);
1856 Tempo const tempo = tempo_at (pos);
1857 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
1859 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1865 TempoMap::tempo_section_at (framepos_t frame) const
1867 Glib::Threads::RWLock::ReaderLock lm (lock);
1869 Metrics::const_iterator i;
1870 TempoSection* prev = 0;
1872 for (i = metrics.begin(); i != metrics.end(); ++i) {
1875 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1877 if ((*i)->frame() > frame) {
1887 abort(); /*NOTREACHED*/
1893 /* don't use this to calculate length (the tempo is only correct for this frame).
1894 do that stuff based on the beat_at_frame and frame_at_beat api
1897 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1899 Glib::Threads::RWLock::ReaderLock lm (lock);
1901 const TempoSection* ts_at = &tempo_section_at (frame);
1902 const TempoSection* ts_after = 0;
1903 Metrics::const_iterator i;
1905 for (i = metrics.begin(); i != metrics.end(); ++i) {
1908 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1910 if ((*i)->frame() > frame) {
1918 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), _frame_rate));
1920 /* must be treated as constant tempo */
1921 return ts_at->frames_per_beat (_frame_rate);
1925 TempoMap::tempo_at (framepos_t frame) const
1927 Glib::Threads::RWLock::ReaderLock lm (lock);
1929 TempoMetric m (metric_at (frame));
1930 TempoSection* prev_ts = 0;
1932 Metrics::const_iterator i;
1934 for (i = metrics.begin(); i != metrics.end(); ++i) {
1936 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1937 if ((prev_ts) && t->frame() > frame) {
1938 /* this is the one past frame */
1939 framepos_t const time = frame - prev_ts->frame();
1940 double const ret = prev_ts->tempo_at_frame (time, _frame_rate);
1941 Tempo const ret_tempo (ret, m.tempo().note_type ());
1953 TempoMap::meter_section_at (framepos_t frame) const
1955 Glib::Threads::RWLock::ReaderLock lm (lock);
1956 Metrics::const_iterator i;
1957 MeterSection* prev = 0;
1959 for (i = metrics.begin(); i != metrics.end(); ++i) {
1962 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1964 if ((*i)->frame() > frame) {
1974 abort(); /*NOTREACHED*/
1981 TempoMap::meter_at (framepos_t frame) const
1983 TempoMetric m (metric_at (frame));
1988 TempoMap::get_state ()
1990 Metrics::const_iterator i;
1991 XMLNode *root = new XMLNode ("TempoMap");
1994 Glib::Threads::RWLock::ReaderLock lm (lock);
1995 for (i = metrics.begin(); i != metrics.end(); ++i) {
1996 root->add_child_nocopy ((*i)->get_state());
2004 TempoMap::set_state (const XMLNode& node, int /*version*/)
2007 Glib::Threads::RWLock::WriterLock lm (lock);
2010 XMLNodeConstIterator niter;
2011 Metrics old_metrics (metrics);
2012 MeterSection* last_meter = 0;
2015 nlist = node.children();
2017 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2018 XMLNode* child = *niter;
2020 if (child->name() == TempoSection::xml_state_node_name) {
2023 TempoSection* ts = new TempoSection (*child);
2024 metrics.push_back (ts);
2026 if (ts->bar_offset() < 0.0) {
2028 //ts->update_bar_offset_from_bbt (*last_meter);
2033 catch (failed_constructor& err){
2034 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2035 metrics = old_metrics;
2039 } else if (child->name() == MeterSection::xml_state_node_name) {
2042 MeterSection* ms = new MeterSection (*child);
2043 metrics.push_back (ms);
2047 catch (failed_constructor& err) {
2048 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2049 metrics = old_metrics;
2055 if (niter == nlist.end()) {
2056 MetricSectionSorter cmp;
2059 /* check for legacy sessions where bbt was the base musical unit for tempo */
2060 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2061 MeterSection* prev_ms;
2062 TempoSection* prev_ts;
2063 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2064 if (prev_ms->beat() < 0.0) {
2065 /*XX we cannot possibly make this work??. */
2066 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());
2067 prev_ms->set_beat (start);
2069 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2070 if (prev_ts->beat() < 0.0) {
2071 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);
2072 prev_ts->set_beat (start);
2077 /* check for multiple tempo/meters at the same location, which
2078 ardour2 somehow allowed.
2081 Metrics::iterator prev = metrics.end();
2082 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2083 if (prev != metrics.end()) {
2085 MeterSection* prev_ms;
2087 TempoSection* prev_ts;
2088 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2089 if (prev_ms->beat() == ms->beat()) {
2090 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2091 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2094 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2095 if (prev_ts->beat() == ts->beat()) {
2096 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2097 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2105 recompute_map (true, -1);
2108 PropertyChanged (PropertyChange ());
2114 TempoMap::dump (std::ostream& o) const
2116 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2117 const MeterSection* m;
2118 const TempoSection* t;
2120 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2122 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2123 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? "
2124 << t->movable() << ')' << endl;
2125 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2126 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2127 << " (movable? " << m->movable() << ')' << endl;
2133 TempoMap::n_tempos() const
2135 Glib::Threads::RWLock::ReaderLock lm (lock);
2138 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2139 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2148 TempoMap::n_meters() const
2150 Glib::Threads::RWLock::ReaderLock lm (lock);
2153 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2154 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2163 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2166 Glib::Threads::RWLock::WriterLock lm (lock);
2167 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2168 if ((*i)->frame() >= where && (*i)->movable ()) {
2169 (*i)->set_frame ((*i)->frame() + amount);
2173 /* now reset the BBT time of all metrics, based on their new
2174 * audio time. This is the only place where we do this reverse
2178 Metrics::iterator i;
2179 const MeterSection* meter;
2180 const TempoSection* tempo;
2184 meter = &first_meter ();
2185 tempo = &first_tempo ();
2190 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2193 MetricSection* prev = 0;
2195 for (i = metrics.begin(); i != metrics.end(); ++i) {
2198 //TempoMetric metric (*meter, *tempo);
2199 MeterSection* ms = const_cast<MeterSection*>(meter);
2200 TempoSection* ts = const_cast<TempoSection*>(tempo);
2203 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2204 ts->set_beat (t->beat());
2206 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2207 ts->set_beat (m->beat());
2209 ts->set_frame (prev->frame());
2213 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2214 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2215 ms->set_beat (start);
2217 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2218 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2219 ms->set_beat (start);
2221 ms->set_frame (prev->frame());
2225 // metric will be at frames=0 bbt=1|1|0 by default
2226 // which is correct for our purpose
2229 // cerr << bbt << endl;
2231 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2232 t->set_beat (beat_at_frame (m->frame()));
2234 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2235 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2236 bbt_time (m->frame(), bbt);
2238 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2244 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2245 /* round up to next beat */
2251 if (bbt.beats != 1) {
2252 /* round up to next bar */
2257 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
2258 m->set_beat (start);
2260 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2262 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2263 abort(); /*NOTREACHED*/
2269 recompute_map (true);
2273 PropertyChanged (PropertyChange ());
2276 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2280 std::list<MetricSection*> metric_kill_list;
2282 TempoSection* last_tempo = NULL;
2283 MeterSection* last_meter = NULL;
2284 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2285 bool meter_after = false; // is there a meter marker likewise?
2287 Glib::Threads::RWLock::WriterLock lm (lock);
2288 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2289 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2290 metric_kill_list.push_back(*i);
2291 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2294 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2298 else if ((*i)->frame() >= where) {
2299 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2300 (*i)->set_frame ((*i)->frame() - amount);
2301 if ((*i)->frame() == where) {
2302 // marker was immediately after end of range
2303 tempo_after = dynamic_cast<TempoSection*> (*i);
2304 meter_after = dynamic_cast<MeterSection*> (*i);
2310 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2311 if (last_tempo && !tempo_after) {
2312 metric_kill_list.remove(last_tempo);
2313 last_tempo->set_frame(where);
2316 if (last_meter && !meter_after) {
2317 metric_kill_list.remove(last_meter);
2318 last_meter->set_frame(where);
2322 //remove all the remaining metrics
2323 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2329 recompute_map (true);
2332 PropertyChanged (PropertyChange ());
2336 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2337 * pos can be -ve, if required.
2340 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2342 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2345 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2347 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2349 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2352 /** Add the BBT interval op to pos and return the result */
2354 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2356 cerr << "framepos_plus_bbt - untested" << endl;
2357 Glib::Threads::RWLock::ReaderLock lm (lock);
2359 Metrics::const_iterator i;
2360 const MeterSection* meter;
2361 const MeterSection* m;
2362 const TempoSection* tempo;
2363 const TempoSection* next_tempo = 0;
2364 const TempoSection* t;
2365 double frames_per_beat;
2366 framepos_t effective_pos = max (pos, (framepos_t) 0);
2368 meter = &first_meter ();
2369 tempo = &first_tempo ();
2374 /* find the starting metrics for tempo & meter */
2376 for (i = metrics.begin(); i != metrics.end(); ++i) {
2378 if ((*i)->frame() > effective_pos) {
2382 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2384 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2389 for (i = metrics.begin(); i != metrics.end(); ++i) {
2390 if ((*i)->frame() > effective_pos) {
2391 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2400 meter -> the Meter for "pos"
2401 tempo -> the Tempo for "pos"
2402 next_tempo -> the Tempo after "pos" or 0
2403 i -> for first new metric after "pos", possibly metrics.end()
2406 /* now comes the complicated part. we have to add one beat a time,
2407 checking for a new metric on every beat.
2411 /* fpb is used for constant tempo */
2412 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2419 /* check if we need to use a new metric section: has adding frames moved us
2420 to or after the start of the next metric section? in which case, use it.
2423 if (i != metrics.end()) {
2424 if ((*i)->frame() <= pos) {
2426 /* about to change tempo or meter, so add the
2427 * number of frames for the bars we've just
2428 * traversed before we change the
2429 * frames_per_beat value.
2432 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2437 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2439 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2444 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2446 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2450 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2457 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2459 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2466 /* given the current meter, have we gone past the end of the bar ? */
2471 /* check if we need to use a new metric section: has adding frames moved us
2472 to or after the start of the next metric section? in which case, use it.
2475 if (i != metrics.end()) {
2476 if ((*i)->frame() <= pos) {
2478 /* about to change tempo or meter, so add the
2479 * number of frames for the beats we've just
2480 * traversed before we change the
2481 * frames_per_beat value.
2484 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2489 pos += tempo->frame_at_beat (beats, _frame_rate);
2491 pos += llrint (beats * frames_per_beat);
2496 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2498 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2502 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2508 pos += tempo->frame_at_beat (beats, _frame_rate);
2510 pos += llrint (beats * frames_per_beat);
2514 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2521 /** Count the number of beats that are equivalent to distance when going forward,
2525 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2527 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2531 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2537 operator<< (std::ostream& o, const Meter& m) {
2538 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2542 operator<< (std::ostream& o, const Tempo& t) {
2543 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2547 operator<< (std::ostream& o, const MetricSection& section) {
2549 o << "MetricSection @ " << section.frame() << ' ';
2551 const TempoSection* ts;
2552 const MeterSection* ms;
2554 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2555 o << *((const Tempo*) ts);
2556 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2557 //o << *((const Meter*) ms);