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 (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
628 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
630 t->set_movable (false);
631 m->set_movable (false);
632 t->set_position_lock_style (AudioTime);
633 m->set_position_lock_style (AudioTime);
635 /* note: frame time is correct (zero) for both of these */
637 _metrics.push_back (t);
638 _metrics.push_back (m);
642 TempoMap::~TempoMap ()
647 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
649 bool removed = false;
652 Glib::Threads::RWLock::WriterLock lm (lock);
653 if ((removed = remove_tempo_locked (tempo))) {
654 if (complete_operation) {
655 recompute_map (_metrics);
660 if (removed && complete_operation) {
661 PropertyChanged (PropertyChange ());
666 TempoMap::remove_tempo_locked (const TempoSection& tempo)
670 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
671 if (dynamic_cast<TempoSection*> (*i) != 0) {
672 if (tempo.frame() == (*i)->frame()) {
673 if ((*i)->movable()) {
685 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
687 bool removed = false;
690 Glib::Threads::RWLock::WriterLock lm (lock);
691 if ((removed = remove_meter_locked (tempo))) {
692 if (complete_operation) {
693 recompute_map (_metrics);
698 if (removed && complete_operation) {
699 PropertyChanged (PropertyChange ());
704 TempoMap::remove_meter_locked (const MeterSection& tempo)
708 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
709 if (dynamic_cast<MeterSection*> (*i) != 0) {
710 if (tempo.frame() == (*i)->frame()) {
711 if ((*i)->movable()) {
723 TempoMap::do_insert (MetricSection* section)
725 bool need_add = true;
726 /* we only allow new meters to be inserted on beat 1 of an existing
730 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
731 //assert (m->bbt().ticks == 0);
733 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
735 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
736 corrected.second.beats = 1;
737 corrected.second.ticks = 0;
738 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
739 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
740 m->bbt(), corrected.second) << endmsg;
741 //m->set_pulse (corrected);
745 /* Look for any existing MetricSection that is of the same type and
746 in the same bar as the new one, and remove it before adding
747 the new one. Note that this means that if we find a matching,
748 existing section, we can break out of the loop since we're
749 guaranteed that there is only one such match.
752 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
754 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
755 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
756 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
757 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
759 if (tempo && insert_tempo) {
762 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
763 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
765 if (!tempo->movable()) {
767 /* can't (re)move this section, so overwrite
768 * its data content (but not its properties as
772 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
773 (*i)->set_position_lock_style (AudioTime);
775 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
776 t->set_type (insert_tempo->type());
785 } else if (meter && insert_meter) {
789 bool const ipm = insert_meter->position_lock_style() == MusicTime;
791 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
793 if (!meter->movable()) {
795 /* can't (re)move this section, so overwrite
796 * its data content (but not its properties as
800 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
801 (*i)->set_position_lock_style (AudioTime);
810 /* non-matching types, so we don't care */
814 /* Add the given MetricSection, if we didn't just reset an existing
819 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
820 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
823 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
824 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
827 bool const ipm = insert_meter->position_lock_style() == MusicTime;
828 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
833 } else if (insert_tempo) {
834 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
835 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
838 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
839 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
846 _metrics.insert (i, section);
847 //dump (_metrics, std::cerr);
852 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
855 Glib::Threads::RWLock::WriterLock lm (lock);
856 TempoSection& first (first_tempo());
857 if (ts.pulse() != first.pulse()) {
858 remove_tempo_locked (ts);
859 add_tempo_locked (tempo, pulse, true, type);
861 first.set_type (type);
863 /* cannot move the first tempo section */
864 *static_cast<Tempo*>(&first) = tempo;
865 recompute_map (_metrics);
870 PropertyChanged (PropertyChange ());
874 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
877 Glib::Threads::RWLock::WriterLock lm (lock);
878 TempoSection& first (first_tempo());
879 if (ts.frame() != first.frame()) {
880 remove_tempo_locked (ts);
881 add_tempo_locked (tempo, frame, true, type);
883 first.set_type (type);
884 first.set_pulse (0.0);
885 first.set_position_lock_style (AudioTime);
887 /* cannot move the first tempo section */
888 *static_cast<Tempo*>(&first) = tempo;
889 recompute_map (_metrics);
894 PropertyChanged (PropertyChange ());
898 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
900 TempoSection* ts = 0;
902 Glib::Threads::RWLock::WriterLock lm (lock);
903 ts = add_tempo_locked (tempo, pulse, true, type);
906 PropertyChanged (PropertyChange ());
912 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
914 TempoSection* ts = 0;
916 Glib::Threads::RWLock::WriterLock lm (lock);
917 ts = add_tempo_locked (tempo, frame, true, type);
921 PropertyChanged (PropertyChange ());
927 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
929 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
934 solve_map (_metrics, t, t->pulse());
941 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
943 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
948 solve_map (_metrics, t, t->frame());
955 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
958 Glib::Threads::RWLock::WriterLock lm (lock);
961 remove_meter_locked (ms);
962 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
964 MeterSection& first (first_meter());
965 /* cannot move the first meter section */
966 *static_cast<Meter*>(&first) = meter;
967 first.set_position_lock_style (AudioTime);
969 recompute_map (_metrics);
972 PropertyChanged (PropertyChange ());
976 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
979 Glib::Threads::RWLock::WriterLock lm (lock);
981 const double beat = ms.beat();
982 const BBT_Time bbt = ms.bbt();
985 remove_meter_locked (ms);
986 add_meter_locked (meter, frame, beat, bbt, true);
988 MeterSection& first (first_meter());
989 TempoSection& first_t (first_tempo());
990 /* cannot move the first meter section */
991 *static_cast<Meter*>(&first) = meter;
992 first.set_position_lock_style (AudioTime);
993 first.set_pulse (0.0);
994 first.set_frame (frame);
995 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
996 first.set_beat (beat);
997 first_t.set_frame (first.frame());
998 first_t.set_pulse (0.0);
999 first_t.set_position_lock_style (AudioTime);
1001 recompute_map (_metrics);
1003 PropertyChanged (PropertyChange ());
1008 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1010 MeterSection* m = 0;
1012 Glib::Threads::RWLock::WriterLock lm (lock);
1013 m = add_meter_locked (meter, beat, where, true);
1018 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1019 dump (_metrics, std::cerr);
1023 PropertyChanged (PropertyChange ());
1029 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1031 MeterSection* m = 0;
1033 Glib::Threads::RWLock::WriterLock lm (lock);
1034 m = add_meter_locked (meter, frame, beat, where, true);
1039 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1040 dump (_metrics, std::cerr);
1044 PropertyChanged (PropertyChange ());
1050 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1052 /* a new meter always starts a new bar on the first beat. so
1053 round the start time appropriately. remember that
1054 `where' is based on the existing tempo map, not
1055 the result after we insert the new meter.
1059 if (where.beats != 1) {
1063 /* new meters *always* start on a beat. */
1065 const double pulse = pulse_at_beat_locked (_metrics, beat);
1066 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1067 do_insert (new_meter);
1070 solve_map (_metrics, new_meter, pulse);
1077 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1079 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1081 double pulse = pulse_at_frame_locked (_metrics, frame);
1082 new_meter->set_pulse (pulse);
1084 do_insert (new_meter);
1087 solve_map (_metrics, new_meter, frame);
1094 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1096 Tempo newtempo (beats_per_minute, note_type);
1099 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1100 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1105 Glib::Threads::RWLock::WriterLock lm (lock);
1106 *((Tempo*) t) = newtempo;
1107 recompute_map (_metrics);
1109 PropertyChanged (PropertyChange ());
1116 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1118 Tempo newtempo (beats_per_minute, note_type);
1121 TempoSection* first;
1122 Metrics::iterator i;
1124 /* find the TempoSection immediately preceding "where"
1127 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1129 if ((*i)->frame() > where) {
1135 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1148 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1158 Glib::Threads::RWLock::WriterLock lm (lock);
1159 /* cannot move the first tempo section */
1160 *((Tempo*)prev) = newtempo;
1161 recompute_map (_metrics);
1164 PropertyChanged (PropertyChange ());
1168 TempoMap::first_meter () const
1170 const MeterSection *m = 0;
1172 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1173 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1178 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1179 abort(); /*NOTREACHED*/
1184 TempoMap::first_meter ()
1186 MeterSection *m = 0;
1188 /* CALLER MUST HOLD LOCK */
1190 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1191 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1196 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1197 abort(); /*NOTREACHED*/
1202 TempoMap::first_tempo () const
1204 const TempoSection *t = 0;
1206 /* CALLER MUST HOLD LOCK */
1208 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1209 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1213 if (!t->movable()) {
1219 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1220 abort(); /*NOTREACHED*/
1225 TempoMap::first_tempo ()
1227 TempoSection *t = 0;
1229 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1230 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1234 if (!t->movable()) {
1240 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1241 abort(); /*NOTREACHED*/
1245 TempoMap::recompute_tempos (Metrics& metrics)
1247 TempoSection* prev_t = 0;
1249 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1252 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1256 if (!t->movable()) {
1262 if (t->position_lock_style() == AudioTime) {
1263 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1264 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1267 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1268 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1275 prev_t->set_c_func (0.0);
1278 /* tempos must be positioned correctly */
1280 TempoMap::recompute_meters (Metrics& metrics)
1282 MeterSection* meter = 0;
1283 MeterSection* prev_m = 0;
1285 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1286 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1287 if (meter->position_lock_style() == AudioTime) {
1289 pair<double, BBT_Time> b_bbt;
1290 if (meter->movable()) {
1291 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor());
1292 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1293 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1294 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1295 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1296 pulse = true_pulse - pulse_off;
1298 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1300 meter->set_beat (b_bbt);
1301 meter->set_pulse (pulse);
1304 pair<double, BBT_Time> new_beat;
1306 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1307 new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1309 /* shouldn't happen - the first is audio-locked */
1310 pulse = pulse_at_beat_locked (metrics, meter->beat());
1311 new_beat = make_pair (pulse, meter->bbt());
1314 meter->set_beat (new_beat);
1315 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1316 meter->set_pulse (pulse);
1322 //dump (_metrics, std::cerr;
1326 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1328 /* CALLER MUST HOLD WRITE LOCK */
1332 /* we will actually stop once we hit
1339 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1342 /* silly call from Session::process() during startup
1347 recompute_tempos (metrics);
1348 recompute_meters (metrics);
1352 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1354 Glib::Threads::RWLock::ReaderLock lm (lock);
1355 TempoMetric m (first_meter(), first_tempo());
1357 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1358 at something, because we insert the default tempo and meter during
1359 TempoMap construction.
1361 now see if we can find better candidates.
1364 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1366 if ((*i)->frame() > frame) {
1380 /* XX meters only */
1382 TempoMap::metric_at (BBT_Time bbt) const
1384 Glib::Threads::RWLock::ReaderLock lm (lock);
1385 TempoMetric m (first_meter(), first_tempo());
1387 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1388 at something, because we insert the default tempo and meter during
1389 TempoMap construction.
1391 now see if we can find better candidates.
1394 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1396 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1397 BBT_Time section_start (mw->bbt());
1399 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1411 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1413 MeterSection* prev_m = 0;
1415 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1417 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1418 if (prev_m && m->beat() > beat) {
1425 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1430 TempoMap::pulse_at_beat (const double& beat) const
1432 Glib::Threads::RWLock::ReaderLock lm (lock);
1433 return pulse_at_beat_locked (_metrics, beat);
1437 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1439 MeterSection* prev_m = 0;
1441 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1443 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1444 if (prev_m && m->pulse() > pulse) {
1451 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1453 return beats_in_section + prev_m->beat();
1457 TempoMap::beat_at_pulse (const double& pulse) const
1459 Glib::Threads::RWLock::ReaderLock lm (lock);
1460 return beat_at_pulse_locked (_metrics, pulse);
1464 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1466 /* HOLD (at least) THE READER LOCK */
1467 TempoSection* prev_t = 0;
1469 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1471 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1475 if (prev_t && t->frame() > frame) {
1476 /*the previous ts is the one containing the frame */
1477 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1484 /* treated as constant for this ts */
1485 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1487 return pulses_in_section + prev_t->pulse();
1491 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1493 /* HOLD THE READER LOCK */
1495 const TempoSection* prev_t = 0;
1497 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1500 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1504 if (prev_t && t->pulse() > pulse) {
1505 return prev_t->frame_at_pulse (pulse, _frame_rate);
1511 /* must be treated as constant, irrespective of _type */
1512 double const pulses_in_section = pulse - prev_t->pulse();
1513 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1515 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1521 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1523 const double pulse = pulse_at_frame_locked (metrics, frame);
1524 return beat_at_pulse_locked (metrics, pulse);
1528 TempoMap::beat_at_frame (const framecnt_t& frame) const
1530 Glib::Threads::RWLock::ReaderLock lm (lock);
1531 return beat_at_frame_locked (_metrics, frame);
1535 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1537 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1542 TempoMap::frame_at_beat (const double& beat) const
1544 Glib::Threads::RWLock::ReaderLock lm (lock);
1545 return frame_at_beat_locked (_metrics, beat);
1549 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1551 /* CALLER HOLDS READ LOCK */
1553 MeterSection* prev_m = 0;
1555 /* because audio-locked meters have 'fake' integral beats,
1556 there is no pulse offset here.
1558 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1560 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1562 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1563 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1571 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1572 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1573 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1579 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1581 Glib::Threads::RWLock::ReaderLock lm (lock);
1582 return bbt_to_beats_locked (_metrics, bbt);
1586 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1588 /* CALLER HOLDS READ LOCK */
1589 MeterSection* prev_m = 0;
1590 const double beats = (b < 0.0) ? 0.0 : b;
1592 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1593 MeterSection* m = 0;
1595 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1597 if (m->beat() > beats) {
1598 /* this is the meter after the one our beat is on*/
1607 const double beats_in_ms = beats - prev_m->beat();
1608 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1609 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1610 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1611 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1615 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1616 ret.beats = (uint32_t) floor (remaining_beats);
1617 ret.bars = total_bars;
1619 /* 0 0 0 to 1 1 0 - based mapping*/
1623 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1625 ret.ticks -= BBT_Time::ticks_per_beat;
1628 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1637 TempoMap::beats_to_bbt (const double& beats)
1639 Glib::Threads::RWLock::ReaderLock lm (lock);
1640 return beats_to_bbt_locked (_metrics, beats);
1644 TempoMap::pulse_to_bbt (const double& pulse)
1646 Glib::Threads::RWLock::ReaderLock lm (lock);
1647 MeterSection* prev_m = 0;
1649 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1650 MeterSection* m = 0;
1652 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1655 double const pulses_to_m = m->pulse() - prev_m->pulse();
1656 if (prev_m->pulse() + pulses_to_m > pulse) {
1657 /* this is the meter after the one our beat is on*/
1666 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1667 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1668 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1669 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1670 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1674 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1675 ret.beats = (uint32_t) floor (remaining_beats);
1676 ret.bars = total_bars;
1678 /* 0 0 0 to 1 1 0 mapping*/
1682 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1684 ret.ticks -= BBT_Time::ticks_per_beat;
1687 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1696 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1703 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1706 Glib::Threads::RWLock::ReaderLock lm (lock);
1707 const double beat = beat_at_frame_locked (_metrics, frame);
1709 bbt = beats_to_bbt_locked (_metrics, beat);
1713 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1715 /* HOLD THE READER LOCK */
1717 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1722 TempoMap::frame_time (const BBT_Time& bbt)
1725 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1729 if (bbt.beats < 1) {
1730 throw std::logic_error ("beats are counted from one");
1732 Glib::Threads::RWLock::ReaderLock lm (lock);
1734 return frame_time_locked (_metrics, bbt);
1738 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1740 TempoSection* prev_t = 0;
1742 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1744 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1749 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1753 if (t->frame() == prev_t->frame()) {
1757 /* precision check ensures pulses and frames align.*/
1758 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1770 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1772 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1774 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1775 if (!t->movable()) {
1776 t->set_active (true);
1779 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1780 t->set_active (false);
1782 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1783 t->set_active (true);
1784 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1793 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1795 TempoSection* prev_t = 0;
1796 TempoSection* section_prev = 0;
1797 framepos_t first_m_frame = 0;
1799 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1801 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1802 if (!m->movable()) {
1803 first_m_frame = m->frame();
1808 if (section->movable() && frame <= first_m_frame) {
1812 section->set_active (true);
1813 section->set_frame (frame);
1815 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1817 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1824 section_prev = prev_t;
1827 if (t->position_lock_style() == MusicTime) {
1828 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1829 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1831 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1832 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1840 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1841 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1844 if (section->position_lock_style() == MusicTime) {
1845 /* we're setting the frame */
1846 section->set_position_lock_style (AudioTime);
1847 recompute_tempos (imaginary);
1848 section->set_position_lock_style (MusicTime);
1850 recompute_tempos (imaginary);
1853 if (check_solved (imaginary, true)) {
1854 recompute_meters (imaginary);
1858 MetricSectionFrameSorter fcmp;
1859 imaginary.sort (fcmp);
1860 if (section->position_lock_style() == MusicTime) {
1861 /* we're setting the frame */
1862 section->set_position_lock_style (AudioTime);
1863 recompute_tempos (imaginary);
1864 section->set_position_lock_style (MusicTime);
1866 recompute_tempos (imaginary);
1869 if (check_solved (imaginary, true)) {
1870 recompute_meters (imaginary);
1874 MetricSectionSorter cmp;
1875 imaginary.sort (cmp);
1876 if (section->position_lock_style() == MusicTime) {
1877 /* we're setting the frame */
1878 section->set_position_lock_style (AudioTime);
1879 recompute_tempos (imaginary);
1880 section->set_position_lock_style (MusicTime);
1882 recompute_tempos (imaginary);
1885 if (check_solved (imaginary, true)) {
1886 recompute_meters (imaginary);
1890 //dump (imaginary, std::cerr);
1896 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1898 TempoSection* prev_t = 0;
1899 TempoSection* section_prev = 0;
1901 section->set_pulse (pulse);
1903 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1905 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1909 if (!t->movable()) {
1916 section_prev = prev_t;
1919 if (t->position_lock_style() == MusicTime) {
1920 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1921 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1923 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1924 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1931 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1932 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1935 if (section->position_lock_style() == AudioTime) {
1936 /* we're setting the pulse */
1937 section->set_position_lock_style (MusicTime);
1938 recompute_tempos (imaginary);
1939 section->set_position_lock_style (AudioTime);
1941 recompute_tempos (imaginary);
1944 if (check_solved (imaginary, false)) {
1945 recompute_meters (imaginary);
1949 MetricSectionSorter cmp;
1950 imaginary.sort (cmp);
1951 if (section->position_lock_style() == AudioTime) {
1952 /* we're setting the pulse */
1953 section->set_position_lock_style (MusicTime);
1954 recompute_tempos (imaginary);
1955 section->set_position_lock_style (AudioTime);
1957 recompute_tempos (imaginary);
1960 if (check_solved (imaginary, false)) {
1961 recompute_meters (imaginary);
1965 MetricSectionFrameSorter fcmp;
1966 imaginary.sort (fcmp);
1967 if (section->position_lock_style() == AudioTime) {
1968 /* we're setting the pulse */
1969 section->set_position_lock_style (MusicTime);
1970 recompute_tempos (imaginary);
1971 section->set_position_lock_style (AudioTime);
1973 recompute_tempos (imaginary);
1976 if (check_solved (imaginary, false)) {
1977 recompute_meters (imaginary);
1981 //dump (imaginary, std::cerr);
1987 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
1989 MeterSection* prev_m = 0;
1991 if (!section->movable()) {
1992 /* lock the first tempo to our first meter */
1993 if (!set_active_tempos (imaginary, frame)) {
1996 TempoSection* first_t = &first_tempo();
1998 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2000 new_section->set_frame (frame);
2001 new_section->set_pulse (0.0);
2002 new_section->set_active (true);
2004 if (solve_map (future_map, new_section, frame)) {
2005 first_t->set_frame (frame);
2006 first_t->set_pulse (0.0);
2007 first_t->set_active (true);
2008 solve_map (imaginary, first_t, frame);
2014 section->set_frame (frame);
2016 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2018 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2021 here we set the beat for this frame.
2022 we set it 'incorrectly' to the next bar's first beat
2023 and use the delat to find the meter's pulse.
2025 double new_pulse = 0.0;
2026 pair<double, BBT_Time> b_bbt;
2028 if (section->movable()) {
2029 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2030 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2031 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2032 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2033 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2034 new_pulse = true_pulse - pulse_off;
2036 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2039 section->set_beat (b_bbt);
2040 section->set_pulse (new_pulse);
2046 double new_pulse = 0.0;
2047 if (m->position_lock_style() == MusicTime) {
2048 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2049 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2050 if (m->frame() > section->frame()) {
2051 /* moving 'section' will affect later meters' beat (but not bbt).*/
2052 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2053 m->set_beat (new_beat);
2056 pair<double, BBT_Time> b_bbt;
2058 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2059 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2060 b_bbt = make_pair (floor_beats + prev_m->beat()
2061 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2062 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2063 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2064 new_pulse = true_pulse - pulse_off;
2066 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2069 m->set_beat (b_bbt);
2071 m->set_pulse (new_pulse);
2077 MetricSectionFrameSorter fcmp;
2078 imaginary.sort (fcmp);
2079 if (section->position_lock_style() == MusicTime) {
2080 /* we're setting the frame */
2081 section->set_position_lock_style (AudioTime);
2082 recompute_meters (imaginary);
2083 section->set_position_lock_style (MusicTime);
2085 recompute_meters (imaginary);
2087 //dump (imaginary, std::cerr);
2091 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2093 MeterSection* prev_m = 0;
2094 MeterSection* section_prev = 0;
2096 section->set_pulse (pulse);
2098 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2100 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2102 section_prev = prev_m;
2106 double new_pulse = 0.0;
2107 if (m->position_lock_style() == MusicTime) {
2108 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2109 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2111 if (new_pulse > section->pulse()) {
2112 /* moving 'section' will affect later meters' beat (but not bbt).*/
2113 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2114 m->set_beat (new_beat);
2117 pair<double, BBT_Time> b_bbt;
2119 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2120 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2121 b_bbt = make_pair (floor_beats + prev_m->beat()
2122 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2123 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2124 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2125 new_pulse = true_pulse - pulse_off;
2127 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2129 m->set_beat (b_bbt);
2131 m->set_pulse (new_pulse);
2138 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2139 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2140 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2141 section->set_beat (b_bbt);
2142 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2145 MetricSectionSorter cmp;
2146 imaginary.sort (cmp);
2147 if (section->position_lock_style() == AudioTime) {
2148 /* we're setting the pulse */
2149 section->set_position_lock_style (MusicTime);
2150 recompute_meters (imaginary);
2151 section->set_position_lock_style (AudioTime);
2153 recompute_meters (imaginary);
2157 /** places a copy of _metrics into copy and returns a pointer
2158 * to section's equivalent in copy.
2161 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2165 TempoSection* ret = 0;
2167 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2168 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2170 if (t->position_lock_style() == MusicTime) {
2171 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2172 ret->set_frame (t->frame());
2174 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2175 ret->set_pulse (t->pulse());
2177 ret->set_active (t->active());
2178 ret->set_movable (t->movable());
2179 copy.push_back (ret);
2182 TempoSection* cp = 0;
2183 if (t->position_lock_style() == MusicTime) {
2184 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2185 cp->set_frame (t->frame());
2187 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2188 cp->set_pulse (t->pulse());
2190 cp->set_active (t->active());
2191 cp->set_movable (t->movable());
2192 copy.push_back (cp);
2194 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2195 MeterSection* cp = 0;
2196 if (m->position_lock_style() == MusicTime) {
2197 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2198 cp->set_frame (m->frame());
2200 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2201 cp->set_pulse (m->pulse());
2203 cp->set_movable (m->movable());
2204 copy.push_back (cp);
2212 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2215 TempoSection* new_section = 0;
2218 Glib::Threads::RWLock::ReaderLock lm (lock);
2219 new_section = copy_metrics_and_point (copy, ts);
2222 double const beat = bbt_to_beats_locked (copy, bbt);
2223 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2225 Metrics::const_iterator d = copy.begin();
2226 while (d != copy.end()) {
2235 * 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,
2236 * taking any possible reordering as a consequence of this into account.
2237 * @param section - the section to be altered
2238 * @param bpm - the new Tempo
2239 * @param bbt - the bbt where the altered tempo will fall
2240 * @return returns - the position in frames where the new tempo section will lie.
2243 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2245 Glib::Threads::RWLock::ReaderLock lm (lock);
2248 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2250 const double beat = bbt_to_beats_locked (future_map, bbt);
2252 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2253 ret = new_section->frame();
2255 ret = frame_at_beat_locked (_metrics, beat);
2258 Metrics::const_iterator d = future_map.begin();
2259 while (d != future_map.end()) {
2267 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2269 Glib::Threads::RWLock::ReaderLock lm (lock);
2272 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2274 if (solve_map (future_map, new_section, frame)) {
2275 ret = new_section->pulse();
2277 ret = pulse_at_frame_locked (_metrics, frame);
2280 Metrics::const_iterator d = future_map.begin();
2281 while (d != future_map.end()) {
2289 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2293 Glib::Threads::RWLock::WriterLock lm (lock);
2294 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2295 if (solve_map (future_map, new_section, frame)) {
2296 solve_map (_metrics, ts, frame);
2300 Metrics::const_iterator d = future_map.begin();
2301 while (d != future_map.end()) {
2306 MetricPositionChanged (); // Emit Signal
2310 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2314 Glib::Threads::RWLock::WriterLock lm (lock);
2315 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2316 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2317 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2321 Metrics::const_iterator d = future_map.begin();
2322 while (d != future_map.end()) {
2327 MetricPositionChanged (); // Emit Signal
2331 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2334 Glib::Threads::RWLock::WriterLock lm (lock);
2335 solve_map (_metrics, ms, frame);
2338 MetricPositionChanged (); // Emit Signal
2342 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2345 Glib::Threads::RWLock::WriterLock lm (lock);
2346 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2349 MetricPositionChanged (); // Emit Signal
2353 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2356 bool can_solve = false;
2358 Glib::Threads::RWLock::WriterLock lm (lock);
2359 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2360 new_section->set_beats_per_minute (bpm.beats_per_minute());
2361 recompute_tempos (future_map);
2363 if (check_solved (future_map, true)) {
2364 ts->set_beats_per_minute (bpm.beats_per_minute());
2365 recompute_map (_metrics);
2370 Metrics::const_iterator d = future_map.begin();
2371 while (d != future_map.end()) {
2376 MetricPositionChanged (); // Emit Signal
2382 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2384 Glib::Threads::RWLock::ReaderLock lm (lock);
2386 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2387 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2388 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2390 return frame_at_beat_locked (_metrics, total_beats);
2394 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2396 return round_to_type (fr, dir, Bar);
2400 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2402 return round_to_type (fr, dir, Beat);
2406 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2408 Glib::Threads::RWLock::ReaderLock lm (lock);
2409 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2410 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2411 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2413 ticks -= beats * BBT_Time::ticks_per_beat;
2416 /* round to next (or same iff dir == RoundUpMaybe) */
2418 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2420 if (mod == 0 && dir == RoundUpMaybe) {
2421 /* right on the subdivision, which is fine, so do nothing */
2423 } else if (mod == 0) {
2424 /* right on the subdivision, so the difference is just the subdivision ticks */
2425 ticks += ticks_one_subdivisions_worth;
2428 /* not on subdivision, compute distance to next subdivision */
2430 ticks += ticks_one_subdivisions_worth - mod;
2433 if (ticks >= BBT_Time::ticks_per_beat) {
2434 ticks -= BBT_Time::ticks_per_beat;
2436 } else if (dir < 0) {
2438 /* round to previous (or same iff dir == RoundDownMaybe) */
2440 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2442 if (difference == 0 && dir == RoundDownAlways) {
2443 /* right on the subdivision, but force-rounding down,
2444 so the difference is just the subdivision ticks */
2445 difference = ticks_one_subdivisions_worth;
2448 if (ticks < difference) {
2449 ticks = BBT_Time::ticks_per_beat - ticks;
2451 ticks -= difference;
2455 /* round to nearest */
2458 /* compute the distance to the previous and next subdivision */
2460 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2462 /* closer to the next subdivision, so shift forward */
2464 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2466 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2468 if (ticks > BBT_Time::ticks_per_beat) {
2470 ticks -= BBT_Time::ticks_per_beat;
2471 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2474 } else if (rem > 0) {
2476 /* closer to previous subdivision, so shift backward */
2480 /* can't go backwards past zero, so ... */
2483 /* step back to previous beat */
2485 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2486 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2488 ticks = lrint (ticks - rem);
2489 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2492 /* on the subdivision, do nothing */
2496 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2502 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2504 if (sub_num == -1) {
2505 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2506 if ((double) when.beats > bpb / 2.0) {
2512 } else if (sub_num == 0) {
2513 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2514 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2516 while ((double) when.beats > bpb) {
2518 when.beats -= (uint32_t) floor (bpb);
2524 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2526 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2527 /* closer to the next subdivision, so shift forward */
2529 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2531 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2533 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2536 } else if (rem > 0) {
2537 /* closer to previous subdivision, so shift backward */
2539 if (rem > when.ticks) {
2540 if (when.beats == 0) {
2541 /* can't go backwards past zero, so ... */
2543 /* step back to previous beat */
2545 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2547 when.ticks = when.ticks - rem;
2553 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2555 Glib::Threads::RWLock::ReaderLock lm (lock);
2557 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2558 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2563 /* find bar previous to 'frame' */
2566 return frame_time_locked (_metrics, bbt);
2568 } else if (dir > 0) {
2569 /* find bar following 'frame' */
2573 return frame_time_locked (_metrics, bbt);
2575 /* true rounding: find nearest bar */
2576 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2579 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2581 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2583 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2594 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2595 } else if (dir > 0) {
2596 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2598 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2607 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2608 framepos_t lower, framepos_t upper)
2610 Glib::Threads::RWLock::ReaderLock lm (lock);
2611 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2612 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2613 /* although the map handles negative beats, bbt doesn't. */
2617 while (cnt <= upper_beat) {
2618 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2619 const TempoSection tempo = tempo_section_at_locked (pos);
2620 const MeterSection meter = meter_section_at_locked (pos);
2621 const BBT_Time bbt = beats_to_bbt (cnt);
2622 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2628 TempoMap::tempo_section_at (framepos_t frame) const
2630 Glib::Threads::RWLock::ReaderLock lm (lock);
2631 return tempo_section_at_locked (frame);
2635 TempoMap::tempo_section_at_locked (framepos_t frame) const
2637 Metrics::const_iterator i;
2638 TempoSection* prev = 0;
2640 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2643 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2647 if (t->frame() > frame) {
2657 abort(); /*NOTREACHED*/
2664 /* don't use this to calculate length (the tempo is only correct for this frame).
2665 do that stuff based on the beat_at_frame and frame_at_beat api
2668 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2670 Glib::Threads::RWLock::ReaderLock lm (lock);
2672 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2673 const TempoSection* ts_after = 0;
2674 Metrics::const_iterator i;
2676 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2679 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2683 if ((*i)->frame() > frame) {
2691 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2693 /* must be treated as constant tempo */
2694 return ts_at->frames_per_beat (_frame_rate);
2698 TempoMap::tempo_at (const framepos_t& frame) const
2700 Glib::Threads::RWLock::ReaderLock lm (lock);
2701 return tempo_at_locked (frame);
2705 TempoMap::tempo_at_locked (const framepos_t& frame) const
2707 TempoSection* prev_t = 0;
2709 Metrics::const_iterator i;
2711 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2713 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2717 if ((prev_t) && t->frame() > frame) {
2718 /* t is the section past frame */
2719 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2720 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2727 const double ret = prev_t->beats_per_minute();
2728 const Tempo ret_tempo (ret, prev_t->note_type ());
2734 TempoMap::meter_section_at (framepos_t frame) const
2736 Glib::Threads::RWLock::ReaderLock lm (lock);
2737 return meter_section_at_locked (frame);
2741 TempoMap::meter_section_at_locked (framepos_t frame) const
2743 Metrics::const_iterator i;
2744 MeterSection* prev = 0;
2746 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2749 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2751 if (prev && (*i)->frame() > frame) {
2761 abort(); /*NOTREACHED*/
2768 TempoMap::meter_at (framepos_t frame) const
2770 TempoMetric m (metric_at (frame));
2775 TempoMap::meter_section_at (const double& beat) const
2777 MeterSection* prev_m = 0;
2778 Glib::Threads::RWLock::ReaderLock lm (lock);
2780 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2782 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2783 if (prev_m && m->beat() > beat) {
2794 TempoMap::fix_legacy_session ()
2796 MeterSection* prev_m = 0;
2797 TempoSection* prev_t = 0;
2799 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2803 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2804 if (!m->movable()) {
2805 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2809 m->set_position_lock_style (AudioTime);
2814 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2815 + (m->bbt().beats - 1)
2816 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2818 m->set_beat (start);
2819 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2820 + (m->bbt().beats - 1)
2821 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2822 m->set_pulse (start_beat / prev_m->note_divisor());
2825 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2831 if (!t->movable()) {
2834 t->set_position_lock_style (AudioTime);
2840 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2841 + (t->legacy_bbt().beats - 1)
2842 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2844 t->set_pulse (beat / prev_m->note_divisor());
2846 /* really shouldn't happen but.. */
2847 t->set_pulse (beat / 4.0);
2856 TempoMap::get_state ()
2858 Metrics::const_iterator i;
2859 XMLNode *root = new XMLNode ("TempoMap");
2862 Glib::Threads::RWLock::ReaderLock lm (lock);
2863 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2864 root->add_child_nocopy ((*i)->get_state());
2872 TempoMap::set_state (const XMLNode& node, int /*version*/)
2875 Glib::Threads::RWLock::WriterLock lm (lock);
2878 XMLNodeConstIterator niter;
2879 Metrics old_metrics (_metrics);
2882 nlist = node.children();
2884 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2885 XMLNode* child = *niter;
2887 if (child->name() == TempoSection::xml_state_node_name) {
2890 TempoSection* ts = new TempoSection (*child);
2891 _metrics.push_back (ts);
2894 catch (failed_constructor& err){
2895 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2896 _metrics = old_metrics;
2900 } else if (child->name() == MeterSection::xml_state_node_name) {
2903 MeterSection* ms = new MeterSection (*child);
2904 _metrics.push_back (ms);
2907 catch (failed_constructor& err) {
2908 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2909 _metrics = old_metrics;
2915 if (niter == nlist.end()) {
2916 MetricSectionSorter cmp;
2917 _metrics.sort (cmp);
2920 /* check for legacy sessions where bbt was the base musical unit for tempo */
2921 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2923 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2924 if (t->legacy_bbt().bars != 0) {
2925 fix_legacy_session();
2932 /* check for multiple tempo/meters at the same location, which
2933 ardour2 somehow allowed.
2936 Metrics::iterator prev = _metrics.end();
2937 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2938 if (prev != _metrics.end()) {
2940 MeterSection* prev_m;
2942 TempoSection* prev_t;
2943 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2944 if (prev_m->pulse() == ms->pulse()) {
2945 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2946 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2949 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2950 if (prev_t->pulse() == ts->pulse()) {
2951 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2952 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2960 recompute_map (_metrics);
2963 PropertyChanged (PropertyChange ());
2969 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2971 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2972 const MeterSection* m;
2973 const TempoSection* t;
2974 const TempoSection* prev_t = 0;
2976 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2978 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2979 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2980 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2981 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2983 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
2984 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;
2987 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2988 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2989 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2992 o << "------" << std::endl;
2996 TempoMap::n_tempos() const
2998 Glib::Threads::RWLock::ReaderLock lm (lock);
3001 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3002 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3011 TempoMap::n_meters() const
3013 Glib::Threads::RWLock::ReaderLock lm (lock);
3016 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3017 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3026 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3029 Glib::Threads::RWLock::WriterLock lm (lock);
3030 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3031 if ((*i)->frame() >= where && (*i)->movable ()) {
3032 (*i)->set_frame ((*i)->frame() + amount);
3036 /* now reset the BBT time of all metrics, based on their new
3037 * audio time. This is the only place where we do this reverse
3041 Metrics::iterator i;
3042 const MeterSection* meter;
3043 const TempoSection* tempo;
3047 meter = &first_meter ();
3048 tempo = &first_tempo ();
3053 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3056 MetricSection* prev = 0;
3058 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3061 //TempoMetric metric (*meter, *tempo);
3062 MeterSection* ms = const_cast<MeterSection*>(meter);
3063 TempoSection* ts = const_cast<TempoSection*>(tempo);
3066 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3070 ts->set_pulse (t->pulse());
3072 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3073 ts->set_pulse (m->pulse());
3075 ts->set_frame (prev->frame());
3079 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3080 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3081 ms->set_beat (start);
3082 ms->set_pulse (m->pulse());
3084 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3088 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3089 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3090 ms->set_beat (start);
3091 ms->set_pulse (t->pulse());
3093 ms->set_frame (prev->frame());
3097 // metric will be at frames=0 bbt=1|1|0 by default
3098 // which is correct for our purpose
3101 // cerr << bbt << endl;
3103 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3107 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3109 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3110 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3111 bbt_time (m->frame(), bbt);
3113 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3119 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3120 /* round up to next beat */
3126 if (bbt.beats != 1) {
3127 /* round up to next bar */
3132 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3133 m->set_beat (start);
3134 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3136 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3138 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3139 abort(); /*NOTREACHED*/
3145 recompute_map (_metrics);
3149 PropertyChanged (PropertyChange ());
3152 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3156 std::list<MetricSection*> metric_kill_list;
3158 TempoSection* last_tempo = NULL;
3159 MeterSection* last_meter = NULL;
3160 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3161 bool meter_after = false; // is there a meter marker likewise?
3163 Glib::Threads::RWLock::WriterLock lm (lock);
3164 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3165 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3166 metric_kill_list.push_back(*i);
3167 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3170 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3174 else if ((*i)->frame() >= where) {
3175 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3176 (*i)->set_frame ((*i)->frame() - amount);
3177 if ((*i)->frame() == where) {
3178 // marker was immediately after end of range
3179 tempo_after = dynamic_cast<TempoSection*> (*i);
3180 meter_after = dynamic_cast<MeterSection*> (*i);
3186 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3187 if (last_tempo && !tempo_after) {
3188 metric_kill_list.remove(last_tempo);
3189 last_tempo->set_frame(where);
3192 if (last_meter && !meter_after) {
3193 metric_kill_list.remove(last_meter);
3194 last_meter->set_frame(where);
3198 //remove all the remaining metrics
3199 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3200 _metrics.remove(*i);
3205 recompute_map (_metrics);
3208 PropertyChanged (PropertyChange ());
3212 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3213 * pos can be -ve, if required.
3216 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3218 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3221 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3223 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3225 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3228 /** Add the BBT interval op to pos and return the result */
3230 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3232 Glib::Threads::RWLock::ReaderLock lm (lock);
3234 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3235 pos_bbt.ticks += op.ticks;
3236 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3238 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3240 pos_bbt.beats += op.beats;
3241 /* the meter in effect will start on the bar */
3242 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3243 while (pos_bbt.beats >= divisions_per_bar + 1) {
3245 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3246 pos_bbt.beats -= divisions_per_bar;
3248 pos_bbt.bars += op.bars;
3250 return frame_time_locked (_metrics, pos_bbt);
3253 /** Count the number of beats that are equivalent to distance when going forward,
3257 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3259 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3263 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3269 operator<< (std::ostream& o, const Meter& m) {
3270 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3274 operator<< (std::ostream& o, const Tempo& t) {
3275 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3279 operator<< (std::ostream& o, const MetricSection& section) {
3281 o << "MetricSection @ " << section.frame() << ' ';
3283 const TempoSection* ts;
3284 const MeterSection* ms;
3286 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3287 o << *((const Tempo*) ts);
3288 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3289 o << *((const Meter*) ms);