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 = floor ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse())
1290 * prev_m->note_divisor() + 0.5);
1292 if (beats + prev_m->beat() < meter->beat()) {
1293 /* tempo change caused a change in beat (bar). */
1294 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1295 b_bbt = make_pair (floor_beats + prev_m->beat()
1296 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1297 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1298 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1299 pulse = true_pulse - pulse_off;
1301 b_bbt = make_pair (meter->beat(), meter->bbt());
1302 pulse = pulse_at_frame_locked (metrics, meter->frame());
1305 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1307 meter->set_beat (b_bbt);
1308 meter->set_pulse (pulse);
1311 pair<double, BBT_Time> new_beat;
1313 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1314 new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1316 /* shouldn't happen - the first is audio-locked */
1317 pulse = pulse_at_beat_locked (metrics, meter->beat());
1318 new_beat = make_pair (pulse, meter->bbt());
1321 meter->set_beat (new_beat);
1322 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1323 meter->set_pulse (pulse);
1329 //dump (_metrics, std::cerr;
1333 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1335 /* CALLER MUST HOLD WRITE LOCK */
1339 /* we will actually stop once we hit
1346 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1349 /* silly call from Session::process() during startup
1354 recompute_tempos (metrics);
1355 recompute_meters (metrics);
1359 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1361 Glib::Threads::RWLock::ReaderLock lm (lock);
1362 TempoMetric m (first_meter(), first_tempo());
1364 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1365 at something, because we insert the default tempo and meter during
1366 TempoMap construction.
1368 now see if we can find better candidates.
1371 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1373 if ((*i)->frame() > frame) {
1387 /* XX meters only */
1389 TempoMap::metric_at (BBT_Time bbt) const
1391 Glib::Threads::RWLock::ReaderLock lm (lock);
1392 TempoMetric m (first_meter(), first_tempo());
1394 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1395 at something, because we insert the default tempo and meter during
1396 TempoMap construction.
1398 now see if we can find better candidates.
1401 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1403 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1404 BBT_Time section_start (mw->bbt());
1406 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1418 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1420 MeterSection* prev_m = 0;
1422 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1424 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1425 if (prev_m && m->beat() > beat) {
1432 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1437 TempoMap::pulse_at_beat (const double& beat) const
1439 Glib::Threads::RWLock::ReaderLock lm (lock);
1440 return pulse_at_beat_locked (_metrics, beat);
1444 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1446 MeterSection* prev_m = 0;
1448 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1450 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1451 if (prev_m && m->pulse() > pulse) {
1452 if ((pulse - prev_m->pulse()) * prev_m->note_divisor() < m->beat()) {
1460 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1462 return beats_in_section + prev_m->beat();
1466 TempoMap::beat_at_pulse (const double& pulse) const
1468 Glib::Threads::RWLock::ReaderLock lm (lock);
1469 return beat_at_pulse_locked (_metrics, pulse);
1472 /* tempo section based */
1474 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1476 /* HOLD (at least) THE READER LOCK */
1477 TempoSection* prev_t = 0;
1479 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1481 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1485 if (prev_t && t->frame() > frame) {
1486 /*the previous ts is the one containing the frame */
1487 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1494 /* treated as constant for this ts */
1495 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1497 return pulses_in_section + prev_t->pulse();
1501 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1503 Glib::Threads::RWLock::ReaderLock lm (lock);
1504 return pulse_at_frame_locked (_metrics, frame);
1507 /* tempo section based */
1509 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1511 /* HOLD THE READER LOCK */
1513 const TempoSection* prev_t = 0;
1515 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1518 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1522 if (prev_t && t->pulse() > pulse) {
1523 return prev_t->frame_at_pulse (pulse, _frame_rate);
1529 /* must be treated as constant, irrespective of _type */
1530 double const pulses_in_section = pulse - prev_t->pulse();
1531 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1533 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1539 TempoMap::frame_at_pulse (const double& pulse) const
1541 Glib::Threads::RWLock::ReaderLock lm (lock);
1542 return frame_at_pulse_locked (_metrics, pulse);
1545 /* meter section based */
1547 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1549 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1550 MeterSection* prev_m = 0;
1551 MeterSection* next_m = 0;
1553 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1555 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1556 if (prev_m && m->frame() > frame) {
1563 if (frame < prev_m->frame()) {
1566 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1568 if (next_m && next_m->beat() < beat) {
1569 return next_m->beat();
1576 TempoMap::beat_at_frame (const framecnt_t& frame) const
1578 Glib::Threads::RWLock::ReaderLock lm (lock);
1579 return beat_at_frame_locked (_metrics, frame);
1582 /* meter section based */
1584 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1586 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1587 MeterSection* prev_m = 0;
1589 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1591 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1592 if (prev_m && m->beat() > beat) {
1599 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1603 TempoMap::frame_at_beat (const double& beat) const
1605 Glib::Threads::RWLock::ReaderLock lm (lock);
1606 return frame_at_beat_locked (_metrics, beat);
1610 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1612 /* CALLER HOLDS READ LOCK */
1614 MeterSection* prev_m = 0;
1616 /* because audio-locked meters have 'fake' integral beats,
1617 there is no pulse offset here.
1619 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1621 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1623 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1624 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1632 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1633 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1634 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1640 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1642 Glib::Threads::RWLock::ReaderLock lm (lock);
1643 return bbt_to_beats_locked (_metrics, bbt);
1647 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1649 /* CALLER HOLDS READ LOCK */
1650 MeterSection* prev_m = 0;
1651 const double beats = (b < 0.0) ? 0.0 : b;
1653 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1654 MeterSection* m = 0;
1656 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1658 if (m->beat() > beats) {
1659 /* this is the meter after the one our beat is on*/
1668 const double beats_in_ms = beats - prev_m->beat();
1669 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1670 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1671 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1672 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1676 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1677 ret.beats = (uint32_t) floor (remaining_beats);
1678 ret.bars = total_bars;
1680 /* 0 0 0 to 1 1 0 - based mapping*/
1684 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1686 ret.ticks -= BBT_Time::ticks_per_beat;
1689 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1698 TempoMap::beats_to_bbt (const double& beats)
1700 Glib::Threads::RWLock::ReaderLock lm (lock);
1701 return beats_to_bbt_locked (_metrics, beats);
1705 TempoMap::pulse_to_bbt (const double& pulse)
1707 Glib::Threads::RWLock::ReaderLock lm (lock);
1708 MeterSection* prev_m = 0;
1710 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1711 MeterSection* m = 0;
1713 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1716 double const pulses_to_m = m->pulse() - prev_m->pulse();
1717 if (prev_m->pulse() + pulses_to_m > pulse) {
1718 /* this is the meter after the one our beat is on*/
1727 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1728 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1729 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1730 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1731 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1735 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1736 ret.beats = (uint32_t) floor (remaining_beats);
1737 ret.bars = total_bars;
1739 /* 0 0 0 to 1 1 0 mapping*/
1743 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1745 ret.ticks -= BBT_Time::ticks_per_beat;
1748 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1757 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1764 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1767 Glib::Threads::RWLock::ReaderLock lm (lock);
1768 const double beat = beat_at_frame_locked (_metrics, frame);
1770 bbt = beats_to_bbt_locked (_metrics, beat);
1773 /* meter section based */
1775 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1777 /* HOLD THE READER LOCK */
1779 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1784 TempoMap::frame_time (const BBT_Time& bbt)
1787 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1791 if (bbt.beats < 1) {
1792 throw std::logic_error ("beats are counted from one");
1794 Glib::Threads::RWLock::ReaderLock lm (lock);
1796 return frame_time_locked (_metrics, bbt);
1800 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1802 TempoSection* prev_t = 0;
1804 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1806 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1811 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1815 if (t->frame() == prev_t->frame()) {
1819 /* precision check ensures pulses and frames align.*/
1820 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1832 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1834 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1836 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1837 if (!t->movable()) {
1838 t->set_active (true);
1841 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1842 t->set_active (false);
1844 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1845 t->set_active (true);
1846 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1855 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1857 TempoSection* prev_t = 0;
1858 TempoSection* section_prev = 0;
1859 framepos_t first_m_frame = 0;
1861 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1863 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1864 if (!m->movable()) {
1865 first_m_frame = m->frame();
1870 if (section->movable() && frame <= first_m_frame) {
1874 section->set_active (true);
1875 section->set_frame (frame);
1877 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1879 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1886 section_prev = prev_t;
1889 if (t->position_lock_style() == MusicTime) {
1890 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1891 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1893 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1894 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1902 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1903 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1906 if (section->position_lock_style() == MusicTime) {
1907 /* we're setting the frame */
1908 section->set_position_lock_style (AudioTime);
1909 recompute_tempos (imaginary);
1910 section->set_position_lock_style (MusicTime);
1912 recompute_tempos (imaginary);
1915 if (check_solved (imaginary, true)) {
1916 recompute_meters (imaginary);
1920 MetricSectionFrameSorter fcmp;
1921 imaginary.sort (fcmp);
1922 if (section->position_lock_style() == MusicTime) {
1923 /* we're setting the frame */
1924 section->set_position_lock_style (AudioTime);
1925 recompute_tempos (imaginary);
1926 section->set_position_lock_style (MusicTime);
1928 recompute_tempos (imaginary);
1931 if (check_solved (imaginary, true)) {
1932 recompute_meters (imaginary);
1936 MetricSectionSorter cmp;
1937 imaginary.sort (cmp);
1938 if (section->position_lock_style() == MusicTime) {
1939 /* we're setting the frame */
1940 section->set_position_lock_style (AudioTime);
1941 recompute_tempos (imaginary);
1942 section->set_position_lock_style (MusicTime);
1944 recompute_tempos (imaginary);
1947 if (check_solved (imaginary, true)) {
1948 recompute_meters (imaginary);
1952 //dump (imaginary, std::cerr);
1958 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1960 TempoSection* prev_t = 0;
1961 TempoSection* section_prev = 0;
1963 section->set_pulse (pulse);
1965 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1967 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1971 if (!t->movable()) {
1978 section_prev = prev_t;
1981 if (t->position_lock_style() == MusicTime) {
1982 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1983 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1985 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1986 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1993 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1994 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1997 if (section->position_lock_style() == AudioTime) {
1998 /* we're setting the pulse */
1999 section->set_position_lock_style (MusicTime);
2000 recompute_tempos (imaginary);
2001 section->set_position_lock_style (AudioTime);
2003 recompute_tempos (imaginary);
2006 if (check_solved (imaginary, false)) {
2007 recompute_meters (imaginary);
2011 MetricSectionSorter cmp;
2012 imaginary.sort (cmp);
2013 if (section->position_lock_style() == AudioTime) {
2014 /* we're setting the pulse */
2015 section->set_position_lock_style (MusicTime);
2016 recompute_tempos (imaginary);
2017 section->set_position_lock_style (AudioTime);
2019 recompute_tempos (imaginary);
2022 if (check_solved (imaginary, false)) {
2023 recompute_meters (imaginary);
2027 MetricSectionFrameSorter fcmp;
2028 imaginary.sort (fcmp);
2029 if (section->position_lock_style() == AudioTime) {
2030 /* we're setting the pulse */
2031 section->set_position_lock_style (MusicTime);
2032 recompute_tempos (imaginary);
2033 section->set_position_lock_style (AudioTime);
2035 recompute_tempos (imaginary);
2038 if (check_solved (imaginary, false)) {
2039 recompute_meters (imaginary);
2043 //dump (imaginary, std::cerr);
2049 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2051 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2052 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2053 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2056 MeterSection* prev_m = 0;
2058 if (!section->movable()) {
2059 /* lock the first tempo to our first meter */
2060 if (!set_active_tempos (imaginary, frame)) {
2063 TempoSection* first_t = &first_tempo();
2065 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2067 new_section->set_frame (frame);
2068 new_section->set_pulse (0.0);
2069 new_section->set_active (true);
2071 if (solve_map (future_map, new_section, frame)) {
2072 first_t->set_frame (frame);
2073 first_t->set_pulse (0.0);
2074 first_t->set_active (true);
2075 solve_map (imaginary, first_t, frame);
2081 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2083 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2086 here we set the beat for this frame.
2087 we set it 'incorrectly' to the next bar's first beat
2088 and use the delta to find the meter's pulse.
2090 double new_pulse = 0.0;
2092 if (section->movable()) {
2093 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2094 if (beats + prev_m->beat() < section->beat()) {
2095 /* disallow position change if it will alter our beat
2096 we allow tempo changes to do this in recompute_meters().
2097 blocking this is an option, but i'm not convinced that
2098 this is what the user would actually want.
2099 here we set the frame/pulse corresponding to its musical position.
2101 new_pulse = ((section->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
2102 section->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2103 section->set_pulse (new_pulse);
2106 new_pulse = pulse_at_frame_locked (imaginary, frame);
2108 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2109 section->set_beat (b_bbt);
2111 section->set_frame (frame);
2112 section->set_pulse (new_pulse);
2118 double new_pulse = 0.0;
2119 if (m->position_lock_style() == MusicTime) {
2120 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2121 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2122 if (m->frame() > section->frame()) {
2123 /* moving 'section' will affect later meters' beat (but not bbt).*/
2124 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2125 m->set_beat (new_beat);
2129 new_pulse = pulse_at_frame_locked (imaginary, m->frame());
2131 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2132 m->set_beat (b_bbt);
2135 m->set_pulse (new_pulse);
2141 MetricSectionFrameSorter fcmp;
2142 imaginary.sort (fcmp);
2143 if (section->position_lock_style() == MusicTime) {
2144 /* we're setting the frame */
2145 section->set_position_lock_style (AudioTime);
2146 recompute_meters (imaginary);
2147 section->set_position_lock_style (MusicTime);
2149 recompute_meters (imaginary);
2151 //dump (imaginary, std::cerr);
2155 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2157 MeterSection* prev_m = 0;
2158 MeterSection* section_prev = 0;
2160 section->set_pulse (pulse);
2162 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2164 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2166 section_prev = prev_m;
2170 double new_pulse = 0.0;
2171 if (m->position_lock_style() == MusicTime) {
2172 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2173 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2175 if (new_pulse > section->pulse()) {
2176 /* moving 'section' will affect later meters' beat (but not bbt).*/
2177 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2178 m->set_beat (new_beat);
2182 new_pulse = pulse_at_frame_locked (imaginary, m->frame());
2184 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2185 m->set_beat (b_bbt);
2188 m->set_pulse (new_pulse);
2195 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2196 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2197 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2198 section->set_beat (b_bbt);
2199 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2202 MetricSectionSorter cmp;
2203 imaginary.sort (cmp);
2204 if (section->position_lock_style() == AudioTime) {
2205 /* we're setting the pulse */
2206 section->set_position_lock_style (MusicTime);
2207 recompute_meters (imaginary);
2208 section->set_position_lock_style (AudioTime);
2210 recompute_meters (imaginary);
2214 /** places a copy of _metrics into copy and returns a pointer
2215 * to section's equivalent in copy.
2218 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2222 TempoSection* ret = 0;
2224 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2225 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2227 if (t->position_lock_style() == MusicTime) {
2228 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2229 ret->set_frame (t->frame());
2231 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2232 ret->set_pulse (t->pulse());
2234 ret->set_active (t->active());
2235 ret->set_movable (t->movable());
2236 copy.push_back (ret);
2239 TempoSection* cp = 0;
2240 if (t->position_lock_style() == MusicTime) {
2241 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2242 cp->set_frame (t->frame());
2244 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2245 cp->set_pulse (t->pulse());
2247 cp->set_active (t->active());
2248 cp->set_movable (t->movable());
2249 copy.push_back (cp);
2251 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2252 MeterSection* cp = 0;
2253 if (m->position_lock_style() == MusicTime) {
2254 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2255 cp->set_frame (m->frame());
2257 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2258 cp->set_pulse (m->pulse());
2260 cp->set_movable (m->movable());
2261 copy.push_back (cp);
2269 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2272 TempoSection* new_section = 0;
2275 Glib::Threads::RWLock::ReaderLock lm (lock);
2276 new_section = copy_metrics_and_point (copy, ts);
2279 double const beat = bbt_to_beats_locked (copy, bbt);
2280 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2282 Metrics::const_iterator d = copy.begin();
2283 while (d != copy.end()) {
2292 * 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,
2293 * taking any possible reordering as a consequence of this into account.
2294 * @param section - the section to be altered
2295 * @param bpm - the new Tempo
2296 * @param bbt - the bbt where the altered tempo will fall
2297 * @return returns - the position in frames where the new tempo section will lie.
2300 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2302 Glib::Threads::RWLock::ReaderLock lm (lock);
2305 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2307 const double beat = bbt_to_beats_locked (future_map, bbt);
2309 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2310 ret = new_section->frame();
2312 ret = frame_at_beat_locked (_metrics, beat);
2315 Metrics::const_iterator d = future_map.begin();
2316 while (d != future_map.end()) {
2324 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2326 Glib::Threads::RWLock::ReaderLock lm (lock);
2329 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2331 if (solve_map (future_map, new_section, frame)) {
2332 ret = new_section->pulse();
2334 ret = pulse_at_frame_locked (_metrics, frame);
2337 Metrics::const_iterator d = future_map.begin();
2338 while (d != future_map.end()) {
2346 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2350 Glib::Threads::RWLock::WriterLock lm (lock);
2351 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2352 if (solve_map (future_map, new_section, frame)) {
2353 solve_map (_metrics, ts, frame);
2357 Metrics::const_iterator d = future_map.begin();
2358 while (d != future_map.end()) {
2363 MetricPositionChanged (); // Emit Signal
2367 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2371 Glib::Threads::RWLock::WriterLock lm (lock);
2372 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2373 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2374 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2378 Metrics::const_iterator d = future_map.begin();
2379 while (d != future_map.end()) {
2384 MetricPositionChanged (); // Emit Signal
2388 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2391 Glib::Threads::RWLock::WriterLock lm (lock);
2392 solve_map (_metrics, ms, frame);
2395 MetricPositionChanged (); // Emit Signal
2399 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2402 Glib::Threads::RWLock::WriterLock lm (lock);
2403 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2406 MetricPositionChanged (); // Emit Signal
2410 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2413 bool can_solve = false;
2415 Glib::Threads::RWLock::WriterLock lm (lock);
2416 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2417 new_section->set_beats_per_minute (bpm.beats_per_minute());
2418 recompute_tempos (future_map);
2420 if (check_solved (future_map, true)) {
2421 ts->set_beats_per_minute (bpm.beats_per_minute());
2422 recompute_map (_metrics);
2427 Metrics::const_iterator d = future_map.begin();
2428 while (d != future_map.end()) {
2433 MetricPositionChanged (); // Emit Signal
2439 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2441 Glib::Threads::RWLock::ReaderLock lm (lock);
2443 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2444 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2445 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2447 return frame_at_beat_locked (_metrics, total_beats);
2451 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2453 return round_to_type (fr, dir, Bar);
2457 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2459 return round_to_type (fr, dir, Beat);
2463 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2465 Glib::Threads::RWLock::ReaderLock lm (lock);
2466 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2467 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2468 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2470 ticks -= beats * BBT_Time::ticks_per_beat;
2473 /* round to next (or same iff dir == RoundUpMaybe) */
2475 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2477 if (mod == 0 && dir == RoundUpMaybe) {
2478 /* right on the subdivision, which is fine, so do nothing */
2480 } else if (mod == 0) {
2481 /* right on the subdivision, so the difference is just the subdivision ticks */
2482 ticks += ticks_one_subdivisions_worth;
2485 /* not on subdivision, compute distance to next subdivision */
2487 ticks += ticks_one_subdivisions_worth - mod;
2490 if (ticks >= BBT_Time::ticks_per_beat) {
2491 ticks -= BBT_Time::ticks_per_beat;
2493 } else if (dir < 0) {
2495 /* round to previous (or same iff dir == RoundDownMaybe) */
2497 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2499 if (difference == 0 && dir == RoundDownAlways) {
2500 /* right on the subdivision, but force-rounding down,
2501 so the difference is just the subdivision ticks */
2502 difference = ticks_one_subdivisions_worth;
2505 if (ticks < difference) {
2506 ticks = BBT_Time::ticks_per_beat - ticks;
2508 ticks -= difference;
2512 /* round to nearest */
2515 /* compute the distance to the previous and next subdivision */
2517 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2519 /* closer to the next subdivision, so shift forward */
2521 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2523 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2525 if (ticks > BBT_Time::ticks_per_beat) {
2527 ticks -= BBT_Time::ticks_per_beat;
2528 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2531 } else if (rem > 0) {
2533 /* closer to previous subdivision, so shift backward */
2537 /* can't go backwards past zero, so ... */
2540 /* step back to previous beat */
2542 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2543 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2545 ticks = lrint (ticks - rem);
2546 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2549 /* on the subdivision, do nothing */
2553 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2559 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2561 if (sub_num == -1) {
2562 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2563 if ((double) when.beats > bpb / 2.0) {
2569 } else if (sub_num == 0) {
2570 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2571 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2573 while ((double) when.beats > bpb) {
2575 when.beats -= (uint32_t) floor (bpb);
2581 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2583 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2584 /* closer to the next subdivision, so shift forward */
2586 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2588 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2590 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2593 } else if (rem > 0) {
2594 /* closer to previous subdivision, so shift backward */
2596 if (rem > when.ticks) {
2597 if (when.beats == 0) {
2598 /* can't go backwards past zero, so ... */
2600 /* step back to previous beat */
2602 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2604 when.ticks = when.ticks - rem;
2610 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2612 Glib::Threads::RWLock::ReaderLock lm (lock);
2614 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2615 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2620 /* find bar previous to 'frame' */
2623 return frame_time_locked (_metrics, bbt);
2625 } else if (dir > 0) {
2626 /* find bar following 'frame' */
2630 return frame_time_locked (_metrics, bbt);
2632 /* true rounding: find nearest bar */
2633 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2636 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2638 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2640 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2651 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2652 } else if (dir > 0) {
2653 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2655 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2664 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2665 framepos_t lower, framepos_t upper)
2667 Glib::Threads::RWLock::ReaderLock lm (lock);
2668 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2670 /* although the map handles negative beats, bbt doesn't. */
2674 while (pos < upper) {
2675 pos = frame_at_beat_locked (_metrics, cnt);
2676 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2677 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2678 const BBT_Time bbt = beats_to_bbt (cnt);
2679 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2685 TempoMap::tempo_section_at (framepos_t frame) const
2687 Glib::Threads::RWLock::ReaderLock lm (lock);
2688 return tempo_section_at_locked (_metrics, frame);
2692 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2694 Metrics::const_iterator i;
2695 TempoSection* prev = 0;
2697 for (i = metrics.begin(); i != metrics.end(); ++i) {
2700 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2704 if (prev && t->frame() > frame) {
2714 abort(); /*NOTREACHED*/
2721 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2723 TempoSection* prev_t = 0;
2724 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2726 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2728 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2729 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2739 /* don't use this to calculate length (the tempo is only correct for this frame).
2740 do that stuff based on the beat_at_frame and frame_at_beat api
2743 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2745 Glib::Threads::RWLock::ReaderLock lm (lock);
2747 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
2748 const TempoSection* ts_after = 0;
2749 Metrics::const_iterator i;
2751 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2754 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2758 if ((*i)->frame() > frame) {
2766 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2768 /* must be treated as constant tempo */
2769 return ts_at->frames_per_beat (_frame_rate);
2773 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
2775 TempoSection* prev_t = 0;
2777 Metrics::const_iterator i;
2779 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2781 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2785 if ((prev_t) && t->frame() > frame) {
2786 /* t is the section past frame */
2787 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2788 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2795 const double ret = prev_t->beats_per_minute();
2796 const Tempo ret_tempo (ret, prev_t->note_type ());
2802 TempoMap::tempo_at (const framepos_t& frame) const
2804 Glib::Threads::RWLock::ReaderLock lm (lock);
2805 return tempo_at_locked (_metrics, frame);
2809 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
2811 Metrics::const_iterator i;
2812 MeterSection* prev = 0;
2814 for (i = metrics.begin(); i != metrics.end(); ++i) {
2817 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2819 if (prev && (*i)->frame() > frame) {
2829 abort(); /*NOTREACHED*/
2837 TempoMap::meter_section_at (framepos_t frame) const
2839 Glib::Threads::RWLock::ReaderLock lm (lock);
2840 return meter_section_at_locked (_metrics, frame);
2844 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2846 MeterSection* prev_m = 0;
2848 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2850 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2851 if (prev_m && m->beat() > beat) {
2862 TempoMap::meter_section_at_beat (double beat) const
2864 Glib::Threads::RWLock::ReaderLock lm (lock);
2865 return meter_section_at_beat_locked (_metrics, beat);
2869 TempoMap::meter_at (framepos_t frame) const
2871 TempoMetric m (metric_at (frame));
2876 TempoMap::fix_legacy_session ()
2878 MeterSection* prev_m = 0;
2879 TempoSection* prev_t = 0;
2881 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2885 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2886 if (!m->movable()) {
2887 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2891 m->set_position_lock_style (AudioTime);
2896 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2897 + (m->bbt().beats - 1)
2898 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2900 m->set_beat (start);
2901 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2902 + (m->bbt().beats - 1)
2903 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2904 m->set_pulse (start_beat / prev_m->note_divisor());
2907 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2913 if (!t->movable()) {
2916 t->set_position_lock_style (AudioTime);
2922 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2923 + (t->legacy_bbt().beats - 1)
2924 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2926 t->set_pulse (beat / prev_m->note_divisor());
2928 /* really shouldn't happen but.. */
2929 t->set_pulse (beat / 4.0);
2938 TempoMap::get_state ()
2940 Metrics::const_iterator i;
2941 XMLNode *root = new XMLNode ("TempoMap");
2944 Glib::Threads::RWLock::ReaderLock lm (lock);
2945 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2946 root->add_child_nocopy ((*i)->get_state());
2954 TempoMap::set_state (const XMLNode& node, int /*version*/)
2957 Glib::Threads::RWLock::WriterLock lm (lock);
2960 XMLNodeConstIterator niter;
2961 Metrics old_metrics (_metrics);
2964 nlist = node.children();
2966 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2967 XMLNode* child = *niter;
2969 if (child->name() == TempoSection::xml_state_node_name) {
2972 TempoSection* ts = new TempoSection (*child);
2973 _metrics.push_back (ts);
2976 catch (failed_constructor& err){
2977 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2978 _metrics = old_metrics;
2982 } else if (child->name() == MeterSection::xml_state_node_name) {
2985 MeterSection* ms = new MeterSection (*child);
2986 _metrics.push_back (ms);
2989 catch (failed_constructor& err) {
2990 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2991 _metrics = old_metrics;
2997 if (niter == nlist.end()) {
2998 MetricSectionSorter cmp;
2999 _metrics.sort (cmp);
3002 /* check for legacy sessions where bbt was the base musical unit for tempo */
3003 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3005 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3006 if (t->legacy_bbt().bars != 0) {
3007 fix_legacy_session();
3014 /* check for multiple tempo/meters at the same location, which
3015 ardour2 somehow allowed.
3018 Metrics::iterator prev = _metrics.end();
3019 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3020 if (prev != _metrics.end()) {
3022 MeterSection* prev_m;
3024 TempoSection* prev_t;
3025 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3026 if (prev_m->pulse() == ms->pulse()) {
3027 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3028 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3031 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3032 if (prev_t->pulse() == ts->pulse()) {
3033 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3034 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3042 recompute_map (_metrics);
3045 PropertyChanged (PropertyChange ());
3051 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3053 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3054 const MeterSection* m;
3055 const TempoSection* t;
3056 const TempoSection* prev_t = 0;
3058 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3060 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3061 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3062 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3063 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3065 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3066 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;
3069 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3070 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3071 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3074 o << "------" << std::endl;
3078 TempoMap::n_tempos() const
3080 Glib::Threads::RWLock::ReaderLock lm (lock);
3083 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3084 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3093 TempoMap::n_meters() const
3095 Glib::Threads::RWLock::ReaderLock lm (lock);
3098 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3099 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3108 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3111 Glib::Threads::RWLock::WriterLock lm (lock);
3112 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3113 if ((*i)->frame() >= where && (*i)->movable ()) {
3114 (*i)->set_frame ((*i)->frame() + amount);
3118 /* now reset the BBT time of all metrics, based on their new
3119 * audio time. This is the only place where we do this reverse
3123 Metrics::iterator i;
3124 const MeterSection* meter;
3125 const TempoSection* tempo;
3129 meter = &first_meter ();
3130 tempo = &first_tempo ();
3135 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3138 MetricSection* prev = 0;
3140 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3143 //TempoMetric metric (*meter, *tempo);
3144 MeterSection* ms = const_cast<MeterSection*>(meter);
3145 TempoSection* ts = const_cast<TempoSection*>(tempo);
3148 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3152 ts->set_pulse (t->pulse());
3154 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3155 ts->set_pulse (m->pulse());
3157 ts->set_frame (prev->frame());
3161 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3162 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3163 ms->set_beat (start);
3164 ms->set_pulse (m->pulse());
3166 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3170 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3171 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3172 ms->set_beat (start);
3173 ms->set_pulse (t->pulse());
3175 ms->set_frame (prev->frame());
3179 // metric will be at frames=0 bbt=1|1|0 by default
3180 // which is correct for our purpose
3183 // cerr << bbt << endl;
3185 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3189 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3191 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3192 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3193 bbt_time (m->frame(), bbt);
3195 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3201 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3202 /* round up to next beat */
3208 if (bbt.beats != 1) {
3209 /* round up to next bar */
3214 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3215 m->set_beat (start);
3216 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3218 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3220 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3221 abort(); /*NOTREACHED*/
3227 recompute_map (_metrics);
3231 PropertyChanged (PropertyChange ());
3234 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3238 std::list<MetricSection*> metric_kill_list;
3240 TempoSection* last_tempo = NULL;
3241 MeterSection* last_meter = NULL;
3242 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3243 bool meter_after = false; // is there a meter marker likewise?
3245 Glib::Threads::RWLock::WriterLock lm (lock);
3246 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3247 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3248 metric_kill_list.push_back(*i);
3249 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3252 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3256 else if ((*i)->frame() >= where) {
3257 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3258 (*i)->set_frame ((*i)->frame() - amount);
3259 if ((*i)->frame() == where) {
3260 // marker was immediately after end of range
3261 tempo_after = dynamic_cast<TempoSection*> (*i);
3262 meter_after = dynamic_cast<MeterSection*> (*i);
3268 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3269 if (last_tempo && !tempo_after) {
3270 metric_kill_list.remove(last_tempo);
3271 last_tempo->set_frame(where);
3274 if (last_meter && !meter_after) {
3275 metric_kill_list.remove(last_meter);
3276 last_meter->set_frame(where);
3280 //remove all the remaining metrics
3281 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3282 _metrics.remove(*i);
3287 recompute_map (_metrics);
3290 PropertyChanged (PropertyChange ());
3294 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3295 * pos can be -ve, if required.
3298 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3300 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3303 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3305 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3307 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3310 /** Add the BBT interval op to pos and return the result */
3312 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3314 Glib::Threads::RWLock::ReaderLock lm (lock);
3316 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3317 pos_bbt.ticks += op.ticks;
3318 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3320 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3322 pos_bbt.beats += op.beats;
3323 /* the meter in effect will start on the bar */
3324 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();
3325 while (pos_bbt.beats >= divisions_per_bar + 1) {
3327 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3328 pos_bbt.beats -= divisions_per_bar;
3330 pos_bbt.bars += op.bars;
3332 return frame_time_locked (_metrics, pos_bbt);
3335 /** Count the number of beats that are equivalent to distance when going forward,
3339 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3341 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3345 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3351 operator<< (std::ostream& o, const Meter& m) {
3352 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3356 operator<< (std::ostream& o, const Tempo& t) {
3357 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3361 operator<< (std::ostream& o, const MetricSection& section) {
3363 o << "MetricSection @ " << section.frame() << ' ';
3365 const TempoSection* ts;
3366 const MeterSection* ms;
3368 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3369 o << *((const Tempo*) ts);
3370 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3371 o << *((const Meter*) ms);