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 MeterSection& prev_m = meter_section_at_locked (metrics, frame);
1544 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1545 if (frame < prev_m.frame()) {
1548 return prev_m.beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m.pulse()) * prev_m.note_divisor();
1552 TempoMap::beat_at_frame (const framecnt_t& frame) const
1554 Glib::Threads::RWLock::ReaderLock lm (lock);
1555 return beat_at_frame_locked (_metrics, frame);
1559 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1561 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1566 TempoMap::frame_at_beat (const double& beat) const
1568 Glib::Threads::RWLock::ReaderLock lm (lock);
1569 return frame_at_beat_locked (_metrics, beat);
1573 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1575 /* CALLER HOLDS READ LOCK */
1577 MeterSection* prev_m = 0;
1579 /* because audio-locked meters have 'fake' integral beats,
1580 there is no pulse offset here.
1582 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1584 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1586 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1587 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1595 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1596 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1597 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1603 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1605 Glib::Threads::RWLock::ReaderLock lm (lock);
1606 return bbt_to_beats_locked (_metrics, bbt);
1610 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1612 /* CALLER HOLDS READ LOCK */
1613 MeterSection* prev_m = 0;
1614 const double beats = (b < 0.0) ? 0.0 : b;
1616 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1617 MeterSection* m = 0;
1619 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1621 if (m->beat() > beats) {
1622 /* this is the meter after the one our beat is on*/
1631 const double beats_in_ms = beats - prev_m->beat();
1632 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1633 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1634 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1635 const double 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_m->divisions_per_bar() + 1) {
1661 TempoMap::beats_to_bbt (const double& beats)
1663 Glib::Threads::RWLock::ReaderLock lm (lock);
1664 return beats_to_bbt_locked (_metrics, beats);
1668 TempoMap::pulse_to_bbt (const double& pulse)
1670 Glib::Threads::RWLock::ReaderLock lm (lock);
1671 MeterSection* prev_m = 0;
1673 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1674 MeterSection* m = 0;
1676 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1679 double const pulses_to_m = m->pulse() - prev_m->pulse();
1680 if (prev_m->pulse() + pulses_to_m > pulse) {
1681 /* this is the meter after the one our beat is on*/
1690 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1691 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1692 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1693 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1694 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1698 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1699 ret.beats = (uint32_t) floor (remaining_beats);
1700 ret.bars = total_bars;
1702 /* 0 0 0 to 1 1 0 mapping*/
1706 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1708 ret.ticks -= BBT_Time::ticks_per_beat;
1711 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1720 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1727 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1730 Glib::Threads::RWLock::ReaderLock lm (lock);
1731 const double beat = beat_at_frame_locked (_metrics, frame);
1733 bbt = beats_to_bbt_locked (_metrics, beat);
1737 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1739 /* HOLD THE READER LOCK */
1741 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1746 TempoMap::frame_time (const BBT_Time& bbt)
1749 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1753 if (bbt.beats < 1) {
1754 throw std::logic_error ("beats are counted from one");
1756 Glib::Threads::RWLock::ReaderLock lm (lock);
1758 return frame_time_locked (_metrics, bbt);
1762 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1764 TempoSection* prev_t = 0;
1766 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1768 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1773 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1777 if (t->frame() == prev_t->frame()) {
1781 /* precision check ensures pulses and frames align.*/
1782 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1794 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1796 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1798 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1799 if (!t->movable()) {
1800 t->set_active (true);
1803 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1804 t->set_active (false);
1806 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1807 t->set_active (true);
1808 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1817 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1819 TempoSection* prev_t = 0;
1820 TempoSection* section_prev = 0;
1821 framepos_t first_m_frame = 0;
1823 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1825 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1826 if (!m->movable()) {
1827 first_m_frame = m->frame();
1832 if (section->movable() && frame <= first_m_frame) {
1836 section->set_active (true);
1837 section->set_frame (frame);
1839 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1841 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1848 section_prev = prev_t;
1851 if (t->position_lock_style() == MusicTime) {
1852 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1853 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1855 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1856 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1864 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1865 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1868 if (section->position_lock_style() == MusicTime) {
1869 /* we're setting the frame */
1870 section->set_position_lock_style (AudioTime);
1871 recompute_tempos (imaginary);
1872 section->set_position_lock_style (MusicTime);
1874 recompute_tempos (imaginary);
1877 if (check_solved (imaginary, true)) {
1878 recompute_meters (imaginary);
1882 MetricSectionFrameSorter fcmp;
1883 imaginary.sort (fcmp);
1884 if (section->position_lock_style() == MusicTime) {
1885 /* we're setting the frame */
1886 section->set_position_lock_style (AudioTime);
1887 recompute_tempos (imaginary);
1888 section->set_position_lock_style (MusicTime);
1890 recompute_tempos (imaginary);
1893 if (check_solved (imaginary, true)) {
1894 recompute_meters (imaginary);
1898 MetricSectionSorter cmp;
1899 imaginary.sort (cmp);
1900 if (section->position_lock_style() == MusicTime) {
1901 /* we're setting the frame */
1902 section->set_position_lock_style (AudioTime);
1903 recompute_tempos (imaginary);
1904 section->set_position_lock_style (MusicTime);
1906 recompute_tempos (imaginary);
1909 if (check_solved (imaginary, true)) {
1910 recompute_meters (imaginary);
1914 //dump (imaginary, std::cerr);
1920 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1922 TempoSection* prev_t = 0;
1923 TempoSection* section_prev = 0;
1925 section->set_pulse (pulse);
1927 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1929 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1933 if (!t->movable()) {
1940 section_prev = prev_t;
1943 if (t->position_lock_style() == MusicTime) {
1944 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1945 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1947 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1948 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1955 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1956 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1959 if (section->position_lock_style() == AudioTime) {
1960 /* we're setting the pulse */
1961 section->set_position_lock_style (MusicTime);
1962 recompute_tempos (imaginary);
1963 section->set_position_lock_style (AudioTime);
1965 recompute_tempos (imaginary);
1968 if (check_solved (imaginary, false)) {
1969 recompute_meters (imaginary);
1973 MetricSectionSorter cmp;
1974 imaginary.sort (cmp);
1975 if (section->position_lock_style() == AudioTime) {
1976 /* we're setting the pulse */
1977 section->set_position_lock_style (MusicTime);
1978 recompute_tempos (imaginary);
1979 section->set_position_lock_style (AudioTime);
1981 recompute_tempos (imaginary);
1984 if (check_solved (imaginary, false)) {
1985 recompute_meters (imaginary);
1989 MetricSectionFrameSorter fcmp;
1990 imaginary.sort (fcmp);
1991 if (section->position_lock_style() == AudioTime) {
1992 /* we're setting the pulse */
1993 section->set_position_lock_style (MusicTime);
1994 recompute_tempos (imaginary);
1995 section->set_position_lock_style (AudioTime);
1997 recompute_tempos (imaginary);
2000 if (check_solved (imaginary, false)) {
2001 recompute_meters (imaginary);
2005 //dump (imaginary, std::cerr);
2011 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2013 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2014 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2015 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2018 MeterSection* prev_m = 0;
2020 if (!section->movable()) {
2021 /* lock the first tempo to our first meter */
2022 if (!set_active_tempos (imaginary, frame)) {
2025 TempoSection* first_t = &first_tempo();
2027 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2029 new_section->set_frame (frame);
2030 new_section->set_pulse (0.0);
2031 new_section->set_active (true);
2033 if (solve_map (future_map, new_section, frame)) {
2034 first_t->set_frame (frame);
2035 first_t->set_pulse (0.0);
2036 first_t->set_active (true);
2037 solve_map (imaginary, first_t, frame);
2043 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2045 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2048 here we set the beat for this frame.
2049 we set it 'incorrectly' to the next bar's first beat
2050 and use the delta to find the meter's pulse.
2052 double new_pulse = 0.0;
2053 pair<double, BBT_Time> b_bbt;
2055 if (section->movable()) {
2056 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2057 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2058 if (floor_beats + prev_m->beat() < section->beat()) {
2059 /* disallow position change if it will alter out beat
2060 we allow tempo changes to do this in recompute_meters().
2061 blocking this is an option, but i'm not convinced that
2062 this is what the user would actually want.
2066 b_bbt = make_pair (section->beat(), section->bbt());
2067 new_pulse = pulse_at_frame_locked (imaginary, frame);
2069 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2071 section->set_frame (frame);
2072 section->set_beat (b_bbt);
2073 section->set_pulse (new_pulse);
2079 double new_pulse = 0.0;
2080 if (m->position_lock_style() == MusicTime) {
2081 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2082 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2083 if (m->frame() > section->frame()) {
2084 /* moving 'section' will affect later meters' beat (but not bbt).*/
2085 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2086 m->set_beat (new_beat);
2089 pair<double, BBT_Time> b_bbt;
2091 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2092 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2093 b_bbt = make_pair (floor_beats + prev_m->beat()
2094 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2095 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2096 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2097 new_pulse = true_pulse - pulse_off;
2099 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2102 m->set_beat (b_bbt);
2104 m->set_pulse (new_pulse);
2110 MetricSectionFrameSorter fcmp;
2111 imaginary.sort (fcmp);
2112 if (section->position_lock_style() == MusicTime) {
2113 /* we're setting the frame */
2114 section->set_position_lock_style (AudioTime);
2115 recompute_meters (imaginary);
2116 section->set_position_lock_style (MusicTime);
2118 recompute_meters (imaginary);
2120 //dump (imaginary, std::cerr);
2124 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2126 MeterSection* prev_m = 0;
2127 MeterSection* section_prev = 0;
2129 section->set_pulse (pulse);
2131 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2133 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2135 section_prev = prev_m;
2139 double new_pulse = 0.0;
2140 if (m->position_lock_style() == MusicTime) {
2141 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2142 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2144 if (new_pulse > section->pulse()) {
2145 /* moving 'section' will affect later meters' beat (but not bbt).*/
2146 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2147 m->set_beat (new_beat);
2150 pair<double, BBT_Time> b_bbt;
2152 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2153 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2154 b_bbt = make_pair (floor_beats + prev_m->beat()
2155 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2156 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2157 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2158 new_pulse = true_pulse - pulse_off;
2160 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2162 m->set_beat (b_bbt);
2164 m->set_pulse (new_pulse);
2171 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2172 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2173 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2174 section->set_beat (b_bbt);
2175 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2178 MetricSectionSorter cmp;
2179 imaginary.sort (cmp);
2180 if (section->position_lock_style() == AudioTime) {
2181 /* we're setting the pulse */
2182 section->set_position_lock_style (MusicTime);
2183 recompute_meters (imaginary);
2184 section->set_position_lock_style (AudioTime);
2186 recompute_meters (imaginary);
2190 /** places a copy of _metrics into copy and returns a pointer
2191 * to section's equivalent in copy.
2194 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2198 TempoSection* ret = 0;
2200 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2201 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2203 if (t->position_lock_style() == MusicTime) {
2204 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2205 ret->set_frame (t->frame());
2207 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2208 ret->set_pulse (t->pulse());
2210 ret->set_active (t->active());
2211 ret->set_movable (t->movable());
2212 copy.push_back (ret);
2215 TempoSection* cp = 0;
2216 if (t->position_lock_style() == MusicTime) {
2217 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2218 cp->set_frame (t->frame());
2220 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2221 cp->set_pulse (t->pulse());
2223 cp->set_active (t->active());
2224 cp->set_movable (t->movable());
2225 copy.push_back (cp);
2227 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2228 MeterSection* cp = 0;
2229 if (m->position_lock_style() == MusicTime) {
2230 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2231 cp->set_frame (m->frame());
2233 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2234 cp->set_pulse (m->pulse());
2236 cp->set_movable (m->movable());
2237 copy.push_back (cp);
2245 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2248 TempoSection* new_section = 0;
2251 Glib::Threads::RWLock::ReaderLock lm (lock);
2252 new_section = copy_metrics_and_point (copy, ts);
2255 double const beat = bbt_to_beats_locked (copy, bbt);
2256 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2258 Metrics::const_iterator d = copy.begin();
2259 while (d != copy.end()) {
2268 * 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,
2269 * taking any possible reordering as a consequence of this into account.
2270 * @param section - the section to be altered
2271 * @param bpm - the new Tempo
2272 * @param bbt - the bbt where the altered tempo will fall
2273 * @return returns - the position in frames where the new tempo section will lie.
2276 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2278 Glib::Threads::RWLock::ReaderLock lm (lock);
2281 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2283 const double beat = bbt_to_beats_locked (future_map, bbt);
2285 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2286 ret = new_section->frame();
2288 ret = frame_at_beat_locked (_metrics, beat);
2291 Metrics::const_iterator d = future_map.begin();
2292 while (d != future_map.end()) {
2300 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2302 Glib::Threads::RWLock::ReaderLock lm (lock);
2305 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2307 if (solve_map (future_map, new_section, frame)) {
2308 ret = new_section->pulse();
2310 ret = pulse_at_frame_locked (_metrics, frame);
2313 Metrics::const_iterator d = future_map.begin();
2314 while (d != future_map.end()) {
2322 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2326 Glib::Threads::RWLock::WriterLock lm (lock);
2327 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2328 if (solve_map (future_map, new_section, frame)) {
2329 solve_map (_metrics, ts, frame);
2333 Metrics::const_iterator d = future_map.begin();
2334 while (d != future_map.end()) {
2339 MetricPositionChanged (); // Emit Signal
2343 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2347 Glib::Threads::RWLock::WriterLock lm (lock);
2348 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2349 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2350 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2354 Metrics::const_iterator d = future_map.begin();
2355 while (d != future_map.end()) {
2360 MetricPositionChanged (); // Emit Signal
2364 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2367 Glib::Threads::RWLock::WriterLock lm (lock);
2368 solve_map (_metrics, ms, frame);
2371 MetricPositionChanged (); // Emit Signal
2375 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2378 Glib::Threads::RWLock::WriterLock lm (lock);
2379 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2382 MetricPositionChanged (); // Emit Signal
2386 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2389 bool can_solve = false;
2391 Glib::Threads::RWLock::WriterLock lm (lock);
2392 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2393 new_section->set_beats_per_minute (bpm.beats_per_minute());
2394 recompute_tempos (future_map);
2396 if (check_solved (future_map, true)) {
2397 ts->set_beats_per_minute (bpm.beats_per_minute());
2398 recompute_map (_metrics);
2403 Metrics::const_iterator d = future_map.begin();
2404 while (d != future_map.end()) {
2409 MetricPositionChanged (); // Emit Signal
2415 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2417 Glib::Threads::RWLock::ReaderLock lm (lock);
2419 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2420 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2421 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2423 return frame_at_beat_locked (_metrics, total_beats);
2427 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2429 return round_to_type (fr, dir, Bar);
2433 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2435 return round_to_type (fr, dir, Beat);
2439 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2441 Glib::Threads::RWLock::ReaderLock lm (lock);
2442 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2443 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2444 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2446 ticks -= beats * BBT_Time::ticks_per_beat;
2449 /* round to next (or same iff dir == RoundUpMaybe) */
2451 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2453 if (mod == 0 && dir == RoundUpMaybe) {
2454 /* right on the subdivision, which is fine, so do nothing */
2456 } else if (mod == 0) {
2457 /* right on the subdivision, so the difference is just the subdivision ticks */
2458 ticks += ticks_one_subdivisions_worth;
2461 /* not on subdivision, compute distance to next subdivision */
2463 ticks += ticks_one_subdivisions_worth - mod;
2466 if (ticks >= BBT_Time::ticks_per_beat) {
2467 ticks -= BBT_Time::ticks_per_beat;
2469 } else if (dir < 0) {
2471 /* round to previous (or same iff dir == RoundDownMaybe) */
2473 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2475 if (difference == 0 && dir == RoundDownAlways) {
2476 /* right on the subdivision, but force-rounding down,
2477 so the difference is just the subdivision ticks */
2478 difference = ticks_one_subdivisions_worth;
2481 if (ticks < difference) {
2482 ticks = BBT_Time::ticks_per_beat - ticks;
2484 ticks -= difference;
2488 /* round to nearest */
2491 /* compute the distance to the previous and next subdivision */
2493 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2495 /* closer to the next subdivision, so shift forward */
2497 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2499 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2501 if (ticks > BBT_Time::ticks_per_beat) {
2503 ticks -= BBT_Time::ticks_per_beat;
2504 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2507 } else if (rem > 0) {
2509 /* closer to previous subdivision, so shift backward */
2513 /* can't go backwards past zero, so ... */
2516 /* step back to previous beat */
2518 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2519 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2521 ticks = lrint (ticks - rem);
2522 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2525 /* on the subdivision, do nothing */
2529 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2535 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2537 if (sub_num == -1) {
2538 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2539 if ((double) when.beats > bpb / 2.0) {
2545 } else if (sub_num == 0) {
2546 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2547 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2549 while ((double) when.beats > bpb) {
2551 when.beats -= (uint32_t) floor (bpb);
2557 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2559 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2560 /* closer to the next subdivision, so shift forward */
2562 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2564 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2566 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2569 } else if (rem > 0) {
2570 /* closer to previous subdivision, so shift backward */
2572 if (rem > when.ticks) {
2573 if (when.beats == 0) {
2574 /* can't go backwards past zero, so ... */
2576 /* step back to previous beat */
2578 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2580 when.ticks = when.ticks - rem;
2586 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2588 Glib::Threads::RWLock::ReaderLock lm (lock);
2590 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2591 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2596 /* find bar previous to 'frame' */
2599 return frame_time_locked (_metrics, bbt);
2601 } else if (dir > 0) {
2602 /* find bar following 'frame' */
2606 return frame_time_locked (_metrics, bbt);
2608 /* true rounding: find nearest bar */
2609 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2612 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2614 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2616 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2627 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2628 } else if (dir > 0) {
2629 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2631 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2640 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2641 framepos_t lower, framepos_t upper)
2643 Glib::Threads::RWLock::ReaderLock lm (lock);
2644 const int32_t upper_beat = (int32_t) floor (beat_at_frame_locked (_metrics, upper));
2645 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2647 /* although the map handles negative beats, bbt doesn't. */
2651 while (cnt <= upper_beat && pos < upper) {
2652 pos = frame_at_beat_locked (_metrics, cnt);
2653 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2654 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2655 const BBT_Time bbt = beats_to_bbt (cnt);
2656 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2662 TempoMap::tempo_section_at (framepos_t frame) const
2664 Glib::Threads::RWLock::ReaderLock lm (lock);
2665 return tempo_section_at_locked (_metrics, frame);
2669 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2671 Metrics::const_iterator i;
2672 TempoSection* prev = 0;
2674 for (i = metrics.begin(); i != metrics.end(); ++i) {
2677 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2681 if (prev && t->frame() > frame) {
2691 abort(); /*NOTREACHED*/
2698 /* don't use this to calculate length (the tempo is only correct for this frame).
2699 do that stuff based on the beat_at_frame and frame_at_beat api
2702 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2704 Glib::Threads::RWLock::ReaderLock lm (lock);
2706 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
2707 const TempoSection* ts_after = 0;
2708 Metrics::const_iterator i;
2710 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2713 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2717 if ((*i)->frame() > frame) {
2725 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2727 /* must be treated as constant tempo */
2728 return ts_at->frames_per_beat (_frame_rate);
2732 TempoMap::tempo_at (const framepos_t& frame) const
2734 Glib::Threads::RWLock::ReaderLock lm (lock);
2735 return tempo_at_locked (_metrics, frame);
2739 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
2741 TempoSection* prev_t = 0;
2743 Metrics::const_iterator i;
2745 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2747 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2751 if ((prev_t) && t->frame() > frame) {
2752 /* t is the section past frame */
2753 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2754 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2761 const double ret = prev_t->beats_per_minute();
2762 const Tempo ret_tempo (ret, prev_t->note_type ());
2768 TempoMap::meter_section_at (framepos_t frame) const
2770 Glib::Threads::RWLock::ReaderLock lm (lock);
2771 return meter_section_at_locked (_metrics, frame);
2775 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
2777 Metrics::const_iterator i;
2778 MeterSection* prev = 0;
2780 for (i = metrics.begin(); i != metrics.end(); ++i) {
2783 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2785 if (prev && (*i)->frame() > frame) {
2795 abort(); /*NOTREACHED*/
2802 TempoMap::meter_at (framepos_t frame) const
2804 TempoMetric m (metric_at (frame));
2809 TempoMap::meter_section_at (const double& beat) const
2811 MeterSection* prev_m = 0;
2812 Glib::Threads::RWLock::ReaderLock lm (lock);
2814 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2816 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2817 if (prev_m && m->beat() > beat) {
2828 TempoMap::fix_legacy_session ()
2830 MeterSection* prev_m = 0;
2831 TempoSection* prev_t = 0;
2833 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2837 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2838 if (!m->movable()) {
2839 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2843 m->set_position_lock_style (AudioTime);
2848 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2849 + (m->bbt().beats - 1)
2850 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2852 m->set_beat (start);
2853 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2854 + (m->bbt().beats - 1)
2855 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2856 m->set_pulse (start_beat / prev_m->note_divisor());
2859 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2865 if (!t->movable()) {
2868 t->set_position_lock_style (AudioTime);
2874 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2875 + (t->legacy_bbt().beats - 1)
2876 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2878 t->set_pulse (beat / prev_m->note_divisor());
2880 /* really shouldn't happen but.. */
2881 t->set_pulse (beat / 4.0);
2890 TempoMap::get_state ()
2892 Metrics::const_iterator i;
2893 XMLNode *root = new XMLNode ("TempoMap");
2896 Glib::Threads::RWLock::ReaderLock lm (lock);
2897 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2898 root->add_child_nocopy ((*i)->get_state());
2906 TempoMap::set_state (const XMLNode& node, int /*version*/)
2909 Glib::Threads::RWLock::WriterLock lm (lock);
2912 XMLNodeConstIterator niter;
2913 Metrics old_metrics (_metrics);
2916 nlist = node.children();
2918 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2919 XMLNode* child = *niter;
2921 if (child->name() == TempoSection::xml_state_node_name) {
2924 TempoSection* ts = new TempoSection (*child);
2925 _metrics.push_back (ts);
2928 catch (failed_constructor& err){
2929 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2930 _metrics = old_metrics;
2934 } else if (child->name() == MeterSection::xml_state_node_name) {
2937 MeterSection* ms = new MeterSection (*child);
2938 _metrics.push_back (ms);
2941 catch (failed_constructor& err) {
2942 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2943 _metrics = old_metrics;
2949 if (niter == nlist.end()) {
2950 MetricSectionSorter cmp;
2951 _metrics.sort (cmp);
2954 /* check for legacy sessions where bbt was the base musical unit for tempo */
2955 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2957 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2958 if (t->legacy_bbt().bars != 0) {
2959 fix_legacy_session();
2966 /* check for multiple tempo/meters at the same location, which
2967 ardour2 somehow allowed.
2970 Metrics::iterator prev = _metrics.end();
2971 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2972 if (prev != _metrics.end()) {
2974 MeterSection* prev_m;
2976 TempoSection* prev_t;
2977 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2978 if (prev_m->pulse() == ms->pulse()) {
2979 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2980 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2983 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2984 if (prev_t->pulse() == ts->pulse()) {
2985 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2986 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2994 recompute_map (_metrics);
2997 PropertyChanged (PropertyChange ());
3003 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3005 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3006 const MeterSection* m;
3007 const TempoSection* t;
3008 const TempoSection* prev_t = 0;
3010 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3012 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3013 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3014 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3015 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3017 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3018 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;
3021 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3022 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3023 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3026 o << "------" << std::endl;
3030 TempoMap::n_tempos() const
3032 Glib::Threads::RWLock::ReaderLock lm (lock);
3035 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3036 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3045 TempoMap::n_meters() const
3047 Glib::Threads::RWLock::ReaderLock lm (lock);
3050 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3051 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3060 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3063 Glib::Threads::RWLock::WriterLock lm (lock);
3064 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3065 if ((*i)->frame() >= where && (*i)->movable ()) {
3066 (*i)->set_frame ((*i)->frame() + amount);
3070 /* now reset the BBT time of all metrics, based on their new
3071 * audio time. This is the only place where we do this reverse
3075 Metrics::iterator i;
3076 const MeterSection* meter;
3077 const TempoSection* tempo;
3081 meter = &first_meter ();
3082 tempo = &first_tempo ();
3087 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3090 MetricSection* prev = 0;
3092 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3095 //TempoMetric metric (*meter, *tempo);
3096 MeterSection* ms = const_cast<MeterSection*>(meter);
3097 TempoSection* ts = const_cast<TempoSection*>(tempo);
3100 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3104 ts->set_pulse (t->pulse());
3106 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3107 ts->set_pulse (m->pulse());
3109 ts->set_frame (prev->frame());
3113 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3114 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3115 ms->set_beat (start);
3116 ms->set_pulse (m->pulse());
3118 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3122 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3123 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3124 ms->set_beat (start);
3125 ms->set_pulse (t->pulse());
3127 ms->set_frame (prev->frame());
3131 // metric will be at frames=0 bbt=1|1|0 by default
3132 // which is correct for our purpose
3135 // cerr << bbt << endl;
3137 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3141 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3143 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3144 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3145 bbt_time (m->frame(), bbt);
3147 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3153 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3154 /* round up to next beat */
3160 if (bbt.beats != 1) {
3161 /* round up to next bar */
3166 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3167 m->set_beat (start);
3168 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3170 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3172 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3173 abort(); /*NOTREACHED*/
3179 recompute_map (_metrics);
3183 PropertyChanged (PropertyChange ());
3186 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3190 std::list<MetricSection*> metric_kill_list;
3192 TempoSection* last_tempo = NULL;
3193 MeterSection* last_meter = NULL;
3194 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3195 bool meter_after = false; // is there a meter marker likewise?
3197 Glib::Threads::RWLock::WriterLock lm (lock);
3198 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3199 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3200 metric_kill_list.push_back(*i);
3201 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3204 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3208 else if ((*i)->frame() >= where) {
3209 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3210 (*i)->set_frame ((*i)->frame() - amount);
3211 if ((*i)->frame() == where) {
3212 // marker was immediately after end of range
3213 tempo_after = dynamic_cast<TempoSection*> (*i);
3214 meter_after = dynamic_cast<MeterSection*> (*i);
3220 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3221 if (last_tempo && !tempo_after) {
3222 metric_kill_list.remove(last_tempo);
3223 last_tempo->set_frame(where);
3226 if (last_meter && !meter_after) {
3227 metric_kill_list.remove(last_meter);
3228 last_meter->set_frame(where);
3232 //remove all the remaining metrics
3233 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3234 _metrics.remove(*i);
3239 recompute_map (_metrics);
3242 PropertyChanged (PropertyChange ());
3246 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3247 * pos can be -ve, if required.
3250 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3252 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3255 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3257 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3259 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3262 /** Add the BBT interval op to pos and return the result */
3264 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3266 Glib::Threads::RWLock::ReaderLock lm (lock);
3268 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3269 pos_bbt.ticks += op.ticks;
3270 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3272 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3274 pos_bbt.beats += op.beats;
3275 /* the meter in effect will start on the bar */
3276 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3277 while (pos_bbt.beats >= divisions_per_bar + 1) {
3279 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3280 pos_bbt.beats -= divisions_per_bar;
3282 pos_bbt.bars += op.bars;
3284 return frame_time_locked (_metrics, pos_bbt);
3287 /** Count the number of beats that are equivalent to distance when going forward,
3291 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3293 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3297 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3303 operator<< (std::ostream& o, const Meter& m) {
3304 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3308 operator<< (std::ostream& o, const Tempo& t) {
3309 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3313 operator<< (std::ostream& o, const MetricSection& section) {
3315 o << "MetricSection @ " << section.frame() << ' ';
3317 const TempoSection* ts;
3318 const MeterSection* ms;
3320 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3321 o << *((const Tempo*) ts);
3322 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3323 o << *((const Meter*) ms);