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;
477 framepos_t frame = 0;
478 pair<double, BBT_Time> start;
480 if ((prop = node.property ("start")) != 0) {
481 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
485 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
487 /* legacy session - start used to be in bbt*/
491 error << _("MeterSection XML node has no \"start\" property") << endmsg;
494 if ((prop = node.property ("pulse")) != 0) {
495 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
496 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
502 if ((prop = node.property ("bbt")) == 0) {
503 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
504 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
508 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
509 //throw failed_constructor();
515 if ((prop = node.property ("frame")) != 0) {
516 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
517 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
523 /* beats-per-bar is old; divisions-per-bar is new */
525 if ((prop = node.property ("divisions-per-bar")) == 0) {
526 if ((prop = node.property ("beats-per-bar")) == 0) {
527 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
528 throw failed_constructor();
531 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
532 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
533 throw failed_constructor();
536 if ((prop = node.property ("note-type")) == 0) {
537 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
538 throw failed_constructor();
540 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
541 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
542 throw failed_constructor();
545 if ((prop = node.property ("lock-style")) == 0) {
546 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
547 set_position_lock_style (MusicTime);
549 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
552 if ((prop = node.property ("movable")) == 0) {
553 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
554 throw failed_constructor();
557 set_movable (string_is_affirmative (prop->value()));
561 MeterSection::get_state() const
563 XMLNode *root = new XMLNode (xml_state_node_name);
567 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
571 root->add_property ("bbt", buf);
572 snprintf (buf, sizeof (buf), "%lf", pulse());
573 root->add_property ("pulse", buf);
574 snprintf (buf, sizeof (buf), "%f", _note_type);
575 root->add_property ("note-type", buf);
576 snprintf (buf, sizeof (buf), "%li", frame());
577 root->add_property ("frame", buf);
578 root->add_property ("lock-style", enum_2_string (position_lock_style()));
579 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
580 root->add_property ("divisions-per-bar", buf);
581 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
582 root->add_property ("movable", buf);
587 /***********************************************************************/
589 struct MetricSectionSorter {
590 bool operator() (const MetricSection* a, const MetricSection* b) {
591 return a->pulse() < b->pulse();
595 struct MetricSectionFrameSorter {
596 bool operator() (const MetricSection* a, const MetricSection* b) {
597 return a->frame() < b->frame();
601 TempoMap::TempoMap (framecnt_t fr)
604 BBT_Time start (1, 1, 0);
606 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
607 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
609 t->set_movable (false);
610 m->set_movable (false);
612 /* note: frame time is correct (zero) for both of these */
614 _metrics.push_back (t);
615 _metrics.push_back (m);
619 TempoMap::~TempoMap ()
624 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
626 bool removed = false;
629 Glib::Threads::RWLock::WriterLock lm (lock);
630 if ((removed = remove_tempo_locked (tempo))) {
631 if (complete_operation) {
632 recompute_map (_metrics);
637 if (removed && complete_operation) {
638 PropertyChanged (PropertyChange ());
643 TempoMap::remove_tempo_locked (const TempoSection& tempo)
647 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
648 if (dynamic_cast<TempoSection*> (*i) != 0) {
649 if (tempo.frame() == (*i)->frame()) {
650 if ((*i)->movable()) {
662 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
664 bool removed = false;
667 Glib::Threads::RWLock::WriterLock lm (lock);
668 if ((removed = remove_meter_locked (tempo))) {
669 if (complete_operation) {
670 recompute_map (_metrics);
675 if (removed && complete_operation) {
676 PropertyChanged (PropertyChange ());
681 TempoMap::remove_meter_locked (const MeterSection& tempo)
685 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
686 if (dynamic_cast<MeterSection*> (*i) != 0) {
687 if (tempo.frame() == (*i)->frame()) {
688 if ((*i)->movable()) {
700 TempoMap::do_insert (MetricSection* section)
702 bool need_add = true;
703 /* we only allow new meters to be inserted on beat 1 of an existing
707 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
708 //assert (m->bbt().ticks == 0);
710 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
712 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
713 corrected.second.beats = 1;
714 corrected.second.ticks = 0;
715 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
716 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
717 m->bbt(), corrected.second) << endmsg;
718 m->set_pulse (corrected);
722 /* Look for any existing MetricSection that is of the same type and
723 in the same bar as the new one, and remove it before adding
724 the new one. Note that this means that if we find a matching,
725 existing section, we can break out of the loop since we're
726 guaranteed that there is only one such match.
729 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
731 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
732 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
733 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
734 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
736 if (tempo && insert_tempo) {
739 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
740 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
742 if (!tempo->movable()) {
744 /* can't (re)move this section, so overwrite
745 * its data content (but not its properties as
749 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
757 } else if (meter && insert_meter) {
761 bool const ipm = insert_meter->position_lock_style() == MusicTime;
763 if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
765 if (!meter->movable()) {
767 /* can't (re)move this section, so overwrite
768 * its data content (but not its properties as
772 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
773 (*i)->set_position_lock_style (insert_meter->position_lock_style());
783 /* non-matching types, so we don't care */
787 /* Add the given MetricSection, if we didn't just reset an existing
792 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
793 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
796 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
797 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
800 bool const ipm = insert_meter->position_lock_style() == MusicTime;
801 if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
806 } else if (insert_tempo) {
807 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
808 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
811 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
812 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
819 _metrics.insert (i, section);
820 //dump (_metrics, std::cerr);
825 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
828 Glib::Threads::RWLock::WriterLock lm (lock);
829 TempoSection& first (first_tempo());
830 if (ts.pulse() != first.pulse()) {
831 remove_tempo_locked (ts);
832 add_tempo_locked (tempo, where, true, type);
834 first.set_type (type);
836 /* cannot move the first tempo section */
837 *static_cast<Tempo*>(&first) = tempo;
838 recompute_map (_metrics);
843 PropertyChanged (PropertyChange ());
847 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
850 Glib::Threads::RWLock::WriterLock lm (lock);
851 TempoSection& first (first_tempo());
852 if (ts.frame() != first.frame()) {
853 remove_tempo_locked (ts);
854 add_tempo_locked (tempo, frame, true, type);
856 first.set_type (type);
858 /* cannot move the first tempo section */
859 *static_cast<Tempo*>(&first) = tempo;
860 recompute_map (_metrics);
864 PropertyChanged (PropertyChange ());
868 TempoMap::add_tempo (const Tempo& tempo, const double& where, ARDOUR::TempoSection::Type type)
871 Glib::Threads::RWLock::WriterLock lm (lock);
872 add_tempo_locked (tempo, where, true, type);
875 PropertyChanged (PropertyChange ());
879 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
882 Glib::Threads::RWLock::WriterLock lm (lock);
883 add_tempo_locked (tempo, frame, true, type);
887 PropertyChanged (PropertyChange ());
891 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
893 double pulse = pulse_at_beat (_metrics, where);
894 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
899 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
904 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
906 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
911 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
916 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
919 Glib::Threads::RWLock::WriterLock lm (lock);
920 MeterSection& first (first_meter());
921 const PositionLockStyle pl = ms.position_lock_style();
922 if (ms.pulse() != first.pulse()) {
923 remove_meter_locked (ms);
924 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
926 /* cannot move the first meter section */
927 *static_cast<Meter*>(&first) = meter;
928 first.set_position_lock_style (pl);
929 recompute_map (_metrics);
933 PropertyChanged (PropertyChange ());
937 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
940 Glib::Threads::RWLock::WriterLock lm (lock);
941 MeterSection& first (first_meter());
942 const PositionLockStyle pl = ms.position_lock_style();
943 if (ms.pulse() != first.pulse()) {
944 remove_meter_locked (ms);
945 add_meter_locked (meter, frame, true);
947 /* cannot move the first meter section */
948 *static_cast<Meter*>(&first) = meter;
949 first.set_position_lock_style (pl);
950 recompute_map (_metrics);
954 PropertyChanged (PropertyChange ());
959 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
962 Glib::Threads::RWLock::WriterLock lm (lock);
963 add_meter_locked (meter, beat, where, true);
968 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
969 dump (_metrics, std::cerr);
973 PropertyChanged (PropertyChange ());
977 TempoMap::add_meter (const Meter& meter, const framepos_t& frame)
980 Glib::Threads::RWLock::WriterLock lm (lock);
981 add_meter_locked (meter, frame, true);
986 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
987 dump (_metrics, std::cerr);
991 PropertyChanged (PropertyChange ());
995 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
997 /* a new meter always starts a new bar on the first beat. so
998 round the start time appropriately. remember that
999 `where' is based on the existing tempo map, not
1000 the result after we insert the new meter.
1004 if (where.beats != 1) {
1008 /* new meters *always* start on a beat. */
1010 double pulse = pulse_at_beat (_metrics, beat);
1012 MeterSection* new_meter = new MeterSection (pulse, where, meter.divisions_per_bar(), meter.note_divisor());
1013 do_insert (new_meter);
1016 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1022 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1025 MeterSection* new_meter = new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor());
1026 double paf = pulse_at_frame_locked (_metrics, frame);
1027 pair<double, BBT_Time> beat = make_pair (paf, beats_to_bbt_locked (_metrics, beat_at_pulse (_metrics, paf)));
1028 new_meter->set_pulse (beat);
1029 do_insert (new_meter);
1032 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1038 * 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,
1039 * taking any possible reordering as a consequence of this into account.
1040 * @param section - the section to be altered
1041 * @param bpm - the new Tempo
1042 * @param bbt - the bbt where the altered tempo will fall
1043 * @return returns - the position in frames where the new tempo section will lie.
1046 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1048 Glib::Threads::RWLock::ReaderLock lm (lock);
1051 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1052 double const beat = bbt_to_beats_locked (future_map, bbt);
1053 if (solve_map (future_map, new_section, bpm, pulse_at_beat (future_map, beat))) {
1054 ret = new_section->frame();
1056 ret = frame_at_beat_locked (_metrics, beat);
1059 Metrics::const_iterator d = future_map.begin();
1060 while (d != future_map.end()) {
1068 TempoMap::predict_tempo_beat (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1070 Glib::Threads::RWLock::ReaderLock lm (lock);
1073 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1075 if (solve_map (future_map, new_section, bpm, frame)) {
1076 ret = beat_at_pulse (future_map, new_section->pulse());
1078 ret = beat_at_frame_locked (_metrics, frame);
1081 Metrics::const_iterator d = future_map.begin();
1082 while (d != future_map.end()) {
1090 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1094 Glib::Threads::RWLock::WriterLock lm (lock);
1095 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1096 if (solve_map (future_map, new_section, bpm, frame)) {
1097 solve_map (_metrics, ts, bpm, frame);
1101 Metrics::const_iterator d = future_map.begin();
1102 while (d != future_map.end()) {
1107 MetricPositionChanged (); // Emit Signal
1111 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
1115 Glib::Threads::RWLock::WriterLock lm (lock);
1116 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1117 if (solve_map (future_map, new_section, bpm, pulse_at_beat (future_map, beat))) {
1118 solve_map (_metrics, ts, bpm, beat);
1122 Metrics::const_iterator d = future_map.begin();
1123 while (d != future_map.end()) {
1128 MetricPositionChanged (); // Emit Signal
1132 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
1135 Glib::Threads::RWLock::WriterLock lm (lock);
1136 solve_map (_metrics, ms, mt, frame);
1139 MetricPositionChanged (); // Emit Signal
1143 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
1146 Glib::Threads::RWLock::WriterLock lm (lock);
1147 solve_map (_metrics, ms, mt, pulse_at_beat (_metrics, beat));
1150 MetricPositionChanged (); // Emit Signal
1154 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1157 TempoSection* ret = 0;
1160 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1161 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1163 if (t->position_lock_style() == MusicTime) {
1164 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1166 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1168 copy.push_back (ret);
1171 if (t->position_lock_style() == MusicTime) {
1172 copy.push_back (new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type()));
1174 copy.push_back (new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type()));
1177 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1178 if (m->position_lock_style() == MusicTime) {
1179 copy.push_back (new MeterSection (m->pulse(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1181 copy.push_back (new MeterSection (m->frame(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1186 recompute_map (copy);
1191 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
1194 TempoSection* new_section = 0;
1197 Glib::Threads::RWLock::ReaderLock lm (lock);
1198 new_section = copy_metrics_and_point (copy, ts);
1201 double const beat = bbt_to_beats_locked (copy, bbt);
1202 bool ret = solve_map (copy, new_section, bpm, beat);
1204 Metrics::const_iterator d = copy.begin();
1205 while (d != copy.end()) {
1214 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1216 Tempo newtempo (beats_per_minute, note_type);
1219 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1220 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1222 Glib::Threads::RWLock::WriterLock lm (lock);
1223 *((Tempo*) t) = newtempo;
1224 recompute_map (_metrics);
1226 PropertyChanged (PropertyChange ());
1233 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1235 Tempo newtempo (beats_per_minute, note_type);
1238 TempoSection* first;
1239 Metrics::iterator i;
1241 /* find the TempoSection immediately preceding "where"
1244 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1246 if ((*i)->frame() > where) {
1252 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1262 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1272 Glib::Threads::RWLock::WriterLock lm (lock);
1273 /* cannot move the first tempo section */
1274 *((Tempo*)prev) = newtempo;
1275 recompute_map (_metrics);
1278 PropertyChanged (PropertyChange ());
1282 TempoMap::first_meter () const
1284 const MeterSection *m = 0;
1286 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1287 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1292 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1293 abort(); /*NOTREACHED*/
1298 TempoMap::first_meter ()
1300 MeterSection *m = 0;
1302 /* CALLER MUST HOLD LOCK */
1304 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1305 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1310 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1311 abort(); /*NOTREACHED*/
1316 TempoMap::first_tempo () const
1318 const TempoSection *t = 0;
1320 /* CALLER MUST HOLD LOCK */
1322 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1323 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1328 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1329 abort(); /*NOTREACHED*/
1334 TempoMap::first_tempo ()
1336 TempoSection *t = 0;
1338 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1339 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1344 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1345 abort(); /*NOTREACHED*/
1349 TempoMap::recompute_tempos (Metrics& metrics)
1351 TempoSection* prev_ts = 0;
1353 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1356 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1358 if (t->position_lock_style() == AudioTime) {
1359 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1360 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1363 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1364 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1373 /* tempos must be positioned correctly */
1375 TempoMap::recompute_meters (Metrics& metrics)
1377 MeterSection* meter = 0;
1378 MeterSection* prev_m = 0;
1379 double accumulated_beats = 0.0;
1380 uint32_t accumulated_bars = 0;
1381 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1382 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1384 const double beats_in_m = (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
1385 accumulated_beats += beats_in_m;
1386 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1389 if (meter->position_lock_style() == AudioTime) {
1390 pair<double, BBT_Time> pr;
1391 pr.first = pulse_at_frame_locked (metrics, meter->frame());
1392 BBT_Time const where = BBT_Time (accumulated_bars + 1, 1, 0);
1394 meter->set_pulse (pr);
1396 meter->set_frame (frame_at_pulse_locked (metrics, meter->pulse()));
1399 meter->set_beat (accumulated_beats);
1406 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1408 /* CALLER MUST HOLD WRITE LOCK */
1412 /* we will actually stop once we hit
1419 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1422 /* silly call from Session::process() during startup
1427 recompute_tempos (metrics);
1428 recompute_meters (metrics);
1432 TempoMap::pulse_at_beat (const Metrics& metrics, const double& beat) const
1434 MeterSection* prev_ms = 0;
1435 double accumulated_beats = 0.0;
1437 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1439 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1440 if (prev_ms && m->beat() > beat) {
1443 accumulated_beats = m->beat();
1449 return prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1453 TempoMap::beat_at_pulse (const Metrics& metrics, const double& pulse) const
1455 MeterSection* prev_ms = 0;
1456 double accumulated_beats = 0.0;
1458 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1460 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1461 if (prev_ms && m->pulse() > pulse) {
1464 accumulated_beats = m->beat();
1469 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1471 return beats_in_section + accumulated_beats;
1475 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1477 Glib::Threads::RWLock::ReaderLock lm (lock);
1478 TempoMetric m (first_meter(), first_tempo());
1480 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1481 at something, because we insert the default tempo and meter during
1482 TempoMap construction.
1484 now see if we can find better candidates.
1487 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1489 if ((*i)->frame() > frame) {
1503 /* XX meters only */
1505 TempoMap::metric_at (BBT_Time bbt) const
1507 Glib::Threads::RWLock::ReaderLock lm (lock);
1508 TempoMetric m (first_meter(), first_tempo());
1510 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1511 at something, because we insert the default tempo and meter during
1512 TempoMap construction.
1514 now see if we can find better candidates.
1517 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1519 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1520 BBT_Time section_start (mw->bbt());
1522 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1534 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1541 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1544 Glib::Threads::RWLock::ReaderLock lm (lock);
1545 frameoffset_t const frame_off = frame_offset_at (_metrics, frame);
1546 double const beat = beat_at_pulse (_metrics, pulse_at_frame_locked (_metrics, frame + frame_off));
1548 bbt = beats_to_bbt_locked (_metrics, beat);
1552 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1554 Glib::Threads::RWLock::ReaderLock lm (lock);
1556 return bbt_to_beats_locked (_metrics, bbt);
1560 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1562 /* CALLER HOLDS READ LOCK */
1564 double accumulated_beats = 0.0;
1565 double accumulated_bars = 0.0;
1566 MeterSection* prev_ms = 0;
1567 /* because audio-locked meters have 'fake' integral beats,
1568 there is no pulse offset here.
1570 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1572 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1573 double bars_to_m = 0.0;
1575 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1576 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1579 accumulated_beats = m->beat();
1580 accumulated_bars += bars_to_m;
1586 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1587 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1588 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1594 TempoMap::beats_to_bbt (const double& beats)
1596 Glib::Threads::RWLock::ReaderLock lm (lock);
1598 return beats_to_bbt_locked (_metrics, beats);
1602 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& beats) const
1604 /* CALLER HOLDS READ LOCK */
1606 MeterSection* prev_ms = 0;
1607 uint32_t accumulated_bars = 0;
1608 double accumulated_beats = 0.0;
1610 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1611 MeterSection* m = 0;
1613 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1616 double const beats_to_m = m->beat() - prev_ms->beat();
1617 if (accumulated_beats + beats_to_m > beats) {
1618 /* this is the meter after the one our beat is on*/
1622 /* we need a whole number of bars. */
1623 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1624 accumulated_beats += beats_to_m;
1631 double const beats_in_ms = beats - accumulated_beats;
1632 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1633 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1634 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1635 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1639 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1640 ret.beats = (uint32_t) floor (remaining_beats);
1641 ret.bars = total_bars;
1643 /* 0 0 0 to 1 1 0 - based mapping*/
1647 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1649 ret.ticks -= BBT_Time::ticks_per_beat;
1652 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1661 TempoMap::pulse_to_bbt (const double& pulse)
1663 Glib::Threads::RWLock::ReaderLock lm (lock);
1664 MeterSection* prev_ms = 0;
1665 uint32_t accumulated_bars = 0;
1666 double accumulated_pulses = 0.0;
1668 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1669 MeterSection* m = 0;
1671 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1674 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1675 if (accumulated_pulses + pulses_to_m > pulse) {
1676 /* this is the meter after the one our beat is on*/
1680 /* we need a whole number of bars. */
1681 accumulated_pulses += pulses_to_m;
1682 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1688 double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1689 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1690 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1691 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1692 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1696 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1697 ret.beats = (uint32_t) floor (remaining_beats);
1698 ret.bars = total_bars;
1700 /* 0 0 0 to 1 1 0 mapping*/
1704 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1706 ret.ticks -= BBT_Time::ticks_per_beat;
1709 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1718 TempoMap::beat_at_frame (const framecnt_t& frame) const
1720 Glib::Threads::RWLock::ReaderLock lm (lock);
1721 return beat_at_frame_locked (_metrics, frame);
1725 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1727 framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
1728 double const pulse = pulse_at_frame_locked (metrics, offset_frame);
1730 return beat_at_pulse (metrics, pulse);
1734 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1736 /* HOLD (at least) THE READER LOCK */
1737 TempoSection* prev_ts = 0;
1738 double accumulated_pulses = 0.0;
1740 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1742 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1745 if (t->frame() > frame) {
1746 /*the previous ts is the one containing the frame */
1747 double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1751 accumulated_pulses = t->pulse();
1756 /* treated as constant for this ts */
1757 double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1759 return pulses_in_section + accumulated_pulses;
1763 TempoMap::frame_at_beat (const double& beat) const
1765 Glib::Threads::RWLock::ReaderLock lm (lock);
1766 return frame_at_beat_locked (_metrics, beat);
1770 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1772 framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat (metrics, beat));
1773 frameoffset_t const frame_off = frame_offset_at (metrics, frame);
1774 return frame - frame_off;
1778 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1780 /* HOLD THE READER LOCK */
1782 const TempoSection* prev_ts = 0;
1783 double accumulated_pulses = 0.0;
1785 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1788 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1790 if (t->pulse() > pulse) {
1791 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1794 accumulated_pulses = t->pulse();
1798 /* must be treated as constant, irrespective of _type */
1799 double const pulses_in_section = pulse - accumulated_pulses;
1800 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1802 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1808 TempoMap::beat_offset_at (const Metrics& metrics, const double& beat) const
1810 MeterSection* prev_m = 0;
1811 double beat_off = 0.0;
1813 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1814 MeterSection* m = 0;
1815 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1817 if (m->beat() > beat) {
1821 if (m->position_lock_style() == AudioTime) {
1822 beat_off += ((m->pulse() - prev_m->pulse()) / prev_m->note_divisor()) - floor ((m->pulse() - prev_m->pulse()) / prev_m->note_divisor());
1833 TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
1835 frameoffset_t frame_off = 0;
1837 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1838 MeterSection* m = 0;
1839 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1840 if (m->frame() > frame) {
1843 if (m->position_lock_style() == AudioTime) {
1844 frame_off += frame_at_pulse_locked (metrics, m->pulse()) - m->frame();
1853 TempoMap::frame_time (const BBT_Time& bbt)
1856 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1860 if (bbt.beats < 1) {
1861 throw std::logic_error ("beats are counted from one");
1863 Glib::Threads::RWLock::ReaderLock lm (lock);
1864 double const beat = bbt_to_beats_locked (_metrics, bbt);
1865 framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1870 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1872 /* HOLD THE READER LOCK */
1874 framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat (metrics, bbt_to_beats_locked (metrics, bbt)));
1880 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1882 TempoSection* prev_ts = 0;
1884 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1886 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1888 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
1891 if (by_frame && t->frame() != prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
1894 if (!by_frame && fabs (t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate)) > 0.00001) {
1895 std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
1896 " |error :" << t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << std::endl <<
1897 "|frame at beat :" << prev_ts->frame_at_pulse (t->pulse(), _frame_rate) << std::endl <<
1898 " |frame at tempo : " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
1910 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1912 TempoSection* prev_ts = 0;
1913 TempoSection* section_prev = 0;
1914 MetricSectionFrameSorter fcmp;
1915 MetricSectionSorter cmp;
1917 section->set_frame (frame);
1918 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1920 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1923 section_prev = prev_ts;
1926 if (t->position_lock_style() == MusicTime) {
1927 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1928 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1930 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1931 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1939 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), section->pulse(), _frame_rate));
1940 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1943 if (section->position_lock_style() == MusicTime) {
1944 /* we're setting the frame */
1945 section->set_position_lock_style (AudioTime);
1946 recompute_tempos (imaginary);
1947 section->set_position_lock_style (MusicTime);
1949 recompute_tempos (imaginary);
1952 if (check_solved (imaginary, true)) {
1953 recompute_meters (imaginary);
1957 imaginary.sort (fcmp);
1958 if (section->position_lock_style() == MusicTime) {
1959 /* we're setting the frame */
1960 section->set_position_lock_style (AudioTime);
1961 recompute_tempos (imaginary);
1962 section->set_position_lock_style (MusicTime);
1964 recompute_tempos (imaginary);
1966 if (check_solved (imaginary, true)) {
1967 recompute_meters (imaginary);
1971 imaginary.sort (cmp);
1972 if (section->position_lock_style() == MusicTime) {
1973 /* we're setting the frame */
1974 section->set_position_lock_style (AudioTime);
1975 recompute_tempos (imaginary);
1976 section->set_position_lock_style (MusicTime);
1978 recompute_tempos (imaginary);
1980 if (check_solved (imaginary, true)) {
1981 recompute_meters (imaginary);
1984 //dump (imaginary, std::cerr);
1990 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
1992 MetricSectionSorter cmp;
1993 MetricSectionFrameSorter fcmp;
1994 TempoSection* prev_ts = 0;
1995 TempoSection* section_prev = 0;
1997 section->set_pulse (pulse);
1999 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2001 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2004 section_prev = prev_ts;
2007 if (t->position_lock_style() == MusicTime) {
2008 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2009 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2011 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2012 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2019 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), section->pulse(), _frame_rate));
2020 section->set_frame (section_prev->frame_at_pulse (section->pulse(), _frame_rate));
2023 if (section->position_lock_style() == AudioTime) {
2024 /* we're setting the pulse */
2025 section->set_position_lock_style (MusicTime);
2026 recompute_tempos (imaginary);
2027 section->set_position_lock_style (AudioTime);
2029 recompute_tempos (imaginary);
2031 if (check_solved (imaginary, false)) {
2032 recompute_meters (imaginary);
2036 imaginary.sort (cmp);
2037 if (section->position_lock_style() == AudioTime) {
2038 /* we're setting the pulse */
2039 section->set_position_lock_style (MusicTime);
2040 recompute_tempos (imaginary);
2041 section->set_position_lock_style (AudioTime);
2043 recompute_tempos (imaginary);
2046 if (check_solved (imaginary, false)) {
2047 recompute_meters (imaginary);
2051 imaginary.sort (fcmp);
2052 if (section->position_lock_style() == AudioTime) {
2053 /* we're setting the pulse */
2054 section->set_position_lock_style (MusicTime);
2055 recompute_tempos (imaginary);
2056 section->set_position_lock_style (AudioTime);
2058 recompute_tempos (imaginary);
2061 if (check_solved (imaginary, false)) {
2062 recompute_meters (imaginary);
2066 //dump (imaginary, std::cerr);
2072 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2074 MeterSection* prev_ms = 0;
2076 pair<double, BBT_Time> b_bbt = make_pair (pulse, BBT_Time (1, 1, 0));
2077 double accumulated_beats = 0.0;
2078 uint32_t accumulated_bars = 0;
2080 section->set_pulse (b_bbt);
2082 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2084 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2086 double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2087 accumulated_beats += beats_in_m;
2088 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2091 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2092 b_bbt = make_pair (pulse, BBT_Time (accumulated_bars + 1, 1, 0));
2093 section->set_pulse (b_bbt);
2094 m->set_beat (accumulated_beats);
2099 if (m->position_lock_style() == MusicTime) {
2100 m->set_frame (frame_at_pulse_locked (imaginary, m->pulse()));
2102 pair<double, BBT_Time> b_bbt = make_pair (pulse_at_frame_locked (imaginary, m->frame()), BBT_Time (accumulated_bars + 1, 1, 0));
2103 m->set_pulse (b_bbt);
2106 m->set_beat (accumulated_beats);
2111 if (section->position_lock_style() == AudioTime) {
2112 /* we're setting the pulse */
2113 section->set_position_lock_style (MusicTime);
2114 recompute_meters (imaginary);
2115 section->set_position_lock_style (AudioTime);
2117 recompute_meters (imaginary);
2122 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
2124 MeterSection* prev_ms = 0;
2125 double accumulated_beats = 0.0;
2126 uint32_t accumulated_bars = 0;
2128 section->set_frame (frame);
2130 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2132 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2134 const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2135 accumulated_beats += beats_in_m;
2136 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2140 here we define the pulse for this frame.
2141 we're going to set it 'incorrectly' to the next integer and use this 'error'
2142 as an offset to the map as far as users of the public methods are concerned.
2143 (meters should go on absolute pulses to keep us sane)
2145 double const pulse_at_f = pulse_at_frame_locked (imaginary, m->frame());
2146 pair<double, BBT_Time> b_bbt = make_pair (pulse_at_f, BBT_Time (accumulated_bars + 1, 1, 0));
2147 m->set_pulse (b_bbt);
2148 m->set_beat (accumulated_beats);
2153 if (m->position_lock_style() == MusicTime) {
2154 m->set_frame (frame_at_pulse_locked (imaginary, m->pulse()));
2156 double const pulse_at_f = ceil (pulse_at_frame_locked (imaginary, frame));
2157 pair<double, BBT_Time> b_bbt = make_pair (pulse_at_f, BBT_Time (accumulated_bars + 1, 1, 0));
2158 m->set_pulse (b_bbt);
2161 m->set_beat (accumulated_beats);
2166 if (section->position_lock_style() == MusicTime) {
2167 /* we're setting the frame */
2168 section->set_position_lock_style (AudioTime);
2169 recompute_meters (imaginary);
2170 section->set_position_lock_style (MusicTime);
2172 recompute_meters (imaginary);
2177 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2179 Glib::Threads::RWLock::ReaderLock lm (lock);
2181 double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2182 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2183 double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2184 framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2185 framecnt_t const ret = time_at_bbt;
2191 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2193 return round_to_type (fr, dir, Bar);
2197 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2199 return round_to_type (fr, dir, Beat);
2203 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2205 Glib::Threads::RWLock::ReaderLock lm (lock);
2206 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2207 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2208 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2210 ticks -= beats * BBT_Time::ticks_per_beat;
2213 /* round to next (or same iff dir == RoundUpMaybe) */
2215 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2217 if (mod == 0 && dir == RoundUpMaybe) {
2218 /* right on the subdivision, which is fine, so do nothing */
2220 } else if (mod == 0) {
2221 /* right on the subdivision, so the difference is just the subdivision ticks */
2222 ticks += ticks_one_subdivisions_worth;
2225 /* not on subdivision, compute distance to next subdivision */
2227 ticks += ticks_one_subdivisions_worth - mod;
2230 if (ticks >= BBT_Time::ticks_per_beat) {
2231 ticks -= BBT_Time::ticks_per_beat;
2233 } else if (dir < 0) {
2235 /* round to previous (or same iff dir == RoundDownMaybe) */
2237 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2239 if (difference == 0 && dir == RoundDownAlways) {
2240 /* right on the subdivision, but force-rounding down,
2241 so the difference is just the subdivision ticks */
2242 difference = ticks_one_subdivisions_worth;
2245 if (ticks < difference) {
2246 ticks = BBT_Time::ticks_per_beat - ticks;
2248 ticks -= difference;
2252 /* round to nearest */
2255 /* compute the distance to the previous and next subdivision */
2257 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2259 /* closer to the next subdivision, so shift forward */
2261 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2263 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2265 if (ticks > BBT_Time::ticks_per_beat) {
2267 ticks -= BBT_Time::ticks_per_beat;
2268 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2271 } else if (rem > 0) {
2273 /* closer to previous subdivision, so shift backward */
2277 /* can't go backwards past zero, so ... */
2280 /* step back to previous beat */
2282 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2283 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2285 ticks = lrint (ticks - rem);
2286 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2289 /* on the subdivision, do nothing */
2293 framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2299 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2301 Glib::Threads::RWLock::ReaderLock lm (lock);
2303 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2304 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2309 /* find bar previous to 'frame' */
2312 return frame_time (bbt);
2314 } else if (dir > 0) {
2315 /* find bar following 'frame' */
2319 return frame_time (bbt);
2321 /* true rounding: find nearest bar */
2322 framepos_t raw_ft = frame_time (bbt);
2325 framepos_t prev_ft = frame_time (bbt);
2327 framepos_t next_ft = frame_time (bbt);
2329 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2340 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2341 } else if (dir > 0) {
2342 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2344 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2353 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2354 framepos_t lower, framepos_t upper)
2356 Glib::Threads::RWLock::ReaderLock lm (lock);
2357 int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2358 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2359 /* although the map handles negative beats, bbt doesn't. */
2363 while (cnt <= upper_beat) {
2364 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2365 TempoSection const tempo = tempo_section_at_locked (pos);
2366 MeterSection const meter = meter_section_at_locked (pos);
2367 BBT_Time const bbt = beats_to_bbt (cnt);
2369 points.push_back (BBTPoint (meter, Tempo (tempo.beats_per_minute(), tempo.note_type()), pos, bbt.bars, bbt.beats));
2375 TempoMap::tempo_section_at (framepos_t frame) const
2377 Glib::Threads::RWLock::ReaderLock lm (lock);
2378 return tempo_section_at_locked (frame);
2382 TempoMap::tempo_section_at_locked (framepos_t frame) const
2384 Metrics::const_iterator i;
2385 TempoSection* prev = 0;
2387 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2390 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2392 if (t->frame() > frame) {
2402 abort(); /*NOTREACHED*/
2409 /* don't use this to calculate length (the tempo is only correct for this frame).
2410 do that stuff based on the beat_at_frame and frame_at_beat api
2413 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2415 Glib::Threads::RWLock::ReaderLock lm (lock);
2417 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2418 const TempoSection* ts_after = 0;
2419 Metrics::const_iterator i;
2421 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2424 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2426 if ((*i)->frame() > frame) {
2434 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2436 /* must be treated as constant tempo */
2437 return ts_at->frames_per_beat (_frame_rate);
2441 TempoMap::tempo_at (const framepos_t& frame) const
2443 Glib::Threads::RWLock::ReaderLock lm (lock);
2444 frameoffset_t const frame_off = frame_offset_at (_metrics, frame);
2445 TempoSection* prev_ts = 0;
2447 Metrics::const_iterator i;
2449 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2451 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2452 if ((prev_ts) && t->frame() > frame) {
2453 /* t is the section past frame */
2454 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2455 Tempo const ret_tempo (ret, prev_ts->note_type());
2462 double const ret = prev_ts->beats_per_minute();
2463 Tempo const ret_tempo (ret, prev_ts->note_type ());
2469 TempoMap::meter_section_at (framepos_t frame) const
2471 Glib::Threads::RWLock::ReaderLock lm (lock);
2472 return meter_section_at_locked (frame);
2476 TempoMap::meter_section_at_locked (framepos_t frame) const
2478 framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2479 Metrics::const_iterator i;
2480 MeterSection* prev = 0;
2482 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2485 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2487 if ((*i)->frame() > frame_off) {
2497 abort(); /*NOTREACHED*/
2504 TempoMap::meter_at (framepos_t frame) const
2506 framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2507 TempoMetric m (metric_at (frame_off));
2513 TempoMap::get_state ()
2515 Metrics::const_iterator i;
2516 XMLNode *root = new XMLNode ("TempoMap");
2519 Glib::Threads::RWLock::ReaderLock lm (lock);
2520 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2521 root->add_child_nocopy ((*i)->get_state());
2529 TempoMap::set_state (const XMLNode& node, int /*version*/)
2532 Glib::Threads::RWLock::WriterLock lm (lock);
2535 XMLNodeConstIterator niter;
2536 Metrics old_metrics (_metrics);
2537 MeterSection* last_meter = 0;
2540 nlist = node.children();
2542 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2543 XMLNode* child = *niter;
2545 if (child->name() == TempoSection::xml_state_node_name) {
2548 TempoSection* ts = new TempoSection (*child);
2549 _metrics.push_back (ts);
2551 if (ts->bar_offset() < 0.0) {
2553 //ts->update_bar_offset_from_bbt (*last_meter);
2558 catch (failed_constructor& err){
2559 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2560 _metrics = old_metrics;
2564 } else if (child->name() == MeterSection::xml_state_node_name) {
2567 MeterSection* ms = new MeterSection (*child);
2568 _metrics.push_back (ms);
2572 catch (failed_constructor& err) {
2573 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2574 _metrics = old_metrics;
2580 if (niter == nlist.end()) {
2581 MetricSectionSorter cmp;
2582 _metrics.sort (cmp);
2584 /* check for legacy sessions where bbt was the base musical unit for tempo */
2585 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2586 MeterSection* prev_ms;
2587 TempoSection* prev_ts;
2588 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2589 if (prev_ms->pulse() < 0.0) {
2590 /*XX we cannot possibly make this work??. */
2591 pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
2592 prev_ms->set_pulse (start);
2594 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2595 if (prev_ts->pulse() < 0.0) {
2596 double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2597 prev_ts->set_pulse (start);
2602 /* check for multiple tempo/meters at the same location, which
2603 ardour2 somehow allowed.
2606 Metrics::iterator prev = _metrics.end();
2607 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2608 if (prev != _metrics.end()) {
2610 MeterSection* prev_ms;
2612 TempoSection* prev_ts;
2613 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2614 if (prev_ms->pulse() == ms->pulse()) {
2615 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2616 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2619 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2620 if (prev_ts->pulse() == ts->pulse()) {
2621 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2622 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2630 recompute_map (_metrics);
2633 PropertyChanged (PropertyChange ());
2639 TempoMap::dump (Metrics& metrics, std::ostream& o) const
2641 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2642 const MeterSection* m;
2643 const TempoSection* t;
2644 const TempoSection* prev_ts = 0;
2646 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2648 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2649 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? "
2650 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2651 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2653 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2654 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;
2657 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2658 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2659 << " pulse: " << m->pulse() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2662 o << "------" << std::endl;
2666 TempoMap::n_tempos() const
2668 Glib::Threads::RWLock::ReaderLock lm (lock);
2671 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2672 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2681 TempoMap::n_meters() const
2683 Glib::Threads::RWLock::ReaderLock lm (lock);
2686 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2687 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2696 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2699 Glib::Threads::RWLock::WriterLock lm (lock);
2700 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2701 if ((*i)->frame() >= where && (*i)->movable ()) {
2702 (*i)->set_frame ((*i)->frame() + amount);
2706 /* now reset the BBT time of all metrics, based on their new
2707 * audio time. This is the only place where we do this reverse
2711 Metrics::iterator i;
2712 const MeterSection* meter;
2713 const TempoSection* tempo;
2717 meter = &first_meter ();
2718 tempo = &first_tempo ();
2723 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2726 MetricSection* prev = 0;
2728 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2731 //TempoMetric metric (*meter, *tempo);
2732 MeterSection* ms = const_cast<MeterSection*>(meter);
2733 TempoSection* ts = const_cast<TempoSection*>(tempo);
2736 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2737 ts->set_pulse (t->pulse());
2739 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2740 ts->set_pulse (m->pulse());
2742 ts->set_frame (prev->frame());
2746 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2747 pair<double, BBT_Time> start = make_pair (m->pulse(), m->bbt());
2748 ms->set_pulse (start);
2750 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2751 pair<double, BBT_Time> start = make_pair (t->pulse(), beats_to_bbt_locked (_metrics, t->pulse()));
2752 ms->set_pulse (start);
2754 ms->set_frame (prev->frame());
2758 // metric will be at frames=0 bbt=1|1|0 by default
2759 // which is correct for our purpose
2762 // cerr << bbt << endl;
2764 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2765 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
2767 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
2768 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2769 bbt_time (m->frame(), bbt);
2771 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2777 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2778 /* round up to next beat */
2784 if (bbt.beats != 1) {
2785 /* round up to next bar */
2790 pair<double, BBT_Time> start = make_pair (pulse_at_frame_locked (_metrics, m->frame()), bbt);
2791 m->set_pulse (start);
2793 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
2795 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2796 abort(); /*NOTREACHED*/
2802 recompute_map (_metrics);
2806 PropertyChanged (PropertyChange ());
2809 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2813 std::list<MetricSection*> metric_kill_list;
2815 TempoSection* last_tempo = NULL;
2816 MeterSection* last_meter = NULL;
2817 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2818 bool meter_after = false; // is there a meter marker likewise?
2820 Glib::Threads::RWLock::WriterLock lm (lock);
2821 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2822 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2823 metric_kill_list.push_back(*i);
2824 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2827 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2831 else if ((*i)->frame() >= where) {
2832 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2833 (*i)->set_frame ((*i)->frame() - amount);
2834 if ((*i)->frame() == where) {
2835 // marker was immediately after end of range
2836 tempo_after = dynamic_cast<TempoSection*> (*i);
2837 meter_after = dynamic_cast<MeterSection*> (*i);
2843 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2844 if (last_tempo && !tempo_after) {
2845 metric_kill_list.remove(last_tempo);
2846 last_tempo->set_frame(where);
2849 if (last_meter && !meter_after) {
2850 metric_kill_list.remove(last_meter);
2851 last_meter->set_frame(where);
2855 //remove all the remaining metrics
2856 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2857 _metrics.remove(*i);
2862 recompute_map (_metrics);
2865 PropertyChanged (PropertyChange ());
2869 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2870 * pos can be -ve, if required.
2873 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2875 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2878 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2880 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2882 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2885 /** Add the BBT interval op to pos and return the result */
2887 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2889 Glib::Threads::RWLock::ReaderLock lm (lock);
2891 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
2892 pos_bbt.ticks += op.ticks;
2893 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
2895 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
2897 pos_bbt.beats += op.beats;
2898 /* the meter in effect will start on the bar */
2899 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
2900 while (pos_bbt.beats >= divisions_per_bar + 1) {
2902 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
2903 pos_bbt.beats -= divisions_per_bar;
2905 pos_bbt.bars += op.bars;
2907 return frame_time_locked (_metrics, pos_bbt);
2910 /** Count the number of beats that are equivalent to distance when going forward,
2914 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2916 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2920 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2926 operator<< (std::ostream& o, const Meter& m) {
2927 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2931 operator<< (std::ostream& o, const Tempo& t) {
2932 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2936 operator<< (std::ostream& o, const MetricSection& section) {
2938 o << "MetricSection @ " << section.frame() << ' ';
2940 const TempoSection* ts;
2941 const MeterSection* ms;
2943 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2944 o << *((const Tempo*) ts);
2945 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2946 //o << *((const Meter*) ms);