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 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1292 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1293 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1294 pulse = true_pulse - pulse_off;
1296 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1298 meter->set_beat (b_bbt);
1299 meter->set_pulse (pulse);
1302 pair<double, BBT_Time> new_beat;
1304 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1305 new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1307 /* shouldn't happen - the first is audio-locked */
1308 pulse = pulse_at_beat_locked (metrics, meter->beat());
1309 new_beat = make_pair (pulse, meter->bbt());
1312 meter->set_beat (new_beat);
1313 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1314 meter->set_pulse (pulse);
1320 //dump (_metrics, std::cerr;
1324 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1326 /* CALLER MUST HOLD WRITE LOCK */
1330 /* we will actually stop once we hit
1337 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1340 /* silly call from Session::process() during startup
1345 recompute_tempos (metrics);
1346 recompute_meters (metrics);
1350 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1352 Glib::Threads::RWLock::ReaderLock lm (lock);
1353 TempoMetric m (first_meter(), first_tempo());
1355 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1356 at something, because we insert the default tempo and meter during
1357 TempoMap construction.
1359 now see if we can find better candidates.
1362 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1364 if ((*i)->frame() > frame) {
1378 /* XX meters only */
1380 TempoMap::metric_at (BBT_Time bbt) const
1382 Glib::Threads::RWLock::ReaderLock lm (lock);
1383 TempoMetric m (first_meter(), first_tempo());
1385 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1386 at something, because we insert the default tempo and meter during
1387 TempoMap construction.
1389 now see if we can find better candidates.
1392 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1394 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1395 BBT_Time section_start (mw->bbt());
1397 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1409 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1411 MeterSection* prev_m = 0;
1413 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1415 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1416 if (prev_m && m->beat() > beat) {
1423 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1428 TempoMap::pulse_at_beat (const double& beat) const
1430 Glib::Threads::RWLock::ReaderLock lm (lock);
1431 return pulse_at_beat_locked (_metrics, beat);
1435 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1437 MeterSection* prev_m = 0;
1439 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1441 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1442 if (prev_m && m->pulse() > pulse) {
1449 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1451 return beats_in_section + prev_m->beat();
1455 TempoMap::beat_at_pulse (const double& pulse) const
1457 Glib::Threads::RWLock::ReaderLock lm (lock);
1458 return beat_at_pulse_locked (_metrics, pulse);
1462 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1464 /* HOLD (at least) THE READER LOCK */
1465 TempoSection* prev_t = 0;
1467 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1469 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1473 if (prev_t && t->frame() > frame) {
1474 /*the previous ts is the one containing the frame */
1475 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1482 /* treated as constant for this ts */
1483 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1485 return pulses_in_section + prev_t->pulse();
1489 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1491 Glib::Threads::RWLock::ReaderLock lm (lock);
1492 return pulse_at_frame_locked (_metrics, frame);
1496 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1498 /* HOLD THE READER LOCK */
1500 const TempoSection* prev_t = 0;
1502 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1505 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1509 if (prev_t && t->pulse() > pulse) {
1510 return prev_t->frame_at_pulse (pulse, _frame_rate);
1516 /* must be treated as constant, irrespective of _type */
1517 double const pulses_in_section = pulse - prev_t->pulse();
1518 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1520 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1526 TempoMap::frame_at_pulse (const double& pulse) const
1528 Glib::Threads::RWLock::ReaderLock lm (lock);
1529 return frame_at_pulse_locked (_metrics, pulse);
1533 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1535 const double pulse = pulse_at_frame_locked (metrics, frame);
1536 return beat_at_pulse_locked (metrics, pulse);
1540 TempoMap::beat_at_frame (const framecnt_t& frame) const
1542 Glib::Threads::RWLock::ReaderLock lm (lock);
1543 return beat_at_frame_locked (_metrics, frame);
1547 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1549 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1554 TempoMap::frame_at_beat (const double& beat) const
1556 Glib::Threads::RWLock::ReaderLock lm (lock);
1557 return frame_at_beat_locked (_metrics, beat);
1561 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1563 /* CALLER HOLDS READ LOCK */
1565 MeterSection* prev_m = 0;
1567 /* because audio-locked meters have 'fake' integral beats,
1568 there is no pulse offset here.
1570 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1572 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1574 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1575 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1583 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1584 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1585 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1591 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1593 Glib::Threads::RWLock::ReaderLock lm (lock);
1594 return bbt_to_beats_locked (_metrics, bbt);
1598 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1600 /* CALLER HOLDS READ LOCK */
1601 MeterSection* prev_m = 0;
1602 const double beats = (b < 0.0) ? 0.0 : b;
1604 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1605 MeterSection* m = 0;
1607 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1609 if (m->beat() > beats) {
1610 /* this is the meter after the one our beat is on*/
1619 const double beats_in_ms = beats - prev_m->beat();
1620 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1621 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1622 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1623 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1627 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1628 ret.beats = (uint32_t) floor (remaining_beats);
1629 ret.bars = total_bars;
1631 /* 0 0 0 to 1 1 0 - based mapping*/
1635 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1637 ret.ticks -= BBT_Time::ticks_per_beat;
1640 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1649 TempoMap::beats_to_bbt (const double& beats)
1651 Glib::Threads::RWLock::ReaderLock lm (lock);
1652 return beats_to_bbt_locked (_metrics, beats);
1656 TempoMap::pulse_to_bbt (const double& pulse)
1658 Glib::Threads::RWLock::ReaderLock lm (lock);
1659 MeterSection* prev_m = 0;
1661 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1662 MeterSection* m = 0;
1664 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1667 double const pulses_to_m = m->pulse() - prev_m->pulse();
1668 if (prev_m->pulse() + pulses_to_m > pulse) {
1669 /* this is the meter after the one our beat is on*/
1678 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1679 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1680 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1681 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1682 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1686 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1687 ret.beats = (uint32_t) floor (remaining_beats);
1688 ret.bars = total_bars;
1690 /* 0 0 0 to 1 1 0 mapping*/
1694 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1696 ret.ticks -= BBT_Time::ticks_per_beat;
1699 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1708 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1715 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1718 Glib::Threads::RWLock::ReaderLock lm (lock);
1719 const double beat = beat_at_frame_locked (_metrics, frame);
1721 bbt = beats_to_bbt_locked (_metrics, beat);
1725 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1727 /* HOLD THE READER LOCK */
1729 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1734 TempoMap::frame_time (const BBT_Time& bbt)
1737 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1741 if (bbt.beats < 1) {
1742 throw std::logic_error ("beats are counted from one");
1744 Glib::Threads::RWLock::ReaderLock lm (lock);
1746 return frame_time_locked (_metrics, bbt);
1750 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1752 TempoSection* prev_t = 0;
1754 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1756 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1761 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1765 if (t->frame() == prev_t->frame()) {
1769 /* precision check ensures pulses and frames align.*/
1770 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1782 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1784 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1786 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1787 if (!t->movable()) {
1788 t->set_active (true);
1791 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1792 t->set_active (false);
1794 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1795 t->set_active (true);
1796 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1805 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1807 TempoSection* prev_t = 0;
1808 TempoSection* section_prev = 0;
1809 framepos_t first_m_frame = 0;
1811 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1813 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1814 if (!m->movable()) {
1815 first_m_frame = m->frame();
1820 if (section->movable() && frame <= first_m_frame) {
1824 section->set_active (true);
1825 section->set_frame (frame);
1827 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1829 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1836 section_prev = prev_t;
1839 if (t->position_lock_style() == MusicTime) {
1840 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1841 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1843 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1844 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1852 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1853 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1856 if (section->position_lock_style() == MusicTime) {
1857 /* we're setting the frame */
1858 section->set_position_lock_style (AudioTime);
1859 recompute_tempos (imaginary);
1860 section->set_position_lock_style (MusicTime);
1862 recompute_tempos (imaginary);
1865 if (check_solved (imaginary, true)) {
1866 recompute_meters (imaginary);
1870 MetricSectionFrameSorter fcmp;
1871 imaginary.sort (fcmp);
1872 if (section->position_lock_style() == MusicTime) {
1873 /* we're setting the frame */
1874 section->set_position_lock_style (AudioTime);
1875 recompute_tempos (imaginary);
1876 section->set_position_lock_style (MusicTime);
1878 recompute_tempos (imaginary);
1881 if (check_solved (imaginary, true)) {
1882 recompute_meters (imaginary);
1886 MetricSectionSorter cmp;
1887 imaginary.sort (cmp);
1888 if (section->position_lock_style() == MusicTime) {
1889 /* we're setting the frame */
1890 section->set_position_lock_style (AudioTime);
1891 recompute_tempos (imaginary);
1892 section->set_position_lock_style (MusicTime);
1894 recompute_tempos (imaginary);
1897 if (check_solved (imaginary, true)) {
1898 recompute_meters (imaginary);
1902 //dump (imaginary, std::cerr);
1908 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1910 TempoSection* prev_t = 0;
1911 TempoSection* section_prev = 0;
1913 section->set_pulse (pulse);
1915 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1917 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1921 if (!t->movable()) {
1928 section_prev = prev_t;
1931 if (t->position_lock_style() == MusicTime) {
1932 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1933 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1935 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1936 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1943 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1944 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1947 if (section->position_lock_style() == AudioTime) {
1948 /* we're setting the pulse */
1949 section->set_position_lock_style (MusicTime);
1950 recompute_tempos (imaginary);
1951 section->set_position_lock_style (AudioTime);
1953 recompute_tempos (imaginary);
1956 if (check_solved (imaginary, false)) {
1957 recompute_meters (imaginary);
1961 MetricSectionSorter cmp;
1962 imaginary.sort (cmp);
1963 if (section->position_lock_style() == AudioTime) {
1964 /* we're setting the pulse */
1965 section->set_position_lock_style (MusicTime);
1966 recompute_tempos (imaginary);
1967 section->set_position_lock_style (AudioTime);
1969 recompute_tempos (imaginary);
1972 if (check_solved (imaginary, false)) {
1973 recompute_meters (imaginary);
1977 MetricSectionFrameSorter fcmp;
1978 imaginary.sort (fcmp);
1979 if (section->position_lock_style() == AudioTime) {
1980 /* we're setting the pulse */
1981 section->set_position_lock_style (MusicTime);
1982 recompute_tempos (imaginary);
1983 section->set_position_lock_style (AudioTime);
1985 recompute_tempos (imaginary);
1988 if (check_solved (imaginary, false)) {
1989 recompute_meters (imaginary);
1993 //dump (imaginary, std::cerr);
1999 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2001 MeterSection* prev_m = 0;
2003 if (!section->movable()) {
2004 /* lock the first tempo to our first meter */
2005 if (!set_active_tempos (imaginary, frame)) {
2008 TempoSection* first_t = &first_tempo();
2010 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2012 new_section->set_frame (frame);
2013 new_section->set_pulse (0.0);
2014 new_section->set_active (true);
2016 if (solve_map (future_map, new_section, frame)) {
2017 first_t->set_frame (frame);
2018 first_t->set_pulse (0.0);
2019 first_t->set_active (true);
2020 solve_map (imaginary, first_t, frame);
2026 section->set_frame (frame);
2028 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2030 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2033 here we set the beat for this frame.
2034 we set it 'incorrectly' to the next bar's first beat
2035 and use the delat to find the meter's pulse.
2037 double new_pulse = 0.0;
2038 pair<double, BBT_Time> b_bbt;
2040 if (section->movable()) {
2041 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2042 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2043 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2044 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2045 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2046 new_pulse = true_pulse - pulse_off;
2048 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2051 section->set_beat (b_bbt);
2052 section->set_pulse (new_pulse);
2058 double new_pulse = 0.0;
2059 if (m->position_lock_style() == MusicTime) {
2060 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2061 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2062 if (m->frame() > section->frame()) {
2063 /* moving 'section' will affect later meters' beat (but not bbt).*/
2064 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2065 m->set_beat (new_beat);
2068 pair<double, BBT_Time> b_bbt;
2070 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2071 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2072 b_bbt = make_pair (floor_beats + prev_m->beat()
2073 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2074 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2075 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2076 new_pulse = true_pulse - pulse_off;
2078 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2081 m->set_beat (b_bbt);
2083 m->set_pulse (new_pulse);
2089 MetricSectionFrameSorter fcmp;
2090 imaginary.sort (fcmp);
2091 if (section->position_lock_style() == MusicTime) {
2092 /* we're setting the frame */
2093 section->set_position_lock_style (AudioTime);
2094 recompute_meters (imaginary);
2095 section->set_position_lock_style (MusicTime);
2097 recompute_meters (imaginary);
2099 //dump (imaginary, std::cerr);
2103 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2105 MeterSection* prev_m = 0;
2106 MeterSection* section_prev = 0;
2108 section->set_pulse (pulse);
2110 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2112 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2114 section_prev = prev_m;
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));
2123 if (new_pulse > section->pulse()) {
2124 /* moving 'section' will affect later meters' beat (but not bbt).*/
2125 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2126 m->set_beat (new_beat);
2129 pair<double, BBT_Time> b_bbt;
2131 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2132 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2133 b_bbt = make_pair (floor_beats + prev_m->beat()
2134 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2135 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2136 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2137 new_pulse = true_pulse - pulse_off;
2139 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2141 m->set_beat (b_bbt);
2143 m->set_pulse (new_pulse);
2150 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2151 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2152 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2153 section->set_beat (b_bbt);
2154 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2157 MetricSectionSorter cmp;
2158 imaginary.sort (cmp);
2159 if (section->position_lock_style() == AudioTime) {
2160 /* we're setting the pulse */
2161 section->set_position_lock_style (MusicTime);
2162 recompute_meters (imaginary);
2163 section->set_position_lock_style (AudioTime);
2165 recompute_meters (imaginary);
2169 /** places a copy of _metrics into copy and returns a pointer
2170 * to section's equivalent in copy.
2173 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2177 TempoSection* ret = 0;
2179 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2180 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2182 if (t->position_lock_style() == MusicTime) {
2183 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2184 ret->set_frame (t->frame());
2186 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2187 ret->set_pulse (t->pulse());
2189 ret->set_active (t->active());
2190 ret->set_movable (t->movable());
2191 copy.push_back (ret);
2194 TempoSection* cp = 0;
2195 if (t->position_lock_style() == MusicTime) {
2196 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2197 cp->set_frame (t->frame());
2199 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2200 cp->set_pulse (t->pulse());
2202 cp->set_active (t->active());
2203 cp->set_movable (t->movable());
2204 copy.push_back (cp);
2206 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2207 MeterSection* cp = 0;
2208 if (m->position_lock_style() == MusicTime) {
2209 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2210 cp->set_frame (m->frame());
2212 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2213 cp->set_pulse (m->pulse());
2215 cp->set_movable (m->movable());
2216 copy.push_back (cp);
2224 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2227 TempoSection* new_section = 0;
2230 Glib::Threads::RWLock::ReaderLock lm (lock);
2231 new_section = copy_metrics_and_point (copy, ts);
2234 double const beat = bbt_to_beats_locked (copy, bbt);
2235 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2237 Metrics::const_iterator d = copy.begin();
2238 while (d != copy.end()) {
2247 * 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,
2248 * taking any possible reordering as a consequence of this into account.
2249 * @param section - the section to be altered
2250 * @param bpm - the new Tempo
2251 * @param bbt - the bbt where the altered tempo will fall
2252 * @return returns - the position in frames where the new tempo section will lie.
2255 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2257 Glib::Threads::RWLock::ReaderLock lm (lock);
2260 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2262 const double beat = bbt_to_beats_locked (future_map, bbt);
2264 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2265 ret = new_section->frame();
2267 ret = frame_at_beat_locked (_metrics, beat);
2270 Metrics::const_iterator d = future_map.begin();
2271 while (d != future_map.end()) {
2279 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2281 Glib::Threads::RWLock::ReaderLock lm (lock);
2284 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2286 if (solve_map (future_map, new_section, frame)) {
2287 ret = new_section->pulse();
2289 ret = pulse_at_frame_locked (_metrics, frame);
2292 Metrics::const_iterator d = future_map.begin();
2293 while (d != future_map.end()) {
2301 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2305 Glib::Threads::RWLock::WriterLock lm (lock);
2306 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2307 if (solve_map (future_map, new_section, frame)) {
2308 solve_map (_metrics, ts, frame);
2312 Metrics::const_iterator d = future_map.begin();
2313 while (d != future_map.end()) {
2318 MetricPositionChanged (); // Emit Signal
2322 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
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, pulse_at_beat_locked (future_map, beat))) {
2329 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2333 Metrics::const_iterator d = future_map.begin();
2334 while (d != future_map.end()) {
2339 MetricPositionChanged (); // Emit Signal
2343 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2346 Glib::Threads::RWLock::WriterLock lm (lock);
2347 solve_map (_metrics, ms, frame);
2350 MetricPositionChanged (); // Emit Signal
2354 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2357 Glib::Threads::RWLock::WriterLock lm (lock);
2358 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2361 MetricPositionChanged (); // Emit Signal
2365 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2368 bool can_solve = false;
2370 Glib::Threads::RWLock::WriterLock lm (lock);
2371 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2372 new_section->set_beats_per_minute (bpm.beats_per_minute());
2373 recompute_tempos (future_map);
2375 if (check_solved (future_map, true)) {
2376 ts->set_beats_per_minute (bpm.beats_per_minute());
2377 recompute_map (_metrics);
2382 Metrics::const_iterator d = future_map.begin();
2383 while (d != future_map.end()) {
2388 MetricPositionChanged (); // Emit Signal
2394 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2396 Glib::Threads::RWLock::ReaderLock lm (lock);
2398 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2399 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2400 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2402 return frame_at_beat_locked (_metrics, total_beats);
2406 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2408 return round_to_type (fr, dir, Bar);
2412 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2414 return round_to_type (fr, dir, Beat);
2418 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2420 Glib::Threads::RWLock::ReaderLock lm (lock);
2421 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2422 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2423 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2425 ticks -= beats * BBT_Time::ticks_per_beat;
2428 /* round to next (or same iff dir == RoundUpMaybe) */
2430 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2432 if (mod == 0 && dir == RoundUpMaybe) {
2433 /* right on the subdivision, which is fine, so do nothing */
2435 } else if (mod == 0) {
2436 /* right on the subdivision, so the difference is just the subdivision ticks */
2437 ticks += ticks_one_subdivisions_worth;
2440 /* not on subdivision, compute distance to next subdivision */
2442 ticks += ticks_one_subdivisions_worth - mod;
2445 if (ticks >= BBT_Time::ticks_per_beat) {
2446 ticks -= BBT_Time::ticks_per_beat;
2448 } else if (dir < 0) {
2450 /* round to previous (or same iff dir == RoundDownMaybe) */
2452 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2454 if (difference == 0 && dir == RoundDownAlways) {
2455 /* right on the subdivision, but force-rounding down,
2456 so the difference is just the subdivision ticks */
2457 difference = ticks_one_subdivisions_worth;
2460 if (ticks < difference) {
2461 ticks = BBT_Time::ticks_per_beat - ticks;
2463 ticks -= difference;
2467 /* round to nearest */
2470 /* compute the distance to the previous and next subdivision */
2472 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2474 /* closer to the next subdivision, so shift forward */
2476 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2478 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2480 if (ticks > BBT_Time::ticks_per_beat) {
2482 ticks -= BBT_Time::ticks_per_beat;
2483 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2486 } else if (rem > 0) {
2488 /* closer to previous subdivision, so shift backward */
2492 /* can't go backwards past zero, so ... */
2495 /* step back to previous beat */
2497 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2498 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2500 ticks = lrint (ticks - rem);
2501 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2504 /* on the subdivision, do nothing */
2508 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2514 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2516 if (sub_num == -1) {
2517 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2518 if ((double) when.beats > bpb / 2.0) {
2524 } else if (sub_num == 0) {
2525 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2526 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2528 while ((double) when.beats > bpb) {
2530 when.beats -= (uint32_t) floor (bpb);
2536 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2538 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2539 /* closer to the next subdivision, so shift forward */
2541 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2543 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2545 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2548 } else if (rem > 0) {
2549 /* closer to previous subdivision, so shift backward */
2551 if (rem > when.ticks) {
2552 if (when.beats == 0) {
2553 /* can't go backwards past zero, so ... */
2555 /* step back to previous beat */
2557 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2559 when.ticks = when.ticks - rem;
2565 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2567 Glib::Threads::RWLock::ReaderLock lm (lock);
2569 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2570 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2575 /* find bar previous to 'frame' */
2578 return frame_time_locked (_metrics, bbt);
2580 } else if (dir > 0) {
2581 /* find bar following 'frame' */
2585 return frame_time_locked (_metrics, bbt);
2587 /* true rounding: find nearest bar */
2588 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2591 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2593 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2595 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2606 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2607 } else if (dir > 0) {
2608 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2610 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2619 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2620 framepos_t lower, framepos_t upper)
2622 Glib::Threads::RWLock::ReaderLock lm (lock);
2623 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2624 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2625 /* although the map handles negative beats, bbt doesn't. */
2629 while (cnt <= upper_beat) {
2630 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2631 const TempoSection tempo = tempo_section_at_locked (pos);
2632 const MeterSection meter = meter_section_at_locked (pos);
2633 const BBT_Time bbt = beats_to_bbt (cnt);
2634 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2640 TempoMap::tempo_section_at (framepos_t frame) const
2642 Glib::Threads::RWLock::ReaderLock lm (lock);
2643 return tempo_section_at_locked (frame);
2647 TempoMap::tempo_section_at_locked (framepos_t frame) const
2649 Metrics::const_iterator i;
2650 TempoSection* prev = 0;
2652 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2655 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2659 if (t->frame() > frame) {
2669 abort(); /*NOTREACHED*/
2676 /* don't use this to calculate length (the tempo is only correct for this frame).
2677 do that stuff based on the beat_at_frame and frame_at_beat api
2680 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2682 Glib::Threads::RWLock::ReaderLock lm (lock);
2684 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2685 const TempoSection* ts_after = 0;
2686 Metrics::const_iterator i;
2688 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2691 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2695 if ((*i)->frame() > frame) {
2703 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2705 /* must be treated as constant tempo */
2706 return ts_at->frames_per_beat (_frame_rate);
2710 TempoMap::tempo_at (const framepos_t& frame) const
2712 Glib::Threads::RWLock::ReaderLock lm (lock);
2713 return tempo_at_locked (frame);
2717 TempoMap::tempo_at_locked (const framepos_t& frame) const
2719 TempoSection* prev_t = 0;
2721 Metrics::const_iterator i;
2723 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2725 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2729 if ((prev_t) && t->frame() > frame) {
2730 /* t is the section past frame */
2731 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2732 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2739 const double ret = prev_t->beats_per_minute();
2740 const Tempo ret_tempo (ret, prev_t->note_type ());
2746 TempoMap::meter_section_at (framepos_t frame) const
2748 Glib::Threads::RWLock::ReaderLock lm (lock);
2749 return meter_section_at_locked (frame);
2753 TempoMap::meter_section_at_locked (framepos_t frame) const
2755 Metrics::const_iterator i;
2756 MeterSection* prev = 0;
2758 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2761 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2763 if (prev && (*i)->frame() > frame) {
2773 abort(); /*NOTREACHED*/
2780 TempoMap::meter_at (framepos_t frame) const
2782 TempoMetric m (metric_at (frame));
2787 TempoMap::meter_section_at (const double& beat) const
2789 MeterSection* prev_m = 0;
2790 Glib::Threads::RWLock::ReaderLock lm (lock);
2792 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2794 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2795 if (prev_m && m->beat() > beat) {
2806 TempoMap::fix_legacy_session ()
2808 MeterSection* prev_m = 0;
2809 TempoSection* prev_t = 0;
2811 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2815 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2816 if (!m->movable()) {
2817 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2821 m->set_position_lock_style (AudioTime);
2826 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2827 + (m->bbt().beats - 1)
2828 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2830 m->set_beat (start);
2831 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2832 + (m->bbt().beats - 1)
2833 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2834 m->set_pulse (start_beat / prev_m->note_divisor());
2837 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2843 if (!t->movable()) {
2846 t->set_position_lock_style (AudioTime);
2852 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2853 + (t->legacy_bbt().beats - 1)
2854 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2856 t->set_pulse (beat / prev_m->note_divisor());
2858 /* really shouldn't happen but.. */
2859 t->set_pulse (beat / 4.0);
2868 TempoMap::get_state ()
2870 Metrics::const_iterator i;
2871 XMLNode *root = new XMLNode ("TempoMap");
2874 Glib::Threads::RWLock::ReaderLock lm (lock);
2875 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2876 root->add_child_nocopy ((*i)->get_state());
2884 TempoMap::set_state (const XMLNode& node, int /*version*/)
2887 Glib::Threads::RWLock::WriterLock lm (lock);
2890 XMLNodeConstIterator niter;
2891 Metrics old_metrics (_metrics);
2894 nlist = node.children();
2896 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2897 XMLNode* child = *niter;
2899 if (child->name() == TempoSection::xml_state_node_name) {
2902 TempoSection* ts = new TempoSection (*child);
2903 _metrics.push_back (ts);
2906 catch (failed_constructor& err){
2907 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2908 _metrics = old_metrics;
2912 } else if (child->name() == MeterSection::xml_state_node_name) {
2915 MeterSection* ms = new MeterSection (*child);
2916 _metrics.push_back (ms);
2919 catch (failed_constructor& err) {
2920 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2921 _metrics = old_metrics;
2927 if (niter == nlist.end()) {
2928 MetricSectionSorter cmp;
2929 _metrics.sort (cmp);
2932 /* check for legacy sessions where bbt was the base musical unit for tempo */
2933 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2935 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2936 if (t->legacy_bbt().bars != 0) {
2937 fix_legacy_session();
2944 /* check for multiple tempo/meters at the same location, which
2945 ardour2 somehow allowed.
2948 Metrics::iterator prev = _metrics.end();
2949 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2950 if (prev != _metrics.end()) {
2952 MeterSection* prev_m;
2954 TempoSection* prev_t;
2955 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2956 if (prev_m->pulse() == ms->pulse()) {
2957 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2958 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2961 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2962 if (prev_t->pulse() == ts->pulse()) {
2963 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2964 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2972 recompute_map (_metrics);
2975 PropertyChanged (PropertyChange ());
2981 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2983 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2984 const MeterSection* m;
2985 const TempoSection* t;
2986 const TempoSection* prev_t = 0;
2988 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2990 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2991 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2992 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2993 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2995 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
2996 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;
2999 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3000 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3001 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3004 o << "------" << std::endl;
3008 TempoMap::n_tempos() const
3010 Glib::Threads::RWLock::ReaderLock lm (lock);
3013 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3014 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3023 TempoMap::n_meters() const
3025 Glib::Threads::RWLock::ReaderLock lm (lock);
3028 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3029 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3038 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3041 Glib::Threads::RWLock::WriterLock lm (lock);
3042 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3043 if ((*i)->frame() >= where && (*i)->movable ()) {
3044 (*i)->set_frame ((*i)->frame() + amount);
3048 /* now reset the BBT time of all metrics, based on their new
3049 * audio time. This is the only place where we do this reverse
3053 Metrics::iterator i;
3054 const MeterSection* meter;
3055 const TempoSection* tempo;
3059 meter = &first_meter ();
3060 tempo = &first_tempo ();
3065 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3068 MetricSection* prev = 0;
3070 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3073 //TempoMetric metric (*meter, *tempo);
3074 MeterSection* ms = const_cast<MeterSection*>(meter);
3075 TempoSection* ts = const_cast<TempoSection*>(tempo);
3078 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3082 ts->set_pulse (t->pulse());
3084 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3085 ts->set_pulse (m->pulse());
3087 ts->set_frame (prev->frame());
3091 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3092 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3093 ms->set_beat (start);
3094 ms->set_pulse (m->pulse());
3096 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3100 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3101 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3102 ms->set_beat (start);
3103 ms->set_pulse (t->pulse());
3105 ms->set_frame (prev->frame());
3109 // metric will be at frames=0 bbt=1|1|0 by default
3110 // which is correct for our purpose
3113 // cerr << bbt << endl;
3115 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3119 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3121 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3122 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3123 bbt_time (m->frame(), bbt);
3125 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3131 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3132 /* round up to next beat */
3138 if (bbt.beats != 1) {
3139 /* round up to next bar */
3144 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3145 m->set_beat (start);
3146 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3148 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3150 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3151 abort(); /*NOTREACHED*/
3157 recompute_map (_metrics);
3161 PropertyChanged (PropertyChange ());
3164 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3168 std::list<MetricSection*> metric_kill_list;
3170 TempoSection* last_tempo = NULL;
3171 MeterSection* last_meter = NULL;
3172 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3173 bool meter_after = false; // is there a meter marker likewise?
3175 Glib::Threads::RWLock::WriterLock lm (lock);
3176 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3177 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3178 metric_kill_list.push_back(*i);
3179 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3182 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3186 else if ((*i)->frame() >= where) {
3187 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3188 (*i)->set_frame ((*i)->frame() - amount);
3189 if ((*i)->frame() == where) {
3190 // marker was immediately after end of range
3191 tempo_after = dynamic_cast<TempoSection*> (*i);
3192 meter_after = dynamic_cast<MeterSection*> (*i);
3198 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3199 if (last_tempo && !tempo_after) {
3200 metric_kill_list.remove(last_tempo);
3201 last_tempo->set_frame(where);
3204 if (last_meter && !meter_after) {
3205 metric_kill_list.remove(last_meter);
3206 last_meter->set_frame(where);
3210 //remove all the remaining metrics
3211 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3212 _metrics.remove(*i);
3217 recompute_map (_metrics);
3220 PropertyChanged (PropertyChange ());
3224 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3225 * pos can be -ve, if required.
3228 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3230 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3233 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3235 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3237 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3240 /** Add the BBT interval op to pos and return the result */
3242 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3244 Glib::Threads::RWLock::ReaderLock lm (lock);
3246 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3247 pos_bbt.ticks += op.ticks;
3248 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3250 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3252 pos_bbt.beats += op.beats;
3253 /* the meter in effect will start on the bar */
3254 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3255 while (pos_bbt.beats >= divisions_per_bar + 1) {
3257 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3258 pos_bbt.beats -= divisions_per_bar;
3260 pos_bbt.bars += op.bars;
3262 return frame_time_locked (_metrics, pos_bbt);
3265 /** Count the number of beats that are equivalent to distance when going forward,
3269 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3271 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3275 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3281 operator<< (std::ostream& o, const Meter& m) {
3282 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3286 operator<< (std::ostream& o, const Tempo& t) {
3287 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3291 operator<< (std::ostream& o, const MetricSection& section) {
3293 o << "MetricSection @ " << section.frame() << ' ';
3295 const TempoSection* ts;
3296 const MeterSection* ms;
3298 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3299 o << *((const Tempo*) ts);
3300 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3301 o << *((const Meter*) ms);