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 (framepos_t f, 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 (double bpm, double b, framecnt_t frame_rate) const
225 if (_type == Constant) {
226 return ((b - pulse()) * frames_per_beat (frame_rate)) + frame();
229 return minute_to_frame (time_at_pulse_tempo (bpm), frame_rate) + frame();
231 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
234 TempoSection::tempo_at_pulse (double b) const
237 if (_type == Constant) {
238 return pulses_per_minute();
240 double const bpm = pulse_tempo_at_pulse (b - 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 (double bpm, framepos_t f, framecnt_t frame_rate) const
251 if (_type == Constant) {
252 double const beats = ((f - frame()) / frames_per_beat (frame_rate)) + pulse();
256 return pulse_at_pulse_tempo (bpm) + 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 (framepos_t f, framecnt_t frame_rate) const
266 if (_type == Constant) {
267 return ((f - frame()) / frames_per_beat (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 (double b, framecnt_t frame_rate) const
281 if (_type == Constant) {
282 return (framepos_t) floor ((b - pulse()) * frames_per_beat (frame_rate)) + frame();
285 return minute_to_frame (time_at_pulse (b - 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 (double end_bpm, double end_pulse, 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 (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
371 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
375 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
377 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
381 TempoSection::frame_to_minute (framecnt_t frame, 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 (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 (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 (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 (double pulse) const
425 return (pulse * _c_func) + pulses_per_minute();
428 /* pulse at time in minutes */
430 TempoSection::pulse_at_time (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 (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));
782 /* non-matching types, so we don't care */
786 /* Add the given MetricSection, if we didn't just reset an existing
791 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
792 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
795 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
796 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
799 bool const ipm = insert_meter->position_lock_style() == MusicTime;
800 if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
805 } else if (insert_tempo) {
806 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
807 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
810 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
811 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
818 _metrics.insert (i, section);
819 //dump (_metrics, std::cerr);
824 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
827 Glib::Threads::RWLock::WriterLock lm (lock);
828 TempoSection& first (first_tempo());
829 if (ts.pulse() != first.pulse()) {
830 remove_tempo_locked (ts);
831 add_tempo_locked (tempo, where, true, type);
833 first.set_type (type);
835 /* cannot move the first tempo section */
836 *static_cast<Tempo*>(&first) = tempo;
837 recompute_map (_metrics);
842 PropertyChanged (PropertyChange ());
846 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
849 Glib::Threads::RWLock::WriterLock lm (lock);
850 TempoSection& first (first_tempo());
851 if (ts.frame() != first.frame()) {
852 remove_tempo_locked (ts);
853 add_tempo_locked (tempo, frame, true, type);
855 first.set_type (type);
857 /* cannot move the first tempo section */
858 *static_cast<Tempo*>(&first) = tempo;
859 recompute_map (_metrics);
863 PropertyChanged (PropertyChange ());
867 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
870 Glib::Threads::RWLock::WriterLock lm (lock);
871 add_tempo_locked (tempo, where, true, type);
874 PropertyChanged (PropertyChange ());
878 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
881 Glib::Threads::RWLock::WriterLock lm (lock);
882 add_tempo_locked (tempo, frame, true, type);
886 PropertyChanged (PropertyChange ());
890 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
892 double pulse = pulse_at_beat (_metrics, where);
893 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
898 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
903 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
905 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
910 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
915 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
918 Glib::Threads::RWLock::WriterLock lm (lock);
919 MeterSection& first (first_meter());
920 if (ms.pulse() != first.pulse()) {
921 remove_meter_locked (ms);
922 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
924 /* cannot move the first meter section */
925 *static_cast<Meter*>(&first) = meter;
926 recompute_map (_metrics);
930 PropertyChanged (PropertyChange ());
934 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
937 Glib::Threads::RWLock::WriterLock lm (lock);
938 MeterSection& first (first_meter());
939 if (ms.pulse() != first.pulse()) {
940 remove_meter_locked (ms);
941 add_meter_locked (meter, frame, true);
943 /* cannot move the first meter section */
944 *static_cast<Meter*>(&first) = meter;
945 recompute_map (_metrics);
949 PropertyChanged (PropertyChange ());
954 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
957 Glib::Threads::RWLock::WriterLock lm (lock);
958 add_meter_locked (meter, beat, where, true);
963 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
964 dump (_metrics, std::cerr);
968 PropertyChanged (PropertyChange ());
972 TempoMap::add_meter (const Meter& meter, framepos_t frame)
975 Glib::Threads::RWLock::WriterLock lm (lock);
976 add_meter_locked (meter, frame, true);
981 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
982 dump (_metrics, std::cerr);
986 PropertyChanged (PropertyChange ());
990 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
992 /* a new meter always starts a new bar on the first beat. so
993 round the start time appropriately. remember that
994 `where' is based on the existing tempo map, not
995 the result after we insert the new meter.
999 if (where.beats != 1) {
1004 /* new meters *always* start on a beat. */
1006 double pulse = pulse_at_beat (_metrics, beat);
1008 MeterSection* new_meter = new MeterSection (pulse, where, meter.divisions_per_bar(), meter.note_divisor());
1009 do_insert (new_meter);
1012 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1018 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1021 MeterSection* new_meter = new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor());
1022 double paf = pulse_at_frame_locked (_metrics, frame);
1023 pair<double, BBT_Time> beat = make_pair (paf, beats_to_bbt_locked (_metrics, beat_at_pulse (_metrics, paf)));
1024 new_meter->set_pulse (beat);
1025 do_insert (new_meter);
1028 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1034 * 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,
1035 * taking any possible reordering as a consequence of this into account.
1036 * @param section - the section to be altered
1037 * @param bpm - the new Tempo
1038 * @param bbt - the bbt where the altered tempo will fall
1039 * @return returns - the position in frames where the new tempo section will lie.
1042 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1044 Glib::Threads::RWLock::ReaderLock lm (lock);
1047 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1048 double const beat = bbt_to_beats_locked (future_map, bbt);
1049 if (solve_map (future_map, new_section, bpm, pulse_at_beat (future_map, beat))) {
1050 ret = new_section->frame();
1052 ret = frame_at_beat_locked (_metrics, beat);
1055 Metrics::const_iterator d = future_map.begin();
1056 while (d != future_map.end()) {
1064 TempoMap::predict_tempo_beat (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1066 Glib::Threads::RWLock::ReaderLock lm (lock);
1069 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1071 if (solve_map (future_map, new_section, bpm, frame)) {
1072 ret = beat_at_pulse (future_map, new_section->pulse());
1074 ret = beat_at_frame_locked (_metrics, frame);
1077 Metrics::const_iterator d = future_map.begin();
1078 while (d != future_map.end()) {
1086 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1090 Glib::Threads::RWLock::WriterLock lm (lock);
1091 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1092 if (solve_map (future_map, new_section, bpm, frame)) {
1093 solve_map (_metrics, ts, bpm, frame);
1097 Metrics::const_iterator d = future_map.begin();
1098 while (d != future_map.end()) {
1103 MetricPositionChanged (); // Emit Signal
1107 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
1111 Glib::Threads::RWLock::WriterLock lm (lock);
1112 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1113 if (solve_map (future_map, new_section, bpm, pulse_at_beat (future_map, beat))) {
1114 solve_map (_metrics, ts, bpm, beat);
1118 Metrics::const_iterator d = future_map.begin();
1119 while (d != future_map.end()) {
1124 MetricPositionChanged (); // Emit Signal
1128 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
1131 Glib::Threads::RWLock::WriterLock lm (lock);
1132 solve_map (_metrics, ms, mt, frame);
1135 MetricPositionChanged (); // Emit Signal
1139 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
1142 Glib::Threads::RWLock::WriterLock lm (lock);
1143 solve_map (_metrics, ms, mt, pulse_at_beat (_metrics, beat));
1146 MetricPositionChanged (); // Emit Signal
1150 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1153 TempoSection* ret = 0;
1156 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1157 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1159 if (t->position_lock_style() == MusicTime) {
1160 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1162 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1164 copy.push_back (ret);
1167 if (t->position_lock_style() == MusicTime) {
1168 copy.push_back (new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type()));
1170 copy.push_back (new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type()));
1173 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1174 if (m->position_lock_style() == MusicTime) {
1175 copy.push_back (new MeterSection (m->pulse(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1177 copy.push_back (new MeterSection (m->frame(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1182 recompute_map (copy);
1187 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
1190 TempoSection* new_section = 0;
1193 Glib::Threads::RWLock::ReaderLock lm (lock);
1194 new_section = copy_metrics_and_point (copy, ts);
1197 double const beat = bbt_to_beats_locked (copy, bbt);
1198 bool ret = solve_map (copy, new_section, bpm, beat);
1200 Metrics::const_iterator d = copy.begin();
1201 while (d != copy.end()) {
1210 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1212 Tempo newtempo (beats_per_minute, note_type);
1215 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1216 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1218 Glib::Threads::RWLock::WriterLock lm (lock);
1219 *((Tempo*) t) = newtempo;
1220 recompute_map (_metrics);
1222 PropertyChanged (PropertyChange ());
1229 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1231 Tempo newtempo (beats_per_minute, note_type);
1234 TempoSection* first;
1235 Metrics::iterator i;
1237 /* find the TempoSection immediately preceding "where"
1240 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1242 if ((*i)->frame() > where) {
1248 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1258 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1268 Glib::Threads::RWLock::WriterLock lm (lock);
1269 /* cannot move the first tempo section */
1270 *((Tempo*)prev) = newtempo;
1271 recompute_map (_metrics);
1274 PropertyChanged (PropertyChange ());
1278 TempoMap::first_meter () const
1280 const MeterSection *m = 0;
1282 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1283 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1288 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1289 abort(); /*NOTREACHED*/
1294 TempoMap::first_meter ()
1296 MeterSection *m = 0;
1298 /* CALLER MUST HOLD LOCK */
1300 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1301 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1306 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1307 abort(); /*NOTREACHED*/
1312 TempoMap::first_tempo () const
1314 const TempoSection *t = 0;
1316 /* CALLER MUST HOLD LOCK */
1318 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1319 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1324 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1325 abort(); /*NOTREACHED*/
1330 TempoMap::first_tempo ()
1332 TempoSection *t = 0;
1334 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1335 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1340 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1341 abort(); /*NOTREACHED*/
1345 TempoMap::recompute_tempos (Metrics& metrics)
1347 TempoSection* prev_ts = 0;
1349 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1352 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1354 if (t->position_lock_style() == AudioTime) {
1355 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1356 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1359 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1360 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1369 /* tempos must be positioned correctly */
1371 TempoMap::recompute_meters (Metrics& metrics)
1373 MeterSection* meter = 0;
1374 MeterSection* prev_m = 0;
1375 double accumulated_beats = 0.0;
1377 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1378 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1380 accumulated_beats += (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
1382 if (meter->position_lock_style() == AudioTime) {
1383 pair<double, BBT_Time> pr;
1384 pr.first = ceil (pulse_at_frame_locked (metrics, meter->frame()));
1385 BBT_Time const where = beats_to_bbt_locked (metrics, accumulated_beats);
1387 meter->set_pulse (pr);
1389 meter->set_frame (frame_at_pulse_locked (metrics, meter->pulse()));
1391 meter->set_beat (accumulated_beats);
1398 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1400 /* CALLER MUST HOLD WRITE LOCK */
1404 /* we will actually stop once we hit
1411 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1414 /* silly call from Session::process() during startup
1419 recompute_tempos (metrics);
1420 recompute_meters (metrics);
1424 TempoMap::pulse_at_beat (const Metrics& metrics, const double& beat) const
1426 MeterSection* prev_ms = 0;
1427 double accumulated_beats = 0.0;
1428 double beats_to_m = 0.0;
1430 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1432 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1434 beats_to_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
1435 if (accumulated_beats + beats_to_m > beat) {
1438 accumulated_beats += (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
1445 return prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1449 TempoMap::beat_at_pulse (const Metrics& metrics, const double& pulse) const
1451 MeterSection* prev_ms = 0;
1452 double accumulated_beats = 0.0;
1454 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1456 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1457 if (m->pulse() > pulse) {
1461 accumulated_beats += (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
1467 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1469 return beats_in_section + accumulated_beats;
1473 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1475 Glib::Threads::RWLock::ReaderLock lm (lock);
1476 TempoMetric m (first_meter(), first_tempo());
1478 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1479 at something, because we insert the default tempo and meter during
1480 TempoMap construction.
1482 now see if we can find better candidates.
1485 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1487 if ((*i)->frame() > frame) {
1501 /* XX meters only */
1503 TempoMap::metric_at (BBT_Time bbt) const
1505 Glib::Threads::RWLock::ReaderLock lm (lock);
1506 TempoMetric m (first_meter(), first_tempo());
1508 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1509 at something, because we insert the default tempo and meter during
1510 TempoMap construction.
1512 now see if we can find better candidates.
1515 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1517 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1518 BBT_Time section_start (mw->bbt());
1520 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1532 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1539 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1542 Glib::Threads::RWLock::ReaderLock lm (lock);
1543 frameoffset_t const frame_off = frame_offset_at (_metrics, frame);
1544 double const beat = beat_at_pulse (_metrics, pulse_at_frame_locked (_metrics, frame + frame_off));
1546 bbt = beats_to_bbt_locked (_metrics, beat);
1550 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1552 Glib::Threads::RWLock::ReaderLock lm (lock);
1554 return bbt_to_beats_locked (_metrics, bbt);
1558 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1560 /* CALLER HOLDS READ LOCK */
1562 double accumulated_beats = 0.0;
1563 double accumulated_bars = 0.0;
1564 MeterSection* prev_ms = 0;
1565 /* because audio-locked meters have 'fake' integral beats,
1566 there is no pulse offset here.
1568 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1570 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1571 double bars_to_m = 0.0;
1573 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1575 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1579 accumulated_beats += m->beat() - prev_ms->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 (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 (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_offset_at (const Metrics& metrics, const double& beat) const
1720 MeterSection* prev_m = 0;
1721 double beat_off = 0.0;
1723 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1724 MeterSection* m = 0;
1725 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1726 if (m->pulse() > beat) {
1730 if (prev_m && m->position_lock_style() == AudioTime) {
1731 beat_off += ((m->pulse() - prev_m->pulse()) / prev_m->note_divisor()) - floor ((m->pulse() - prev_m->pulse()) / prev_m->note_divisor());
1742 TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
1744 frameoffset_t frame_off = 0;
1746 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1747 MeterSection* m = 0;
1748 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1749 if (m->frame() > frame) {
1752 if (m->position_lock_style() == AudioTime) {
1753 frame_off += frame_at_pulse_locked (metrics, m->pulse()) - m->frame();
1762 TempoMap::beat_at_frame (framecnt_t frame) const
1764 Glib::Threads::RWLock::ReaderLock lm (lock);
1765 return beat_at_frame_locked (_metrics, frame);
1769 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1771 framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
1772 double const pulse = pulse_at_frame_locked (metrics, offset_frame);
1774 return beat_at_pulse (metrics, pulse);
1778 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1780 /* HOLD (at least) THE READER LOCK */
1781 TempoSection* prev_ts = 0;
1782 double accumulated_pulses = 0.0;
1784 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1786 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1788 if (prev_ts && t->frame() > frame) {
1789 /*the previous ts is the one containing the frame */
1790 double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1794 accumulated_pulses = t->pulse();
1799 /* treated as constant for this ts */
1800 double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1802 return pulses_in_section + accumulated_pulses;
1806 TempoMap::frame_at_beat (double beat) const
1808 Glib::Threads::RWLock::ReaderLock lm (lock);
1809 return frame_at_beat_locked (_metrics, beat);
1813 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1815 framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat (metrics, beat));
1816 frameoffset_t const frame_off = frame_offset_at (metrics, frame);
1817 return frame - frame_off;
1821 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1823 /* HOLD THE READER LOCK */
1825 const TempoSection* prev_ts = 0;
1826 double accumulated_beats = 0.0;
1828 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1831 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1832 if (prev_ts && t->pulse() > pulse) {
1833 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1836 accumulated_beats = t->pulse();
1840 /* must be treated as constant, irrespective of _type */
1841 double const pulses_in_section = pulse - accumulated_beats;
1842 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1844 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1850 TempoMap::frame_time (const BBT_Time& bbt)
1853 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1857 if (bbt.beats < 1) {
1858 throw std::logic_error ("beats are counted from one");
1860 Glib::Threads::RWLock::ReaderLock lm (lock);
1861 double beat = bbt_to_beats_locked (_metrics, bbt);
1862 framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1867 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1869 /* HOLD THE READER LOCK */
1871 framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat (metrics, bbt_to_beats_locked (metrics, bbt)));
1877 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1879 TempoSection* prev_ts = 0;
1881 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1883 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1885 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
1888 if (by_frame && t->frame() != prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
1892 if (!by_frame && fabs (t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate)) > 0.00001) {
1893 std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
1894 " |error :" << t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << std::endl <<
1895 "|frame at beat :" << prev_ts->frame_at_pulse (t->pulse(), _frame_rate) << std::endl <<
1896 " |frame at tempo : " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
1909 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1911 TempoSection* prev_ts = 0;
1912 TempoSection* section_prev = 0;
1913 MetricSectionFrameSorter fcmp;
1914 MetricSectionSorter cmp;
1916 section->set_frame (frame);
1917 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1919 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1922 section_prev = prev_ts;
1925 if (t->position_lock_style() == MusicTime) {
1926 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1927 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1929 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1930 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1938 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), section->pulse(), _frame_rate));
1939 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1942 if (section->position_lock_style() == MusicTime) {
1943 /* we're setting the frame */
1944 section->set_position_lock_style (AudioTime);
1945 recompute_tempos (imaginary);
1946 section->set_position_lock_style (MusicTime);
1948 recompute_tempos (imaginary);
1951 if (check_solved (imaginary, true)) {
1952 recompute_meters (imaginary);
1956 imaginary.sort (fcmp);
1957 if (section->position_lock_style() == MusicTime) {
1958 /* we're setting the frame */
1959 section->set_position_lock_style (AudioTime);
1960 recompute_tempos (imaginary);
1961 section->set_position_lock_style (MusicTime);
1963 recompute_tempos (imaginary);
1965 if (check_solved (imaginary, true)) {
1966 recompute_meters (imaginary);
1970 imaginary.sort (cmp);
1971 if (section->position_lock_style() == MusicTime) {
1972 /* we're setting the frame */
1973 section->set_position_lock_style (AudioTime);
1974 recompute_tempos (imaginary);
1975 section->set_position_lock_style (MusicTime);
1977 recompute_tempos (imaginary);
1979 if (check_solved (imaginary, true)) {
1980 recompute_meters (imaginary);
1984 //dump (imaginary, std::cerr);
1989 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& beat)
1991 MetricSectionSorter cmp;
1992 MetricSectionFrameSorter fcmp;
1993 TempoSection* prev_ts = 0;
1994 TempoSection* section_prev = 0;
1996 section->set_pulse (beat);
1998 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2000 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2003 section_prev = prev_ts;
2006 if (t->position_lock_style() == MusicTime) {
2007 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2008 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2010 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2011 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2018 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), section->pulse(), _frame_rate));
2019 section->set_frame (section_prev->frame_at_pulse (section->pulse(), _frame_rate));
2022 if (section->position_lock_style() == AudioTime) {
2023 /* we're setting the beat */
2024 section->set_position_lock_style (MusicTime);
2025 recompute_tempos (imaginary);
2026 section->set_position_lock_style (AudioTime);
2028 recompute_tempos (imaginary);
2030 if (check_solved (imaginary, false)) {
2031 recompute_meters (imaginary);
2035 imaginary.sort (cmp);
2036 if (section->position_lock_style() == AudioTime) {
2037 /* we're setting the beat */
2038 section->set_position_lock_style (MusicTime);
2039 recompute_tempos (imaginary);
2040 section->set_position_lock_style (AudioTime);
2042 recompute_tempos (imaginary);
2045 if (check_solved (imaginary, false)) {
2046 recompute_meters (imaginary);
2050 imaginary.sort (fcmp);
2051 if (section->position_lock_style() == AudioTime) {
2052 /* we're setting the beat */
2053 section->set_position_lock_style (MusicTime);
2054 recompute_tempos (imaginary);
2055 section->set_position_lock_style (AudioTime);
2057 recompute_tempos (imaginary);
2060 if (check_solved (imaginary, false)) {
2061 recompute_meters (imaginary);
2065 //dump (imaginary, std::cerr);
2071 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& beat)
2073 MeterSection* prev_ms = 0;
2075 pair<double, BBT_Time> b_bbt = make_pair (beat, beats_to_bbt_locked (imaginary, beat));
2076 section->set_pulse (b_bbt);
2077 MetricSectionSorter cmp;
2078 imaginary.sort (cmp);
2080 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2082 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2085 section->set_frame (frame_at_pulse_locked (imaginary, pulse_at_beat (imaginary, beat)));
2089 if (m->position_lock_style() == MusicTime) {
2090 m->set_frame (frame_at_pulse_locked (imaginary, m->pulse()));
2092 pair<double, BBT_Time> b_bbt = make_pair (pulse_at_frame_locked (imaginary, m->frame()), BBT_Time (1, 1, 0));
2093 b_bbt.second = beats_to_bbt_locked (imaginary, beat_at_pulse (imaginary, b_bbt.first));
2094 m->set_pulse (b_bbt);
2101 if (section->position_lock_style() == AudioTime) {
2102 /* we're setting the beat */
2103 section->set_position_lock_style (MusicTime);
2104 recompute_meters (imaginary);
2105 section->set_position_lock_style (AudioTime);
2107 recompute_meters (imaginary);
2112 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
2114 MeterSection* prev_ms = 0;
2115 section->set_frame (frame);
2117 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2119 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2123 here we define the beat for this frame.
2124 we're going to set it 'incorrectly' to the next integer and use this 'error'
2125 as an offset to the map as far as users of the public methods are concerned.
2126 (meters should go on absolute beats to keep us sane)
2128 double const pulse_at_f = ceil (pulse_at_frame_locked (imaginary, m->frame()));
2129 pair<double, BBT_Time> b_bbt = make_pair (pulse_at_f, beats_to_bbt_locked (imaginary, beat_at_pulse (imaginary, pulse_at_f)));
2130 m->set_pulse (b_bbt);
2134 if (m->position_lock_style() == MusicTime) {
2135 m->set_frame (frame_at_pulse_locked (imaginary, m->pulse()));
2137 double const pulse_at_f = ceil (pulse_at_frame_locked (imaginary, frame));
2138 pair<double, BBT_Time> b_bbt = make_pair (pulse_at_f, beats_to_bbt_locked (imaginary, beat_at_pulse (imaginary, pulse_at_f)));
2139 m->set_pulse (b_bbt);
2146 if (section->position_lock_style() == MusicTime) {
2147 /* we're setting the frame */
2148 section->set_position_lock_style (AudioTime);
2149 recompute_meters (imaginary);
2150 section->set_position_lock_style (MusicTime);
2152 recompute_meters (imaginary);
2157 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2159 Glib::Threads::RWLock::ReaderLock lm (lock);
2161 double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2162 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2163 double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2164 framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2165 framecnt_t const ret = time_at_bbt;
2171 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2173 return round_to_type (fr, dir, Bar);
2177 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2179 return round_to_type (fr, dir, Beat);
2183 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2185 Glib::Threads::RWLock::ReaderLock lm (lock);
2186 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2187 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2188 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2190 ticks -= beats * BBT_Time::ticks_per_beat;
2193 /* round to next (or same iff dir == RoundUpMaybe) */
2195 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2197 if (mod == 0 && dir == RoundUpMaybe) {
2198 /* right on the subdivision, which is fine, so do nothing */
2200 } else if (mod == 0) {
2201 /* right on the subdivision, so the difference is just the subdivision ticks */
2202 ticks += ticks_one_subdivisions_worth;
2205 /* not on subdivision, compute distance to next subdivision */
2207 ticks += ticks_one_subdivisions_worth - mod;
2210 if (ticks >= BBT_Time::ticks_per_beat) {
2211 ticks -= BBT_Time::ticks_per_beat;
2213 } else if (dir < 0) {
2215 /* round to previous (or same iff dir == RoundDownMaybe) */
2217 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2219 if (difference == 0 && dir == RoundDownAlways) {
2220 /* right on the subdivision, but force-rounding down,
2221 so the difference is just the subdivision ticks */
2222 difference = ticks_one_subdivisions_worth;
2225 if (ticks < difference) {
2226 ticks = BBT_Time::ticks_per_beat - ticks;
2228 ticks -= difference;
2232 /* round to nearest */
2235 /* compute the distance to the previous and next subdivision */
2237 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2239 /* closer to the next subdivision, so shift forward */
2241 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2243 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2245 if (ticks > BBT_Time::ticks_per_beat) {
2247 ticks -= BBT_Time::ticks_per_beat;
2248 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2251 } else if (rem > 0) {
2253 /* closer to previous subdivision, so shift backward */
2257 /* can't go backwards past zero, so ... */
2260 /* step back to previous beat */
2262 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2263 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2265 ticks = lrint (ticks - rem);
2266 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2269 /* on the subdivision, do nothing */
2273 framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2279 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2281 Glib::Threads::RWLock::ReaderLock lm (lock);
2283 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2284 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2289 /* find bar previous to 'frame' */
2292 return frame_time (bbt);
2294 } else if (dir > 0) {
2295 /* find bar following 'frame' */
2299 return frame_time (bbt);
2301 /* true rounding: find nearest bar */
2302 framepos_t raw_ft = frame_time (bbt);
2305 framepos_t prev_ft = frame_time (bbt);
2307 framepos_t next_ft = frame_time (bbt);
2309 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2320 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2321 } else if (dir > 0) {
2322 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2324 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2333 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2334 framepos_t lower, framepos_t upper)
2336 Glib::Threads::RWLock::ReaderLock lm (lock);
2337 uint32_t const upper_beat = (uint32_t) ceil (beat_at_frame_locked (_metrics, upper));
2338 uint32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2340 while (cnt <= upper_beat) {
2341 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2342 TempoSection const tempo = tempo_section_at_locked (pos);
2343 MeterSection const meter = meter_section_at_locked (pos);
2344 BBT_Time const bbt = beats_to_bbt (cnt);
2346 points.push_back (BBTPoint (meter, Tempo (tempo.beats_per_minute(), tempo.note_type()), pos, bbt.bars, bbt.beats));
2352 TempoMap::tempo_section_at (framepos_t frame) const
2354 Glib::Threads::RWLock::ReaderLock lm (lock);
2355 return tempo_section_at_locked (frame);
2359 TempoMap::tempo_section_at_locked (framepos_t frame) const
2361 Metrics::const_iterator i;
2362 TempoSection* prev = 0;
2364 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2367 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2369 if (t->frame() > frame) {
2379 abort(); /*NOTREACHED*/
2386 /* don't use this to calculate length (the tempo is only correct for this frame).
2387 do that stuff based on the beat_at_frame and frame_at_beat api
2390 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2392 Glib::Threads::RWLock::ReaderLock lm (lock);
2394 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2395 const TempoSection* ts_after = 0;
2396 Metrics::const_iterator i;
2398 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2401 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2403 if ((*i)->frame() > frame) {
2411 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2413 /* must be treated as constant tempo */
2414 return ts_at->frames_per_beat (_frame_rate);
2418 TempoMap::tempo_at (framepos_t frame) const
2420 Glib::Threads::RWLock::ReaderLock lm (lock);
2421 frameoffset_t const frame_off = frame + frame_offset_at (_metrics, frame);
2422 TempoSection* prev_ts = 0;
2424 Metrics::const_iterator i;
2426 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2428 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2429 if ((prev_ts) && t->frame() > frame) {
2430 /* t is the section past frame */
2431 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2432 Tempo const ret_tempo (ret, prev_ts->note_type());
2439 double const ret = prev_ts->beats_per_minute();
2440 Tempo const ret_tempo (ret, prev_ts->note_type ());
2446 TempoMap::meter_section_at (framepos_t frame) const
2448 Glib::Threads::RWLock::ReaderLock lm (lock);
2449 return meter_section_at_locked (frame);
2453 TempoMap::meter_section_at_locked (framepos_t frame) const
2455 framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2456 Metrics::const_iterator i;
2457 MeterSection* prev = 0;
2459 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2462 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2464 if ((*i)->frame() > frame_off) {
2474 abort(); /*NOTREACHED*/
2481 TempoMap::meter_at (framepos_t frame) const
2483 framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2484 TempoMetric m (metric_at (frame_off));
2490 TempoMap::get_state ()
2492 Metrics::const_iterator i;
2493 XMLNode *root = new XMLNode ("TempoMap");
2496 Glib::Threads::RWLock::ReaderLock lm (lock);
2497 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2498 root->add_child_nocopy ((*i)->get_state());
2506 TempoMap::set_state (const XMLNode& node, int /*version*/)
2509 Glib::Threads::RWLock::WriterLock lm (lock);
2512 XMLNodeConstIterator niter;
2513 Metrics old_metrics (_metrics);
2514 MeterSection* last_meter = 0;
2517 nlist = node.children();
2519 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2520 XMLNode* child = *niter;
2522 if (child->name() == TempoSection::xml_state_node_name) {
2525 TempoSection* ts = new TempoSection (*child);
2526 _metrics.push_back (ts);
2528 if (ts->bar_offset() < 0.0) {
2530 //ts->update_bar_offset_from_bbt (*last_meter);
2535 catch (failed_constructor& err){
2536 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2537 _metrics = old_metrics;
2541 } else if (child->name() == MeterSection::xml_state_node_name) {
2544 MeterSection* ms = new MeterSection (*child);
2545 _metrics.push_back (ms);
2549 catch (failed_constructor& err) {
2550 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2551 _metrics = old_metrics;
2557 if (niter == nlist.end()) {
2558 MetricSectionSorter cmp;
2559 _metrics.sort (cmp);
2561 /* check for legacy sessions where bbt was the base musical unit for tempo */
2562 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2563 MeterSection* prev_ms;
2564 TempoSection* prev_ts;
2565 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2566 if (prev_ms->pulse() < 0.0) {
2567 /*XX we cannot possibly make this work??. */
2568 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());
2569 prev_ms->set_pulse (start);
2571 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2572 if (prev_ts->pulse() < 0.0) {
2573 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);
2574 prev_ts->set_pulse (start);
2579 /* check for multiple tempo/meters at the same location, which
2580 ardour2 somehow allowed.
2583 Metrics::iterator prev = _metrics.end();
2584 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2585 if (prev != _metrics.end()) {
2587 MeterSection* prev_ms;
2589 TempoSection* prev_ts;
2590 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2591 if (prev_ms->pulse() == ms->pulse()) {
2592 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2593 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2596 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2597 if (prev_ts->pulse() == ts->pulse()) {
2598 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2599 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2607 recompute_map (_metrics);
2610 PropertyChanged (PropertyChange ());
2616 TempoMap::dump (Metrics& metrics, std::ostream& o) const
2618 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2619 const MeterSection* m;
2620 const TempoSection* t;
2621 const TempoSection* prev_ts = 0;
2623 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2625 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2626 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? "
2627 << t->movable() << ')' << endl;
2629 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2630 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2631 o << "calculated : " << prev_ts->tempo_at_pulse (t->pulse()) << " | " << 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;
2632 o << "------" << std::endl;
2634 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2635 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2636 << " (movable? " << m->movable() << ')' << endl;
2643 TempoMap::n_tempos() const
2645 Glib::Threads::RWLock::ReaderLock lm (lock);
2648 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2649 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2658 TempoMap::n_meters() const
2660 Glib::Threads::RWLock::ReaderLock lm (lock);
2663 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2664 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2673 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2676 Glib::Threads::RWLock::WriterLock lm (lock);
2677 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2678 if ((*i)->frame() >= where && (*i)->movable ()) {
2679 (*i)->set_frame ((*i)->frame() + amount);
2683 /* now reset the BBT time of all metrics, based on their new
2684 * audio time. This is the only place where we do this reverse
2688 Metrics::iterator i;
2689 const MeterSection* meter;
2690 const TempoSection* tempo;
2694 meter = &first_meter ();
2695 tempo = &first_tempo ();
2700 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2703 MetricSection* prev = 0;
2705 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2708 //TempoMetric metric (*meter, *tempo);
2709 MeterSection* ms = const_cast<MeterSection*>(meter);
2710 TempoSection* ts = const_cast<TempoSection*>(tempo);
2713 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2714 ts->set_pulse (t->pulse());
2716 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2717 ts->set_pulse (m->pulse());
2719 ts->set_frame (prev->frame());
2723 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2724 pair<double, BBT_Time> start = make_pair (m->pulse(), m->bbt());
2725 ms->set_pulse (start);
2727 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2728 pair<double, BBT_Time> start = make_pair (t->pulse(), beats_to_bbt_locked (_metrics, t->pulse()));
2729 ms->set_pulse (start);
2731 ms->set_frame (prev->frame());
2735 // metric will be at frames=0 bbt=1|1|0 by default
2736 // which is correct for our purpose
2739 // cerr << bbt << endl;
2741 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2742 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
2744 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
2745 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2746 bbt_time (m->frame(), bbt);
2748 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2754 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2755 /* round up to next beat */
2761 if (bbt.beats != 1) {
2762 /* round up to next bar */
2767 pair<double, BBT_Time> start = make_pair (pulse_at_frame_locked (_metrics, m->frame()), bbt);
2768 m->set_pulse (start);
2770 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
2772 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2773 abort(); /*NOTREACHED*/
2779 recompute_map (_metrics);
2783 PropertyChanged (PropertyChange ());
2786 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2790 std::list<MetricSection*> metric_kill_list;
2792 TempoSection* last_tempo = NULL;
2793 MeterSection* last_meter = NULL;
2794 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2795 bool meter_after = false; // is there a meter marker likewise?
2797 Glib::Threads::RWLock::WriterLock lm (lock);
2798 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2799 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2800 metric_kill_list.push_back(*i);
2801 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2804 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2808 else if ((*i)->frame() >= where) {
2809 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2810 (*i)->set_frame ((*i)->frame() - amount);
2811 if ((*i)->frame() == where) {
2812 // marker was immediately after end of range
2813 tempo_after = dynamic_cast<TempoSection*> (*i);
2814 meter_after = dynamic_cast<MeterSection*> (*i);
2820 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2821 if (last_tempo && !tempo_after) {
2822 metric_kill_list.remove(last_tempo);
2823 last_tempo->set_frame(where);
2826 if (last_meter && !meter_after) {
2827 metric_kill_list.remove(last_meter);
2828 last_meter->set_frame(where);
2832 //remove all the remaining metrics
2833 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2834 _metrics.remove(*i);
2839 recompute_map (_metrics);
2842 PropertyChanged (PropertyChange ());
2846 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2847 * pos can be -ve, if required.
2850 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2852 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2855 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2857 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2859 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2862 /** Add the BBT interval op to pos and return the result */
2864 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2866 Glib::Threads::RWLock::ReaderLock lm (lock);
2868 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
2869 pos_bbt.ticks += op.ticks;
2870 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
2872 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
2874 pos_bbt.beats += op.beats;
2875 /* the meter in effect will start on the bar */
2876 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
2877 while (pos_bbt.beats >= divisions_per_bar + 1) {
2879 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
2880 pos_bbt.beats -= divisions_per_bar;
2882 pos_bbt.bars += op.bars;
2884 return frame_time_locked (_metrics, pos_bbt);
2887 /** Count the number of beats that are equivalent to distance when going forward,
2891 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2893 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2897 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2903 operator<< (std::ostream& o, const Meter& m) {
2904 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2908 operator<< (std::ostream& o, const Tempo& t) {
2909 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2913 operator<< (std::ostream& o, const MetricSection& section) {
2915 o << "MetricSection @ " << section.frame() << ' ';
2917 const TempoSection* ts;
2918 const MeterSection* ms;
2920 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2921 o << *((const Tempo*) ts);
2922 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2923 //o << *((const Meter*) ms);