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())
80 , _locked_to_meter (false)
82 const XMLProperty *prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses per minute occurs in this section.
222 beat b is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_pulse_tempo (ppm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 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:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 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.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
461 const XMLProperty *prop;
465 framepos_t frame = 0;
466 pair<double, BBT_Time> start;
468 if ((prop = node.property ("start")) != 0) {
469 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
473 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
475 /* legacy session - start used to be in bbt*/
476 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
481 if ((prop = node.property ("pulse")) != 0) {
482 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
483 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
488 if ((prop = node.property ("beat")) != 0) {
489 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
490 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
496 if ((prop = node.property ("bbt")) == 0) {
497 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
498 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
502 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
503 throw failed_constructor();
509 if ((prop = node.property ("frame")) != 0) {
510 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
511 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
517 /* beats-per-bar is old; divisions-per-bar is new */
519 if ((prop = node.property ("divisions-per-bar")) == 0) {
520 if ((prop = node.property ("beats-per-bar")) == 0) {
521 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
522 throw failed_constructor();
525 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
526 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
527 throw failed_constructor();
530 if ((prop = node.property ("note-type")) == 0) {
531 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
532 throw failed_constructor();
534 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
535 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
536 throw failed_constructor();
539 if ((prop = node.property ("movable")) == 0) {
540 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
541 throw failed_constructor();
544 set_movable (string_is_affirmative (prop->value()));
546 if ((prop = node.property ("lock-style")) == 0) {
547 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
549 set_position_lock_style (MusicTime);
551 set_position_lock_style (AudioTime);
554 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
559 MeterSection::get_state() const
561 XMLNode *root = new XMLNode (xml_state_node_name);
565 snprintf (buf, sizeof (buf), "%lf", pulse());
566 root->add_property ("pulse", buf);
567 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
571 root->add_property ("bbt", buf);
572 snprintf (buf, sizeof (buf), "%lf", beat());
573 root->add_property ("beat", buf);
574 snprintf (buf, sizeof (buf), "%f", _note_type);
575 root->add_property ("note-type", buf);
576 snprintf (buf, sizeof (buf), "%li", frame());
577 root->add_property ("frame", buf);
578 root->add_property ("lock-style", enum_2_string (position_lock_style()));
579 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
580 root->add_property ("divisions-per-bar", buf);
581 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
582 root->add_property ("movable", buf);
587 /***********************************************************************/
591 Tempo can be thought of as a source of the musical pulse.
592 Meters divide that pulse into measures and beats.
593 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
594 at any particular time.
595 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
596 It should rather be thought of as tempo note divisions per minute.
598 TempoSections, which are nice to think of in whole pulses per minute,
599 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
600 and beats (via note_divisor) are used to form a tempo map.
601 TempoSections and MeterSections may be locked to either audio or music (position lock style).
602 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
603 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
605 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 Both tempos and meters have a pulse position and a frame position.
612 Meters also have a beat position, which is always 0.0 for the first meter.
614 A tempo locked to music is locked to musical pulses.
615 A meter locked to music is locked to beats.
617 Recomputing the tempo map is the process where the 'missing' position
618 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
620 It is important to keep the _metrics in an order that makes sense.
621 Because ramped MusicTime and AudioTime tempos can interact with each other,
622 reordering is frequent. Care must be taken to keep _metrics in a solved state.
623 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
625 struct MetricSectionSorter {
626 bool operator() (const MetricSection* a, const MetricSection* b) {
627 return a->pulse() < b->pulse();
631 struct MetricSectionFrameSorter {
632 bool operator() (const MetricSection* a, const MetricSection* b) {
633 return a->frame() < b->frame();
637 TempoMap::TempoMap (framecnt_t fr)
640 BBT_Time start (1, 1, 0);
642 TempoSection *t = new TempoSection ((framepos_t) 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
643 MeterSection *m = new MeterSection ((framepos_t) 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
645 t->set_movable (false);
646 m->set_movable (false);
648 /* note: frame time is correct (zero) for both of these */
650 _metrics.push_back (t);
651 _metrics.push_back (m);
655 TempoMap::~TempoMap ()
657 Metrics::const_iterator d = _metrics.begin();
658 while (d != _metrics.end()) {
666 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
668 bool removed = false;
671 Glib::Threads::RWLock::WriterLock lm (lock);
672 if ((removed = remove_tempo_locked (tempo))) {
673 if (complete_operation) {
674 recompute_map (_metrics);
679 if (removed && complete_operation) {
680 PropertyChanged (PropertyChange ());
685 TempoMap::remove_tempo_locked (const TempoSection& tempo)
689 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
690 if (dynamic_cast<TempoSection*> (*i) != 0) {
691 if (tempo.frame() == (*i)->frame()) {
692 if ((*i)->movable()) {
705 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
707 bool removed = false;
710 Glib::Threads::RWLock::WriterLock lm (lock);
711 if ((removed = remove_meter_locked (tempo))) {
712 if (complete_operation) {
713 recompute_map (_metrics);
718 if (removed && complete_operation) {
719 PropertyChanged (PropertyChange ());
724 TempoMap::remove_meter_locked (const MeterSection& meter)
728 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
730 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
731 if (meter.frame() == (*i)->frame()) {
732 if (t->locked_to_meter()) {
741 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
742 if (dynamic_cast<MeterSection*> (*i) != 0) {
743 if (meter.frame() == (*i)->frame()) {
744 if ((*i)->movable()) {
757 TempoMap::do_insert (MetricSection* section)
759 bool need_add = true;
760 /* we only allow new meters to be inserted on beat 1 of an existing
764 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
766 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
768 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
769 corrected.second.beats = 1;
770 corrected.second.ticks = 0;
771 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
772 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
773 m->bbt(), corrected.second) << endmsg;
774 //m->set_pulse (corrected);
778 /* Look for any existing MetricSection that is of the same type and
779 in the same bar as the new one, and remove it before adding
780 the new one. Note that this means that if we find a matching,
781 existing section, we can break out of the loop since we're
782 guaranteed that there is only one such match.
785 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
787 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
788 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
789 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
790 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
792 if (tempo && insert_tempo) {
795 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
796 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
798 if (!tempo->movable()) {
800 /* can't (re)move this section, so overwrite
801 * its data content (but not its properties as
805 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
806 (*i)->set_position_lock_style (AudioTime);
808 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
809 t->set_type (insert_tempo->type());
819 } else if (meter && insert_meter) {
823 bool const ipm = insert_meter->position_lock_style() == MusicTime;
825 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
827 if (!meter->movable()) {
829 /* can't (re)move this section, so overwrite
830 * its data content (but not its properties as
834 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
835 (*i)->set_position_lock_style (AudioTime);
845 /* non-matching types, so we don't care */
849 /* Add the given MetricSection, if we didn't just reset an existing
854 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
855 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
858 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
859 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
862 bool const ipm = insert_meter->position_lock_style() == MusicTime;
863 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
868 } else if (insert_tempo) {
869 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
870 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
873 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
874 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
881 _metrics.insert (i, section);
882 //dump (_metrics, std::cout);
887 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
890 Glib::Threads::RWLock::WriterLock lm (lock);
891 TempoSection& first (first_tempo());
892 if (ts.pulse() != first.pulse()) {
893 remove_tempo_locked (ts);
894 add_tempo_locked (tempo, pulse, true, type);
896 first.set_type (type);
898 /* cannot move the first tempo section */
899 *static_cast<Tempo*>(&first) = tempo;
900 recompute_map (_metrics);
905 PropertyChanged (PropertyChange ());
909 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
912 Glib::Threads::RWLock::WriterLock lm (lock);
913 TempoSection& first (first_tempo());
914 if (ts.frame() != first.frame()) {
915 remove_tempo_locked (ts);
916 add_tempo_locked (tempo, frame, true, type);
918 first.set_type (type);
919 first.set_pulse (0.0);
920 first.set_position_lock_style (AudioTime);
922 /* cannot move the first tempo section */
923 *static_cast<Tempo*>(&first) = tempo;
924 recompute_map (_metrics);
929 PropertyChanged (PropertyChange ());
933 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
935 TempoSection* ts = 0;
937 Glib::Threads::RWLock::WriterLock lm (lock);
938 ts = add_tempo_locked (tempo, pulse, true, type);
941 PropertyChanged (PropertyChange ());
947 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
949 TempoSection* ts = 0;
951 Glib::Threads::RWLock::WriterLock lm (lock);
952 ts = add_tempo_locked (tempo, frame, true, type);
956 PropertyChanged (PropertyChange ());
962 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
964 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
969 solve_map (_metrics, t, t->pulse());
970 recompute_meters (_metrics);
977 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
979 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
984 solve_map (_metrics, t, t->frame());
985 recompute_meters (_metrics);
992 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
995 Glib::Threads::RWLock::WriterLock lm (lock);
998 remove_meter_locked (ms);
999 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
1001 MeterSection& first (first_meter());
1002 /* cannot move the first meter section */
1003 *static_cast<Meter*>(&first) = meter;
1004 first.set_position_lock_style (AudioTime);
1006 recompute_map (_metrics);
1009 PropertyChanged (PropertyChange ());
1013 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1016 Glib::Threads::RWLock::WriterLock lm (lock);
1018 const double beat = ms.beat();
1019 const BBT_Time bbt = ms.bbt();
1022 remove_meter_locked (ms);
1023 add_meter_locked (meter, frame, beat, bbt, true);
1025 MeterSection& first (first_meter());
1026 TempoSection& first_t (first_tempo());
1027 /* cannot move the first meter section */
1028 *static_cast<Meter*>(&first) = meter;
1029 first.set_position_lock_style (AudioTime);
1030 first.set_pulse (0.0);
1031 first.set_frame (frame);
1032 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1033 first.set_beat (beat);
1034 first_t.set_frame (first.frame());
1035 first_t.set_pulse (0.0);
1036 first_t.set_position_lock_style (AudioTime);
1038 recompute_map (_metrics);
1040 PropertyChanged (PropertyChange ());
1045 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1047 MeterSection* m = 0;
1049 Glib::Threads::RWLock::WriterLock lm (lock);
1050 m = add_meter_locked (meter, beat, where, true);
1055 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1056 dump (_metrics, std::cerr);
1060 PropertyChanged (PropertyChange ());
1066 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1068 MeterSection* m = 0;
1070 Glib::Threads::RWLock::WriterLock lm (lock);
1071 m = add_meter_locked (meter, frame, beat, where, true);
1076 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1077 dump (_metrics, std::cerr);
1081 PropertyChanged (PropertyChange ());
1087 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, bool recompute)
1089 /* a new meter always starts a new bar on the first beat. so
1090 round the start time appropriately. remember that
1091 `where' is based on the existing tempo map, not
1092 the result after we insert the new meter.
1096 const double pulse = pulse_at_beat_locked (_metrics, beat);
1097 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1099 do_insert (new_meter);
1102 solve_map (_metrics, new_meter, where);
1109 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, const Timecode::BBT_Time& where, bool recompute)
1111 /* add meter-locked tempo */
1112 TempoSection* t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1114 t->set_locked_to_meter (true);
1117 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1118 new_meter->set_pulse (pulse_at_frame_locked (_metrics, frame));
1120 do_insert (new_meter);
1123 solve_map (_metrics, new_meter, frame);
1130 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1132 Tempo newtempo (beats_per_minute, note_type);
1135 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1136 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1141 Glib::Threads::RWLock::WriterLock lm (lock);
1142 *((Tempo*) t) = newtempo;
1143 recompute_map (_metrics);
1145 PropertyChanged (PropertyChange ());
1152 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1154 Tempo newtempo (beats_per_minute, note_type);
1157 TempoSection* first;
1158 Metrics::iterator i;
1160 /* find the TempoSection immediately preceding "where"
1163 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1165 if ((*i)->frame() > where) {
1171 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1184 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1194 Glib::Threads::RWLock::WriterLock lm (lock);
1195 /* cannot move the first tempo section */
1196 *((Tempo*)prev) = newtempo;
1197 recompute_map (_metrics);
1200 PropertyChanged (PropertyChange ());
1204 TempoMap::first_meter () const
1206 const MeterSection *m = 0;
1208 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1209 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1214 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1215 abort(); /*NOTREACHED*/
1220 TempoMap::first_meter ()
1222 MeterSection *m = 0;
1224 /* CALLER MUST HOLD LOCK */
1226 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1227 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1232 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1233 abort(); /*NOTREACHED*/
1238 TempoMap::first_tempo () const
1240 const TempoSection *t = 0;
1242 /* CALLER MUST HOLD LOCK */
1244 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1245 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1249 if (!t->movable()) {
1255 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1256 abort(); /*NOTREACHED*/
1261 TempoMap::first_tempo ()
1263 TempoSection *t = 0;
1265 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1266 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1270 if (!t->movable()) {
1276 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1277 abort(); /*NOTREACHED*/
1281 TempoMap::recompute_tempos (Metrics& metrics)
1283 TempoSection* prev_t = 0;
1285 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1288 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1292 if (!t->movable()) {
1300 if (t->position_lock_style() == AudioTime) {
1301 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1302 if (!t->locked_to_meter()) {
1303 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1307 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1308 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1315 prev_t->set_c_func (0.0);
1318 /* tempos must be positioned correctly.
1319 the current approach is to use a meter's bbt time as its base position unit.
1320 this means that a meter's beat may change, but its bbt may not.
1321 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1322 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1325 TempoMap::recompute_meters (Metrics& metrics)
1327 MeterSection* meter = 0;
1328 MeterSection* prev_m = 0;
1330 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1331 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1332 if (meter->position_lock_style() == AudioTime) {
1334 pair<double, BBT_Time> b_bbt;
1335 TempoSection* meter_locked_tempo = 0;
1336 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1338 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1339 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1340 meter_locked_tempo = t;
1347 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1348 if (beats + prev_m->beat() != meter->beat()) {
1349 /* reordering caused a bbt change */
1350 b_bbt = make_pair (beats + prev_m->beat()
1351 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1352 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1354 } else if (meter->movable()) {
1355 b_bbt = make_pair (meter->beat(), meter->bbt());
1356 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1359 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1361 if (meter_locked_tempo) {
1362 meter_locked_tempo->set_pulse (pulse);
1363 recompute_tempos (metrics);
1365 meter->set_beat (b_bbt);
1366 meter->set_pulse (pulse);
1371 pair<double, BBT_Time> new_beat;
1373 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1374 if (beats + prev_m->beat() != meter->beat()) {
1375 /* reordering caused a bbt change */
1376 new_beat = make_pair (beats + prev_m->beat()
1377 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1379 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1381 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1383 /* shouldn't happen - the first is audio-locked */
1384 pulse = pulse_at_beat_locked (metrics, meter->beat());
1385 new_beat = make_pair (meter->beat(), meter->bbt());
1388 meter->set_beat (new_beat);
1389 meter->set_pulse (pulse);
1390 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1399 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1401 /* CALLER MUST HOLD WRITE LOCK */
1405 /* we will actually stop once we hit
1412 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1415 /* silly call from Session::process() during startup
1420 recompute_tempos (metrics);
1421 recompute_meters (metrics);
1425 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1427 Glib::Threads::RWLock::ReaderLock lm (lock);
1428 TempoMetric m (first_meter(), first_tempo());
1430 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1431 at something, because we insert the default tempo and meter during
1432 TempoMap construction.
1434 now see if we can find better candidates.
1437 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1439 if ((*i)->frame() > frame) {
1453 /* XX meters only */
1455 TempoMap::metric_at (BBT_Time bbt) const
1457 Glib::Threads::RWLock::ReaderLock lm (lock);
1458 TempoMetric m (first_meter(), first_tempo());
1460 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1461 at something, because we insert the default tempo and meter during
1462 TempoMap construction.
1464 now see if we can find better candidates.
1467 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1469 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1470 BBT_Time section_start (mw->bbt());
1472 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1484 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1486 MeterSection* prev_m = 0;
1488 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1490 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1491 if (prev_m && m->beat() > beat) {
1498 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1503 TempoMap::pulse_at_beat (const double& beat) const
1505 Glib::Threads::RWLock::ReaderLock lm (lock);
1506 return pulse_at_beat_locked (_metrics, beat);
1510 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1512 MeterSection* prev_m = 0;
1514 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1516 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1517 if (prev_m && m->pulse() > pulse) {
1518 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1526 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1531 TempoMap::beat_at_pulse (const double& pulse) const
1533 Glib::Threads::RWLock::ReaderLock lm (lock);
1534 return beat_at_pulse_locked (_metrics, pulse);
1537 /* tempo section based */
1539 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1541 /* HOLD (at least) THE READER LOCK */
1542 TempoSection* prev_t = 0;
1544 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1546 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1550 if (prev_t && t->frame() > frame) {
1551 /*the previous ts is the one containing the frame */
1552 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1559 /* treated as constant for this ts */
1560 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1562 return pulses_in_section + prev_t->pulse();
1566 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1568 Glib::Threads::RWLock::ReaderLock lm (lock);
1569 return pulse_at_frame_locked (_metrics, frame);
1572 /* tempo section based */
1574 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1576 /* HOLD THE READER LOCK */
1578 const TempoSection* prev_t = 0;
1580 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1583 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1587 if (prev_t && t->pulse() > pulse) {
1588 return prev_t->frame_at_pulse (pulse, _frame_rate);
1594 /* must be treated as constant, irrespective of _type */
1595 double const pulses_in_section = pulse - prev_t->pulse();
1596 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1598 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1604 TempoMap::frame_at_pulse (const double& pulse) const
1606 Glib::Threads::RWLock::ReaderLock lm (lock);
1607 return frame_at_pulse_locked (_metrics, pulse);
1610 /* meter section based */
1612 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1614 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1615 MeterSection* prev_m = 0;
1616 MeterSection* next_m = 0;
1618 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1620 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1621 if (prev_m && m->frame() > frame) {
1628 if (frame < prev_m->frame()) {
1631 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1633 if (next_m && next_m->beat() < beat) {
1634 return next_m->beat();
1641 TempoMap::beat_at_frame (const framecnt_t& frame) const
1643 Glib::Threads::RWLock::ReaderLock lm (lock);
1644 return beat_at_frame_locked (_metrics, frame);
1647 /* meter section based */
1649 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1651 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1652 MeterSection* prev_m = 0;
1654 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1656 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1657 if (prev_m && m->beat() > beat) {
1664 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1668 TempoMap::frame_at_beat (const double& beat) const
1670 Glib::Threads::RWLock::ReaderLock lm (lock);
1671 return frame_at_beat_locked (_metrics, beat);
1675 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1677 /* CALLER HOLDS READ LOCK */
1679 MeterSection* prev_m = 0;
1681 /* because audio-locked meters have 'fake' integral beats,
1682 there is no pulse offset here.
1684 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1686 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1688 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1689 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1697 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1698 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1699 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1705 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1707 Glib::Threads::RWLock::ReaderLock lm (lock);
1708 return bbt_to_beats_locked (_metrics, bbt);
1712 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1714 /* CALLER HOLDS READ LOCK */
1715 MeterSection* prev_m = 0;
1716 const double beats = (b < 0.0) ? 0.0 : b;
1718 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1719 MeterSection* m = 0;
1721 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1723 if (m->beat() > beats) {
1724 /* this is the meter after the one our beat is on*/
1733 const double beats_in_ms = beats - prev_m->beat();
1734 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1735 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1736 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1737 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1741 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1742 ret.beats = (uint32_t) floor (remaining_beats);
1743 ret.bars = total_bars;
1745 /* 0 0 0 to 1 1 0 - based mapping*/
1749 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1751 ret.ticks -= BBT_Time::ticks_per_beat;
1754 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1763 TempoMap::beats_to_bbt (const double& beats)
1765 Glib::Threads::RWLock::ReaderLock lm (lock);
1766 return beats_to_bbt_locked (_metrics, beats);
1770 TempoMap::pulse_to_bbt (const double& pulse)
1772 Glib::Threads::RWLock::ReaderLock lm (lock);
1773 MeterSection* prev_m = 0;
1775 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1776 MeterSection* m = 0;
1778 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1781 double const pulses_to_m = m->pulse() - prev_m->pulse();
1782 if (prev_m->pulse() + pulses_to_m > pulse) {
1783 /* this is the meter after the one our beat is on*/
1792 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1793 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1794 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1795 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1796 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1800 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1801 ret.beats = (uint32_t) floor (remaining_beats);
1802 ret.bars = total_bars;
1804 /* 0 0 0 to 1 1 0 mapping*/
1808 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1810 ret.ticks -= BBT_Time::ticks_per_beat;
1813 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1822 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1829 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1832 Glib::Threads::RWLock::ReaderLock lm (lock);
1833 const double beat = beat_at_frame_locked (_metrics, frame);
1835 bbt = beats_to_bbt_locked (_metrics, beat);
1838 /* meter section based */
1840 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1842 /* HOLD THE READER LOCK */
1844 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1849 TempoMap::frame_time (const BBT_Time& bbt)
1852 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1856 if (bbt.beats < 1) {
1857 throw std::logic_error ("beats are counted from one");
1859 Glib::Threads::RWLock::ReaderLock lm (lock);
1861 return frame_time_locked (_metrics, bbt);
1865 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1867 TempoSection* prev_t = 0;
1868 MeterSection* prev_m = 0;
1870 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1873 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1878 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1882 if (t->frame() == prev_t->frame()) {
1886 /* precision check ensures pulses and frames align.*/
1887 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1888 if (!t->locked_to_meter()) {
1896 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1897 if (prev_m && m->position_lock_style() == AudioTime) {
1898 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1899 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1900 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1902 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1916 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1918 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1920 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1921 if (!t->movable()) {
1922 t->set_active (true);
1925 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1926 t->set_active (false);
1928 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1929 t->set_active (true);
1930 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1939 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1941 TempoSection* prev_t = 0;
1942 TempoSection* section_prev = 0;
1943 framepos_t first_m_frame = 0;
1945 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1947 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1948 if (!m->movable()) {
1949 first_m_frame = m->frame();
1954 if (section->movable() && frame <= first_m_frame) {
1958 section->set_active (true);
1959 section->set_frame (frame);
1961 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1963 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1970 section_prev = prev_t;
1973 if (t->position_lock_style() == MusicTime) {
1974 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1975 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1977 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1978 if (!t->locked_to_meter()) {
1979 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1988 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1989 if (!section->locked_to_meter()) {
1990 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1994 if (section->position_lock_style() == MusicTime) {
1995 /* we're setting the frame */
1996 section->set_position_lock_style (AudioTime);
1997 recompute_tempos (imaginary);
1998 section->set_position_lock_style (MusicTime);
2000 recompute_tempos (imaginary);
2003 if (check_solved (imaginary, true)) {
2007 MetricSectionFrameSorter fcmp;
2008 imaginary.sort (fcmp);
2009 if (section->position_lock_style() == MusicTime) {
2010 /* we're setting the frame */
2011 section->set_position_lock_style (AudioTime);
2012 recompute_tempos (imaginary);
2013 section->set_position_lock_style (MusicTime);
2015 recompute_tempos (imaginary);
2018 if (check_solved (imaginary, true)) {
2026 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
2028 TempoSection* prev_t = 0;
2029 TempoSection* section_prev = 0;
2031 section->set_pulse (pulse);
2033 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2035 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2039 if (!t->movable()) {
2046 section_prev = prev_t;
2049 if (t->position_lock_style() == MusicTime) {
2050 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2051 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2053 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2054 if (!t->locked_to_meter()) {
2055 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2063 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2064 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2067 if (section->position_lock_style() == AudioTime) {
2068 /* we're setting the pulse */
2069 section->set_position_lock_style (MusicTime);
2070 recompute_tempos (imaginary);
2071 section->set_position_lock_style (AudioTime);
2073 recompute_tempos (imaginary);
2076 if (check_solved (imaginary, false)) {
2080 MetricSectionSorter cmp;
2081 imaginary.sort (cmp);
2082 if (section->position_lock_style() == AudioTime) {
2083 /* we're setting the pulse */
2084 section->set_position_lock_style (MusicTime);
2085 recompute_tempos (imaginary);
2086 section->set_position_lock_style (AudioTime);
2088 recompute_tempos (imaginary);
2091 if (check_solved (imaginary, false)) {
2099 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2101 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2102 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2103 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2107 if (!section->movable()) {
2108 /* lock the first tempo to our first meter */
2109 if (!set_active_tempos (imaginary, frame)) {
2114 /* it would make sense to bail out if there is no audio-locked meter,
2115 however it may be desirable to move a music-locked meter by frame at some point.
2117 TempoSection* meter_locked_tempo = 0;
2118 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2120 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2121 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2122 meter_locked_tempo = t;
2128 MeterSection* prev_m = 0;
2130 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2132 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2134 if (prev_m && section->movable()) {
2135 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2136 if (beats + prev_m->beat() < section->beat()) {
2137 /* disallow position change if it will alter our beat
2138 we allow tempo changes to do this in recompute_meters().
2139 blocking this is an option, but i'm not convinced that
2140 this is what the user would actually want.
2141 here we set the frame/pulse corresponding to its musical position.
2144 if (meter_locked_tempo) {
2146 bool solved = false;
2147 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2148 const double new_pulse = ((section->beat() - prev_m->beat())
2149 / prev_m->note_divisor()) + prev_m->pulse();
2150 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2151 if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
2152 meter_locked_tempo->set_pulse (new_pulse);
2153 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2154 section->set_frame (smallest_frame);
2155 section->set_pulse (new_pulse);
2160 Metrics::const_iterator d = future_map.begin();
2161 while (d != future_map.end()) {
2172 if (meter_locked_tempo) {
2174 bool solved = false;
2176 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2177 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2178 meter_copy->set_frame (frame);
2180 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2181 section->set_frame (frame);
2182 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2183 / prev_m->note_divisor()) + prev_m->pulse());
2184 solve_map (imaginary, meter_locked_tempo, frame);
2189 Metrics::const_iterator d = future_map.begin();
2190 while (d != future_map.end()) {
2201 /* not movable (first meter atm) */
2202 if (meter_locked_tempo) {
2204 bool solved = false;
2205 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2207 tempo_copy->set_frame (frame);
2208 tempo_copy->set_pulse (0.0);
2210 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2211 section->set_frame (frame);
2212 meter_locked_tempo->set_frame (frame);
2213 meter_locked_tempo->set_pulse (0.0);
2214 solve_map (imaginary, meter_locked_tempo, frame);
2219 Metrics::const_iterator d = future_map.begin();
2220 while (d != future_map.end()) {
2232 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2233 section->set_beat (b_bbt);
2234 section->set_pulse (0.0);
2244 MetricSectionFrameSorter fcmp;
2245 imaginary.sort (fcmp);
2246 if (section->position_lock_style() == MusicTime) {
2247 /* we're setting the frame */
2248 section->set_position_lock_style (AudioTime);
2249 recompute_meters (imaginary);
2250 section->set_position_lock_style (MusicTime);
2252 recompute_meters (imaginary);
2259 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2261 /* disallow setting section to an existing meter's bbt */
2262 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2264 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2265 if (m != section && m->bbt().bars == when.bars) {
2271 MeterSection* prev_m = 0;
2272 MeterSection* section_prev = 0;
2274 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2276 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2277 pair<double, BBT_Time> b_bbt;
2278 double new_pulse = 0.0;
2280 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2281 section_prev = prev_m;
2282 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2283 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2284 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2286 section->set_beat (b_bbt);
2287 section->set_pulse (pulse);
2288 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2293 if (m->position_lock_style() == AudioTime) {
2294 TempoSection* meter_locked_tempo = 0;
2295 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2297 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2298 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2299 meter_locked_tempo = t;
2306 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2308 if (beats + prev_m->beat() != m->beat()) {
2309 /* tempo/ meter change caused a change in beat (bar). */
2310 b_bbt = make_pair (beats + prev_m->beat()
2311 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2312 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2313 } else if (m->movable()) {
2314 b_bbt = make_pair (m->beat(), m->bbt());
2315 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2318 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2320 if (meter_locked_tempo) {
2321 meter_locked_tempo->set_pulse (new_pulse);
2322 recompute_tempos (imaginary);
2324 m->set_beat (b_bbt);
2325 m->set_pulse (new_pulse);
2329 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2330 if (beats + prev_m->beat() != m->beat()) {
2331 /* tempo/ meter change caused a change in beat (bar). */
2332 b_bbt = make_pair (beats + prev_m->beat()
2333 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2335 b_bbt = make_pair (beats + prev_m->beat()
2338 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2339 m->set_beat (b_bbt);
2340 m->set_pulse (new_pulse);
2341 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2348 if (!section_prev) {
2350 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2351 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2352 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2354 section->set_beat (b_bbt);
2355 section->set_pulse (pulse);
2356 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2359 MetricSectionSorter cmp;
2360 imaginary.sort (cmp);
2362 if (section->position_lock_style() == AudioTime) {
2363 /* we're setting the pulse */
2364 section->set_position_lock_style (MusicTime);
2365 recompute_meters (imaginary);
2366 section->set_position_lock_style (AudioTime);
2368 recompute_meters (imaginary);
2374 /** places a copy of _metrics into copy and returns a pointer
2375 * to section's equivalent in copy.
2378 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2380 TempoSection* ret = 0;
2382 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2385 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2387 ret = new TempoSection (*t);
2388 copy.push_back (ret);
2392 TempoSection* cp = new TempoSection (*t);
2393 copy.push_back (cp);
2395 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2396 MeterSection* cp = new MeterSection (*m);
2397 copy.push_back (cp);
2405 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2407 MeterSection* ret = 0;
2409 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2412 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2413 TempoSection* cp = new TempoSection (*t);
2414 copy.push_back (cp);
2417 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2419 ret = new MeterSection (*m);
2420 copy.push_back (ret);
2423 MeterSection* cp = new MeterSection (*m);
2424 copy.push_back (cp);
2432 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2435 TempoSection* tempo_copy = 0;
2438 Glib::Threads::RWLock::ReaderLock lm (lock);
2439 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2445 const double beat = bbt_to_beats_locked (copy, bbt);
2446 const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2448 Metrics::const_iterator d = copy.begin();
2449 while (d != copy.end()) {
2458 * 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,
2459 * taking any possible reordering as a consequence of this into account.
2460 * @param section - the section to be altered
2461 * @param bpm - the new Tempo
2462 * @param bbt - the bbt where the altered tempo will fall
2463 * @return returns - the position in frames where the new tempo section will lie.
2466 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2468 Glib::Threads::RWLock::ReaderLock lm (lock);
2471 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2475 const double beat = bbt_to_beats_locked (future_map, bbt);
2477 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2478 ret = tempo_copy->frame();
2480 ret = section->frame();
2483 Metrics::const_iterator d = future_map.begin();
2484 while (d != future_map.end()) {
2492 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2494 Glib::Threads::RWLock::ReaderLock lm (lock);
2497 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2499 if (solve_map (future_map, tempo_copy, frame)) {
2500 ret = tempo_copy->pulse();
2502 ret = section->pulse();
2505 Metrics::const_iterator d = future_map.begin();
2506 while (d != future_map.end()) {
2514 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2518 Glib::Threads::RWLock::WriterLock lm (lock);
2519 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2520 if (solve_map (future_map, tempo_copy, frame)) {
2521 solve_map (_metrics, ts, frame);
2522 recompute_meters (_metrics);
2526 Metrics::const_iterator d = future_map.begin();
2527 while (d != future_map.end()) {
2532 MetricPositionChanged (); // Emit Signal
2536 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2540 Glib::Threads::RWLock::WriterLock lm (lock);
2541 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2542 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2543 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2544 recompute_meters (_metrics);
2548 Metrics::const_iterator d = future_map.begin();
2549 while (d != future_map.end()) {
2554 MetricPositionChanged (); // Emit Signal
2558 TempoMap::gui_move_tempo_pulse (TempoSection* ts, const double& pulse)
2562 Glib::Threads::RWLock::WriterLock lm (lock);
2563 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2564 if (solve_map (future_map, tempo_copy, pulse)) {
2565 solve_map (_metrics, ts, pulse);
2566 recompute_meters (_metrics);
2570 Metrics::const_iterator d = future_map.begin();
2571 while (d != future_map.end()) {
2576 MetricPositionChanged (); // Emit Signal
2580 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2584 Glib::Threads::RWLock::WriterLock lm (lock);
2585 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2586 if (solve_map (future_map, copy, frame)) {
2587 solve_map (_metrics, ms, frame);
2588 recompute_tempos (_metrics);
2592 Metrics::const_iterator d = future_map.begin();
2593 while (d != future_map.end()) {
2598 MetricPositionChanged (); // Emit Signal
2602 TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
2606 Glib::Threads::RWLock::WriterLock lm (lock);
2607 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2608 if (solve_map (future_map, copy, bbt)) {
2609 solve_map (_metrics, ms, bbt);
2610 recompute_tempos (_metrics);
2614 Metrics::const_iterator d = future_map.begin();
2615 while (d != future_map.end()) {
2620 MetricPositionChanged (); // Emit Signal
2624 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2627 bool can_solve = false;
2629 Glib::Threads::RWLock::WriterLock lm (lock);
2630 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2631 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2632 recompute_tempos (future_map);
2634 if (check_solved (future_map, true)) {
2635 ts->set_beats_per_minute (bpm.beats_per_minute());
2636 recompute_map (_metrics);
2641 Metrics::const_iterator d = future_map.begin();
2642 while (d != future_map.end()) {
2647 MetricPositionChanged (); // Emit Signal
2653 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2656 TempoSection* ts = 0;
2658 if (ms->position_lock_style() == AudioTime) {
2659 /* disabled for now due to faked tempo locked to meter pulse */
2664 Glib::Threads::RWLock::WriterLock lm (lock);
2665 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2669 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2670 TempoSection* prev_to_prev_t = 0;
2671 const frameoffset_t fr_off = frame - ms->frame();
2672 double new_bpm = 0.0;
2674 if (prev_t && prev_t->pulse() > 0.0) {
2675 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2678 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2679 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2681 double contribution = 0.0;
2682 frameoffset_t frame_contribution = 0.0;
2683 frameoffset_t prev_t_frame_contribution = 0.0;
2685 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2686 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2687 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2688 frame_contribution = contribution * (double) fr_off;
2689 prev_t_frame_contribution = fr_off - frame_contribution;
2692 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2694 if (prev_t->position_lock_style() == MusicTime) {
2695 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2696 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2697 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2700 /* prev to prev is irrelevant */
2701 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2702 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2704 if (frame_pulse != prev_t->pulse()) {
2705 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2707 new_bpm = prev_t->beats_per_minute();
2712 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2713 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2714 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2716 /* prev_to_prev_t is irrelevant */
2718 if (frame != prev_t->frame()) {
2719 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2721 new_bpm = prev_t->beats_per_minute();
2725 } else if (prev_t->c_func() < 0.0) {
2726 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2727 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2729 /* prev_to_prev_t is irrelevant */
2730 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2733 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2734 if (diff > -0.1 && diff < 0.1) {
2735 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2736 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2739 } else if (prev_t->c_func() > 0.0) {
2740 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2741 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2743 /* prev_to_prev_t is irrelevant */
2744 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2747 /* limits - a bit clunky, but meh */
2748 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2749 if (diff > -0.1 && diff < 0.1) {
2750 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2751 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2755 prev_t->set_beats_per_minute (new_bpm);
2756 recompute_tempos (future_map);
2757 recompute_meters (future_map);
2759 if (check_solved (future_map, true)) {
2761 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2762 prev_t->set_beats_per_minute (new_bpm);
2763 recompute_tempos (_metrics);
2765 if (ms->position_lock_style() == AudioTime) {
2766 ms->set_frame (frame);
2769 recompute_meters (_metrics);
2773 Metrics::const_iterator d = future_map.begin();
2774 while (d != future_map.end()) {
2779 MetricPositionChanged (); // Emit Signal
2783 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2785 Glib::Threads::RWLock::ReaderLock lm (lock);
2787 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2788 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2789 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2791 return frame_at_beat_locked (_metrics, total_beats);
2795 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2797 return round_to_type (fr, dir, Bar);
2801 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2803 return round_to_type (fr, dir, Beat);
2807 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2809 Glib::Threads::RWLock::ReaderLock lm (lock);
2810 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2811 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2812 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2814 ticks -= beats * BBT_Time::ticks_per_beat;
2817 /* round to next (or same iff dir == RoundUpMaybe) */
2819 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2821 if (mod == 0 && dir == RoundUpMaybe) {
2822 /* right on the subdivision, which is fine, so do nothing */
2824 } else if (mod == 0) {
2825 /* right on the subdivision, so the difference is just the subdivision ticks */
2826 ticks += ticks_one_subdivisions_worth;
2829 /* not on subdivision, compute distance to next subdivision */
2831 ticks += ticks_one_subdivisions_worth - mod;
2834 if (ticks >= BBT_Time::ticks_per_beat) {
2835 ticks -= BBT_Time::ticks_per_beat;
2837 } else if (dir < 0) {
2839 /* round to previous (or same iff dir == RoundDownMaybe) */
2841 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2843 if (difference == 0 && dir == RoundDownAlways) {
2844 /* right on the subdivision, but force-rounding down,
2845 so the difference is just the subdivision ticks */
2846 difference = ticks_one_subdivisions_worth;
2849 if (ticks < difference) {
2850 ticks = BBT_Time::ticks_per_beat - ticks;
2852 ticks -= difference;
2856 /* round to nearest */
2859 /* compute the distance to the previous and next subdivision */
2861 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2863 /* closer to the next subdivision, so shift forward */
2865 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2867 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2869 if (ticks > BBT_Time::ticks_per_beat) {
2871 ticks -= BBT_Time::ticks_per_beat;
2872 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2875 } else if (rem > 0) {
2877 /* closer to previous subdivision, so shift backward */
2881 /* can't go backwards past zero, so ... */
2884 /* step back to previous beat */
2886 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2887 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2889 ticks = lrint (ticks - rem);
2890 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2893 /* on the subdivision, do nothing */
2897 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2903 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
2905 if (sub_num == -1) {
2910 } else if (dir < 0) {
2914 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2915 if ((double) when.beats > bpb / 2.0) {
2924 } else if (sub_num == 0) {
2925 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2926 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2928 while ((double) when.beats > bpb) {
2930 when.beats -= (uint32_t) floor (bpb);
2938 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2941 /* round to next (or same iff dir == RoundUpMaybe) */
2943 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
2945 if (mod == 0 && dir == RoundUpMaybe) {
2946 /* right on the subdivision, which is fine, so do nothing */
2948 } else if (mod == 0) {
2949 /* right on the subdivision, so the difference is just the subdivision ticks */
2950 when.ticks += ticks_one_subdivisions_worth;
2953 /* not on subdivision, compute distance to next subdivision */
2955 when.ticks += ticks_one_subdivisions_worth - mod;
2958 if (when.ticks >= BBT_Time::ticks_per_beat) {
2959 when.ticks -= BBT_Time::ticks_per_beat;
2962 } else if (dir < 0) {
2963 /* round to previous (or same iff dir == RoundDownMaybe) */
2965 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
2967 if (difference == 0 && dir == RoundDownAlways) {
2968 /* right on the subdivision, but force-rounding down,
2969 so the difference is just the subdivision ticks */
2970 difference = ticks_one_subdivisions_worth;
2973 if (when.ticks < difference) {
2974 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
2976 when.ticks -= difference;
2980 /* round to nearest */ double rem;
2981 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2982 /* closer to the next subdivision, so shift forward */
2984 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2986 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2988 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2991 } else if (rem > 0) {
2992 /* closer to previous subdivision, so shift backward */
2994 if (rem > when.ticks) {
2995 if (when.beats == 0) {
2996 /* can't go backwards past zero, so ... */
2998 /* step back to previous beat */
3000 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
3002 when.ticks = when.ticks - rem;
3009 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3011 Glib::Threads::RWLock::ReaderLock lm (lock);
3013 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3014 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3019 /* find bar previous to 'frame' */
3022 return frame_time_locked (_metrics, bbt);
3024 } else if (dir > 0) {
3025 /* find bar following 'frame' */
3029 return frame_time_locked (_metrics, bbt);
3031 /* true rounding: find nearest bar */
3032 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3035 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3037 framepos_t next_ft = frame_time_locked (_metrics, bbt);
3039 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3050 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3051 } else if (dir > 0) {
3052 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3054 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3063 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3064 framepos_t lower, framepos_t upper)
3066 Glib::Threads::RWLock::ReaderLock lm (lock);
3067 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3069 /* although the map handles negative beats, bbt doesn't. */
3073 while (pos < upper) {
3074 pos = frame_at_beat_locked (_metrics, cnt);
3075 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3076 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3077 const BBT_Time bbt = beats_to_bbt (cnt);
3078 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3084 TempoMap::tempo_section_at (framepos_t frame) const
3086 Glib::Threads::RWLock::ReaderLock lm (lock);
3087 return tempo_section_at_locked (_metrics, frame);
3091 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3093 Metrics::const_iterator i;
3094 TempoSection* prev = 0;
3096 for (i = metrics.begin(); i != metrics.end(); ++i) {
3099 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3103 if (prev && t->frame() > frame) {
3113 abort(); /*NOTREACHED*/
3120 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3122 TempoSection* prev_t = 0;
3123 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3125 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3127 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3128 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3139 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3141 TempoSection* prev_t = 0;
3143 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3145 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3146 if (prev_t && t->pulse() > pulse) {
3156 /* don't use this to calculate length (the tempo is only correct for this frame).
3157 do that stuff based on the beat_at_frame and frame_at_beat api
3160 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3162 Glib::Threads::RWLock::ReaderLock lm (lock);
3164 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3165 const TempoSection* ts_after = 0;
3166 Metrics::const_iterator i;
3168 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3171 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3175 if ((*i)->frame() > frame) {
3183 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3185 /* must be treated as constant tempo */
3186 return ts_at->frames_per_beat (_frame_rate);
3190 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3192 TempoSection* prev_t = 0;
3194 Metrics::const_iterator i;
3196 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3198 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3202 if ((prev_t) && t->frame() > frame) {
3203 /* t is the section past frame */
3204 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3205 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3212 const double ret = prev_t->beats_per_minute();
3213 const Tempo ret_tempo (ret, prev_t->note_type ());
3219 TempoMap::tempo_at (const framepos_t& frame) const
3221 Glib::Threads::RWLock::ReaderLock lm (lock);
3222 return tempo_at_locked (_metrics, frame);
3226 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3228 Metrics::const_iterator i;
3229 MeterSection* prev = 0;
3231 for (i = metrics.begin(); i != metrics.end(); ++i) {
3234 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3236 if (prev && (*i)->frame() > frame) {
3246 abort(); /*NOTREACHED*/
3254 TempoMap::meter_section_at (framepos_t frame) const
3256 Glib::Threads::RWLock::ReaderLock lm (lock);
3257 return meter_section_at_locked (_metrics, frame);
3261 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3263 MeterSection* prev_m = 0;
3265 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3267 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3268 if (prev_m && m->beat() > beat) {
3279 TempoMap::meter_section_at_beat (double beat) const
3281 Glib::Threads::RWLock::ReaderLock lm (lock);
3282 return meter_section_at_beat_locked (_metrics, beat);
3286 TempoMap::meter_at (framepos_t frame) const
3288 TempoMetric m (metric_at (frame));
3293 TempoMap::fix_legacy_session ()
3295 MeterSection* prev_m = 0;
3296 TempoSection* prev_t = 0;
3298 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3302 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3303 if (!m->movable()) {
3304 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3308 m->set_position_lock_style (AudioTime);
3313 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3314 + (m->bbt().beats - 1)
3315 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3317 m->set_beat (start);
3318 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3319 + (m->bbt().beats - 1)
3320 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3321 m->set_pulse (start_beat / prev_m->note_divisor());
3324 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3330 if (!t->movable()) {
3333 t->set_position_lock_style (AudioTime);
3339 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3340 + (t->legacy_bbt().beats - 1)
3341 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3343 t->set_pulse (beat / prev_m->note_divisor());
3345 /* really shouldn't happen but.. */
3346 t->set_pulse (beat / 4.0);
3355 TempoMap::get_state ()
3357 Metrics::const_iterator i;
3358 XMLNode *root = new XMLNode ("TempoMap");
3361 Glib::Threads::RWLock::ReaderLock lm (lock);
3362 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3363 root->add_child_nocopy ((*i)->get_state());
3371 TempoMap::set_state (const XMLNode& node, int /*version*/)
3374 Glib::Threads::RWLock::WriterLock lm (lock);
3377 XMLNodeConstIterator niter;
3378 Metrics old_metrics (_metrics);
3381 nlist = node.children();
3383 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3384 XMLNode* child = *niter;
3386 if (child->name() == TempoSection::xml_state_node_name) {
3389 TempoSection* ts = new TempoSection (*child);
3390 _metrics.push_back (ts);
3393 catch (failed_constructor& err){
3394 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3395 _metrics = old_metrics;
3399 } else if (child->name() == MeterSection::xml_state_node_name) {
3402 MeterSection* ms = new MeterSection (*child);
3403 _metrics.push_back (ms);
3406 catch (failed_constructor& err) {
3407 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3408 _metrics = old_metrics;
3414 if (niter == nlist.end()) {
3415 MetricSectionSorter cmp;
3416 _metrics.sort (cmp);
3419 /* check for legacy sessions where bbt was the base musical unit for tempo */
3420 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3422 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3423 if (t->legacy_bbt().bars != 0) {
3424 fix_legacy_session();
3431 /* check for multiple tempo/meters at the same location, which
3432 ardour2 somehow allowed.
3435 Metrics::iterator prev = _metrics.end();
3436 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3437 if (prev != _metrics.end()) {
3439 MeterSection* prev_m;
3441 TempoSection* prev_t;
3442 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3443 if (prev_m->pulse() == ms->pulse()) {
3444 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3445 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3448 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3449 if (prev_t->pulse() == ts->pulse()) {
3450 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3451 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3459 recompute_map (_metrics);
3462 PropertyChanged (PropertyChange ());
3468 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3470 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3471 const MeterSection* m;
3472 const TempoSection* t;
3473 const TempoSection* prev_t = 0;
3475 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3477 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3478 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3479 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3480 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3482 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3483 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;
3486 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3487 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3488 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3491 o << "------" << std::endl;
3495 TempoMap::n_tempos() const
3497 Glib::Threads::RWLock::ReaderLock lm (lock);
3500 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3501 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3510 TempoMap::n_meters() const
3512 Glib::Threads::RWLock::ReaderLock lm (lock);
3515 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3516 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3525 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3528 Glib::Threads::RWLock::WriterLock lm (lock);
3529 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3530 if ((*i)->frame() >= where && (*i)->movable ()) {
3531 (*i)->set_frame ((*i)->frame() + amount);
3535 /* now reset the BBT time of all metrics, based on their new
3536 * audio time. This is the only place where we do this reverse
3540 Metrics::iterator i;
3541 const MeterSection* meter;
3542 const TempoSection* tempo;
3546 meter = &first_meter ();
3547 tempo = &first_tempo ();
3552 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3555 MetricSection* prev = 0;
3557 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3560 //TempoMetric metric (*meter, *tempo);
3561 MeterSection* ms = const_cast<MeterSection*>(meter);
3562 TempoSection* ts = const_cast<TempoSection*>(tempo);
3565 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3569 ts->set_pulse (t->pulse());
3571 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3572 ts->set_pulse (m->pulse());
3574 ts->set_frame (prev->frame());
3578 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3579 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3580 ms->set_beat (start);
3581 ms->set_pulse (m->pulse());
3583 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3587 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3588 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3589 ms->set_beat (start);
3590 ms->set_pulse (t->pulse());
3592 ms->set_frame (prev->frame());
3596 // metric will be at frames=0 bbt=1|1|0 by default
3597 // which is correct for our purpose
3600 // cerr << bbt << endl;
3602 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3606 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3608 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3609 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3610 bbt_time (m->frame(), bbt);
3612 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3618 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3619 /* round up to next beat */
3625 if (bbt.beats != 1) {
3626 /* round up to next bar */
3631 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3632 m->set_beat (start);
3633 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3635 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3637 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3638 abort(); /*NOTREACHED*/
3644 recompute_map (_metrics);
3648 PropertyChanged (PropertyChange ());
3651 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3655 std::list<MetricSection*> metric_kill_list;
3657 TempoSection* last_tempo = NULL;
3658 MeterSection* last_meter = NULL;
3659 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3660 bool meter_after = false; // is there a meter marker likewise?
3662 Glib::Threads::RWLock::WriterLock lm (lock);
3663 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3664 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3665 metric_kill_list.push_back(*i);
3666 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3669 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3673 else if ((*i)->frame() >= where) {
3674 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3675 (*i)->set_frame ((*i)->frame() - amount);
3676 if ((*i)->frame() == where) {
3677 // marker was immediately after end of range
3678 tempo_after = dynamic_cast<TempoSection*> (*i);
3679 meter_after = dynamic_cast<MeterSection*> (*i);
3685 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3686 if (last_tempo && !tempo_after) {
3687 metric_kill_list.remove(last_tempo);
3688 last_tempo->set_frame(where);
3691 if (last_meter && !meter_after) {
3692 metric_kill_list.remove(last_meter);
3693 last_meter->set_frame(where);
3697 //remove all the remaining metrics
3698 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3699 _metrics.remove(*i);
3704 recompute_map (_metrics);
3707 PropertyChanged (PropertyChange ());
3711 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3712 * pos can be -ve, if required.
3715 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3717 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3720 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3722 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3724 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3727 /** Add the BBT interval op to pos and return the result */
3729 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3731 Glib::Threads::RWLock::ReaderLock lm (lock);
3733 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3734 pos_bbt.ticks += op.ticks;
3735 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3737 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3739 pos_bbt.beats += op.beats;
3740 /* the meter in effect will start on the bar */
3741 double divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3742 while (pos_bbt.beats >= divisions_per_bar + 1) {
3744 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3745 pos_bbt.beats -= divisions_per_bar;
3747 pos_bbt.bars += op.bars;
3749 return frame_time_locked (_metrics, pos_bbt);
3752 /** Count the number of beats that are equivalent to distance when going forward,
3756 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3758 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3762 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3768 operator<< (std::ostream& o, const Meter& m) {
3769 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3773 operator<< (std::ostream& o, const Tempo& t) {
3774 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3778 operator<< (std::ostream& o, const MetricSection& section) {
3780 o << "MetricSection @ " << section.frame() << ' ';
3782 const TempoSection* ts;
3783 const MeterSection* ms;
3785 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3786 o << *((const Tempo*) ts);
3787 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3788 o << *((const Meter*) ms);