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)
77 , Tempo (TempoMap::default_tempo())
81 const XMLProperty *prop;
87 _legacy_bbt = BBT_Time (0, 0, 0);
89 if ((prop = node.property ("start")) != 0) {
90 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
94 /* legacy session - start used to be in bbt*/
97 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
101 if ((prop = node.property ("pulse")) != 0) {
102 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
103 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
109 if ((prop = node.property ("frame")) != 0) {
110 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
111 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
117 if ((prop = node.property ("beats-per-minute")) == 0) {
118 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
119 throw failed_constructor();
122 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
123 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
124 throw failed_constructor();
127 if ((prop = node.property ("note-type")) == 0) {
128 /* older session, make note type be quarter by default */
131 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
132 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
133 throw failed_constructor();
137 if ((prop = node.property ("movable")) == 0) {
138 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
139 throw failed_constructor();
142 set_movable (string_is_affirmative (prop->value()));
144 if ((prop = node.property ("active")) == 0) {
145 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
148 set_active (string_is_affirmative (prop->value()));
151 if ((prop = node.property ("tempo-type")) == 0) {
154 _type = Type (string_2_enum (prop->value(), _type));
157 if ((prop = node.property ("lock-style")) == 0) {
159 set_position_lock_style (MusicTime);
161 set_position_lock_style (AudioTime);
164 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
169 TempoSection::get_state() const
171 XMLNode *root = new XMLNode (xml_state_node_name);
175 snprintf (buf, sizeof (buf), "%f", pulse());
176 root->add_property ("pulse", buf);
177 snprintf (buf, sizeof (buf), "%li", frame());
178 root->add_property ("frame", buf);
179 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
180 root->add_property ("beats-per-minute", buf);
181 snprintf (buf, sizeof (buf), "%f", _note_type);
182 root->add_property ("note-type", buf);
183 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
184 root->add_property ("movable", buf);
185 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
186 root->add_property ("active", buf);
187 root->add_property ("tempo-type", enum_2_string (_type));
188 root->add_property ("lock-style", enum_2_string (position_lock_style()));
194 TempoSection::set_type (Type type)
199 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
202 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
205 if (_type == Constant || _c_func == 0.0) {
206 return pulses_per_minute();
209 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
212 /** returns the zero-based frame (relative to session)
213 where the tempo in whole pulses per minute occurs in this section.
214 beat b is only used for constant tempos.
215 note that the tempo map may have multiple such values.
218 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
220 if (_type == Constant || _c_func == 0.0) {
221 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
224 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
226 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
229 TempoSection::tempo_at_pulse (const double& p) const
232 if (_type == Constant || _c_func == 0.0) {
233 return pulses_per_minute();
235 double const ppm = pulse_tempo_at_pulse (p - pulse());
239 /** returns the zero-based beat (relative to session)
240 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
241 note that the session tempo map may have multiple beats at a given tempo.
244 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
246 if (_type == Constant || _c_func == 0.0) {
247 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
250 return pulse_at_pulse_tempo (ppm) + pulse();
253 /** returns the zero-based pulse (relative to session origin)
254 where the zero-based frame (relative to session)
258 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
260 if (_type == Constant || _c_func == 0.0) {
261 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
264 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
267 /** returns the zero-based frame (relative to session start frame)
268 where the zero-based pulse (relative to session start)
273 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
275 if (_type == Constant || _c_func == 0.0) {
276 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
279 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
287 Tt----|-----------------*|
288 Ta----|--------------|* |
294 _______________|___|____
295 time a t (next tempo)
298 Duration in beats at time a is the integral of some Tempo function.
299 In our case, the Tempo function (Tempo at time t) is
302 with function constant
307 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
308 b(t) = T0(e^(ct) - 1) / c
310 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:
311 t(b) = log((cb / T0) + 1) / c
313 The time t at which Tempo T occurs is a as above:
314 t(T) = log(T / T0) / c
316 The beat at which a Tempo T occurs is:
319 The Tempo at which beat b occurs is:
322 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
323 Our problem is that we usually don't know t.
324 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.
325 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
326 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
328 By substituting our expanded t as a in the c function above, our problem is reduced to:
329 c = T0 (e^(log (Ta / T0)) - 1) / b
331 We can now store c for future time calculations.
332 If the following tempo section (the one that defines c in conjunction with this one)
333 is changed or moved, c is no longer valid.
335 The public methods are session-relative.
337 Most of this stuff is taken from this paper:
340 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
343 Zurich University of Arts
344 Institute for Computer Music and Sound Technology
346 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
351 compute this ramp's function constant using the end tempo (in whole pulses per minute)
352 and duration (pulses into global start) of some later tempo section.
355 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
357 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
358 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
361 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
363 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
365 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
369 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
371 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
375 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
377 return (frame / (double) frame_rate) / 60.0;
380 /* position function */
382 TempoSection::a_func (double end_bpm, double c_func) const
384 return log (end_bpm / pulses_per_minute()) / c_func;
387 /*function constant*/
389 TempoSection::c_func (double end_bpm, double end_time) const
391 return log (end_bpm / pulses_per_minute()) / end_time;
394 /* tempo in ppm at time in minutes */
396 TempoSection::pulse_tempo_at_time (const double& time) const
398 return exp (_c_func * time) * pulses_per_minute();
401 /* time in minutes at tempo in ppm */
403 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
405 return log (pulse_tempo / pulses_per_minute()) / _c_func;
408 /* tick at tempo in ppm */
410 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
412 return (pulse_tempo - pulses_per_minute()) / _c_func;
415 /* tempo in ppm at tick */
417 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
419 return (pulse * _c_func) + pulses_per_minute();
422 /* pulse at time in minutes */
424 TempoSection::pulse_at_time (const double& time) const
426 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
429 /* time in minutes at pulse */
431 TempoSection::time_at_pulse (const double& pulse) const
433 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
436 /***********************************************************************/
438 const string MeterSection::xml_state_node_name = "Meter";
440 MeterSection::MeterSection (const XMLNode& node)
441 : MetricSection (0.0), Meter (TempoMap::default_meter())
443 XMLProperty const * prop;
446 const XMLProperty *prop;
450 framepos_t frame = 0;
451 pair<double, BBT_Time> start;
453 if ((prop = node.property ("start")) != 0) {
454 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
458 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
460 /* legacy session - start used to be in bbt*/
461 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
466 if ((prop = node.property ("pulse")) != 0) {
467 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
468 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
473 if ((prop = node.property ("beat")) != 0) {
474 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
475 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
481 if ((prop = node.property ("bbt")) == 0) {
482 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
483 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
487 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
488 throw failed_constructor();
494 if ((prop = node.property ("frame")) != 0) {
495 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
496 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
502 /* beats-per-bar is old; divisions-per-bar is new */
504 if ((prop = node.property ("divisions-per-bar")) == 0) {
505 if ((prop = node.property ("beats-per-bar")) == 0) {
506 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
507 throw failed_constructor();
510 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
511 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
512 throw failed_constructor();
515 if ((prop = node.property ("note-type")) == 0) {
516 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
517 throw failed_constructor();
519 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
520 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
521 throw failed_constructor();
524 if ((prop = node.property ("movable")) == 0) {
525 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
526 throw failed_constructor();
529 set_movable (string_is_affirmative (prop->value()));
531 if ((prop = node.property ("lock-style")) == 0) {
532 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
534 set_position_lock_style (MusicTime);
536 set_position_lock_style (AudioTime);
539 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
544 MeterSection::get_state() const
546 XMLNode *root = new XMLNode (xml_state_node_name);
550 snprintf (buf, sizeof (buf), "%lf", pulse());
551 root->add_property ("pulse", buf);
552 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
556 root->add_property ("bbt", buf);
557 snprintf (buf, sizeof (buf), "%lf", beat());
558 root->add_property ("beat", buf);
559 snprintf (buf, sizeof (buf), "%f", _note_type);
560 root->add_property ("note-type", buf);
561 snprintf (buf, sizeof (buf), "%li", frame());
562 root->add_property ("frame", buf);
563 root->add_property ("lock-style", enum_2_string (position_lock_style()));
564 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
565 root->add_property ("divisions-per-bar", buf);
566 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
567 root->add_property ("movable", buf);
572 /***********************************************************************/
576 Tempo can be thought of as a source of the musical pulse.
577 Meters divide that pulse into measures and beats.
578 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
579 at any particular time.
580 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
581 It should rather be thought of as tempo note divisions per minute.
583 TempoSections, which are nice to think of in whole pulses per minute,
584 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
585 and beats (via note_divisor) are used to form a tempo map.
586 TempoSections and MeterSections may be locked to either audio or music (position lock style).
587 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
588 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
590 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
592 The first tempo and first meter are special. they must move together, and must be locked to audio.
593 Audio locked tempos which lie before the first meter are made inactive.
594 They will be re-activated if the first meter is again placed before them.
596 Both tempos and meters have a pulse position and a frame position.
597 Meters also have a beat position, which is always 0.0 for the first meter.
599 A tempo locked to music is locked to musical pulses.
600 A meter locked to music is locked to beats.
602 Recomputing the tempo map is the process where the 'missing' position
603 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
605 It is important to keep the _metrics in an order that makes sense.
606 Because ramped MusicTime and AudioTime tempos can interact with each other,
607 reordering is frequent. Care must be taken to keep _metrics in a solved state.
608 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
610 struct MetricSectionSorter {
611 bool operator() (const MetricSection* a, const MetricSection* b) {
612 return a->pulse() < b->pulse();
616 struct MetricSectionFrameSorter {
617 bool operator() (const MetricSection* a, const MetricSection* b) {
618 return a->frame() < b->frame();
622 TempoMap::TempoMap (framecnt_t fr)
625 BBT_Time start (1, 1, 0);
627 TempoSection *t = new TempoSection ((framepos_t) 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
628 MeterSection *m = new MeterSection ((framepos_t) 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
630 t->set_movable (false);
631 m->set_movable (false);
633 /* note: frame time is correct (zero) for both of these */
635 _metrics.push_back (t);
636 _metrics.push_back (m);
640 TempoMap::~TempoMap ()
645 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
647 bool removed = false;
650 Glib::Threads::RWLock::WriterLock lm (lock);
651 if ((removed = remove_tempo_locked (tempo))) {
652 if (complete_operation) {
653 recompute_map (_metrics);
658 if (removed && complete_operation) {
659 PropertyChanged (PropertyChange ());
664 TempoMap::remove_tempo_locked (const TempoSection& tempo)
668 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
669 if (dynamic_cast<TempoSection*> (*i) != 0) {
670 if (tempo.frame() == (*i)->frame()) {
671 if ((*i)->movable()) {
683 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
685 bool removed = false;
688 Glib::Threads::RWLock::WriterLock lm (lock);
689 if ((removed = remove_meter_locked (tempo))) {
690 if (complete_operation) {
691 recompute_map (_metrics);
696 if (removed && complete_operation) {
697 PropertyChanged (PropertyChange ());
702 TempoMap::remove_meter_locked (const MeterSection& tempo)
706 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
707 if (dynamic_cast<MeterSection*> (*i) != 0) {
708 if (tempo.frame() == (*i)->frame()) {
709 if ((*i)->movable()) {
721 TempoMap::do_insert (MetricSection* section)
723 bool need_add = true;
724 /* we only allow new meters to be inserted on beat 1 of an existing
728 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
729 //assert (m->bbt().ticks == 0);
731 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
733 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
734 corrected.second.beats = 1;
735 corrected.second.ticks = 0;
736 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
737 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
738 m->bbt(), corrected.second) << endmsg;
739 //m->set_pulse (corrected);
743 /* Look for any existing MetricSection that is of the same type and
744 in the same bar as the new one, and remove it before adding
745 the new one. Note that this means that if we find a matching,
746 existing section, we can break out of the loop since we're
747 guaranteed that there is only one such match.
750 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
752 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
753 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
754 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
755 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
757 if (tempo && insert_tempo) {
760 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
761 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
763 if (!tempo->movable()) {
765 /* can't (re)move this section, so overwrite
766 * its data content (but not its properties as
770 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
771 (*i)->set_position_lock_style (AudioTime);
773 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
774 t->set_type (insert_tempo->type());
783 } else if (meter && insert_meter) {
787 bool const ipm = insert_meter->position_lock_style() == MusicTime;
789 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
791 if (!meter->movable()) {
793 /* can't (re)move this section, so overwrite
794 * its data content (but not its properties as
798 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
799 (*i)->set_position_lock_style (AudioTime);
808 /* non-matching types, so we don't care */
812 /* Add the given MetricSection, if we didn't just reset an existing
817 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
818 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
821 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
822 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
825 bool const ipm = insert_meter->position_lock_style() == MusicTime;
826 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
831 } else if (insert_tempo) {
832 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
833 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
836 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
837 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
844 _metrics.insert (i, section);
845 //dump (_metrics, std::cerr);
850 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
853 Glib::Threads::RWLock::WriterLock lm (lock);
854 TempoSection& first (first_tempo());
855 if (ts.pulse() != first.pulse()) {
856 remove_tempo_locked (ts);
857 add_tempo_locked (tempo, pulse, true, type);
859 first.set_type (type);
861 /* cannot move the first tempo section */
862 *static_cast<Tempo*>(&first) = tempo;
863 recompute_map (_metrics);
868 PropertyChanged (PropertyChange ());
872 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
875 Glib::Threads::RWLock::WriterLock lm (lock);
876 TempoSection& first (first_tempo());
877 if (ts.frame() != first.frame()) {
878 remove_tempo_locked (ts);
879 add_tempo_locked (tempo, frame, true, type);
881 first.set_type (type);
882 first.set_pulse (0.0);
883 first.set_position_lock_style (AudioTime);
885 /* cannot move the first tempo section */
886 *static_cast<Tempo*>(&first) = tempo;
887 recompute_map (_metrics);
892 PropertyChanged (PropertyChange ());
896 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
898 TempoSection* ts = 0;
900 Glib::Threads::RWLock::WriterLock lm (lock);
901 ts = add_tempo_locked (tempo, pulse, true, type);
904 PropertyChanged (PropertyChange ());
910 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
912 TempoSection* ts = 0;
914 Glib::Threads::RWLock::WriterLock lm (lock);
915 ts = add_tempo_locked (tempo, frame, true, type);
919 PropertyChanged (PropertyChange ());
925 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
927 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
932 solve_map (_metrics, t, t->pulse());
939 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
941 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
946 solve_map (_metrics, t, t->frame());
953 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
956 Glib::Threads::RWLock::WriterLock lm (lock);
959 remove_meter_locked (ms);
960 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
962 MeterSection& first (first_meter());
963 /* cannot move the first meter section */
964 *static_cast<Meter*>(&first) = meter;
965 first.set_position_lock_style (AudioTime);
967 recompute_map (_metrics);
970 PropertyChanged (PropertyChange ());
974 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
977 Glib::Threads::RWLock::WriterLock lm (lock);
979 const double beat = ms.beat();
980 const BBT_Time bbt = ms.bbt();
983 remove_meter_locked (ms);
984 add_meter_locked (meter, frame, beat, bbt, true);
986 MeterSection& first (first_meter());
987 TempoSection& first_t (first_tempo());
988 /* cannot move the first meter section */
989 *static_cast<Meter*>(&first) = meter;
990 first.set_position_lock_style (AudioTime);
991 first.set_pulse (0.0);
992 first.set_frame (frame);
993 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
994 first.set_beat (beat);
995 first_t.set_frame (first.frame());
996 first_t.set_pulse (0.0);
997 first_t.set_position_lock_style (AudioTime);
999 recompute_map (_metrics);
1001 PropertyChanged (PropertyChange ());
1006 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1008 MeterSection* m = 0;
1010 Glib::Threads::RWLock::WriterLock lm (lock);
1011 m = add_meter_locked (meter, beat, where, true);
1016 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1017 dump (_metrics, std::cerr);
1021 PropertyChanged (PropertyChange ());
1027 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1029 MeterSection* m = 0;
1031 Glib::Threads::RWLock::WriterLock lm (lock);
1032 m = add_meter_locked (meter, frame, beat, where, true);
1037 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1038 dump (_metrics, std::cerr);
1042 PropertyChanged (PropertyChange ());
1048 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1050 /* a new meter always starts a new bar on the first beat. so
1051 round the start time appropriately. remember that
1052 `where' is based on the existing tempo map, not
1053 the result after we insert the new meter.
1057 if (where.beats != 1) {
1061 /* new meters *always* start on a beat. */
1063 const double pulse = pulse_at_beat_locked (_metrics, beat);
1064 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1065 do_insert (new_meter);
1068 solve_map (_metrics, new_meter, pulse);
1075 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1077 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1079 double pulse = pulse_at_frame_locked (_metrics, frame);
1080 new_meter->set_pulse (pulse);
1082 do_insert (new_meter);
1085 solve_map (_metrics, new_meter, frame);
1092 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1094 Tempo newtempo (beats_per_minute, note_type);
1097 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1098 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1103 Glib::Threads::RWLock::WriterLock lm (lock);
1104 *((Tempo*) t) = newtempo;
1105 recompute_map (_metrics);
1107 PropertyChanged (PropertyChange ());
1114 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1116 Tempo newtempo (beats_per_minute, note_type);
1119 TempoSection* first;
1120 Metrics::iterator i;
1122 /* find the TempoSection immediately preceding "where"
1125 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1127 if ((*i)->frame() > where) {
1133 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1146 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1156 Glib::Threads::RWLock::WriterLock lm (lock);
1157 /* cannot move the first tempo section */
1158 *((Tempo*)prev) = newtempo;
1159 recompute_map (_metrics);
1162 PropertyChanged (PropertyChange ());
1166 TempoMap::first_meter () const
1168 const MeterSection *m = 0;
1170 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1171 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1176 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1177 abort(); /*NOTREACHED*/
1182 TempoMap::first_meter ()
1184 MeterSection *m = 0;
1186 /* CALLER MUST HOLD LOCK */
1188 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1189 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1194 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1195 abort(); /*NOTREACHED*/
1200 TempoMap::first_tempo () const
1202 const TempoSection *t = 0;
1204 /* CALLER MUST HOLD LOCK */
1206 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1207 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1211 if (!t->movable()) {
1217 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1218 abort(); /*NOTREACHED*/
1223 TempoMap::first_tempo ()
1225 TempoSection *t = 0;
1227 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1228 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1232 if (!t->movable()) {
1238 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1239 abort(); /*NOTREACHED*/
1243 TempoMap::recompute_tempos (Metrics& metrics)
1245 TempoSection* prev_t = 0;
1247 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1250 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1254 if (!t->movable()) {
1260 if (t->position_lock_style() == AudioTime) {
1261 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1262 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1265 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1266 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1273 prev_t->set_c_func (0.0);
1276 /* tempos must be positioned correctly */
1278 TempoMap::recompute_meters (Metrics& metrics)
1280 MeterSection* meter = 0;
1281 MeterSection* prev_m = 0;
1283 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1284 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1285 if (meter->position_lock_style() == AudioTime) {
1287 pair<double, BBT_Time> b_bbt;
1288 if (meter->movable()) {
1289 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor());
1290 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1291 if (floor_beats + prev_m->beat() < meter->beat()) {
1292 /* tempo change caused a change in beat (bar). */
1293 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1294 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1295 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1296 pulse = true_pulse - pulse_off;
1298 b_bbt = make_pair (meter->beat(), meter->bbt());
1299 pulse = pulse_at_frame_locked (metrics, meter->frame());
1302 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1304 meter->set_beat (b_bbt);
1305 meter->set_pulse (pulse);
1308 pair<double, BBT_Time> new_beat;
1310 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1311 new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1313 /* shouldn't happen - the first is audio-locked */
1314 pulse = pulse_at_beat_locked (metrics, meter->beat());
1315 new_beat = make_pair (pulse, meter->bbt());
1318 meter->set_beat (new_beat);
1319 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1320 meter->set_pulse (pulse);
1326 //dump (_metrics, std::cerr;
1330 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1332 /* CALLER MUST HOLD WRITE LOCK */
1336 /* we will actually stop once we hit
1343 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1346 /* silly call from Session::process() during startup
1351 recompute_tempos (metrics);
1352 recompute_meters (metrics);
1356 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1358 Glib::Threads::RWLock::ReaderLock lm (lock);
1359 TempoMetric m (first_meter(), first_tempo());
1361 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1362 at something, because we insert the default tempo and meter during
1363 TempoMap construction.
1365 now see if we can find better candidates.
1368 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1370 if ((*i)->frame() > frame) {
1384 /* XX meters only */
1386 TempoMap::metric_at (BBT_Time bbt) const
1388 Glib::Threads::RWLock::ReaderLock lm (lock);
1389 TempoMetric m (first_meter(), first_tempo());
1391 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1392 at something, because we insert the default tempo and meter during
1393 TempoMap construction.
1395 now see if we can find better candidates.
1398 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1400 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1401 BBT_Time section_start (mw->bbt());
1403 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1415 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1417 MeterSection* prev_m = 0;
1419 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1421 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1422 if (prev_m && m->beat() > beat) {
1429 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1434 TempoMap::pulse_at_beat (const double& beat) const
1436 Glib::Threads::RWLock::ReaderLock lm (lock);
1437 return pulse_at_beat_locked (_metrics, beat);
1441 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1443 MeterSection* prev_m = 0;
1445 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1447 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1448 if (prev_m && m->pulse() > pulse) {
1449 if ((pulse - prev_m->pulse()) * prev_m->note_divisor() < m->beat()) {
1457 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1459 return beats_in_section + prev_m->beat();
1463 TempoMap::beat_at_pulse (const double& pulse) const
1465 Glib::Threads::RWLock::ReaderLock lm (lock);
1466 return beat_at_pulse_locked (_metrics, pulse);
1470 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1472 /* HOLD (at least) THE READER LOCK */
1473 TempoSection* prev_t = 0;
1475 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1477 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1481 if (prev_t && t->frame() > frame) {
1482 /*the previous ts is the one containing the frame */
1483 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1490 /* treated as constant for this ts */
1491 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1493 return pulses_in_section + prev_t->pulse();
1497 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1499 Glib::Threads::RWLock::ReaderLock lm (lock);
1500 return pulse_at_frame_locked (_metrics, frame);
1504 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1506 /* HOLD THE READER LOCK */
1508 const TempoSection* prev_t = 0;
1510 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1513 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1517 if (prev_t && t->pulse() > pulse) {
1518 return prev_t->frame_at_pulse (pulse, _frame_rate);
1524 /* must be treated as constant, irrespective of _type */
1525 double const pulses_in_section = pulse - prev_t->pulse();
1526 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1528 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1534 TempoMap::frame_at_pulse (const double& pulse) const
1536 Glib::Threads::RWLock::ReaderLock lm (lock);
1537 return frame_at_pulse_locked (_metrics, pulse);
1541 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1543 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1544 MeterSection* prev_m = 0;
1545 MeterSection* next_m = 0;
1547 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1549 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1550 if (prev_m && m->frame() > frame) {
1557 if (frame < prev_m->frame()) {
1560 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1562 if (next_m && next_m->beat() < beat) {
1563 return next_m->beat();
1570 TempoMap::beat_at_frame (const framecnt_t& frame) const
1572 Glib::Threads::RWLock::ReaderLock lm (lock);
1573 return beat_at_frame_locked (_metrics, frame);
1577 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1579 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1580 MeterSection* prev_m = 0;
1582 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1584 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1585 if (prev_m && m->beat() > beat) {
1592 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1596 TempoMap::frame_at_beat (const double& beat) const
1598 Glib::Threads::RWLock::ReaderLock lm (lock);
1599 return frame_at_beat_locked (_metrics, beat);
1603 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1605 /* CALLER HOLDS READ LOCK */
1607 MeterSection* prev_m = 0;
1609 /* because audio-locked meters have 'fake' integral beats,
1610 there is no pulse offset here.
1612 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1614 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1616 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1617 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1625 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1626 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1627 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1633 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1635 Glib::Threads::RWLock::ReaderLock lm (lock);
1636 return bbt_to_beats_locked (_metrics, bbt);
1640 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1642 /* CALLER HOLDS READ LOCK */
1643 MeterSection* prev_m = 0;
1644 const double beats = (b < 0.0) ? 0.0 : b;
1646 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1647 MeterSection* m = 0;
1649 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1651 if (m->beat() > beats) {
1652 /* this is the meter after the one our beat is on*/
1661 const double beats_in_ms = beats - prev_m->beat();
1662 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1663 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1664 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1665 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1669 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1670 ret.beats = (uint32_t) floor (remaining_beats);
1671 ret.bars = total_bars;
1673 /* 0 0 0 to 1 1 0 - based mapping*/
1677 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1679 ret.ticks -= BBT_Time::ticks_per_beat;
1682 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1691 TempoMap::beats_to_bbt (const double& beats)
1693 Glib::Threads::RWLock::ReaderLock lm (lock);
1694 return beats_to_bbt_locked (_metrics, beats);
1698 TempoMap::pulse_to_bbt (const double& pulse)
1700 Glib::Threads::RWLock::ReaderLock lm (lock);
1701 MeterSection* prev_m = 0;
1703 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1704 MeterSection* m = 0;
1706 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1709 double const pulses_to_m = m->pulse() - prev_m->pulse();
1710 if (prev_m->pulse() + pulses_to_m > pulse) {
1711 /* this is the meter after the one our beat is on*/
1720 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1721 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1722 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1723 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1724 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1728 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1729 ret.beats = (uint32_t) floor (remaining_beats);
1730 ret.bars = total_bars;
1732 /* 0 0 0 to 1 1 0 mapping*/
1736 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1738 ret.ticks -= BBT_Time::ticks_per_beat;
1741 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1750 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1757 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1760 Glib::Threads::RWLock::ReaderLock lm (lock);
1761 const double beat = beat_at_frame_locked (_metrics, frame);
1763 bbt = beats_to_bbt_locked (_metrics, beat);
1767 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1769 /* HOLD THE READER LOCK */
1771 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1776 TempoMap::frame_time (const BBT_Time& bbt)
1779 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1783 if (bbt.beats < 1) {
1784 throw std::logic_error ("beats are counted from one");
1786 Glib::Threads::RWLock::ReaderLock lm (lock);
1788 return frame_time_locked (_metrics, bbt);
1792 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1794 TempoSection* prev_t = 0;
1796 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1798 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1803 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1807 if (t->frame() == prev_t->frame()) {
1811 /* precision check ensures pulses and frames align.*/
1812 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1824 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1826 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1828 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1829 if (!t->movable()) {
1830 t->set_active (true);
1833 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1834 t->set_active (false);
1836 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1837 t->set_active (true);
1838 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1847 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1849 TempoSection* prev_t = 0;
1850 TempoSection* section_prev = 0;
1851 framepos_t first_m_frame = 0;
1853 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1855 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1856 if (!m->movable()) {
1857 first_m_frame = m->frame();
1862 if (section->movable() && frame <= first_m_frame) {
1866 section->set_active (true);
1867 section->set_frame (frame);
1869 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1871 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1878 section_prev = prev_t;
1881 if (t->position_lock_style() == MusicTime) {
1882 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1883 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1885 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1886 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1894 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1895 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1898 if (section->position_lock_style() == MusicTime) {
1899 /* we're setting the frame */
1900 section->set_position_lock_style (AudioTime);
1901 recompute_tempos (imaginary);
1902 section->set_position_lock_style (MusicTime);
1904 recompute_tempos (imaginary);
1907 if (check_solved (imaginary, true)) {
1908 recompute_meters (imaginary);
1912 MetricSectionFrameSorter fcmp;
1913 imaginary.sort (fcmp);
1914 if (section->position_lock_style() == MusicTime) {
1915 /* we're setting the frame */
1916 section->set_position_lock_style (AudioTime);
1917 recompute_tempos (imaginary);
1918 section->set_position_lock_style (MusicTime);
1920 recompute_tempos (imaginary);
1923 if (check_solved (imaginary, true)) {
1924 recompute_meters (imaginary);
1928 MetricSectionSorter cmp;
1929 imaginary.sort (cmp);
1930 if (section->position_lock_style() == MusicTime) {
1931 /* we're setting the frame */
1932 section->set_position_lock_style (AudioTime);
1933 recompute_tempos (imaginary);
1934 section->set_position_lock_style (MusicTime);
1936 recompute_tempos (imaginary);
1939 if (check_solved (imaginary, true)) {
1940 recompute_meters (imaginary);
1944 //dump (imaginary, std::cerr);
1950 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1952 TempoSection* prev_t = 0;
1953 TempoSection* section_prev = 0;
1955 section->set_pulse (pulse);
1957 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1959 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1963 if (!t->movable()) {
1970 section_prev = prev_t;
1973 if (t->position_lock_style() == MusicTime) {
1974 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1975 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1977 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1978 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1985 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1986 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1989 if (section->position_lock_style() == AudioTime) {
1990 /* we're setting the pulse */
1991 section->set_position_lock_style (MusicTime);
1992 recompute_tempos (imaginary);
1993 section->set_position_lock_style (AudioTime);
1995 recompute_tempos (imaginary);
1998 if (check_solved (imaginary, false)) {
1999 recompute_meters (imaginary);
2003 MetricSectionSorter cmp;
2004 imaginary.sort (cmp);
2005 if (section->position_lock_style() == AudioTime) {
2006 /* we're setting the pulse */
2007 section->set_position_lock_style (MusicTime);
2008 recompute_tempos (imaginary);
2009 section->set_position_lock_style (AudioTime);
2011 recompute_tempos (imaginary);
2014 if (check_solved (imaginary, false)) {
2015 recompute_meters (imaginary);
2019 MetricSectionFrameSorter fcmp;
2020 imaginary.sort (fcmp);
2021 if (section->position_lock_style() == AudioTime) {
2022 /* we're setting the pulse */
2023 section->set_position_lock_style (MusicTime);
2024 recompute_tempos (imaginary);
2025 section->set_position_lock_style (AudioTime);
2027 recompute_tempos (imaginary);
2030 if (check_solved (imaginary, false)) {
2031 recompute_meters (imaginary);
2035 //dump (imaginary, std::cerr);
2041 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2043 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2044 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2045 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2048 MeterSection* prev_m = 0;
2050 if (!section->movable()) {
2051 /* lock the first tempo to our first meter */
2052 if (!set_active_tempos (imaginary, frame)) {
2055 TempoSection* first_t = &first_tempo();
2057 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2059 new_section->set_frame (frame);
2060 new_section->set_pulse (0.0);
2061 new_section->set_active (true);
2063 if (solve_map (future_map, new_section, frame)) {
2064 first_t->set_frame (frame);
2065 first_t->set_pulse (0.0);
2066 first_t->set_active (true);
2067 solve_map (imaginary, first_t, frame);
2073 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2075 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2078 here we set the beat for this frame.
2079 we set it 'incorrectly' to the next bar's first beat
2080 and use the delta to find the meter's pulse.
2082 double new_pulse = 0.0;
2083 pair<double, BBT_Time> b_bbt;
2085 if (section->movable()) {
2086 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2087 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2088 if (floor_beats + prev_m->beat() < section->beat()) {
2089 /* disallow position change if it will alter out beat
2090 we allow tempo changes to do this in recompute_meters().
2091 blocking this is an option, but i'm not convinced that
2092 this is what the user would actually want.
2096 b_bbt = make_pair (section->beat(), section->bbt());
2097 new_pulse = pulse_at_frame_locked (imaginary, frame);
2099 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2101 section->set_frame (frame);
2102 section->set_beat (b_bbt);
2103 section->set_pulse (new_pulse);
2109 double new_pulse = 0.0;
2110 if (m->position_lock_style() == MusicTime) {
2111 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2112 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2113 if (m->frame() > section->frame()) {
2114 /* moving 'section' will affect later meters' beat (but not bbt).*/
2115 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2116 m->set_beat (new_beat);
2119 pair<double, BBT_Time> b_bbt;
2121 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2122 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2123 b_bbt = make_pair (floor_beats + prev_m->beat()
2124 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2125 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2126 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2127 new_pulse = true_pulse - pulse_off;
2129 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2132 m->set_beat (b_bbt);
2134 m->set_pulse (new_pulse);
2140 MetricSectionFrameSorter fcmp;
2141 imaginary.sort (fcmp);
2142 if (section->position_lock_style() == MusicTime) {
2143 /* we're setting the frame */
2144 section->set_position_lock_style (AudioTime);
2145 recompute_meters (imaginary);
2146 section->set_position_lock_style (MusicTime);
2148 recompute_meters (imaginary);
2150 //dump (imaginary, std::cerr);
2154 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2156 MeterSection* prev_m = 0;
2157 MeterSection* section_prev = 0;
2159 section->set_pulse (pulse);
2161 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2163 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2165 section_prev = prev_m;
2169 double new_pulse = 0.0;
2170 if (m->position_lock_style() == MusicTime) {
2171 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2172 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2174 if (new_pulse > section->pulse()) {
2175 /* moving 'section' will affect later meters' beat (but not bbt).*/
2176 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2177 m->set_beat (new_beat);
2180 pair<double, BBT_Time> b_bbt;
2182 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2183 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2184 b_bbt = make_pair (floor_beats + prev_m->beat()
2185 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2186 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2187 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2188 new_pulse = true_pulse - pulse_off;
2190 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2192 m->set_beat (b_bbt);
2194 m->set_pulse (new_pulse);
2201 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2202 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2203 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2204 section->set_beat (b_bbt);
2205 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2208 MetricSectionSorter cmp;
2209 imaginary.sort (cmp);
2210 if (section->position_lock_style() == AudioTime) {
2211 /* we're setting the pulse */
2212 section->set_position_lock_style (MusicTime);
2213 recompute_meters (imaginary);
2214 section->set_position_lock_style (AudioTime);
2216 recompute_meters (imaginary);
2220 /** places a copy of _metrics into copy and returns a pointer
2221 * to section's equivalent in copy.
2224 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2228 TempoSection* ret = 0;
2230 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2231 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2233 if (t->position_lock_style() == MusicTime) {
2234 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2235 ret->set_frame (t->frame());
2237 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2238 ret->set_pulse (t->pulse());
2240 ret->set_active (t->active());
2241 ret->set_movable (t->movable());
2242 copy.push_back (ret);
2245 TempoSection* cp = 0;
2246 if (t->position_lock_style() == MusicTime) {
2247 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2248 cp->set_frame (t->frame());
2250 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2251 cp->set_pulse (t->pulse());
2253 cp->set_active (t->active());
2254 cp->set_movable (t->movable());
2255 copy.push_back (cp);
2257 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2258 MeterSection* cp = 0;
2259 if (m->position_lock_style() == MusicTime) {
2260 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2261 cp->set_frame (m->frame());
2263 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2264 cp->set_pulse (m->pulse());
2266 cp->set_movable (m->movable());
2267 copy.push_back (cp);
2275 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2278 TempoSection* new_section = 0;
2281 Glib::Threads::RWLock::ReaderLock lm (lock);
2282 new_section = copy_metrics_and_point (copy, ts);
2285 double const beat = bbt_to_beats_locked (copy, bbt);
2286 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2288 Metrics::const_iterator d = copy.begin();
2289 while (d != copy.end()) {
2298 * 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,
2299 * taking any possible reordering as a consequence of this into account.
2300 * @param section - the section to be altered
2301 * @param bpm - the new Tempo
2302 * @param bbt - the bbt where the altered tempo will fall
2303 * @return returns - the position in frames where the new tempo section will lie.
2306 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2308 Glib::Threads::RWLock::ReaderLock lm (lock);
2311 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2313 const double beat = bbt_to_beats_locked (future_map, bbt);
2315 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2316 ret = new_section->frame();
2318 ret = frame_at_beat_locked (_metrics, beat);
2321 Metrics::const_iterator d = future_map.begin();
2322 while (d != future_map.end()) {
2330 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2332 Glib::Threads::RWLock::ReaderLock lm (lock);
2335 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2337 if (solve_map (future_map, new_section, frame)) {
2338 ret = new_section->pulse();
2340 ret = pulse_at_frame_locked (_metrics, frame);
2343 Metrics::const_iterator d = future_map.begin();
2344 while (d != future_map.end()) {
2352 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2356 Glib::Threads::RWLock::WriterLock lm (lock);
2357 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2358 if (solve_map (future_map, new_section, frame)) {
2359 solve_map (_metrics, ts, frame);
2363 Metrics::const_iterator d = future_map.begin();
2364 while (d != future_map.end()) {
2369 MetricPositionChanged (); // Emit Signal
2373 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2377 Glib::Threads::RWLock::WriterLock lm (lock);
2378 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2379 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2380 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2384 Metrics::const_iterator d = future_map.begin();
2385 while (d != future_map.end()) {
2390 MetricPositionChanged (); // Emit Signal
2394 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2397 Glib::Threads::RWLock::WriterLock lm (lock);
2398 solve_map (_metrics, ms, frame);
2401 MetricPositionChanged (); // Emit Signal
2405 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2408 Glib::Threads::RWLock::WriterLock lm (lock);
2409 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2412 MetricPositionChanged (); // Emit Signal
2416 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2419 bool can_solve = false;
2421 Glib::Threads::RWLock::WriterLock lm (lock);
2422 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2423 new_section->set_beats_per_minute (bpm.beats_per_minute());
2424 recompute_tempos (future_map);
2426 if (check_solved (future_map, true)) {
2427 ts->set_beats_per_minute (bpm.beats_per_minute());
2428 recompute_map (_metrics);
2433 Metrics::const_iterator d = future_map.begin();
2434 while (d != future_map.end()) {
2439 MetricPositionChanged (); // Emit Signal
2445 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2447 Glib::Threads::RWLock::ReaderLock lm (lock);
2449 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2450 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2451 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2453 return frame_at_beat_locked (_metrics, total_beats);
2457 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2459 return round_to_type (fr, dir, Bar);
2463 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2465 return round_to_type (fr, dir, Beat);
2469 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2471 Glib::Threads::RWLock::ReaderLock lm (lock);
2472 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2473 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2474 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2476 ticks -= beats * BBT_Time::ticks_per_beat;
2479 /* round to next (or same iff dir == RoundUpMaybe) */
2481 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2483 if (mod == 0 && dir == RoundUpMaybe) {
2484 /* right on the subdivision, which is fine, so do nothing */
2486 } else if (mod == 0) {
2487 /* right on the subdivision, so the difference is just the subdivision ticks */
2488 ticks += ticks_one_subdivisions_worth;
2491 /* not on subdivision, compute distance to next subdivision */
2493 ticks += ticks_one_subdivisions_worth - mod;
2496 if (ticks >= BBT_Time::ticks_per_beat) {
2497 ticks -= BBT_Time::ticks_per_beat;
2499 } else if (dir < 0) {
2501 /* round to previous (or same iff dir == RoundDownMaybe) */
2503 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2505 if (difference == 0 && dir == RoundDownAlways) {
2506 /* right on the subdivision, but force-rounding down,
2507 so the difference is just the subdivision ticks */
2508 difference = ticks_one_subdivisions_worth;
2511 if (ticks < difference) {
2512 ticks = BBT_Time::ticks_per_beat - ticks;
2514 ticks -= difference;
2518 /* round to nearest */
2521 /* compute the distance to the previous and next subdivision */
2523 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2525 /* closer to the next subdivision, so shift forward */
2527 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2529 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2531 if (ticks > BBT_Time::ticks_per_beat) {
2533 ticks -= BBT_Time::ticks_per_beat;
2534 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2537 } else if (rem > 0) {
2539 /* closer to previous subdivision, so shift backward */
2543 /* can't go backwards past zero, so ... */
2546 /* step back to previous beat */
2548 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2549 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2551 ticks = lrint (ticks - rem);
2552 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2555 /* on the subdivision, do nothing */
2559 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2565 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2567 if (sub_num == -1) {
2568 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2569 if ((double) when.beats > bpb / 2.0) {
2575 } else if (sub_num == 0) {
2576 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2577 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2579 while ((double) when.beats > bpb) {
2581 when.beats -= (uint32_t) floor (bpb);
2587 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2589 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2590 /* closer to the next subdivision, so shift forward */
2592 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2594 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2596 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2599 } else if (rem > 0) {
2600 /* closer to previous subdivision, so shift backward */
2602 if (rem > when.ticks) {
2603 if (when.beats == 0) {
2604 /* can't go backwards past zero, so ... */
2606 /* step back to previous beat */
2608 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2610 when.ticks = when.ticks - rem;
2616 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2618 Glib::Threads::RWLock::ReaderLock lm (lock);
2620 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2621 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2626 /* find bar previous to 'frame' */
2629 return frame_time_locked (_metrics, bbt);
2631 } else if (dir > 0) {
2632 /* find bar following 'frame' */
2636 return frame_time_locked (_metrics, bbt);
2638 /* true rounding: find nearest bar */
2639 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2642 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2644 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2646 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2657 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2658 } else if (dir > 0) {
2659 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2661 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2670 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2671 framepos_t lower, framepos_t upper)
2673 Glib::Threads::RWLock::ReaderLock lm (lock);
2674 const int32_t upper_beat = (int32_t) floor (beat_at_frame_locked (_metrics, upper));
2675 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2677 /* although the map handles negative beats, bbt doesn't. */
2681 while (pos < upper) {
2682 pos = frame_at_beat_locked (_metrics, cnt);
2683 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2684 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2685 const BBT_Time bbt = beats_to_bbt (cnt);
2686 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2692 TempoMap::tempo_section_at (framepos_t frame) const
2694 Glib::Threads::RWLock::ReaderLock lm (lock);
2695 return tempo_section_at_locked (_metrics, frame);
2699 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2701 Metrics::const_iterator i;
2702 TempoSection* prev = 0;
2704 for (i = metrics.begin(); i != metrics.end(); ++i) {
2707 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2711 if (prev && t->frame() > frame) {
2721 abort(); /*NOTREACHED*/
2728 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2730 TempoSection* prev_t = 0;
2731 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2733 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2735 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2736 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2746 /* don't use this to calculate length (the tempo is only correct for this frame).
2747 do that stuff based on the beat_at_frame and frame_at_beat api
2750 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2752 Glib::Threads::RWLock::ReaderLock lm (lock);
2754 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
2755 const TempoSection* ts_after = 0;
2756 Metrics::const_iterator i;
2758 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2761 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2765 if ((*i)->frame() > frame) {
2773 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2775 /* must be treated as constant tempo */
2776 return ts_at->frames_per_beat (_frame_rate);
2780 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
2782 TempoSection* prev_t = 0;
2784 Metrics::const_iterator i;
2786 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2788 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2792 if ((prev_t) && t->frame() > frame) {
2793 /* t is the section past frame */
2794 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2795 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2802 const double ret = prev_t->beats_per_minute();
2803 const Tempo ret_tempo (ret, prev_t->note_type ());
2809 TempoMap::tempo_at (const framepos_t& frame) const
2811 Glib::Threads::RWLock::ReaderLock lm (lock);
2812 return tempo_at_locked (_metrics, frame);
2816 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
2818 Metrics::const_iterator i;
2819 MeterSection* prev = 0;
2821 for (i = metrics.begin(); i != metrics.end(); ++i) {
2824 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2826 if (prev && (*i)->frame() > frame) {
2836 abort(); /*NOTREACHED*/
2844 TempoMap::meter_section_at (framepos_t frame) const
2846 Glib::Threads::RWLock::ReaderLock lm (lock);
2847 return meter_section_at_locked (_metrics, frame);
2851 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2853 MeterSection* prev_m = 0;
2855 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2857 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2858 if (prev_m && m->beat() > beat) {
2869 TempoMap::meter_section_at_beat (double beat) const
2871 Glib::Threads::RWLock::ReaderLock lm (lock);
2872 return meter_section_at_beat_locked (_metrics, beat);
2876 TempoMap::meter_at (framepos_t frame) const
2878 TempoMetric m (metric_at (frame));
2883 TempoMap::fix_legacy_session ()
2885 MeterSection* prev_m = 0;
2886 TempoSection* prev_t = 0;
2888 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2892 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2893 if (!m->movable()) {
2894 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2898 m->set_position_lock_style (AudioTime);
2903 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2904 + (m->bbt().beats - 1)
2905 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2907 m->set_beat (start);
2908 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2909 + (m->bbt().beats - 1)
2910 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2911 m->set_pulse (start_beat / prev_m->note_divisor());
2914 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2920 if (!t->movable()) {
2923 t->set_position_lock_style (AudioTime);
2929 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2930 + (t->legacy_bbt().beats - 1)
2931 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2933 t->set_pulse (beat / prev_m->note_divisor());
2935 /* really shouldn't happen but.. */
2936 t->set_pulse (beat / 4.0);
2945 TempoMap::get_state ()
2947 Metrics::const_iterator i;
2948 XMLNode *root = new XMLNode ("TempoMap");
2951 Glib::Threads::RWLock::ReaderLock lm (lock);
2952 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2953 root->add_child_nocopy ((*i)->get_state());
2961 TempoMap::set_state (const XMLNode& node, int /*version*/)
2964 Glib::Threads::RWLock::WriterLock lm (lock);
2967 XMLNodeConstIterator niter;
2968 Metrics old_metrics (_metrics);
2971 nlist = node.children();
2973 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2974 XMLNode* child = *niter;
2976 if (child->name() == TempoSection::xml_state_node_name) {
2979 TempoSection* ts = new TempoSection (*child);
2980 _metrics.push_back (ts);
2983 catch (failed_constructor& err){
2984 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2985 _metrics = old_metrics;
2989 } else if (child->name() == MeterSection::xml_state_node_name) {
2992 MeterSection* ms = new MeterSection (*child);
2993 _metrics.push_back (ms);
2996 catch (failed_constructor& err) {
2997 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2998 _metrics = old_metrics;
3004 if (niter == nlist.end()) {
3005 MetricSectionSorter cmp;
3006 _metrics.sort (cmp);
3009 /* check for legacy sessions where bbt was the base musical unit for tempo */
3010 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3012 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3013 if (t->legacy_bbt().bars != 0) {
3014 fix_legacy_session();
3021 /* check for multiple tempo/meters at the same location, which
3022 ardour2 somehow allowed.
3025 Metrics::iterator prev = _metrics.end();
3026 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3027 if (prev != _metrics.end()) {
3029 MeterSection* prev_m;
3031 TempoSection* prev_t;
3032 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3033 if (prev_m->pulse() == ms->pulse()) {
3034 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3035 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3038 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3039 if (prev_t->pulse() == ts->pulse()) {
3040 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3041 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3049 recompute_map (_metrics);
3052 PropertyChanged (PropertyChange ());
3058 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3060 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3061 const MeterSection* m;
3062 const TempoSection* t;
3063 const TempoSection* prev_t = 0;
3065 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3067 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3068 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3069 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3070 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3072 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3073 o << "calculated : " << prev_t->tempo_at_pulse (t->pulse()) * prev_t->note_type() << " | " << prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << " | " << prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
3076 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3077 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3078 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3081 o << "------" << std::endl;
3085 TempoMap::n_tempos() const
3087 Glib::Threads::RWLock::ReaderLock lm (lock);
3090 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3091 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3100 TempoMap::n_meters() const
3102 Glib::Threads::RWLock::ReaderLock lm (lock);
3105 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3106 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3115 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3118 Glib::Threads::RWLock::WriterLock lm (lock);
3119 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3120 if ((*i)->frame() >= where && (*i)->movable ()) {
3121 (*i)->set_frame ((*i)->frame() + amount);
3125 /* now reset the BBT time of all metrics, based on their new
3126 * audio time. This is the only place where we do this reverse
3130 Metrics::iterator i;
3131 const MeterSection* meter;
3132 const TempoSection* tempo;
3136 meter = &first_meter ();
3137 tempo = &first_tempo ();
3142 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3145 MetricSection* prev = 0;
3147 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3150 //TempoMetric metric (*meter, *tempo);
3151 MeterSection* ms = const_cast<MeterSection*>(meter);
3152 TempoSection* ts = const_cast<TempoSection*>(tempo);
3155 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3159 ts->set_pulse (t->pulse());
3161 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3162 ts->set_pulse (m->pulse());
3164 ts->set_frame (prev->frame());
3168 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3169 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3170 ms->set_beat (start);
3171 ms->set_pulse (m->pulse());
3173 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3177 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3178 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3179 ms->set_beat (start);
3180 ms->set_pulse (t->pulse());
3182 ms->set_frame (prev->frame());
3186 // metric will be at frames=0 bbt=1|1|0 by default
3187 // which is correct for our purpose
3190 // cerr << bbt << endl;
3192 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3196 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3198 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3199 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3200 bbt_time (m->frame(), bbt);
3202 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3208 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3209 /* round up to next beat */
3215 if (bbt.beats != 1) {
3216 /* round up to next bar */
3221 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3222 m->set_beat (start);
3223 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3225 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3227 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3228 abort(); /*NOTREACHED*/
3234 recompute_map (_metrics);
3238 PropertyChanged (PropertyChange ());
3241 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3245 std::list<MetricSection*> metric_kill_list;
3247 TempoSection* last_tempo = NULL;
3248 MeterSection* last_meter = NULL;
3249 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3250 bool meter_after = false; // is there a meter marker likewise?
3252 Glib::Threads::RWLock::WriterLock lm (lock);
3253 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3254 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3255 metric_kill_list.push_back(*i);
3256 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3259 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3263 else if ((*i)->frame() >= where) {
3264 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3265 (*i)->set_frame ((*i)->frame() - amount);
3266 if ((*i)->frame() == where) {
3267 // marker was immediately after end of range
3268 tempo_after = dynamic_cast<TempoSection*> (*i);
3269 meter_after = dynamic_cast<MeterSection*> (*i);
3275 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3276 if (last_tempo && !tempo_after) {
3277 metric_kill_list.remove(last_tempo);
3278 last_tempo->set_frame(where);
3281 if (last_meter && !meter_after) {
3282 metric_kill_list.remove(last_meter);
3283 last_meter->set_frame(where);
3287 //remove all the remaining metrics
3288 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3289 _metrics.remove(*i);
3294 recompute_map (_metrics);
3297 PropertyChanged (PropertyChange ());
3301 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3302 * pos can be -ve, if required.
3305 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3307 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3310 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3312 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3314 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3317 /** Add the BBT interval op to pos and return the result */
3319 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3321 Glib::Threads::RWLock::ReaderLock lm (lock);
3323 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3324 pos_bbt.ticks += op.ticks;
3325 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3327 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3329 pos_bbt.beats += op.beats;
3330 /* the meter in effect will start on the bar */
3331 double divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3332 while (pos_bbt.beats >= divisions_per_bar + 1) {
3334 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3335 pos_bbt.beats -= divisions_per_bar;
3337 pos_bbt.bars += op.bars;
3339 return frame_time_locked (_metrics, pos_bbt);
3342 /** Count the number of beats that are equivalent to distance when going forward,
3346 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3348 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3352 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3358 operator<< (std::ostream& o, const Meter& m) {
3359 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3363 operator<< (std::ostream& o, const Tempo& t) {
3364 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3368 operator<< (std::ostream& o, const MetricSection& section) {
3370 o << "MetricSection @ " << section.frame() << ' ';
3372 const TempoSection* ts;
3373 const MeterSection* ms;
3375 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3376 o << *((const Tempo*) ts);
3377 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3378 o << *((const Meter*) ms);