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 ("bar-offset")) == 0) {
144 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
145 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
146 throw failed_constructor();
150 if ((prop = node.property ("tempo-type")) == 0) {
153 _type = Type (string_2_enum (prop->value(), _type));
156 if ((prop = node.property ("lock-style")) == 0) {
157 set_position_lock_style (MusicTime);
159 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
164 TempoSection::get_state() const
166 XMLNode *root = new XMLNode (xml_state_node_name);
170 snprintf (buf, sizeof (buf), "%f", pulse());
171 root->add_property ("pulse", buf);
172 snprintf (buf, sizeof (buf), "%li", frame());
173 root->add_property ("frame", buf);
174 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
175 root->add_property ("beats-per-minute", buf);
176 snprintf (buf, sizeof (buf), "%f", _note_type);
177 root->add_property ("note-type", buf);
178 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
179 // root->add_property ("bar-offset", buf);
180 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
181 root->add_property ("movable", buf);
182 root->add_property ("tempo-type", enum_2_string (_type));
183 root->add_property ("lock-style", enum_2_string (position_lock_style()));
190 TempoSection::update_bar_offset_from_bbt (const Meter& m)
192 _bar_offset = (pulse() * BBT_Time::ticks_per_beat) /
193 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
195 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, pulse(), m.divisions_per_bar()));
199 TempoSection::set_type (Type type)
204 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
207 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
210 if (_type == Constant) {
211 return pulses_per_minute();
214 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
217 /** returns the zero-based frame (relative to session)
218 where the tempo in whole pulses per minute occurs in this section.
219 beat b is only used for constant tempos.
220 note that the tempo map may have multiple such values.
223 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
225 if (_type == Constant) {
226 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
229 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
231 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
234 TempoSection::tempo_at_pulse (const double& p) const
237 if (_type == Constant) {
238 return pulses_per_minute();
240 double const ppm = pulse_tempo_at_pulse (p - pulse());
244 /** returns the zero-based beat (relative to session)
245 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
246 note that the session tempo map may have multiple beats at a given tempo.
249 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
251 if (_type == Constant) {
252 double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
256 return pulse_at_pulse_tempo (ppm) + pulse();
259 /** returns the zero-based pulse (relative to session origin)
260 where the zero-based frame (relative to session)
264 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
266 if (_type == Constant) {
267 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
270 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
273 /** returns the zero-based frame (relative to session start frame)
274 where the zero-based pulse (relative to session start)
279 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
281 if (_type == Constant) {
282 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
285 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
293 Tt----|-----------------*|
294 Ta----|--------------|* |
300 _______________|___|____
301 time a t (next tempo)
304 Duration in beats at time a is the integral of some Tempo function.
305 In our case, the Tempo function (Tempo at time t) is
308 with function constant
313 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
314 b(t) = T0(e^(ct) - 1) / c
316 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:
317 t(b) = log((cb / T0) + 1) / c
319 The time t at which Tempo T occurs is a as above:
320 t(T) = log(T / T0) / c
322 The beat at which a Tempo T occurs is:
325 The Tempo at which beat b occurs is:
328 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
329 Our problem is that we usually don't know t.
330 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.
331 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
332 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
334 By substituting our expanded t as a in the c function above, our problem is reduced to:
335 c = T0 (e^(log (Ta / T0)) - 1) / b
337 We can now store c for future time calculations.
338 If the following tempo section (the one that defines c in conjunction with this one)
339 is changed or moved, c is no longer valid.
341 The public methods are session-relative.
343 Most of this stuff is taken from this paper:
346 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
349 Zurich University of Arts
350 Institute for Computer Music and Sound Technology
352 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
357 compute this ramp's function constant using the end tempo (in whole pulses per minute)
358 and duration (pulses into global start) of some later tempo section.
361 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
363 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
364 return pulses_per_minute() * (exp (log_tempo_ratio) - 1) / (end_pulse - pulse());
367 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
369 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
371 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
375 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
377 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
381 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
383 return (frame / (double) frame_rate) / 60.0;
386 /* position function */
388 TempoSection::a_func (double end_bpm, double c_func) const
390 return log (end_bpm / pulses_per_minute()) / c_func;
393 /*function constant*/
395 TempoSection::c_func (double end_bpm, double end_time) const
397 return log (end_bpm / pulses_per_minute()) / end_time;
400 /* tempo in ppm at time in minutes */
402 TempoSection::pulse_tempo_at_time (const double& time) const
404 return exp (_c_func * time) * pulses_per_minute();
407 /* time in minutes at tempo in ppm */
409 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
411 return log (pulse_tempo / pulses_per_minute()) / _c_func;
414 /* tick at tempo in ppm */
416 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
418 return (pulse_tempo - pulses_per_minute()) / _c_func;
421 /* tempo in ppm at tick */
423 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
425 return (pulse * _c_func) + pulses_per_minute();
428 /* pulse at time in minutes */
430 TempoSection::pulse_at_time (const double& time) const
432 return ((exp (_c_func * time)) - 1) * (pulses_per_minute() / _c_func);
435 /* time in minutes at pulse */
437 TempoSection::time_at_pulse (const double& pulse) const
439 return log (((_c_func * pulse) / pulses_per_minute()) + 1) / _c_func;
444 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
448 if (_bar_offset < 0.0) {
455 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
456 new_beat = ticks / BBT_Time::ticks_per_beat;
458 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
459 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
461 set_pulse (new_beat);
464 /***********************************************************************/
466 const string MeterSection::xml_state_node_name = "Meter";
468 MeterSection::MeterSection (const XMLNode& node)
469 : MetricSection (0.0), Meter (TempoMap::default_meter())
471 XMLProperty const * prop;
474 const XMLProperty *prop;
478 framepos_t frame = 0;
479 pair<double, BBT_Time> start;
481 if ((prop = node.property ("start")) != 0) {
482 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
486 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
488 /* legacy session - start used to be in bbt*/
492 error << _("MeterSection XML node has no \"start\" property") << endmsg;
495 if ((prop = node.property ("pulse")) != 0) {
496 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
497 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
502 if ((prop = node.property ("beat")) != 0) {
503 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
504 error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
510 if ((prop = node.property ("bbt")) == 0) {
511 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
512 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
516 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
517 //throw failed_constructor();
523 if ((prop = node.property ("frame")) != 0) {
524 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
525 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
531 /* beats-per-bar is old; divisions-per-bar is new */
533 if ((prop = node.property ("divisions-per-bar")) == 0) {
534 if ((prop = node.property ("beats-per-bar")) == 0) {
535 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
536 throw failed_constructor();
539 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
540 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
541 throw failed_constructor();
544 if ((prop = node.property ("note-type")) == 0) {
545 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
546 throw failed_constructor();
548 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
549 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
550 throw failed_constructor();
553 if ((prop = node.property ("lock-style")) == 0) {
554 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
555 set_position_lock_style (MusicTime);
557 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
560 if ((prop = node.property ("movable")) == 0) {
561 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
562 throw failed_constructor();
565 set_movable (string_is_affirmative (prop->value()));
569 MeterSection::get_state() const
571 XMLNode *root = new XMLNode (xml_state_node_name);
575 snprintf (buf, sizeof (buf), "%lf", pulse());
576 root->add_property ("pulse", buf);
577 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
581 root->add_property ("bbt", buf);
582 snprintf (buf, sizeof (buf), "%lf", beat());
583 root->add_property ("beat", buf);
584 snprintf (buf, sizeof (buf), "%f", _note_type);
585 root->add_property ("note-type", buf);
586 snprintf (buf, sizeof (buf), "%li", frame());
587 root->add_property ("frame", buf);
588 root->add_property ("lock-style", enum_2_string (position_lock_style()));
589 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
590 root->add_property ("divisions-per-bar", buf);
591 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
592 root->add_property ("movable", buf);
597 /***********************************************************************/
599 struct MetricSectionSorter {
600 bool operator() (const MetricSection* a, const MetricSection* b) {
601 return a->pulse() < b->pulse();
605 struct MetricSectionFrameSorter {
606 bool operator() (const MetricSection* a, const MetricSection* b) {
607 return a->frame() < b->frame();
611 TempoMap::TempoMap (framecnt_t fr)
614 BBT_Time start (1, 1, 0);
616 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
617 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
619 t->set_movable (false);
620 m->set_movable (false);
622 /* note: frame time is correct (zero) for both of these */
624 _metrics.push_back (t);
625 _metrics.push_back (m);
629 TempoMap::~TempoMap ()
634 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
636 bool removed = false;
639 Glib::Threads::RWLock::WriterLock lm (lock);
640 if ((removed = remove_tempo_locked (tempo))) {
641 if (complete_operation) {
642 recompute_map (_metrics);
647 if (removed && complete_operation) {
648 PropertyChanged (PropertyChange ());
653 TempoMap::remove_tempo_locked (const TempoSection& tempo)
657 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
658 if (dynamic_cast<TempoSection*> (*i) != 0) {
659 if (tempo.frame() == (*i)->frame()) {
660 if ((*i)->movable()) {
672 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
674 bool removed = false;
677 Glib::Threads::RWLock::WriterLock lm (lock);
678 if ((removed = remove_meter_locked (tempo))) {
679 if (complete_operation) {
680 recompute_map (_metrics);
685 if (removed && complete_operation) {
686 PropertyChanged (PropertyChange ());
691 TempoMap::remove_meter_locked (const MeterSection& tempo)
695 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
696 if (dynamic_cast<MeterSection*> (*i) != 0) {
697 if (tempo.frame() == (*i)->frame()) {
698 if ((*i)->movable()) {
710 TempoMap::do_insert (MetricSection* section)
712 bool need_add = true;
713 /* we only allow new meters to be inserted on beat 1 of an existing
717 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
718 //assert (m->bbt().ticks == 0);
720 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
722 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
723 corrected.second.beats = 1;
724 corrected.second.ticks = 0;
725 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
726 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
727 m->bbt(), corrected.second) << endmsg;
728 //m->set_pulse (corrected);
732 /* Look for any existing MetricSection that is of the same type and
733 in the same bar as the new one, and remove it before adding
734 the new one. Note that this means that if we find a matching,
735 existing section, we can break out of the loop since we're
736 guaranteed that there is only one such match.
739 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
741 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
742 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
743 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
744 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
746 if (tempo && insert_tempo) {
749 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
750 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
752 if (!tempo->movable()) {
754 /* can't (re)move this section, so overwrite
755 * its data content (but not its properties as
759 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
767 } else if (meter && insert_meter) {
771 bool const ipm = insert_meter->position_lock_style() == MusicTime;
773 if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
775 if (!meter->movable()) {
777 /* can't (re)move this section, so overwrite
778 * its data content (but not its properties as
782 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
783 (*i)->set_position_lock_style (insert_meter->position_lock_style());
793 /* non-matching types, so we don't care */
797 /* Add the given MetricSection, if we didn't just reset an existing
802 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
803 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
806 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
807 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
810 bool const ipm = insert_meter->position_lock_style() == MusicTime;
811 if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
816 } else if (insert_tempo) {
817 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
818 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
821 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
822 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
829 _metrics.insert (i, section);
830 //dump (_metrics, std::cerr);
835 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
838 Glib::Threads::RWLock::WriterLock lm (lock);
839 TempoSection& first (first_tempo());
840 if (ts.pulse() != first.pulse()) {
841 remove_tempo_locked (ts);
842 add_tempo_locked (tempo, pulse, true, type);
844 first.set_type (type);
846 /* cannot move the first tempo section */
847 *static_cast<Tempo*>(&first) = tempo;
848 recompute_map (_metrics);
853 PropertyChanged (PropertyChange ());
857 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
860 Glib::Threads::RWLock::WriterLock lm (lock);
861 TempoSection& first (first_tempo());
862 if (ts.frame() != first.frame()) {
863 remove_tempo_locked (ts);
864 add_tempo_locked (tempo, frame, true, type);
866 first.set_type (type);
868 /* cannot move the first tempo section */
869 *static_cast<Tempo*>(&first) = tempo;
870 recompute_map (_metrics);
874 PropertyChanged (PropertyChange ());
878 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
881 Glib::Threads::RWLock::WriterLock lm (lock);
882 add_tempo_locked (tempo, pulse, true, type);
885 PropertyChanged (PropertyChange ());
889 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
892 Glib::Threads::RWLock::WriterLock lm (lock);
893 add_tempo_locked (tempo, frame, true, type);
897 PropertyChanged (PropertyChange ());
901 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
903 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
908 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
913 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
915 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
920 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
925 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
928 Glib::Threads::RWLock::WriterLock lm (lock);
929 MeterSection& first (first_meter());
930 const PositionLockStyle pl = ms.position_lock_style();
931 if (ms.pulse() != first.pulse()) {
932 remove_meter_locked (ms);
933 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
935 /* cannot move the first meter section */
936 *static_cast<Meter*>(&first) = meter;
937 first.set_position_lock_style (pl);
938 recompute_map (_metrics);
942 PropertyChanged (PropertyChange ());
946 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
949 Glib::Threads::RWLock::WriterLock lm (lock);
950 MeterSection& first (first_meter());
951 const PositionLockStyle pl = ms.position_lock_style();
952 if (ms.pulse() != first.pulse()) {
953 remove_meter_locked (ms);
954 add_meter_locked (meter, frame, true);
956 /* cannot move the first meter section */
957 *static_cast<Meter*>(&first) = meter;
958 first.set_position_lock_style (pl);
959 first.set_frame (frame);
960 recompute_map (_metrics);
964 PropertyChanged (PropertyChange ());
969 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
972 Glib::Threads::RWLock::WriterLock lm (lock);
973 add_meter_locked (meter, beat, where, true);
978 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
979 dump (_metrics, std::cerr);
983 PropertyChanged (PropertyChange ());
987 TempoMap::add_meter (const Meter& meter, const framepos_t& frame)
990 Glib::Threads::RWLock::WriterLock lm (lock);
991 add_meter_locked (meter, frame, true);
996 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
997 dump (_metrics, std::cerr);
1001 PropertyChanged (PropertyChange ());
1005 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1007 /* a new meter always starts a new bar on the first beat. so
1008 round the start time appropriately. remember that
1009 `where' is based on the existing tempo map, not
1010 the result after we insert the new meter.
1014 if (where.beats != 1) {
1018 /* new meters *always* start on a beat. */
1020 double pulse = pulse_at_beat_locked (_metrics, beat);
1022 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1023 do_insert (new_meter);
1026 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1032 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1035 MeterSection* new_meter = new MeterSection (frame, 0.0, meter.divisions_per_bar(), meter.note_divisor());
1037 do_insert (new_meter);
1040 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1046 * 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,
1047 * taking any possible reordering as a consequence of this into account.
1048 * @param section - the section to be altered
1049 * @param bpm - the new Tempo
1050 * @param bbt - the bbt where the altered tempo will fall
1051 * @return returns - the position in frames where the new tempo section will lie.
1054 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1056 Glib::Threads::RWLock::ReaderLock lm (lock);
1059 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1061 double const beat = bbt_to_beats_locked (future_map, bbt);
1062 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
1063 ret = new_section->frame();
1065 ret = frame_at_beat_locked (_metrics, beat);
1068 Metrics::const_iterator d = future_map.begin();
1069 while (d != future_map.end()) {
1077 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1079 Glib::Threads::RWLock::ReaderLock lm (lock);
1082 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1084 if (solve_map (future_map, new_section, bpm, frame)) {
1085 ret = new_section->pulse();
1087 ret = pulse_at_frame_locked (_metrics, frame);
1090 Metrics::const_iterator d = future_map.begin();
1091 while (d != future_map.end()) {
1099 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1103 Glib::Threads::RWLock::WriterLock lm (lock);
1104 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1105 if (solve_map (future_map, new_section, bpm, frame)) {
1106 solve_map (_metrics, ts, bpm, frame);
1110 Metrics::const_iterator d = future_map.begin();
1111 while (d != future_map.end()) {
1116 MetricPositionChanged (); // Emit Signal
1120 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
1124 Glib::Threads::RWLock::WriterLock lm (lock);
1125 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1126 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
1127 solve_map (_metrics, ts, bpm, beat);
1131 Metrics::const_iterator d = future_map.begin();
1132 while (d != future_map.end()) {
1137 MetricPositionChanged (); // Emit Signal
1141 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
1144 Glib::Threads::RWLock::WriterLock lm (lock);
1145 solve_map (_metrics, ms, mt, frame);
1148 MetricPositionChanged (); // Emit Signal
1152 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
1155 Glib::Threads::RWLock::WriterLock lm (lock);
1156 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
1159 MetricPositionChanged (); // Emit Signal
1163 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
1166 bool can_solve = false;
1168 Glib::Threads::RWLock::WriterLock lm (lock);
1169 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1170 new_section->set_beats_per_minute (bpm.beats_per_minute());
1171 recompute_tempos (future_map);
1173 if (check_solved (future_map, true)) {
1174 ts->set_beats_per_minute (bpm.beats_per_minute());
1175 recompute_map (_metrics);
1180 Metrics::const_iterator d = future_map.begin();
1181 while (d != future_map.end()) {
1186 MetricPositionChanged (); // Emit Signal
1191 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1194 TempoSection* ret = 0;
1197 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1198 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1200 if (t->position_lock_style() == MusicTime) {
1201 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1203 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1205 copy.push_back (ret);
1208 if (t->position_lock_style() == MusicTime) {
1209 copy.push_back (new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type()));
1211 copy.push_back (new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type()));
1214 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1215 if (m->position_lock_style() == MusicTime) {
1216 copy.push_back (new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1218 copy.push_back (new MeterSection (m->frame(), m->beat(), m->divisions_per_bar(), m->note_divisor()));
1222 recompute_map (copy);
1227 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
1230 TempoSection* new_section = 0;
1233 Glib::Threads::RWLock::ReaderLock lm (lock);
1234 new_section = copy_metrics_and_point (copy, ts);
1237 double const beat = bbt_to_beats_locked (copy, bbt);
1238 bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
1240 Metrics::const_iterator d = copy.begin();
1241 while (d != copy.end()) {
1250 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1252 Tempo newtempo (beats_per_minute, note_type);
1255 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1256 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1258 Glib::Threads::RWLock::WriterLock lm (lock);
1259 *((Tempo*) t) = newtempo;
1260 recompute_map (_metrics);
1262 PropertyChanged (PropertyChange ());
1269 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1271 Tempo newtempo (beats_per_minute, note_type);
1274 TempoSection* first;
1275 Metrics::iterator i;
1277 /* find the TempoSection immediately preceding "where"
1280 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1282 if ((*i)->frame() > where) {
1288 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1298 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1308 Glib::Threads::RWLock::WriterLock lm (lock);
1309 /* cannot move the first tempo section */
1310 *((Tempo*)prev) = newtempo;
1311 recompute_map (_metrics);
1314 PropertyChanged (PropertyChange ());
1318 TempoMap::first_meter () const
1320 const MeterSection *m = 0;
1322 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1323 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1328 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1329 abort(); /*NOTREACHED*/
1334 TempoMap::first_meter ()
1336 MeterSection *m = 0;
1338 /* CALLER MUST HOLD LOCK */
1340 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1341 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1346 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1347 abort(); /*NOTREACHED*/
1352 TempoMap::first_tempo () const
1354 const TempoSection *t = 0;
1356 /* CALLER MUST HOLD LOCK */
1358 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1359 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1364 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1365 abort(); /*NOTREACHED*/
1370 TempoMap::first_tempo ()
1372 TempoSection *t = 0;
1374 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1375 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1380 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1381 abort(); /*NOTREACHED*/
1385 TempoMap::recompute_tempos (Metrics& metrics)
1387 TempoSection* prev_ts = 0;
1389 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1392 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1394 if (t->position_lock_style() == AudioTime) {
1395 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1396 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1399 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1400 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1409 /* tempos must be positioned correctly */
1411 TempoMap::recompute_meters (Metrics& metrics)
1413 MeterSection* meter = 0;
1414 MeterSection* prev_m = 0;
1415 double accumulated_beats = 0.0;
1416 uint32_t accumulated_bars = 0;
1417 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1418 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1420 const double beats_in_m = (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
1421 accumulated_beats += beats_in_m;
1422 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1425 if (meter->position_lock_style() == AudioTime) {
1426 pair<double, BBT_Time> bt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
1427 meter->set_beat (bt);
1429 const double pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1430 meter->set_pulse (pulse);
1432 const double pulse = pulse_at_frame_locked (metrics, meter->frame());
1433 meter->set_pulse (pulse);
1437 const double pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1438 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1439 meter->set_pulse (pulse);
1441 const double pulse = pulse_at_beat_locked (metrics, meter->beat());
1442 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1443 meter->set_pulse (pulse);
1453 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1455 /* CALLER MUST HOLD WRITE LOCK */
1459 /* we will actually stop once we hit
1466 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1469 /* silly call from Session::process() during startup
1474 recompute_tempos (metrics);
1475 recompute_meters (metrics);
1479 TempoMap::pulse_at_beat (const double& beat) const
1481 Glib::Threads::RWLock::ReaderLock lm (lock);
1482 return pulse_at_beat_locked (_metrics, beat);
1486 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1488 MeterSection* prev_ms = 0;
1489 double accumulated_beats = 0.0;
1491 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1493 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1494 if (prev_ms && m->beat() > beat) {
1497 accumulated_beats = m->beat();
1502 double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1507 TempoMap::beat_at_pulse (const double& pulse) const
1509 Glib::Threads::RWLock::ReaderLock lm (lock);
1510 return beat_at_pulse_locked (_metrics, pulse);
1514 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1516 MeterSection* prev_ms = 0;
1517 double accumulated_beats = 0.0;
1519 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1521 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1522 if (prev_ms && m->pulse() > pulse) {
1525 accumulated_beats = m->beat();
1530 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1532 return beats_in_section + accumulated_beats;
1536 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1538 Glib::Threads::RWLock::ReaderLock lm (lock);
1539 TempoMetric m (first_meter(), first_tempo());
1541 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1542 at something, because we insert the default tempo and meter during
1543 TempoMap construction.
1545 now see if we can find better candidates.
1548 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1550 if ((*i)->frame() > frame) {
1564 /* XX meters only */
1566 TempoMap::metric_at (BBT_Time bbt) const
1568 Glib::Threads::RWLock::ReaderLock lm (lock);
1569 TempoMetric m (first_meter(), first_tempo());
1571 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1572 at something, because we insert the default tempo and meter during
1573 TempoMap construction.
1575 now see if we can find better candidates.
1578 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1580 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1581 BBT_Time section_start (mw->bbt());
1583 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1595 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1602 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1605 Glib::Threads::RWLock::ReaderLock lm (lock);
1606 double const beat = beat_at_frame_locked (_metrics, frame);
1608 bbt = beats_to_bbt_locked (_metrics, beat);
1612 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1614 Glib::Threads::RWLock::ReaderLock lm (lock);
1616 return bbt_to_beats_locked (_metrics, bbt);
1620 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1622 /* CALLER HOLDS READ LOCK */
1624 double accumulated_beats = 0.0;
1625 double accumulated_bars = 0.0;
1626 MeterSection* prev_ms = 0;
1627 /* because audio-locked meters have 'fake' integral beats,
1628 there is no pulse offset here.
1630 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1632 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1633 double bars_to_m = 0.0;
1635 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1636 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1639 accumulated_beats = m->beat();
1640 accumulated_bars += bars_to_m;
1646 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1647 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1648 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1654 TempoMap::beats_to_bbt (const double& beats)
1656 Glib::Threads::RWLock::ReaderLock lm (lock);
1658 return beats_to_bbt_locked (_metrics, beats);
1662 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1664 /* CALLER HOLDS READ LOCK */
1665 MeterSection* prev_ms = 0;
1666 const double beats = (b < 0.0) ? 0.0 : b;
1667 uint32_t accumulated_bars = 0;
1668 double accumulated_beats = 0.0;
1670 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1671 MeterSection* m = 0;
1673 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1676 double const beats_to_m = m->beat() - prev_ms->beat();
1677 if (accumulated_beats + beats_to_m > beats) {
1678 /* this is the meter after the one our beat is on*/
1682 /* we need a whole number of bars. */
1683 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1684 accumulated_beats += beats_to_m;
1691 double const beats_in_ms = beats - accumulated_beats;
1692 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1693 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1694 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1695 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1699 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1700 ret.beats = (uint32_t) floor (remaining_beats);
1701 ret.bars = total_bars;
1703 /* 0 0 0 to 1 1 0 - based mapping*/
1707 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1709 ret.ticks -= BBT_Time::ticks_per_beat;
1712 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1721 TempoMap::pulse_to_bbt (const double& pulse)
1723 Glib::Threads::RWLock::ReaderLock lm (lock);
1724 MeterSection* prev_ms = 0;
1725 uint32_t accumulated_bars = 0;
1726 double accumulated_pulses = 0.0;
1728 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1729 MeterSection* m = 0;
1731 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1734 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1735 if (accumulated_pulses + pulses_to_m > pulse) {
1736 /* this is the meter after the one our beat is on*/
1740 /* we need a whole number of bars. */
1741 accumulated_pulses += pulses_to_m;
1742 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1748 double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1749 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1750 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1751 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1752 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1756 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1757 ret.beats = (uint32_t) floor (remaining_beats);
1758 ret.bars = total_bars;
1760 /* 0 0 0 to 1 1 0 mapping*/
1764 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1766 ret.ticks -= BBT_Time::ticks_per_beat;
1769 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1778 TempoMap::beat_at_frame (const framecnt_t& frame) const
1780 Glib::Threads::RWLock::ReaderLock lm (lock);
1781 return beat_at_frame_locked (_metrics, frame);
1785 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1787 framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
1788 double const pulse = pulse_at_frame_locked (metrics, offset_frame);
1790 return beat_at_pulse_locked (metrics, pulse);
1794 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1796 /* HOLD (at least) THE READER LOCK */
1797 TempoSection* prev_ts = 0;
1798 double accumulated_pulses = 0.0;
1800 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1802 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1804 if (prev_ts && t->frame() > frame) {
1805 /*the previous ts is the one containing the frame */
1806 double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1809 accumulated_pulses = t->pulse();
1814 /* treated as constant for this ts */
1815 double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1817 return pulses_in_section + accumulated_pulses;
1821 TempoMap::frame_at_beat (const double& beat) const
1823 Glib::Threads::RWLock::ReaderLock lm (lock);
1824 return frame_at_beat_locked (_metrics, beat);
1828 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1830 framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1831 frameoffset_t const frame_off = frame_offset_at (metrics, frame);
1832 return frame - frame_off;
1836 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1838 /* HOLD THE READER LOCK */
1840 const TempoSection* prev_ts = 0;
1841 double accumulated_pulses = 0.0;
1843 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1846 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1847 if (prev_ts && t->pulse() > pulse) {
1848 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1851 accumulated_pulses = t->pulse();
1855 /* must be treated as constant, irrespective of _type */
1856 double const pulses_in_section = pulse - accumulated_pulses;
1857 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1859 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1865 TempoMap::beat_offset_at (const Metrics& metrics, const double& beat) const
1867 MeterSection* prev_m = 0;
1868 double beat_off = first_meter().pulse();
1870 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1871 MeterSection* m = 0;
1872 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1874 if (m->beat() > beat) {
1878 if (m->position_lock_style() == AudioTime) {
1879 beat_off += ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) - floor (m->pulse() - prev_m->pulse());
1890 TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
1892 frameoffset_t frame_off = 0;
1893 MeterSection* prev_m = 0;
1895 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1896 MeterSection* m = 0;
1897 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1898 if (m->frame() > frame) {
1901 if (prev_m && m->position_lock_style() == AudioTime) {
1902 const double pulse = prev_m->pulse() + ((m->beat() - prev_m->beat()) / prev_m->note_divisor());
1903 frame_off += frame_at_pulse_locked (metrics, pulse) - m->frame();
1913 TempoMap::frame_time (const BBT_Time& bbt)
1916 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1920 if (bbt.beats < 1) {
1921 throw std::logic_error ("beats are counted from one");
1923 Glib::Threads::RWLock::ReaderLock lm (lock);
1924 double const beat = bbt_to_beats_locked (_metrics, bbt);
1925 framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1930 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1932 /* HOLD THE READER LOCK */
1934 framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt)));
1940 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1942 TempoSection* prev_ts = 0;
1944 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1946 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1948 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
1951 if (by_frame && t->frame() != prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
1955 if (!by_frame && fabs (t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate)) > 0.00001) {
1956 std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
1957 " |error :" << t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << std::endl <<
1958 "|frame at beat :" << prev_ts->frame_at_pulse (t->pulse(), _frame_rate) << std::endl <<
1959 " |frame at tempo : " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
1972 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1974 TempoSection* prev_ts = 0;
1975 TempoSection* section_prev = 0;
1976 MetricSectionFrameSorter fcmp;
1977 MetricSectionSorter cmp;
1979 section->set_frame (frame);
1981 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1983 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1986 section_prev = prev_ts;
1989 if (t->position_lock_style() == MusicTime) {
1990 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1991 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1993 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1994 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2002 section_prev->set_c_func (section_prev->compute_c_func_frame (bpm.pulses_per_minute(), frame, _frame_rate));
2003 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2006 if (section->position_lock_style() == MusicTime) {
2007 /* we're setting the frame */
2008 section->set_position_lock_style (AudioTime);
2009 recompute_tempos (imaginary);
2010 section->set_position_lock_style (MusicTime);
2012 recompute_tempos (imaginary);
2015 if (check_solved (imaginary, true)) {
2016 recompute_meters (imaginary);
2020 imaginary.sort (fcmp);
2021 if (section->position_lock_style() == MusicTime) {
2022 /* we're setting the frame */
2023 section->set_position_lock_style (AudioTime);
2024 recompute_tempos (imaginary);
2025 section->set_position_lock_style (MusicTime);
2027 recompute_tempos (imaginary);
2029 if (check_solved (imaginary, true)) {
2030 recompute_meters (imaginary);
2034 imaginary.sort (cmp);
2035 if (section->position_lock_style() == MusicTime) {
2036 /* we're setting the frame */
2037 section->set_position_lock_style (AudioTime);
2038 recompute_tempos (imaginary);
2039 section->set_position_lock_style (MusicTime);
2041 recompute_tempos (imaginary);
2043 if (check_solved (imaginary, true)) {
2044 recompute_meters (imaginary);
2047 //dump (imaginary, std::cerr);
2053 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
2055 MetricSectionSorter cmp;
2056 MetricSectionFrameSorter fcmp;
2057 TempoSection* prev_ts = 0;
2058 TempoSection* section_prev = 0;
2060 section->set_pulse (pulse);
2061 section->set_beats_per_minute (bpm.beats_per_minute());
2063 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2065 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2068 section_prev = prev_ts;
2071 if (t->position_lock_style() == MusicTime) {
2072 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2073 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2075 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2076 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2083 section_prev->set_c_func (section_prev->compute_c_func_pulse (bpm.pulses_per_minute(), pulse, _frame_rate));
2084 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2087 if (section->position_lock_style() == AudioTime) {
2088 /* we're setting the pulse */
2089 section->set_position_lock_style (MusicTime);
2090 recompute_tempos (imaginary);
2091 section->set_position_lock_style (AudioTime);
2093 recompute_tempos (imaginary);
2095 if (check_solved (imaginary, false)) {
2096 recompute_meters (imaginary);
2100 imaginary.sort (cmp);
2101 if (section->position_lock_style() == AudioTime) {
2102 /* we're setting the pulse */
2103 section->set_position_lock_style (MusicTime);
2104 recompute_tempos (imaginary);
2105 section->set_position_lock_style (AudioTime);
2107 recompute_tempos (imaginary);
2110 if (check_solved (imaginary, false)) {
2111 recompute_meters (imaginary);
2115 imaginary.sort (fcmp);
2116 if (section->position_lock_style() == AudioTime) {
2117 /* we're setting the pulse */
2118 section->set_position_lock_style (MusicTime);
2119 recompute_tempos (imaginary);
2120 section->set_position_lock_style (AudioTime);
2122 recompute_tempos (imaginary);
2125 if (check_solved (imaginary, false)) {
2126 recompute_meters (imaginary);
2130 //dump (imaginary, std::cerr);
2136 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2138 MeterSection* prev_ms = 0;
2140 double accumulated_beats = 0.0;
2141 uint32_t accumulated_bars = 0;
2143 section->set_pulse (pulse);
2145 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2147 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2149 double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2150 accumulated_beats += beats_in_m;
2151 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2154 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2155 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2156 section->set_beat (b_bbt);
2161 if (m->position_lock_style() == MusicTime) {
2162 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2163 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2164 m->set_pulse (pulse);
2166 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2167 m->set_beat (b_bbt);
2168 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2169 m->set_pulse (pulse);
2176 if (section->position_lock_style() == AudioTime) {
2177 /* we're setting the pulse */
2178 section->set_position_lock_style (MusicTime);
2179 recompute_meters (imaginary);
2180 section->set_position_lock_style (AudioTime);
2182 recompute_meters (imaginary);
2187 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
2189 MeterSection* prev_ms = 0;
2190 double accumulated_beats = 0.0;
2191 uint32_t accumulated_bars = 0;
2193 section->set_frame (frame);
2195 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2197 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2199 const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2200 accumulated_beats += beats_in_m;
2201 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2205 here we define the pulse for this frame.
2206 we're going to set it 'incorrectly' to the next integer and use this 'error'
2207 as an offset to the map as far as users of the public methods are concerned.
2208 (meters should go on absolute pulses to keep us sane)
2210 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2211 m->set_pulse (pulse_at_frame_locked (imaginary, frame));
2212 m->set_beat (b_bbt);
2217 if (m->position_lock_style() == MusicTime) {
2218 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2219 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2220 m->set_pulse (pulse);
2222 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2223 m->set_beat (b_bbt);
2224 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2225 m->set_pulse (pulse);
2232 if (section->position_lock_style() == MusicTime) {
2233 /* we're setting the frame */
2234 section->set_position_lock_style (AudioTime);
2235 recompute_meters (imaginary);
2236 section->set_position_lock_style (MusicTime);
2238 recompute_meters (imaginary);
2240 //dump (imaginary, std::cerr);
2244 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2246 Glib::Threads::RWLock::ReaderLock lm (lock);
2248 double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2249 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2250 double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2251 framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2252 framecnt_t const ret = time_at_bbt;
2258 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2260 return round_to_type (fr, dir, Bar);
2264 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2266 return round_to_type (fr, dir, Beat);
2270 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2272 Glib::Threads::RWLock::ReaderLock lm (lock);
2273 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2274 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2275 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2277 ticks -= beats * BBT_Time::ticks_per_beat;
2280 /* round to next (or same iff dir == RoundUpMaybe) */
2282 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2284 if (mod == 0 && dir == RoundUpMaybe) {
2285 /* right on the subdivision, which is fine, so do nothing */
2287 } else if (mod == 0) {
2288 /* right on the subdivision, so the difference is just the subdivision ticks */
2289 ticks += ticks_one_subdivisions_worth;
2292 /* not on subdivision, compute distance to next subdivision */
2294 ticks += ticks_one_subdivisions_worth - mod;
2297 if (ticks >= BBT_Time::ticks_per_beat) {
2298 ticks -= BBT_Time::ticks_per_beat;
2300 } else if (dir < 0) {
2302 /* round to previous (or same iff dir == RoundDownMaybe) */
2304 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2306 if (difference == 0 && dir == RoundDownAlways) {
2307 /* right on the subdivision, but force-rounding down,
2308 so the difference is just the subdivision ticks */
2309 difference = ticks_one_subdivisions_worth;
2312 if (ticks < difference) {
2313 ticks = BBT_Time::ticks_per_beat - ticks;
2315 ticks -= difference;
2319 /* round to nearest */
2322 /* compute the distance to the previous and next subdivision */
2324 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2326 /* closer to the next subdivision, so shift forward */
2328 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2330 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2332 if (ticks > BBT_Time::ticks_per_beat) {
2334 ticks -= BBT_Time::ticks_per_beat;
2335 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2338 } else if (rem > 0) {
2340 /* closer to previous subdivision, so shift backward */
2344 /* can't go backwards past zero, so ... */
2347 /* step back to previous beat */
2349 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2350 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2352 ticks = lrint (ticks - rem);
2353 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2356 /* on the subdivision, do nothing */
2360 framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2366 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2368 Glib::Threads::RWLock::ReaderLock lm (lock);
2370 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2371 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2376 /* find bar previous to 'frame' */
2379 return frame_time (bbt);
2381 } else if (dir > 0) {
2382 /* find bar following 'frame' */
2386 return frame_time (bbt);
2388 /* true rounding: find nearest bar */
2389 framepos_t raw_ft = frame_time (bbt);
2392 framepos_t prev_ft = frame_time (bbt);
2394 framepos_t next_ft = frame_time (bbt);
2396 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2407 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2408 } else if (dir > 0) {
2409 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2411 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2420 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2421 framepos_t lower, framepos_t upper)
2423 Glib::Threads::RWLock::ReaderLock lm (lock);
2424 int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2425 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2426 /* although the map handles negative beats, bbt doesn't. */
2430 while (cnt <= upper_beat) {
2431 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2432 TempoSection const tempo = tempo_section_at_locked (pos);
2433 MeterSection const meter = meter_section_at_locked (pos);
2434 BBT_Time const bbt = beats_to_bbt (cnt);
2436 points.push_back (BBTPoint (meter, Tempo (tempo.beats_per_minute(), tempo.note_type()), pos, bbt.bars, bbt.beats));
2442 TempoMap::tempo_section_at (framepos_t frame) const
2444 Glib::Threads::RWLock::ReaderLock lm (lock);
2445 return tempo_section_at_locked (frame);
2449 TempoMap::tempo_section_at_locked (framepos_t frame) const
2451 Metrics::const_iterator i;
2452 TempoSection* prev = 0;
2454 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2457 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2459 if (t->frame() > frame) {
2469 abort(); /*NOTREACHED*/
2476 /* don't use this to calculate length (the tempo is only correct for this frame).
2477 do that stuff based on the beat_at_frame and frame_at_beat api
2480 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2482 Glib::Threads::RWLock::ReaderLock lm (lock);
2484 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2485 const TempoSection* ts_after = 0;
2486 Metrics::const_iterator i;
2488 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2491 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2493 if ((*i)->frame() > frame) {
2501 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2503 /* must be treated as constant tempo */
2504 return ts_at->frames_per_beat (_frame_rate);
2508 TempoMap::tempo_at (const framepos_t& frame) const
2510 Glib::Threads::RWLock::ReaderLock lm (lock);
2511 //frameoffset_t const frame_off = frame_offset_at (_metrics, frame);
2512 TempoSection* prev_ts = 0;
2514 Metrics::const_iterator i;
2516 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2518 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2519 if ((prev_ts) && t->frame() > frame) {
2520 /* t is the section past frame */
2521 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2522 Tempo const ret_tempo (ret, prev_ts->note_type());
2529 double const ret = prev_ts->beats_per_minute();
2530 Tempo const ret_tempo (ret, prev_ts->note_type ());
2536 TempoMap::meter_section_at (framepos_t frame) const
2538 Glib::Threads::RWLock::ReaderLock lm (lock);
2539 return meter_section_at_locked (frame);
2543 TempoMap::meter_section_at_locked (framepos_t frame) const
2545 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2546 Metrics::const_iterator i;
2547 MeterSection* prev = 0;
2549 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2552 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2554 if (prev && (*i)->frame() > frame) {
2564 abort(); /*NOTREACHED*/
2571 TempoMap::meter_at (framepos_t frame) const
2573 TempoMetric m (metric_at (frame));
2578 TempoMap::get_state ()
2580 Metrics::const_iterator i;
2581 XMLNode *root = new XMLNode ("TempoMap");
2584 Glib::Threads::RWLock::ReaderLock lm (lock);
2585 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2586 root->add_child_nocopy ((*i)->get_state());
2594 TempoMap::set_state (const XMLNode& node, int /*version*/)
2597 Glib::Threads::RWLock::WriterLock lm (lock);
2600 XMLNodeConstIterator niter;
2601 Metrics old_metrics (_metrics);
2602 MeterSection* last_meter = 0;
2605 nlist = node.children();
2607 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2608 XMLNode* child = *niter;
2610 if (child->name() == TempoSection::xml_state_node_name) {
2613 TempoSection* ts = new TempoSection (*child);
2614 _metrics.push_back (ts);
2616 if (ts->bar_offset() < 0.0) {
2618 //ts->update_bar_offset_from_bbt (*last_meter);
2623 catch (failed_constructor& err){
2624 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2625 _metrics = old_metrics;
2629 } else if (child->name() == MeterSection::xml_state_node_name) {
2632 MeterSection* ms = new MeterSection (*child);
2633 _metrics.push_back (ms);
2637 catch (failed_constructor& err) {
2638 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2639 _metrics = old_metrics;
2645 if (niter == nlist.end()) {
2646 MetricSectionSorter cmp;
2647 _metrics.sort (cmp);
2649 /* check for legacy sessions where bbt was the base musical unit for tempo */
2650 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2653 MeterSection* prev_ms = 0;
2654 TempoSection* prev_ts = 0;
2655 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2656 if (prev_ms && prev_ms->pulse() < 0.0) {
2657 /*XX we cannot possibly make this work??. */
2658 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());
2659 prev_ms->set_beat (start);
2660 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);
2661 prev_ms->set_pulse (start_pulse);
2664 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2665 if (prev_ts && prev_ts->pulse() < 0.0) {
2666 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);
2667 prev_ts->set_pulse (start);
2672 /* check for multiple tempo/meters at the same location, which
2673 ardour2 somehow allowed.
2676 Metrics::iterator prev = _metrics.end();
2677 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2678 if (prev != _metrics.end()) {
2680 MeterSection* prev_ms;
2682 TempoSection* prev_ts;
2683 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2684 if (prev_ms->pulse() == ms->pulse()) {
2685 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2686 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2689 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2690 if (prev_ts->pulse() == ts->pulse()) {
2691 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2692 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2700 recompute_map (_metrics);
2703 PropertyChanged (PropertyChange ());
2709 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2711 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2712 const MeterSection* m;
2713 const TempoSection* t;
2714 const TempoSection* prev_ts = 0;
2716 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2718 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2719 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? "
2720 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2721 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2723 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2724 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;
2727 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2728 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2729 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2732 o << "------" << std::endl;
2736 TempoMap::n_tempos() const
2738 Glib::Threads::RWLock::ReaderLock lm (lock);
2741 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2742 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2751 TempoMap::n_meters() const
2753 Glib::Threads::RWLock::ReaderLock lm (lock);
2756 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2757 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2766 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2769 Glib::Threads::RWLock::WriterLock lm (lock);
2770 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2771 if ((*i)->frame() >= where && (*i)->movable ()) {
2772 (*i)->set_frame ((*i)->frame() + amount);
2776 /* now reset the BBT time of all metrics, based on their new
2777 * audio time. This is the only place where we do this reverse
2781 Metrics::iterator i;
2782 const MeterSection* meter;
2783 const TempoSection* tempo;
2787 meter = &first_meter ();
2788 tempo = &first_tempo ();
2793 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2796 MetricSection* prev = 0;
2798 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2801 //TempoMetric metric (*meter, *tempo);
2802 MeterSection* ms = const_cast<MeterSection*>(meter);
2803 TempoSection* ts = const_cast<TempoSection*>(tempo);
2806 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2807 ts->set_pulse (t->pulse());
2809 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2810 ts->set_pulse (m->pulse());
2812 ts->set_frame (prev->frame());
2816 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2817 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2818 ms->set_beat (start);
2819 ms->set_pulse (m->pulse());
2821 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2822 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
2823 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
2824 ms->set_beat (start);
2825 ms->set_pulse (t->pulse());
2827 ms->set_frame (prev->frame());
2831 // metric will be at frames=0 bbt=1|1|0 by default
2832 // which is correct for our purpose
2835 // cerr << bbt << endl;
2837 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2838 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
2840 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
2841 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2842 bbt_time (m->frame(), bbt);
2844 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2850 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2851 /* round up to next beat */
2857 if (bbt.beats != 1) {
2858 /* round up to next bar */
2863 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
2864 m->set_beat (start);
2865 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
2867 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
2869 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2870 abort(); /*NOTREACHED*/
2876 recompute_map (_metrics);
2880 PropertyChanged (PropertyChange ());
2883 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2887 std::list<MetricSection*> metric_kill_list;
2889 TempoSection* last_tempo = NULL;
2890 MeterSection* last_meter = NULL;
2891 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2892 bool meter_after = false; // is there a meter marker likewise?
2894 Glib::Threads::RWLock::WriterLock lm (lock);
2895 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2896 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2897 metric_kill_list.push_back(*i);
2898 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2901 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2905 else if ((*i)->frame() >= where) {
2906 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2907 (*i)->set_frame ((*i)->frame() - amount);
2908 if ((*i)->frame() == where) {
2909 // marker was immediately after end of range
2910 tempo_after = dynamic_cast<TempoSection*> (*i);
2911 meter_after = dynamic_cast<MeterSection*> (*i);
2917 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2918 if (last_tempo && !tempo_after) {
2919 metric_kill_list.remove(last_tempo);
2920 last_tempo->set_frame(where);
2923 if (last_meter && !meter_after) {
2924 metric_kill_list.remove(last_meter);
2925 last_meter->set_frame(where);
2929 //remove all the remaining metrics
2930 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2931 _metrics.remove(*i);
2936 recompute_map (_metrics);
2939 PropertyChanged (PropertyChange ());
2943 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2944 * pos can be -ve, if required.
2947 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2949 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2952 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2954 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2956 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2959 /** Add the BBT interval op to pos and return the result */
2961 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2963 Glib::Threads::RWLock::ReaderLock lm (lock);
2965 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
2966 pos_bbt.ticks += op.ticks;
2967 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
2969 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
2971 pos_bbt.beats += op.beats;
2972 /* the meter in effect will start on the bar */
2973 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
2974 while (pos_bbt.beats >= divisions_per_bar + 1) {
2976 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
2977 pos_bbt.beats -= divisions_per_bar;
2979 pos_bbt.bars += op.bars;
2981 return frame_time_locked (_metrics, pos_bbt);
2984 /** Count the number of beats that are equivalent to distance when going forward,
2988 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2990 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
2994 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3000 operator<< (std::ostream& o, const Meter& m) {
3001 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3005 operator<< (std::ostream& o, const Tempo& t) {
3006 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3010 operator<< (std::ostream& o, const MetricSection& section) {
3012 o << "MetricSection @ " << section.frame() << ' ';
3014 const TempoSection* ts;
3015 const MeterSection* ms;
3017 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3018 o << *((const Tempo*) ts);
3019 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3020 //o << *((const Meter*) ms);