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;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
76 : MetricSection (0.0), Tempo (TempoMap::default_tempo())
78 const XMLProperty *prop;
84 if ((prop = node.property ("start")) != 0) {
85 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
89 /* legacy session - start used to be in bbt*/
95 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
99 if ((prop = node.property ("pulse")) != 0) {
100 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
101 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
106 if ((prop = node.property ("frame")) != 0) {
107 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
108 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
114 if ((prop = node.property ("beats-per-minute")) == 0) {
115 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
116 throw failed_constructor();
119 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
120 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
121 throw failed_constructor();
124 if ((prop = node.property ("note-type")) == 0) {
125 /* older session, make note type be quarter by default */
128 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
129 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
130 throw failed_constructor();
134 if ((prop = node.property ("movable")) == 0) {
135 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
136 throw failed_constructor();
139 set_movable (string_is_affirmative (prop->value()));
141 if ((prop = node.property ("active")) == 0) {
142 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
145 set_active (string_is_affirmative (prop->value()));
148 if ((prop = node.property ("bar-offset")) == 0) {
151 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
152 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
153 throw failed_constructor();
157 if ((prop = node.property ("tempo-type")) == 0) {
160 _type = Type (string_2_enum (prop->value(), _type));
163 if ((prop = node.property ("lock-style")) == 0) {
164 set_position_lock_style (MusicTime);
166 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
171 TempoSection::get_state() const
173 XMLNode *root = new XMLNode (xml_state_node_name);
177 snprintf (buf, sizeof (buf), "%f", pulse());
178 root->add_property ("pulse", buf);
179 snprintf (buf, sizeof (buf), "%li", frame());
180 root->add_property ("frame", buf);
181 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
182 root->add_property ("beats-per-minute", buf);
183 snprintf (buf, sizeof (buf), "%f", _note_type);
184 root->add_property ("note-type", buf);
185 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
186 // root->add_property ("bar-offset", buf);
187 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
188 root->add_property ("movable", buf);
189 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
190 root->add_property ("active", buf);
191 root->add_property ("tempo-type", enum_2_string (_type));
192 root->add_property ("lock-style", enum_2_string (position_lock_style()));
199 TempoSection::update_bar_offset_from_bbt (const Meter& m)
201 _bar_offset = (pulse() * BBT_Time::ticks_per_beat) /
202 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
204 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, pulse(), m.divisions_per_bar()));
208 TempoSection::set_type (Type type)
213 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
216 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
219 if (_type == Constant) {
220 return pulses_per_minute();
223 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
226 /** returns the zero-based frame (relative to session)
227 where the tempo in whole pulses per minute occurs in this section.
228 beat b is only used for constant tempos.
229 note that the tempo map may have multiple such values.
232 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
234 if (_type == Constant) {
235 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
238 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
240 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
243 TempoSection::tempo_at_pulse (const double& p) const
246 if (_type == Constant) {
247 return pulses_per_minute();
249 double const ppm = pulse_tempo_at_pulse (p - pulse());
253 /** returns the zero-based beat (relative to session)
254 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
255 note that the session tempo map may have multiple beats at a given tempo.
258 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
260 if (_type == Constant) {
261 double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
265 return pulse_at_pulse_tempo (ppm) + pulse();
268 /** returns the zero-based pulse (relative to session origin)
269 where the zero-based frame (relative to session)
273 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
275 if (_type == Constant) {
276 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
279 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
282 /** returns the zero-based frame (relative to session start frame)
283 where the zero-based pulse (relative to session start)
288 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
290 if (_type == Constant) {
291 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
294 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
302 Tt----|-----------------*|
303 Ta----|--------------|* |
309 _______________|___|____
310 time a t (next tempo)
313 Duration in beats at time a is the integral of some Tempo function.
314 In our case, the Tempo function (Tempo at time t) is
317 with function constant
322 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
323 b(t) = T0(e^(ct) - 1) / c
325 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:
326 t(b) = log((cb / T0) + 1) / c
328 The time t at which Tempo T occurs is a as above:
329 t(T) = log(T / T0) / c
331 The beat at which a Tempo T occurs is:
334 The Tempo at which beat b occurs is:
337 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
338 Our problem is that we usually don't know t.
339 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.
340 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
341 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
343 By substituting our expanded t as a in the c function above, our problem is reduced to:
344 c = T0 (e^(log (Ta / T0)) - 1) / b
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (exp (log_tempo_ratio) - 1) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_bpm, double c_func) const
399 return log (end_bpm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_bpm, double end_time) const
406 return log (end_bpm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return ((exp (_c_func * time)) - 1) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log (((_c_func * pulse) / pulses_per_minute()) + 1) / _c_func;
453 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
457 if (_bar_offset < 0.0) {
464 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
465 new_beat = ticks / BBT_Time::ticks_per_beat;
467 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
468 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
470 set_pulse (new_beat);
473 /***********************************************************************/
475 const string MeterSection::xml_state_node_name = "Meter";
477 MeterSection::MeterSection (const XMLNode& node)
478 : MetricSection (0.0), Meter (TempoMap::default_meter())
480 XMLProperty const * prop;
483 const XMLProperty *prop;
487 framepos_t frame = 0;
488 pair<double, BBT_Time> start;
490 if ((prop = node.property ("start")) != 0) {
491 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
495 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
497 /* legacy session - start used to be in bbt*/
501 error << _("MeterSection XML node has no \"start\" property") << endmsg;
504 if ((prop = node.property ("pulse")) != 0) {
505 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
506 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
511 if ((prop = node.property ("beat")) != 0) {
512 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
513 error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
519 if ((prop = node.property ("bbt")) == 0) {
520 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
521 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
525 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
526 //throw failed_constructor();
532 if ((prop = node.property ("frame")) != 0) {
533 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
534 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
540 /* beats-per-bar is old; divisions-per-bar is new */
542 if ((prop = node.property ("divisions-per-bar")) == 0) {
543 if ((prop = node.property ("beats-per-bar")) == 0) {
544 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
545 throw failed_constructor();
548 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
549 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
550 throw failed_constructor();
553 if ((prop = node.property ("note-type")) == 0) {
554 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
555 throw failed_constructor();
557 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
558 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
559 throw failed_constructor();
562 if ((prop = node.property ("lock-style")) == 0) {
563 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
564 set_position_lock_style (MusicTime);
566 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
569 if ((prop = node.property ("movable")) == 0) {
570 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
571 throw failed_constructor();
574 set_movable (string_is_affirmative (prop->value()));
578 MeterSection::get_state() const
580 XMLNode *root = new XMLNode (xml_state_node_name);
584 snprintf (buf, sizeof (buf), "%lf", pulse());
585 root->add_property ("pulse", buf);
586 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
590 root->add_property ("bbt", buf);
591 snprintf (buf, sizeof (buf), "%lf", beat());
592 root->add_property ("beat", buf);
593 snprintf (buf, sizeof (buf), "%f", _note_type);
594 root->add_property ("note-type", buf);
595 snprintf (buf, sizeof (buf), "%li", frame());
596 root->add_property ("frame", buf);
597 root->add_property ("lock-style", enum_2_string (position_lock_style()));
598 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
599 root->add_property ("divisions-per-bar", buf);
600 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
601 root->add_property ("movable", buf);
606 /***********************************************************************/
608 struct MetricSectionSorter {
609 bool operator() (const MetricSection* a, const MetricSection* b) {
610 return a->pulse() < b->pulse();
614 struct MetricSectionFrameSorter {
615 bool operator() (const MetricSection* a, const MetricSection* b) {
616 return a->frame() < b->frame();
620 TempoMap::TempoMap (framecnt_t fr)
623 BBT_Time start (1, 1, 0);
625 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
626 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
628 t->set_movable (false);
629 m->set_movable (false);
631 /* note: frame time is correct (zero) for both of these */
633 _metrics.push_back (t);
634 _metrics.push_back (m);
638 TempoMap::~TempoMap ()
643 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
645 bool removed = false;
648 Glib::Threads::RWLock::WriterLock lm (lock);
649 if ((removed = remove_tempo_locked (tempo))) {
650 if (complete_operation) {
651 recompute_map (_metrics);
656 if (removed && complete_operation) {
657 PropertyChanged (PropertyChange ());
662 TempoMap::remove_tempo_locked (const TempoSection& tempo)
666 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
667 if (dynamic_cast<TempoSection*> (*i) != 0) {
668 if (tempo.frame() == (*i)->frame()) {
669 if ((*i)->movable()) {
681 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
683 bool removed = false;
686 Glib::Threads::RWLock::WriterLock lm (lock);
687 if ((removed = remove_meter_locked (tempo))) {
688 if (complete_operation) {
689 recompute_map (_metrics);
694 if (removed && complete_operation) {
695 PropertyChanged (PropertyChange ());
700 TempoMap::remove_meter_locked (const MeterSection& tempo)
704 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
705 if (dynamic_cast<MeterSection*> (*i) != 0) {
706 if (tempo.frame() == (*i)->frame()) {
707 if ((*i)->movable()) {
719 TempoMap::do_insert (MetricSection* section)
721 bool need_add = true;
722 /* we only allow new meters to be inserted on beat 1 of an existing
726 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
727 //assert (m->bbt().ticks == 0);
729 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
731 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
732 corrected.second.beats = 1;
733 corrected.second.ticks = 0;
734 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
735 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
736 m->bbt(), corrected.second) << endmsg;
737 //m->set_pulse (corrected);
741 /* Look for any existing MetricSection that is of the same type and
742 in the same bar as the new one, and remove it before adding
743 the new one. Note that this means that if we find a matching,
744 existing section, we can break out of the loop since we're
745 guaranteed that there is only one such match.
748 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
750 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
751 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
752 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
753 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
755 if (tempo && insert_tempo) {
758 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
759 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
761 if (!tempo->movable()) {
763 /* can't (re)move this section, so overwrite
764 * its data content (but not its properties as
768 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
769 (*i)->set_position_lock_style (AudioTime);
777 } else if (meter && insert_meter) {
781 bool const ipm = insert_meter->position_lock_style() == MusicTime;
783 if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
785 if (!meter->movable()) {
787 /* can't (re)move this section, so overwrite
788 * its data content (but not its properties as
792 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
793 (*i)->set_position_lock_style (insert_meter->position_lock_style());
803 /* non-matching types, so we don't care */
807 /* Add the given MetricSection, if we didn't just reset an existing
812 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
813 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
816 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
817 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
820 bool const ipm = insert_meter->position_lock_style() == MusicTime;
821 if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
826 } else if (insert_tempo) {
827 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
828 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
831 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
832 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
839 _metrics.insert (i, section);
840 //dump (_metrics, std::cerr);
845 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
848 Glib::Threads::RWLock::WriterLock lm (lock);
849 TempoSection& first (first_tempo());
850 if (ts.pulse() != first.pulse()) {
851 remove_tempo_locked (ts);
852 add_tempo_locked (tempo, pulse, true, type);
854 first.set_type (type);
856 /* cannot move the first tempo section */
857 *static_cast<Tempo*>(&first) = tempo;
858 recompute_map (_metrics);
863 PropertyChanged (PropertyChange ());
867 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
870 Glib::Threads::RWLock::WriterLock lm (lock);
871 TempoSection& first (first_tempo());
872 if (ts.frame() != first.frame()) {
873 remove_tempo_locked (ts);
874 add_tempo_locked (tempo, frame, true, type);
876 first.set_type (type);
877 first.set_pulse (0.0);
878 first.set_position_lock_style (AudioTime);
880 /* cannot move the first tempo section */
881 *static_cast<Tempo*>(&first) = tempo;
882 recompute_map (_metrics);
887 PropertyChanged (PropertyChange ());
891 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
894 Glib::Threads::RWLock::WriterLock lm (lock);
895 add_tempo_locked (tempo, pulse, true, type);
898 PropertyChanged (PropertyChange ());
902 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
905 Glib::Threads::RWLock::WriterLock lm (lock);
906 add_tempo_locked (tempo, frame, true, type);
910 PropertyChanged (PropertyChange ());
914 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
916 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
921 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
926 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
928 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
933 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
938 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
941 Glib::Threads::RWLock::WriterLock lm (lock);
942 MeterSection& first (first_meter());
944 const PositionLockStyle pl = ms.position_lock_style();
945 if (ms.pulse() != first.pulse()) {
946 remove_meter_locked (ms);
947 add_meter_locked (meter, pulse_at_beat_locked (_metrics, bbt_to_beats_locked (_metrics, where)), where, true);
949 /* cannot move the first meter section */
950 *static_cast<Meter*>(&first) = meter;
951 first.set_position_lock_style (pl);
952 recompute_map (_metrics);
956 PropertyChanged (PropertyChange ());
960 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
963 Glib::Threads::RWLock::WriterLock lm (lock);
964 MeterSection& first (first_meter());
965 TempoSection& first_t (first_tempo());
967 if (ms.pulse() != first.pulse()) {
968 remove_meter_locked (ms);
969 add_meter_locked (meter, frame, true);
971 /* cannot move the first meter section */
972 *static_cast<Meter*>(&first) = meter;
973 first.set_position_lock_style (AudioTime);
974 first.set_pulse (0.0);
975 first.set_frame (frame);
976 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
977 first.set_beat (beat);
978 recompute_meters (_metrics);
979 first_t.set_frame (first.frame());
980 first_t.set_pulse (0.0);
981 first_t.set_position_lock_style (AudioTime);
983 recompute_map (_metrics);
986 PropertyChanged (PropertyChange ());
991 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
994 Glib::Threads::RWLock::WriterLock lm (lock);
995 add_meter_locked (meter, beat, where, true);
1000 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1001 dump (_metrics, std::cerr);
1005 PropertyChanged (PropertyChange ());
1009 TempoMap::add_meter (const Meter& meter, const framepos_t& frame)
1012 Glib::Threads::RWLock::WriterLock lm (lock);
1013 add_meter_locked (meter, frame, true);
1018 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1019 dump (_metrics, std::cerr);
1023 PropertyChanged (PropertyChange ());
1027 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1029 /* a new meter always starts a new bar on the first beat. so
1030 round the start time appropriately. remember that
1031 `where' is based on the existing tempo map, not
1032 the result after we insert the new meter.
1036 if (where.beats != 1) {
1040 /* new meters *always* start on a beat. */
1042 double pulse = pulse_at_beat_locked (_metrics, beat);
1044 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1045 do_insert (new_meter);
1048 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1054 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1057 MeterSection* new_meter = new MeterSection (frame, 0.0, meter.divisions_per_bar(), meter.note_divisor());
1059 do_insert (new_meter);
1062 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1068 * This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
1069 * taking any possible reordering as a consequence of this into account.
1070 * @param section - the section to be altered
1071 * @param bpm - the new Tempo
1072 * @param bbt - the bbt where the altered tempo will fall
1073 * @return returns - the position in frames where the new tempo section will lie.
1076 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1078 Glib::Threads::RWLock::ReaderLock lm (lock);
1081 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1083 double const beat = bbt_to_beats_locked (future_map, bbt);
1084 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
1085 ret = new_section->frame();
1087 ret = frame_at_beat_locked (_metrics, beat);
1090 Metrics::const_iterator d = future_map.begin();
1091 while (d != future_map.end()) {
1099 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1101 Glib::Threads::RWLock::ReaderLock lm (lock);
1104 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1106 if (solve_map (future_map, new_section, bpm, frame)) {
1107 ret = new_section->pulse();
1109 ret = pulse_at_frame_locked (_metrics, frame);
1112 Metrics::const_iterator d = future_map.begin();
1113 while (d != future_map.end()) {
1121 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1125 Glib::Threads::RWLock::WriterLock lm (lock);
1126 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1127 if (solve_map (future_map, new_section, bpm, frame)) {
1128 solve_map (_metrics, ts, bpm, frame);
1132 Metrics::const_iterator d = future_map.begin();
1133 while (d != future_map.end()) {
1138 MetricPositionChanged (); // Emit Signal
1142 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
1146 Glib::Threads::RWLock::WriterLock lm (lock);
1147 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1148 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
1149 solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
1153 Metrics::const_iterator d = future_map.begin();
1154 while (d != future_map.end()) {
1159 MetricPositionChanged (); // Emit Signal
1163 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
1166 Glib::Threads::RWLock::WriterLock lm (lock);
1167 solve_map (_metrics, ms, mt, frame);
1170 MetricPositionChanged (); // Emit Signal
1174 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
1177 Glib::Threads::RWLock::WriterLock lm (lock);
1178 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
1181 MetricPositionChanged (); // Emit Signal
1185 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
1188 bool can_solve = false;
1190 Glib::Threads::RWLock::WriterLock lm (lock);
1191 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1192 new_section->set_beats_per_minute (bpm.beats_per_minute());
1193 recompute_tempos (future_map);
1195 if (check_solved (future_map, true)) {
1196 ts->set_beats_per_minute (bpm.beats_per_minute());
1197 recompute_map (_metrics);
1202 Metrics::const_iterator d = future_map.begin();
1203 while (d != future_map.end()) {
1208 MetricPositionChanged (); // Emit Signal
1214 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1217 TempoSection* ret = 0;
1220 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1221 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1223 if (t->position_lock_style() == MusicTime) {
1224 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1226 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1228 ret->set_active (t->active());
1229 ret->set_movable (t->movable());
1230 copy.push_back (ret);
1233 TempoSection* cp = 0;
1234 if (t->position_lock_style() == MusicTime) {
1235 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1237 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1239 cp->set_active (t->active());
1240 cp->set_movable (t->movable());
1241 copy.push_back (cp);
1243 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1244 if (m->position_lock_style() == MusicTime) {
1245 copy.push_back (new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1247 copy.push_back (new MeterSection (m->frame(), m->beat(), m->divisions_per_bar(), m->note_divisor()));
1251 recompute_map (copy);
1256 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
1259 TempoSection* new_section = 0;
1262 Glib::Threads::RWLock::ReaderLock lm (lock);
1263 new_section = copy_metrics_and_point (copy, ts);
1266 double const beat = bbt_to_beats_locked (copy, bbt);
1267 bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
1269 Metrics::const_iterator d = copy.begin();
1270 while (d != copy.end()) {
1279 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1281 Tempo newtempo (beats_per_minute, note_type);
1284 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1285 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1290 Glib::Threads::RWLock::WriterLock lm (lock);
1291 *((Tempo*) t) = newtempo;
1292 recompute_map (_metrics);
1294 PropertyChanged (PropertyChange ());
1301 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1303 Tempo newtempo (beats_per_minute, note_type);
1306 TempoSection* first;
1307 Metrics::iterator i;
1309 /* find the TempoSection immediately preceding "where"
1312 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1314 if ((*i)->frame() > where) {
1320 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1333 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1343 Glib::Threads::RWLock::WriterLock lm (lock);
1344 /* cannot move the first tempo section */
1345 *((Tempo*)prev) = newtempo;
1346 recompute_map (_metrics);
1349 PropertyChanged (PropertyChange ());
1353 TempoMap::first_meter () const
1355 const MeterSection *m = 0;
1357 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1358 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1363 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1364 abort(); /*NOTREACHED*/
1369 TempoMap::first_meter ()
1371 MeterSection *m = 0;
1373 /* CALLER MUST HOLD LOCK */
1375 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1376 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1381 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1382 abort(); /*NOTREACHED*/
1387 TempoMap::first_tempo () const
1389 const TempoSection *t = 0;
1391 /* CALLER MUST HOLD LOCK */
1393 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1394 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1402 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1403 abort(); /*NOTREACHED*/
1408 TempoMap::first_tempo ()
1410 TempoSection *t = 0;
1412 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1413 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1421 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1422 abort(); /*NOTREACHED*/
1426 TempoMap::recompute_tempos (Metrics& metrics)
1428 TempoSection* prev_ts = 0;
1430 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1433 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1438 if (t->position_lock_style() == AudioTime) {
1439 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1440 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1443 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1444 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1451 prev_ts->set_c_func (0.0);
1454 /* tempos must be positioned correctly */
1456 TempoMap::recompute_meters (Metrics& metrics)
1458 MeterSection* meter = 0;
1459 MeterSection* prev_m = 0;
1460 double accumulated_beats = 0.0;
1461 uint32_t accumulated_bars = 0;
1463 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1464 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1466 const double beats_in_m = (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
1467 accumulated_beats += beats_in_m;
1468 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1470 if (meter->position_lock_style() == AudioTime) {
1472 pair<double, BBT_Time> bt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
1473 meter->set_beat (bt);
1475 pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1477 if (meter->movable()) {
1478 pulse = pulse_at_frame_locked (metrics, meter->frame());
1483 meter->set_pulse (pulse);
1487 pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1489 pulse = pulse_at_beat_locked (metrics, meter->beat());
1491 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1492 meter->set_pulse (pulse);
1501 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1503 /* CALLER MUST HOLD WRITE LOCK */
1507 /* we will actually stop once we hit
1514 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1517 /* silly call from Session::process() during startup
1522 recompute_tempos (metrics);
1523 recompute_meters (metrics);
1527 TempoMap::pulse_at_beat (const double& beat) const
1529 Glib::Threads::RWLock::ReaderLock lm (lock);
1530 return pulse_at_beat_locked (_metrics, beat);
1534 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1536 MeterSection* prev_ms = 0;
1537 double accumulated_beats = 0.0;
1539 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1541 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1542 if (prev_ms && m->beat() > beat) {
1545 accumulated_beats = m->beat();
1550 double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1555 TempoMap::beat_at_pulse (const double& pulse) const
1557 Glib::Threads::RWLock::ReaderLock lm (lock);
1558 return beat_at_pulse_locked (_metrics, pulse);
1562 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1564 MeterSection* prev_ms = 0;
1565 double accumulated_beats = 0.0;
1567 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1569 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1570 if (prev_ms && m->pulse() > pulse) {
1573 accumulated_beats = m->beat();
1578 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1580 return beats_in_section + accumulated_beats;
1584 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1586 Glib::Threads::RWLock::ReaderLock lm (lock);
1587 TempoMetric m (first_meter(), first_tempo());
1589 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1590 at something, because we insert the default tempo and meter during
1591 TempoMap construction.
1593 now see if we can find better candidates.
1596 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1598 if ((*i)->frame() > frame) {
1612 /* XX meters only */
1614 TempoMap::metric_at (BBT_Time bbt) const
1616 Glib::Threads::RWLock::ReaderLock lm (lock);
1617 TempoMetric m (first_meter(), first_tempo());
1619 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1620 at something, because we insert the default tempo and meter during
1621 TempoMap construction.
1623 now see if we can find better candidates.
1626 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1628 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1629 BBT_Time section_start (mw->bbt());
1631 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1643 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1650 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1653 Glib::Threads::RWLock::ReaderLock lm (lock);
1654 double const beat = beat_at_frame_locked (_metrics, frame);
1656 bbt = beats_to_bbt_locked (_metrics, beat);
1660 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1662 Glib::Threads::RWLock::ReaderLock lm (lock);
1664 return bbt_to_beats_locked (_metrics, bbt);
1668 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1670 /* CALLER HOLDS READ LOCK */
1672 double accumulated_beats = 0.0;
1673 double accumulated_bars = 0.0;
1674 MeterSection* prev_ms = 0;
1675 /* because audio-locked meters have 'fake' integral beats,
1676 there is no pulse offset here.
1678 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1680 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1681 double bars_to_m = 0.0;
1683 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1684 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1687 accumulated_beats = m->beat();
1688 accumulated_bars += bars_to_m;
1694 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1695 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1696 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1702 TempoMap::beats_to_bbt (const double& beats)
1704 Glib::Threads::RWLock::ReaderLock lm (lock);
1706 return beats_to_bbt_locked (_metrics, beats);
1710 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1712 /* CALLER HOLDS READ LOCK */
1713 MeterSection* prev_ms = 0;
1714 const double beats = (b < 0.0) ? 0.0 : b;
1715 uint32_t accumulated_bars = 0;
1716 double accumulated_beats = 0.0;
1718 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1719 MeterSection* m = 0;
1721 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1724 double const beats_to_m = m->beat() - prev_ms->beat();
1725 if (accumulated_beats + beats_to_m > beats) {
1726 /* this is the meter after the one our beat is on*/
1730 /* we need a whole number of bars. */
1731 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1732 accumulated_beats += beats_to_m;
1739 double const beats_in_ms = beats - accumulated_beats;
1740 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1741 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1742 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1743 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1747 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1748 ret.beats = (uint32_t) floor (remaining_beats);
1749 ret.bars = total_bars;
1751 /* 0 0 0 to 1 1 0 - based mapping*/
1755 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1757 ret.ticks -= BBT_Time::ticks_per_beat;
1760 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1769 TempoMap::pulse_to_bbt (const double& pulse)
1771 Glib::Threads::RWLock::ReaderLock lm (lock);
1772 MeterSection* prev_ms = 0;
1773 uint32_t accumulated_bars = 0;
1774 double accumulated_pulses = 0.0;
1776 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1777 MeterSection* m = 0;
1779 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1782 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1783 if (accumulated_pulses + pulses_to_m > pulse) {
1784 /* this is the meter after the one our beat is on*/
1788 /* we need a whole number of bars. */
1789 accumulated_pulses += pulses_to_m;
1790 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1796 double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1797 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1798 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1799 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1800 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1804 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1805 ret.beats = (uint32_t) floor (remaining_beats);
1806 ret.bars = total_bars;
1808 /* 0 0 0 to 1 1 0 mapping*/
1812 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1814 ret.ticks -= BBT_Time::ticks_per_beat;
1817 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1826 TempoMap::beat_at_frame (const framecnt_t& frame) const
1828 Glib::Threads::RWLock::ReaderLock lm (lock);
1829 return beat_at_frame_locked (_metrics, frame);
1833 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1835 framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
1836 double const pulse = pulse_at_frame_locked (metrics, offset_frame);
1838 return beat_at_pulse_locked (metrics, pulse);
1842 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1844 /* HOLD (at least) THE READER LOCK */
1845 TempoSection* prev_ts = 0;
1846 double accumulated_pulses = 0.0;
1848 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1850 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1854 if (prev_ts && t->frame() > frame) {
1855 /*the previous ts is the one containing the frame */
1856 double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1859 accumulated_pulses = t->pulse();
1864 /* treated as constant for this ts */
1865 double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1867 return pulses_in_section + accumulated_pulses;
1871 TempoMap::frame_at_beat (const double& beat) const
1873 Glib::Threads::RWLock::ReaderLock lm (lock);
1874 return frame_at_beat_locked (_metrics, beat);
1878 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1880 framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1881 frameoffset_t const frame_off = frame_offset_at (metrics, frame);
1882 return frame - frame_off;
1886 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1888 /* HOLD THE READER LOCK */
1890 const TempoSection* prev_ts = 0;
1891 double accumulated_pulses = 0.0;
1893 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1896 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1900 if (prev_ts && t->pulse() > pulse) {
1901 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1904 accumulated_pulses = t->pulse();
1908 /* must be treated as constant, irrespective of _type */
1909 double const pulses_in_section = pulse - accumulated_pulses;
1910 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1912 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1918 TempoMap::beat_offset_at (const Metrics& metrics, const double& beat) const
1920 MeterSection* prev_m = 0;
1921 double beat_off = first_meter().pulse();
1923 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1924 MeterSection* m = 0;
1925 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1927 if (m->beat() > beat) {
1931 if (m->position_lock_style() == AudioTime) {
1932 beat_off += ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) - floor (m->pulse() - prev_m->pulse());
1943 TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
1945 frameoffset_t frame_off = 0;
1946 MeterSection* prev_m = 0;
1948 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1949 MeterSection* m = 0;
1950 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1951 if (m->frame() > frame) {
1954 if (prev_m && m->position_lock_style() == AudioTime) {
1955 const double pulse = prev_m->pulse() + ((m->beat() - prev_m->beat()) / prev_m->note_divisor());
1956 frame_off += frame_at_pulse_locked (metrics, pulse) - m->frame();
1966 TempoMap::frame_time (const BBT_Time& bbt)
1969 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1973 if (bbt.beats < 1) {
1974 throw std::logic_error ("beats are counted from one");
1976 Glib::Threads::RWLock::ReaderLock lm (lock);
1977 double const beat = bbt_to_beats_locked (_metrics, bbt);
1978 framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1983 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1985 /* HOLD THE READER LOCK */
1987 framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt)));
1993 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1995 TempoSection* prev_ts = 0;
1997 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1999 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2004 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
2007 if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
2011 if (!by_frame && fabs (t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate)) > 0.00001) {
2012 std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
2013 " |error :" << t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << std::endl <<
2014 "|frame at beat :" << prev_ts->frame_at_pulse (t->pulse(), _frame_rate) << std::endl <<
2015 " |frame at tempo : " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2028 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2031 TempoSection* prev_ts = 0;
2032 TempoSection* section_prev = 0;
2033 MetricSectionFrameSorter fcmp;
2034 MetricSectionSorter cmp;
2035 framepos_t first_m_frame = 0;
2036 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2038 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2039 if (!m->movable()) {
2040 first_m_frame = m->frame();
2044 if (section->movable() && frame <= first_m_frame) {
2047 section->set_active (true);
2049 section->set_frame (frame);
2051 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2053 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2060 section_prev = prev_ts;
2063 if (t->position_lock_style() == MusicTime) {
2064 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2065 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2067 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2068 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2076 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2077 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2080 if (section->position_lock_style() == MusicTime) {
2081 /* we're setting the frame */
2082 section->set_position_lock_style (AudioTime);
2083 recompute_tempos (imaginary);
2084 section->set_position_lock_style (MusicTime);
2086 recompute_tempos (imaginary);
2089 if (check_solved (imaginary, true)) {
2090 recompute_meters (imaginary);
2094 imaginary.sort (fcmp);
2095 if (section->position_lock_style() == MusicTime) {
2096 /* we're setting the frame */
2097 section->set_position_lock_style (AudioTime);
2098 recompute_tempos (imaginary);
2099 section->set_position_lock_style (MusicTime);
2101 recompute_tempos (imaginary);
2103 if (check_solved (imaginary, true)) {
2104 recompute_meters (imaginary);
2108 imaginary.sort (cmp);
2109 if (section->position_lock_style() == MusicTime) {
2110 /* we're setting the frame */
2111 section->set_position_lock_style (AudioTime);
2112 recompute_tempos (imaginary);
2113 section->set_position_lock_style (MusicTime);
2115 recompute_tempos (imaginary);
2117 if (check_solved (imaginary, true)) {
2118 recompute_meters (imaginary);
2121 //dump (imaginary, std::cerr);
2127 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
2129 MetricSectionSorter cmp;
2130 MetricSectionFrameSorter fcmp;
2131 TempoSection* prev_ts = 0;
2132 TempoSection* section_prev = 0;
2134 section->set_pulse (pulse);
2136 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2138 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2144 section_prev = prev_ts;
2147 if (t->position_lock_style() == MusicTime) {
2148 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2149 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2151 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2152 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2159 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2160 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2163 if (section->position_lock_style() == AudioTime) {
2164 /* we're setting the pulse */
2165 section->set_position_lock_style (MusicTime);
2166 recompute_tempos (imaginary);
2167 section->set_position_lock_style (AudioTime);
2169 recompute_tempos (imaginary);
2171 if (check_solved (imaginary, false)) {
2172 recompute_meters (imaginary);
2176 imaginary.sort (cmp);
2177 if (section->position_lock_style() == AudioTime) {
2178 /* we're setting the pulse */
2179 section->set_position_lock_style (MusicTime);
2180 recompute_tempos (imaginary);
2181 section->set_position_lock_style (AudioTime);
2183 recompute_tempos (imaginary);
2186 if (check_solved (imaginary, false)) {
2187 recompute_meters (imaginary);
2191 imaginary.sort (fcmp);
2192 if (section->position_lock_style() == AudioTime) {
2193 /* we're setting the pulse */
2194 section->set_position_lock_style (MusicTime);
2195 recompute_tempos (imaginary);
2196 section->set_position_lock_style (AudioTime);
2198 recompute_tempos (imaginary);
2201 if (check_solved (imaginary, false)) {
2202 recompute_meters (imaginary);
2206 //dump (imaginary, std::cerr);
2212 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2214 MeterSection* prev_ms = 0;
2215 double accumulated_beats = 0.0;
2216 uint32_t accumulated_bars = 0;
2218 section->set_pulse (pulse);
2220 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2222 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2224 double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2225 accumulated_beats += beats_in_m;
2226 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2229 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2230 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2231 section->set_beat (b_bbt);
2236 if (m->position_lock_style() == MusicTime) {
2237 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2238 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2239 m->set_pulse (pulse);
2241 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2242 m->set_beat (b_bbt);
2243 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2244 m->set_pulse (pulse);
2251 if (section->position_lock_style() == AudioTime) {
2252 /* we're setting the pulse */
2253 section->set_position_lock_style (MusicTime);
2254 recompute_meters (imaginary);
2255 section->set_position_lock_style (AudioTime);
2257 recompute_meters (imaginary);
2262 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
2264 MeterSection* prev_ms = 0;
2266 if (!section->movable()) {
2267 TempoSection* first_t;
2268 for (Metrics::const_iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2270 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2271 if (!t->movable()) {
2272 t->set_active (true);
2275 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2276 t->set_active (false);
2278 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2279 t->set_active (true);
2280 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2287 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2289 new_section->set_frame (frame);
2290 new_section->set_pulse (0.0);
2291 new_section->set_active (true);
2293 if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
2294 first_t->set_frame (frame);
2295 first_t->set_pulse (0.0);
2296 first_t->set_active (true);
2297 solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
2303 double accumulated_beats = 0.0;
2304 uint32_t accumulated_bars = 0;
2306 section->set_frame (frame);
2308 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2310 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2312 const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2313 accumulated_beats += beats_in_m;
2314 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2318 here we define the pulse for this frame.
2319 we're going to set it 'incorrectly' to the next integer and use this 'error'
2320 as an offset to the map as far as users of the public methods are concerned.
2321 (meters should go on absolute pulses to keep us sane)
2323 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2325 m->set_pulse (pulse_at_frame_locked (imaginary, frame));
2329 m->set_beat (b_bbt);
2334 if (m->position_lock_style() == MusicTime) {
2335 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2336 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2337 m->set_pulse (pulse);
2339 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2340 m->set_beat (b_bbt);
2341 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2342 m->set_pulse (pulse);
2349 if (section->position_lock_style() == MusicTime) {
2350 /* we're setting the frame */
2351 section->set_position_lock_style (AudioTime);
2352 recompute_meters (imaginary);
2353 section->set_position_lock_style (MusicTime);
2355 recompute_meters (imaginary);
2357 //dump (imaginary, std::cerr);
2361 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2363 Glib::Threads::RWLock::ReaderLock lm (lock);
2365 double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2366 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2367 double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2368 framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2369 framecnt_t const ret = time_at_bbt;
2375 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2377 return round_to_type (fr, dir, Bar);
2381 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2383 return round_to_type (fr, dir, Beat);
2387 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2389 Glib::Threads::RWLock::ReaderLock lm (lock);
2390 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2391 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2392 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2394 ticks -= beats * BBT_Time::ticks_per_beat;
2397 /* round to next (or same iff dir == RoundUpMaybe) */
2399 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2401 if (mod == 0 && dir == RoundUpMaybe) {
2402 /* right on the subdivision, which is fine, so do nothing */
2404 } else if (mod == 0) {
2405 /* right on the subdivision, so the difference is just the subdivision ticks */
2406 ticks += ticks_one_subdivisions_worth;
2409 /* not on subdivision, compute distance to next subdivision */
2411 ticks += ticks_one_subdivisions_worth - mod;
2414 if (ticks >= BBT_Time::ticks_per_beat) {
2415 ticks -= BBT_Time::ticks_per_beat;
2417 } else if (dir < 0) {
2419 /* round to previous (or same iff dir == RoundDownMaybe) */
2421 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2423 if (difference == 0 && dir == RoundDownAlways) {
2424 /* right on the subdivision, but force-rounding down,
2425 so the difference is just the subdivision ticks */
2426 difference = ticks_one_subdivisions_worth;
2429 if (ticks < difference) {
2430 ticks = BBT_Time::ticks_per_beat - ticks;
2432 ticks -= difference;
2436 /* round to nearest */
2439 /* compute the distance to the previous and next subdivision */
2441 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2443 /* closer to the next subdivision, so shift forward */
2445 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2447 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2449 if (ticks > BBT_Time::ticks_per_beat) {
2451 ticks -= BBT_Time::ticks_per_beat;
2452 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2455 } else if (rem > 0) {
2457 /* closer to previous subdivision, so shift backward */
2461 /* can't go backwards past zero, so ... */
2464 /* step back to previous beat */
2466 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2467 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2469 ticks = lrint (ticks - rem);
2470 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2473 /* on the subdivision, do nothing */
2477 framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2483 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2485 if (sub_num == -1) {
2486 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2487 if ((double) when.beats > bpb / 2.0) {
2493 } else if (sub_num == 0) {
2494 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2495 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2497 while ((double) when.beats > bpb) {
2499 when.beats -= (uint32_t) floor (bpb);
2505 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2507 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2508 /* closer to the next subdivision, so shift forward */
2510 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2512 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2514 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2517 } else if (rem > 0) {
2518 /* closer to previous subdivision, so shift backward */
2520 if (rem > when.ticks) {
2521 if (when.beats == 0) {
2522 /* can't go backwards past zero, so ... */
2524 /* step back to previous beat */
2526 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2528 when.ticks = when.ticks - rem;
2534 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2536 Glib::Threads::RWLock::ReaderLock lm (lock);
2538 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2539 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2544 /* find bar previous to 'frame' */
2547 return frame_time (bbt);
2549 } else if (dir > 0) {
2550 /* find bar following 'frame' */
2554 return frame_time (bbt);
2556 /* true rounding: find nearest bar */
2557 framepos_t raw_ft = frame_time (bbt);
2560 framepos_t prev_ft = frame_time (bbt);
2562 framepos_t next_ft = frame_time (bbt);
2564 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2575 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2576 } else if (dir > 0) {
2577 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2579 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2588 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2589 framepos_t lower, framepos_t upper)
2591 Glib::Threads::RWLock::ReaderLock lm (lock);
2592 int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2593 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2594 /* although the map handles negative beats, bbt doesn't. */
2598 while (cnt <= upper_beat) {
2599 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2600 TempoSection const tempo = tempo_section_at_locked (pos);
2601 MeterSection const meter = meter_section_at_locked (pos);
2602 BBT_Time const bbt = beats_to_bbt (cnt);
2604 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
2610 TempoMap::tempo_section_at (framepos_t frame) const
2612 Glib::Threads::RWLock::ReaderLock lm (lock);
2613 return tempo_section_at_locked (frame);
2617 TempoMap::tempo_section_at_locked (framepos_t frame) const
2619 Metrics::const_iterator i;
2620 TempoSection* prev = 0;
2622 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2625 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2629 if (t->frame() > frame) {
2639 abort(); /*NOTREACHED*/
2646 /* don't use this to calculate length (the tempo is only correct for this frame).
2647 do that stuff based on the beat_at_frame and frame_at_beat api
2650 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2652 Glib::Threads::RWLock::ReaderLock lm (lock);
2654 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2655 const TempoSection* ts_after = 0;
2656 Metrics::const_iterator i;
2658 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2661 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2665 if ((*i)->frame() > frame) {
2673 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2675 /* must be treated as constant tempo */
2676 return ts_at->frames_per_beat (_frame_rate);
2680 TempoMap::tempo_at (const framepos_t& frame) const
2682 Glib::Threads::RWLock::ReaderLock lm (lock);
2683 return tempo_at_locked (frame);
2687 TempoMap::tempo_at_locked (const framepos_t& frame) const
2689 TempoSection* prev_ts = 0;
2691 Metrics::const_iterator i;
2693 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2695 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2699 if ((prev_ts) && t->frame() > frame) {
2700 /* t is the section past frame */
2701 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2702 Tempo const ret_tempo (ret, prev_ts->note_type());
2709 double const ret = prev_ts->beats_per_minute();
2710 Tempo const ret_tempo (ret, prev_ts->note_type ());
2716 TempoMap::meter_section_at (framepos_t frame) const
2718 Glib::Threads::RWLock::ReaderLock lm (lock);
2719 return meter_section_at_locked (frame);
2723 TempoMap::meter_section_at_locked (framepos_t frame) const
2725 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2726 Metrics::const_iterator i;
2727 MeterSection* prev = 0;
2729 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2732 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2734 if (prev && (*i)->frame() > frame) {
2744 abort(); /*NOTREACHED*/
2751 TempoMap::meter_at (framepos_t frame) const
2753 TempoMetric m (metric_at (frame));
2758 TempoMap::meter_section_at (const double& beat) const
2760 MeterSection* prev_ms = 0;
2761 Glib::Threads::RWLock::ReaderLock lm (lock);
2763 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2765 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2766 if (prev_ms && m->beat() > beat) {
2777 TempoMap::get_state ()
2779 Metrics::const_iterator i;
2780 XMLNode *root = new XMLNode ("TempoMap");
2783 Glib::Threads::RWLock::ReaderLock lm (lock);
2784 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2785 root->add_child_nocopy ((*i)->get_state());
2793 TempoMap::set_state (const XMLNode& node, int /*version*/)
2796 Glib::Threads::RWLock::WriterLock lm (lock);
2799 XMLNodeConstIterator niter;
2800 Metrics old_metrics (_metrics);
2801 MeterSection* last_meter = 0;
2804 nlist = node.children();
2806 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2807 XMLNode* child = *niter;
2809 if (child->name() == TempoSection::xml_state_node_name) {
2812 TempoSection* ts = new TempoSection (*child);
2813 _metrics.push_back (ts);
2815 if (ts->bar_offset() < 0.0) {
2817 //ts->update_bar_offset_from_bbt (*last_meter);
2822 catch (failed_constructor& err){
2823 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2824 _metrics = old_metrics;
2828 } else if (child->name() == MeterSection::xml_state_node_name) {
2831 MeterSection* ms = new MeterSection (*child);
2832 _metrics.push_back (ms);
2836 catch (failed_constructor& err) {
2837 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2838 _metrics = old_metrics;
2844 if (niter == nlist.end()) {
2845 MetricSectionSorter cmp;
2846 _metrics.sort (cmp);
2848 /* check for legacy sessions where bbt was the base musical unit for tempo */
2849 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2852 MeterSection* prev_ms = 0;
2853 TempoSection* prev_ts = 0;
2854 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2855 if (prev_ms && prev_ms->pulse() < 0.0) {
2856 /*XX we cannot possibly make this work??. */
2857 pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
2858 prev_ms->set_beat (start);
2859 const double start_pulse = ((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat);
2860 prev_ms->set_pulse (start_pulse);
2863 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2867 if (prev_ts && prev_ts->pulse() < 0.0) {
2868 double const start = ((prev_ts->legacy_bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2869 prev_ts->set_pulse (start);
2874 /* check for multiple tempo/meters at the same location, which
2875 ardour2 somehow allowed.
2878 Metrics::iterator prev = _metrics.end();
2879 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2880 if (prev != _metrics.end()) {
2882 MeterSection* prev_ms;
2884 TempoSection* prev_ts;
2885 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2886 if (prev_ms->pulse() == ms->pulse()) {
2887 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2888 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2891 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2892 if (prev_ts->pulse() == ts->pulse()) {
2893 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2894 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2902 recompute_map (_metrics);
2905 PropertyChanged (PropertyChange ());
2911 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2913 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2914 const MeterSection* m;
2915 const TempoSection* t;
2916 const TempoSection* prev_ts = 0;
2918 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2920 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2921 o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2922 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2923 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2925 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2926 o << "calculated : " << prev_ts->tempo_at_pulse (t->pulse()) * prev_ts->note_type() << " | " << prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << " | " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2929 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2930 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2931 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2934 o << "------" << std::endl;
2938 TempoMap::n_tempos() const
2940 Glib::Threads::RWLock::ReaderLock lm (lock);
2943 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2944 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2953 TempoMap::n_meters() const
2955 Glib::Threads::RWLock::ReaderLock lm (lock);
2958 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2959 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2968 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2971 Glib::Threads::RWLock::WriterLock lm (lock);
2972 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2973 if ((*i)->frame() >= where && (*i)->movable ()) {
2974 (*i)->set_frame ((*i)->frame() + amount);
2978 /* now reset the BBT time of all metrics, based on their new
2979 * audio time. This is the only place where we do this reverse
2983 Metrics::iterator i;
2984 const MeterSection* meter;
2985 const TempoSection* tempo;
2989 meter = &first_meter ();
2990 tempo = &first_tempo ();
2995 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2998 MetricSection* prev = 0;
3000 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3003 //TempoMetric metric (*meter, *tempo);
3004 MeterSection* ms = const_cast<MeterSection*>(meter);
3005 TempoSection* ts = const_cast<TempoSection*>(tempo);
3008 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3012 ts->set_pulse (t->pulse());
3014 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3015 ts->set_pulse (m->pulse());
3017 ts->set_frame (prev->frame());
3021 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3022 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3023 ms->set_beat (start);
3024 ms->set_pulse (m->pulse());
3026 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3030 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3031 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3032 ms->set_beat (start);
3033 ms->set_pulse (t->pulse());
3035 ms->set_frame (prev->frame());
3039 // metric will be at frames=0 bbt=1|1|0 by default
3040 // which is correct for our purpose
3043 // cerr << bbt << endl;
3045 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3049 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3051 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3052 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3053 bbt_time (m->frame(), bbt);
3055 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3061 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3062 /* round up to next beat */
3068 if (bbt.beats != 1) {
3069 /* round up to next bar */
3074 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3075 m->set_beat (start);
3076 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3078 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3080 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3081 abort(); /*NOTREACHED*/
3087 recompute_map (_metrics);
3091 PropertyChanged (PropertyChange ());
3094 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3098 std::list<MetricSection*> metric_kill_list;
3100 TempoSection* last_tempo = NULL;
3101 MeterSection* last_meter = NULL;
3102 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3103 bool meter_after = false; // is there a meter marker likewise?
3105 Glib::Threads::RWLock::WriterLock lm (lock);
3106 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3107 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3108 metric_kill_list.push_back(*i);
3109 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3112 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3116 else if ((*i)->frame() >= where) {
3117 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3118 (*i)->set_frame ((*i)->frame() - amount);
3119 if ((*i)->frame() == where) {
3120 // marker was immediately after end of range
3121 tempo_after = dynamic_cast<TempoSection*> (*i);
3122 meter_after = dynamic_cast<MeterSection*> (*i);
3128 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3129 if (last_tempo && !tempo_after) {
3130 metric_kill_list.remove(last_tempo);
3131 last_tempo->set_frame(where);
3134 if (last_meter && !meter_after) {
3135 metric_kill_list.remove(last_meter);
3136 last_meter->set_frame(where);
3140 //remove all the remaining metrics
3141 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3142 _metrics.remove(*i);
3147 recompute_map (_metrics);
3150 PropertyChanged (PropertyChange ());
3154 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3155 * pos can be -ve, if required.
3158 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3160 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3163 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3165 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3167 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3170 /** Add the BBT interval op to pos and return the result */
3172 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3174 Glib::Threads::RWLock::ReaderLock lm (lock);
3176 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3177 pos_bbt.ticks += op.ticks;
3178 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3180 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3182 pos_bbt.beats += op.beats;
3183 /* the meter in effect will start on the bar */
3184 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3185 while (pos_bbt.beats >= divisions_per_bar + 1) {
3187 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3188 pos_bbt.beats -= divisions_per_bar;
3190 pos_bbt.bars += op.bars;
3192 return frame_time_locked (_metrics, pos_bbt);
3195 /** Count the number of beats that are equivalent to distance when going forward,
3199 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3201 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3205 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3211 operator<< (std::ostream& o, const Meter& m) {
3212 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3216 operator<< (std::ostream& o, const Tempo& t) {
3217 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3221 operator<< (std::ostream& o, const MetricSection& section) {
3223 o << "MetricSection @ " << section.frame() << ' ';
3225 const TempoSection* ts;
3226 const MeterSection* ms;
3228 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3229 o << *((const Tempo*) ts);
3230 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3231 //o << *((const Meter*) ms);