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 frameoffset_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 - (frameoffset_t) 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 (frameoffset_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 (frameoffset_t) floor ((time * 60.0 * (frameoffset_t) frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const frameoffset_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 ()
660 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
662 bool removed = false;
665 Glib::Threads::RWLock::WriterLock lm (lock);
666 if ((removed = remove_tempo_locked (tempo))) {
667 if (complete_operation) {
668 recompute_map (_metrics);
673 if (removed && complete_operation) {
674 PropertyChanged (PropertyChange ());
679 TempoMap::remove_tempo_locked (const TempoSection& tempo)
683 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
684 if (dynamic_cast<TempoSection*> (*i) != 0) {
685 if (tempo.frame() == (*i)->frame()) {
686 if ((*i)->movable()) {
699 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
701 bool removed = false;
704 Glib::Threads::RWLock::WriterLock lm (lock);
705 if ((removed = remove_meter_locked (tempo))) {
706 if (complete_operation) {
707 recompute_map (_metrics);
712 if (removed && complete_operation) {
713 PropertyChanged (PropertyChange ());
718 TempoMap::remove_meter_locked (const MeterSection& meter)
722 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
724 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
725 if (meter.frame() == (*i)->frame()) {
726 if (t->locked_to_meter()) {
735 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
736 if (dynamic_cast<MeterSection*> (*i) != 0) {
737 if (meter.frame() == (*i)->frame()) {
738 if ((*i)->movable()) {
751 TempoMap::do_insert (MetricSection* section)
753 bool need_add = true;
754 /* we only allow new meters to be inserted on beat 1 of an existing
758 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
759 //assert (m->bbt().ticks == 0);
761 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
763 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
764 corrected.second.beats = 1;
765 corrected.second.ticks = 0;
766 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
767 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
768 m->bbt(), corrected.second) << endmsg;
769 //m->set_pulse (corrected);
773 /* Look for any existing MetricSection that is of the same type and
774 in the same bar as the new one, and remove it before adding
775 the new one. Note that this means that if we find a matching,
776 existing section, we can break out of the loop since we're
777 guaranteed that there is only one such match.
780 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
782 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
783 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
784 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
785 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
787 if (tempo && insert_tempo) {
790 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
791 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
793 if (!tempo->movable()) {
795 /* can't (re)move this section, so overwrite
796 * its data content (but not its properties as
800 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
801 (*i)->set_position_lock_style (AudioTime);
803 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
804 t->set_type (insert_tempo->type());
814 } else if (meter && insert_meter) {
818 bool const ipm = insert_meter->position_lock_style() == MusicTime;
820 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
822 if (!meter->movable()) {
824 /* can't (re)move this section, so overwrite
825 * its data content (but not its properties as
829 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
830 (*i)->set_position_lock_style (AudioTime);
840 /* non-matching types, so we don't care */
844 /* Add the given MetricSection, if we didn't just reset an existing
849 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
850 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
853 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
854 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
857 bool const ipm = insert_meter->position_lock_style() == MusicTime;
858 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
863 } else if (insert_tempo) {
864 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
865 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
868 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
869 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
876 _metrics.insert (i, section);
877 //dump (_metrics, std::cerr);
882 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
885 Glib::Threads::RWLock::WriterLock lm (lock);
886 TempoSection& first (first_tempo());
887 if (ts.pulse() != first.pulse()) {
888 remove_tempo_locked (ts);
889 add_tempo_locked (tempo, pulse, true, type);
891 first.set_type (type);
893 /* cannot move the first tempo section */
894 *static_cast<Tempo*>(&first) = tempo;
895 recompute_map (_metrics);
900 PropertyChanged (PropertyChange ());
904 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
907 Glib::Threads::RWLock::WriterLock lm (lock);
908 TempoSection& first (first_tempo());
909 if (ts.frame() != first.frame()) {
910 remove_tempo_locked (ts);
911 add_tempo_locked (tempo, frame, true, type);
913 first.set_type (type);
914 first.set_pulse (0.0);
915 first.set_position_lock_style (AudioTime);
917 /* cannot move the first tempo section */
918 *static_cast<Tempo*>(&first) = tempo;
919 recompute_map (_metrics);
924 PropertyChanged (PropertyChange ());
928 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
930 TempoSection* ts = 0;
932 Glib::Threads::RWLock::WriterLock lm (lock);
933 ts = add_tempo_locked (tempo, pulse, true, type);
936 PropertyChanged (PropertyChange ());
942 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
944 TempoSection* ts = 0;
946 Glib::Threads::RWLock::WriterLock lm (lock);
947 ts = add_tempo_locked (tempo, frame, true, type);
951 PropertyChanged (PropertyChange ());
957 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
959 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
964 solve_map (_metrics, t, t->pulse());
965 recompute_meters (_metrics);
972 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
974 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
979 solve_map (_metrics, t, t->frame());
980 recompute_meters (_metrics);
987 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
990 Glib::Threads::RWLock::WriterLock lm (lock);
993 remove_meter_locked (ms);
994 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
996 MeterSection& first (first_meter());
997 /* cannot move the first meter section */
998 *static_cast<Meter*>(&first) = meter;
999 first.set_position_lock_style (AudioTime);
1001 recompute_map (_metrics);
1004 PropertyChanged (PropertyChange ());
1008 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1011 Glib::Threads::RWLock::WriterLock lm (lock);
1013 const double beat = ms.beat();
1014 const BBT_Time bbt = ms.bbt();
1017 remove_meter_locked (ms);
1018 add_meter_locked (meter, frame, beat, bbt, true);
1020 MeterSection& first (first_meter());
1021 TempoSection& first_t (first_tempo());
1022 /* cannot move the first meter section */
1023 *static_cast<Meter*>(&first) = meter;
1024 first.set_position_lock_style (AudioTime);
1025 first.set_pulse (0.0);
1026 first.set_frame (frame);
1027 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1028 first.set_beat (beat);
1029 first_t.set_frame (first.frame());
1030 first_t.set_pulse (0.0);
1031 first_t.set_position_lock_style (AudioTime);
1033 recompute_map (_metrics);
1035 PropertyChanged (PropertyChange ());
1040 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1042 MeterSection* m = 0;
1044 Glib::Threads::RWLock::WriterLock lm (lock);
1045 m = add_meter_locked (meter, beat, where, true);
1050 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1051 dump (_metrics, std::cerr);
1055 PropertyChanged (PropertyChange ());
1061 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1063 MeterSection* m = 0;
1065 Glib::Threads::RWLock::WriterLock lm (lock);
1066 m = add_meter_locked (meter, frame, beat, where, true);
1071 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1072 dump (_metrics, std::cerr);
1076 PropertyChanged (PropertyChange ());
1082 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1084 /* a new meter always starts a new bar on the first beat. so
1085 round the start time appropriately. remember that
1086 `where' is based on the existing tempo map, not
1087 the result after we insert the new meter.
1091 if (where.beats != 1) {
1095 /* new meters *always* start on a beat. */
1097 const double pulse = pulse_at_beat_locked (_metrics, beat);
1098 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, pulse);
1109 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1111 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1112 TempoSection* t = 0;
1113 double pulse = pulse_at_frame_locked (_metrics, frame);
1114 new_meter->set_pulse (pulse);
1116 do_insert (new_meter);
1118 /* add meter-locked tempo */
1119 t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1120 t->set_locked_to_meter (true);
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 */
1320 TempoMap::recompute_meters (Metrics& metrics)
1322 MeterSection* meter = 0;
1323 MeterSection* prev_m = 0;
1325 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1326 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1327 if (meter->position_lock_style() == AudioTime) {
1329 pair<double, BBT_Time> b_bbt;
1330 if (meter->movable()) {
1331 b_bbt = make_pair (meter->beat(), meter->bbt());
1332 pulse = pulse_at_frame_locked (metrics, meter->frame());
1334 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1336 meter->set_beat (b_bbt);
1337 meter->set_pulse (pulse);
1340 pair<double, BBT_Time> new_beat;
1342 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1343 //new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1345 /* shouldn't happen - the first is audio-locked */
1346 pulse = pulse_at_beat_locked (metrics, meter->beat());
1347 new_beat = make_pair (meter->beat(), meter->bbt());
1350 //meter->set_beat (new_beat);
1351 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1352 meter->set_pulse (pulse);
1358 //dump (_metrics, std::cerr;
1362 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1364 /* CALLER MUST HOLD WRITE LOCK */
1368 /* we will actually stop once we hit
1375 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1378 /* silly call from Session::process() during startup
1383 recompute_tempos (metrics);
1384 recompute_meters (metrics);
1388 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1390 Glib::Threads::RWLock::ReaderLock lm (lock);
1391 TempoMetric m (first_meter(), first_tempo());
1393 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1394 at something, because we insert the default tempo and meter during
1395 TempoMap construction.
1397 now see if we can find better candidates.
1400 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1402 if ((*i)->frame() > frame) {
1416 /* XX meters only */
1418 TempoMap::metric_at (BBT_Time bbt) const
1420 Glib::Threads::RWLock::ReaderLock lm (lock);
1421 TempoMetric m (first_meter(), first_tempo());
1423 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1424 at something, because we insert the default tempo and meter during
1425 TempoMap construction.
1427 now see if we can find better candidates.
1430 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1432 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1433 BBT_Time section_start (mw->bbt());
1435 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1447 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1449 MeterSection* prev_m = 0;
1451 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1453 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1454 if (prev_m && m->beat() > beat) {
1461 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1466 TempoMap::pulse_at_beat (const double& beat) const
1468 Glib::Threads::RWLock::ReaderLock lm (lock);
1469 return pulse_at_beat_locked (_metrics, beat);
1473 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1475 MeterSection* prev_m = 0;
1477 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1479 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1480 if (prev_m && m->pulse() > pulse) {
1481 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1489 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1494 TempoMap::beat_at_pulse (const double& pulse) const
1496 Glib::Threads::RWLock::ReaderLock lm (lock);
1497 return beat_at_pulse_locked (_metrics, pulse);
1500 /* tempo section based */
1502 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1504 /* HOLD (at least) THE READER LOCK */
1505 TempoSection* prev_t = 0;
1507 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1509 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1513 if (prev_t && t->frame() > frame) {
1514 /*the previous ts is the one containing the frame */
1515 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1522 /* treated as constant for this ts */
1523 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1525 return pulses_in_section + prev_t->pulse();
1529 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1531 Glib::Threads::RWLock::ReaderLock lm (lock);
1532 return pulse_at_frame_locked (_metrics, frame);
1535 /* tempo section based */
1537 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1539 /* HOLD THE READER LOCK */
1541 const TempoSection* prev_t = 0;
1543 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1546 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1550 if (prev_t && t->pulse() > pulse) {
1551 return prev_t->frame_at_pulse (pulse, _frame_rate);
1557 /* must be treated as constant, irrespective of _type */
1558 double const pulses_in_section = pulse - prev_t->pulse();
1559 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1561 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1567 TempoMap::frame_at_pulse (const double& pulse) const
1569 Glib::Threads::RWLock::ReaderLock lm (lock);
1570 return frame_at_pulse_locked (_metrics, pulse);
1573 /* meter section based */
1575 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1577 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1578 MeterSection* prev_m = 0;
1579 MeterSection* next_m = 0;
1581 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1583 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1584 if (prev_m && m->frame() > frame) {
1591 if (frame < prev_m->frame()) {
1594 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1596 if (next_m && next_m->beat() < beat) {
1597 return next_m->beat();
1604 TempoMap::beat_at_frame (const framecnt_t& frame) const
1606 Glib::Threads::RWLock::ReaderLock lm (lock);
1607 return beat_at_frame_locked (_metrics, frame);
1610 /* meter section based */
1612 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1614 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1615 MeterSection* prev_m = 0;
1617 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1619 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1620 if (prev_m && m->beat() > beat) {
1627 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1631 TempoMap::frame_at_beat (const double& beat) const
1633 Glib::Threads::RWLock::ReaderLock lm (lock);
1634 return frame_at_beat_locked (_metrics, beat);
1638 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1640 /* CALLER HOLDS READ LOCK */
1642 MeterSection* prev_m = 0;
1644 /* because audio-locked meters have 'fake' integral beats,
1645 there is no pulse offset here.
1647 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1649 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1651 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1652 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1660 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1661 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1662 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1668 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1670 Glib::Threads::RWLock::ReaderLock lm (lock);
1671 return bbt_to_beats_locked (_metrics, bbt);
1675 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1677 /* CALLER HOLDS READ LOCK */
1678 MeterSection* prev_m = 0;
1679 const double beats = (b < 0.0) ? 0.0 : b;
1681 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1682 MeterSection* m = 0;
1684 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1686 if (m->beat() > beats) {
1687 /* this is the meter after the one our beat is on*/
1696 const double beats_in_ms = beats - prev_m->beat();
1697 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1698 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1699 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1700 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1704 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1705 ret.beats = (uint32_t) floor (remaining_beats);
1706 ret.bars = total_bars;
1708 /* 0 0 0 to 1 1 0 - based mapping*/
1712 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1714 ret.ticks -= BBT_Time::ticks_per_beat;
1717 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1726 TempoMap::beats_to_bbt (const double& beats)
1728 Glib::Threads::RWLock::ReaderLock lm (lock);
1729 return beats_to_bbt_locked (_metrics, beats);
1733 TempoMap::pulse_to_bbt (const double& pulse)
1735 Glib::Threads::RWLock::ReaderLock lm (lock);
1736 MeterSection* prev_m = 0;
1738 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1739 MeterSection* m = 0;
1741 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1744 double const pulses_to_m = m->pulse() - prev_m->pulse();
1745 if (prev_m->pulse() + pulses_to_m > pulse) {
1746 /* this is the meter after the one our beat is on*/
1755 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1756 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1757 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1758 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1759 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1763 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1764 ret.beats = (uint32_t) floor (remaining_beats);
1765 ret.bars = total_bars;
1767 /* 0 0 0 to 1 1 0 mapping*/
1771 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1773 ret.ticks -= BBT_Time::ticks_per_beat;
1776 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1785 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1792 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1795 Glib::Threads::RWLock::ReaderLock lm (lock);
1796 const double beat = beat_at_frame_locked (_metrics, frame);
1798 bbt = beats_to_bbt_locked (_metrics, beat);
1801 /* meter section based */
1803 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1805 /* HOLD THE READER LOCK */
1807 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1812 TempoMap::frame_time (const BBT_Time& bbt)
1815 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1819 if (bbt.beats < 1) {
1820 throw std::logic_error ("beats are counted from one");
1822 Glib::Threads::RWLock::ReaderLock lm (lock);
1824 return frame_time_locked (_metrics, bbt);
1828 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1830 TempoSection* prev_t = 0;
1831 MeterSection* prev_m = 0;
1833 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1836 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1841 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1845 if (t->frame() == prev_t->frame()) {
1849 /* precision check ensures pulses and frames align.*/
1850 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1851 if (!t->locked_to_meter()) {
1859 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1860 if (prev_m && m->position_lock_style() == AudioTime) {
1861 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1862 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1863 const frameoffset_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1864 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1878 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1880 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1882 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1883 if (!t->movable()) {
1884 t->set_active (true);
1887 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1888 t->set_active (false);
1890 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1891 t->set_active (true);
1892 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1901 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1903 TempoSection* prev_t = 0;
1904 TempoSection* section_prev = 0;
1905 framepos_t first_m_frame = 0;
1907 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1909 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1910 if (!m->movable()) {
1911 first_m_frame = m->frame();
1916 if (section->movable() && frame <= first_m_frame) {
1920 section->set_active (true);
1921 section->set_frame (frame);
1923 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1925 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1932 section_prev = prev_t;
1935 if (t->position_lock_style() == MusicTime) {
1936 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1937 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1939 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1940 if (!t->locked_to_meter()) {
1941 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1950 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1951 if (!section->locked_to_meter()) {
1952 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1956 if (section->position_lock_style() == MusicTime) {
1957 /* we're setting the frame */
1958 section->set_position_lock_style (AudioTime);
1959 recompute_tempos (imaginary);
1960 section->set_position_lock_style (MusicTime);
1962 recompute_tempos (imaginary);
1965 recompute_meters (imaginary);
1966 if (check_solved (imaginary, true)) {
1970 MetricSectionFrameSorter fcmp;
1971 imaginary.sort (fcmp);
1972 if (section->position_lock_style() == MusicTime) {
1973 /* we're setting the frame */
1974 section->set_position_lock_style (AudioTime);
1975 recompute_tempos (imaginary);
1976 section->set_position_lock_style (MusicTime);
1978 recompute_tempos (imaginary);
1981 recompute_meters (imaginary);
1982 if (check_solved (imaginary, true)) {
1986 //dump (imaginary, std::cerr);
1992 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1994 TempoSection* prev_t = 0;
1995 TempoSection* section_prev = 0;
1997 section->set_pulse (pulse);
1999 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2001 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2005 if (!t->movable()) {
2012 section_prev = prev_t;
2015 if (t->position_lock_style() == MusicTime) {
2016 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2017 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2019 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2020 if (!t->locked_to_meter()) {
2021 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2029 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2030 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2033 if (section->position_lock_style() == AudioTime) {
2034 /* we're setting the pulse */
2035 section->set_position_lock_style (MusicTime);
2036 recompute_tempos (imaginary);
2037 section->set_position_lock_style (AudioTime);
2039 recompute_tempos (imaginary);
2042 recompute_meters (imaginary);
2043 if (check_solved (imaginary, false)) {
2047 MetricSectionSorter cmp;
2048 imaginary.sort (cmp);
2049 if (section->position_lock_style() == AudioTime) {
2050 /* we're setting the pulse */
2051 section->set_position_lock_style (MusicTime);
2052 recompute_tempos (imaginary);
2053 section->set_position_lock_style (AudioTime);
2055 recompute_tempos (imaginary);
2058 recompute_meters (imaginary);
2059 if (check_solved (imaginary, false)) {
2063 //dump (imaginary, std::cerr);
2069 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2071 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2072 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2073 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2077 if (!section->movable()) {
2078 /* lock the first tempo to our first meter */
2079 if (!set_active_tempos (imaginary, frame)) {
2084 TempoSection* meter_locked_tempo = 0;
2085 for (Metrics::const_iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2087 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2088 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2089 if (t->frame() == section->frame()) {
2090 meter_locked_tempo = t;
2097 MeterSection* prev_m = 0;
2099 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2101 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2103 if (prev_m && section->movable()) {
2104 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2105 if (beats + prev_m->beat() < section->beat()) {
2106 /* disallow position change if it will alter our beat
2107 we allow tempo changes to do this in recompute_meters().
2108 blocking this is an option, but i'm not convinced that
2109 this is what the user would actually want.
2110 here we set the frame/pulse corresponding to its musical position.
2113 if (meter_locked_tempo) {
2115 TempoSection* new_section = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2119 const double new_pulse = ((section->beat() - prev_m->beat())
2120 / prev_m->note_divisor()) + prev_m->pulse();
2122 if (solve_map (future_map, new_section, section->frame())) {
2123 meter_locked_tempo->set_pulse (new_pulse);
2124 solve_map (imaginary, meter_locked_tempo, section->frame());
2125 section->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2126 section->set_pulse (new_pulse);
2133 if (meter_locked_tempo) {
2135 TempoSection* new_section = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2139 new_section->set_active (true);
2141 if (solve_map (future_map, new_section, frame)) {
2142 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2143 / prev_m->note_divisor()) + prev_m->pulse());
2144 solve_map (imaginary, meter_locked_tempo, frame);
2151 if (meter_locked_tempo) {
2153 TempoSection* new_section = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2157 new_section->set_frame (frame);
2158 new_section->set_pulse (0.0);
2159 new_section->set_active (true);
2161 if (solve_map (future_map, new_section, frame)) {
2162 meter_locked_tempo->set_frame (frame);
2163 meter_locked_tempo->set_pulse (0.0);
2164 meter_locked_tempo->set_active (true);
2165 solve_map (imaginary, meter_locked_tempo, frame);
2172 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2173 section->set_beat (b_bbt);
2174 section->set_pulse (0.0);
2177 section->set_frame (frame);
2185 MetricSectionFrameSorter fcmp;
2186 imaginary.sort (fcmp);
2187 if (section->position_lock_style() == MusicTime) {
2188 /* we're setting the frame */
2189 section->set_position_lock_style (AudioTime);
2190 recompute_meters (imaginary);
2191 section->set_position_lock_style (MusicTime);
2193 recompute_meters (imaginary);
2195 //dump (imaginary, std::cerr);
2200 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2202 MeterSection* prev_m = 0;
2204 section->set_pulse (pulse);
2206 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2208 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2209 double new_pulse = 0.0;
2210 pair<double, BBT_Time> b_bbt;
2212 if (prev_m && m == section){
2213 /* the first meter is always audio-locked, so prev_m should exist.
2214 should we allow setting audio locked meters by pulse?
2216 const double beats = floor (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + 0.5);
2217 const int32_t bars = (beats) / prev_m->divisions_per_bar();
2218 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), BBT_Time (bars + prev_m->bbt().bars, 1, 0));
2219 section->set_beat (b_bbt);
2220 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2224 if (m->position_lock_style() == AudioTime) {
2226 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2227 if (beats + prev_m->beat() != m->beat()) {
2228 /* tempo/ meter change caused a change in beat (bar). */
2229 b_bbt = make_pair (beats + prev_m->beat()
2230 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2231 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2233 b_bbt = make_pair (m->beat(), m->bbt());
2234 new_pulse = pulse_at_frame_locked (imaginary, m->frame());
2237 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2240 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2241 b_bbt = make_pair (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2243 m->set_beat (b_bbt);
2244 m->set_pulse (new_pulse);
2249 MetricSectionSorter cmp;
2250 imaginary.sort (cmp);
2251 if (section->position_lock_style() == AudioTime) {
2252 /* we're setting the pulse */
2253 section->set_position_lock_style (MusicTime);
2254 recompute_meters (imaginary);
2255 section->set_position_lock_style (AudioTime);
2257 recompute_meters (imaginary);
2262 /** places a copy of _metrics into copy and returns a pointer
2263 * to section's equivalent in copy.
2266 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2268 TempoSection* ret = 0;
2270 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2273 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2275 ret = new TempoSection (*t);
2276 copy.push_back (ret);
2280 TempoSection* cp = new TempoSection (*t);
2281 copy.push_back (cp);
2283 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2284 MeterSection* cp = new MeterSection (*m);
2285 copy.push_back (cp);
2293 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2295 MeterSection* ret = 0;
2297 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2300 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2301 TempoSection* cp = new TempoSection (*t);
2302 copy.push_back (cp);
2305 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2307 ret = new MeterSection (*m);
2308 copy.push_back (ret);
2311 MeterSection* cp = new MeterSection (*m);
2312 copy.push_back (cp);
2320 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2323 TempoSection* new_section = 0;
2326 Glib::Threads::RWLock::ReaderLock lm (lock);
2327 new_section = copy_metrics_and_point (_metrics, copy, ts);
2333 const double beat = bbt_to_beats_locked (copy, bbt);
2334 const bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2336 Metrics::const_iterator d = copy.begin();
2337 while (d != copy.end()) {
2346 * 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,
2347 * taking any possible reordering as a consequence of this into account.
2348 * @param section - the section to be altered
2349 * @param bpm - the new Tempo
2350 * @param bbt - the bbt where the altered tempo will fall
2351 * @return returns - the position in frames where the new tempo section will lie.
2354 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2356 Glib::Threads::RWLock::ReaderLock lm (lock);
2359 TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, section);
2363 const double beat = bbt_to_beats_locked (future_map, bbt);
2365 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2366 ret = new_section->frame();
2368 ret = frame_at_beat_locked (_metrics, beat);
2371 Metrics::const_iterator d = future_map.begin();
2372 while (d != future_map.end()) {
2380 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2382 Glib::Threads::RWLock::ReaderLock lm (lock);
2385 TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, section);
2387 if (solve_map (future_map, new_section, frame)) {
2388 ret = new_section->pulse();
2390 ret = pulse_at_frame_locked (_metrics, frame);
2393 Metrics::const_iterator d = future_map.begin();
2394 while (d != future_map.end()) {
2402 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2406 Glib::Threads::RWLock::WriterLock lm (lock);
2407 TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, ts);
2408 if (solve_map (future_map, new_section, frame)) {
2409 solve_map (_metrics, ts, frame);
2410 recompute_meters (_metrics);
2414 Metrics::const_iterator d = future_map.begin();
2415 while (d != future_map.end()) {
2420 MetricPositionChanged (); // Emit Signal
2424 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2428 Glib::Threads::RWLock::WriterLock lm (lock);
2429 TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, ts);
2430 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2431 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2432 recompute_meters (_metrics);
2436 Metrics::const_iterator d = future_map.begin();
2437 while (d != future_map.end()) {
2442 MetricPositionChanged (); // Emit Signal
2446 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2450 Glib::Threads::RWLock::WriterLock lm (lock);
2451 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2452 if (solve_map (future_map, copy, frame)) {
2453 solve_map (_metrics, ms, frame);
2457 Metrics::const_iterator d = future_map.begin();
2458 while (d != future_map.end()) {
2463 MetricPositionChanged (); // Emit Signal
2467 TempoMap::gui_move_meter (MeterSection* ms, const double& pulse)
2471 Glib::Threads::RWLock::WriterLock lm (lock);
2472 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2473 if (solve_map (future_map, copy, pulse)) {
2474 solve_map (_metrics, ms, pulse);
2478 Metrics::const_iterator d = future_map.begin();
2479 while (d != future_map.end()) {
2484 MetricPositionChanged (); // Emit Signal
2488 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2491 bool can_solve = false;
2493 Glib::Threads::RWLock::WriterLock lm (lock);
2494 TempoSection* new_section = copy_metrics_and_point (_metrics, future_map, ts);
2495 new_section->set_beats_per_minute (bpm.beats_per_minute());
2496 recompute_tempos (future_map);
2498 if (check_solved (future_map, true)) {
2499 ts->set_beats_per_minute (bpm.beats_per_minute());
2500 recompute_map (_metrics);
2505 Metrics::const_iterator d = future_map.begin();
2506 while (d != future_map.end()) {
2511 MetricPositionChanged (); // Emit Signal
2517 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2520 TempoSection* ts = 0;
2522 if (ms->position_lock_style() == AudioTime) {
2523 /* disabled for now due to faked tempo locked to meter pulse */
2527 Glib::Threads::RWLock::WriterLock lm (lock);
2528 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2532 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2533 TempoSection* prev_to_prev_t = 0;
2534 const frameoffset_t fr_off = frame - ms->frame();
2535 double new_bpm = 0.0;
2538 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2541 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2542 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2544 double contribution = 0.0;
2545 frameoffset_t frame_contribution = 0.0;
2546 frameoffset_t prev_t_frame_contribution = 0.0;
2548 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2549 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2550 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2551 frame_contribution = contribution * (double) fr_off;
2552 prev_t_frame_contribution = fr_off - frame_contribution;
2555 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2557 if (prev_t->position_lock_style() == MusicTime) {
2558 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2559 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2560 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2563 /* prev to prev is irrelevant */
2564 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2565 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2567 if (frame_pulse != prev_t->pulse()) {
2568 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2570 new_bpm = prev_t->beats_per_minute();
2575 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2576 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2577 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2579 /* prev_to_prev_t is irrelevant */
2581 if (frame != prev_t->frame()) {
2582 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2584 new_bpm = prev_t->beats_per_minute();
2588 } else if (prev_t->c_func() < 0.0) {
2589 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2590 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2592 /* prev_to_prev_t is irrelevant */
2593 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2596 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2597 if (diff > -0.1 && diff < 0.1) {
2598 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2599 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2602 } else if (prev_t->c_func() > 0.0) {
2603 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2604 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2606 /* prev_to_prev_t is irrelevant */
2607 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2610 /* limits - a bit clunky, but meh */
2611 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2612 if (diff > -0.1 && diff < 0.1) {
2613 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2614 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2618 prev_t->set_beats_per_minute (new_bpm);
2619 recompute_tempos (future_map);
2620 recompute_meters (future_map);
2622 if (check_solved (future_map, true)) {
2624 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2625 prev_t->set_beats_per_minute (new_bpm);
2626 recompute_tempos (_metrics);
2628 if (ms->position_lock_style() == AudioTime) {
2629 ms->set_frame (frame);
2632 recompute_meters (_metrics);
2636 Metrics::const_iterator d = future_map.begin();
2637 while (d != future_map.end()) {
2642 MetricPositionChanged (); // Emit Signal
2647 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2649 Glib::Threads::RWLock::ReaderLock lm (lock);
2651 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2652 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2653 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2655 return frame_at_beat_locked (_metrics, total_beats);
2659 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2661 return round_to_type (fr, dir, Bar);
2665 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2667 return round_to_type (fr, dir, Beat);
2671 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2673 Glib::Threads::RWLock::ReaderLock lm (lock);
2674 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2675 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2676 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2678 ticks -= beats * BBT_Time::ticks_per_beat;
2681 /* round to next (or same iff dir == RoundUpMaybe) */
2683 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2685 if (mod == 0 && dir == RoundUpMaybe) {
2686 /* right on the subdivision, which is fine, so do nothing */
2688 } else if (mod == 0) {
2689 /* right on the subdivision, so the difference is just the subdivision ticks */
2690 ticks += ticks_one_subdivisions_worth;
2693 /* not on subdivision, compute distance to next subdivision */
2695 ticks += ticks_one_subdivisions_worth - mod;
2698 if (ticks >= BBT_Time::ticks_per_beat) {
2699 ticks -= BBT_Time::ticks_per_beat;
2701 } else if (dir < 0) {
2703 /* round to previous (or same iff dir == RoundDownMaybe) */
2705 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2707 if (difference == 0 && dir == RoundDownAlways) {
2708 /* right on the subdivision, but force-rounding down,
2709 so the difference is just the subdivision ticks */
2710 difference = ticks_one_subdivisions_worth;
2713 if (ticks < difference) {
2714 ticks = BBT_Time::ticks_per_beat - ticks;
2716 ticks -= difference;
2720 /* round to nearest */
2723 /* compute the distance to the previous and next subdivision */
2725 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2727 /* closer to the next subdivision, so shift forward */
2729 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2731 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2733 if (ticks > BBT_Time::ticks_per_beat) {
2735 ticks -= BBT_Time::ticks_per_beat;
2736 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2739 } else if (rem > 0) {
2741 /* closer to previous subdivision, so shift backward */
2745 /* can't go backwards past zero, so ... */
2748 /* step back to previous beat */
2750 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2751 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2753 ticks = lrint (ticks - rem);
2754 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2757 /* on the subdivision, do nothing */
2761 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2767 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2769 if (sub_num == -1) {
2770 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2771 if ((double) when.beats > bpb / 2.0) {
2777 } else if (sub_num == 0) {
2778 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2779 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2781 while ((double) when.beats > bpb) {
2783 when.beats -= (uint32_t) floor (bpb);
2789 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2791 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2792 /* closer to the next subdivision, so shift forward */
2794 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2796 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2798 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2801 } else if (rem > 0) {
2802 /* closer to previous subdivision, so shift backward */
2804 if (rem > when.ticks) {
2805 if (when.beats == 0) {
2806 /* can't go backwards past zero, so ... */
2808 /* step back to previous beat */
2810 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2812 when.ticks = when.ticks - rem;
2818 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2820 Glib::Threads::RWLock::ReaderLock lm (lock);
2822 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2823 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2828 /* find bar previous to 'frame' */
2831 return frame_time_locked (_metrics, bbt);
2833 } else if (dir > 0) {
2834 /* find bar following 'frame' */
2838 return frame_time_locked (_metrics, bbt);
2840 /* true rounding: find nearest bar */
2841 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2844 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2846 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2848 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2859 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2860 } else if (dir > 0) {
2861 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2863 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2872 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2873 framepos_t lower, framepos_t upper)
2875 Glib::Threads::RWLock::ReaderLock lm (lock);
2876 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2878 /* although the map handles negative beats, bbt doesn't. */
2882 while (pos < upper) {
2883 pos = frame_at_beat_locked (_metrics, cnt);
2884 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2885 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2886 const BBT_Time bbt = beats_to_bbt (cnt);
2887 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2893 TempoMap::tempo_section_at (framepos_t frame) const
2895 Glib::Threads::RWLock::ReaderLock lm (lock);
2896 return tempo_section_at_locked (_metrics, frame);
2900 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2902 Metrics::const_iterator i;
2903 TempoSection* prev = 0;
2905 for (i = metrics.begin(); i != metrics.end(); ++i) {
2908 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2912 if (prev && t->frame() > frame) {
2922 abort(); /*NOTREACHED*/
2929 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2931 TempoSection* prev_t = 0;
2932 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2934 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2936 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2937 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2948 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2950 TempoSection* prev_t = 0;
2952 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2954 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2955 if (prev_t && t->pulse() > pulse) {
2965 /* don't use this to calculate length (the tempo is only correct for this frame).
2966 do that stuff based on the beat_at_frame and frame_at_beat api
2969 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2971 Glib::Threads::RWLock::ReaderLock lm (lock);
2973 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
2974 const TempoSection* ts_after = 0;
2975 Metrics::const_iterator i;
2977 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2980 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2984 if ((*i)->frame() > frame) {
2992 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2994 /* must be treated as constant tempo */
2995 return ts_at->frames_per_beat (_frame_rate);
2999 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3001 TempoSection* prev_t = 0;
3003 Metrics::const_iterator i;
3005 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3007 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3011 if ((prev_t) && t->frame() > frame) {
3012 /* t is the section past frame */
3013 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3014 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3021 const double ret = prev_t->beats_per_minute();
3022 const Tempo ret_tempo (ret, prev_t->note_type ());
3028 TempoMap::tempo_at (const framepos_t& frame) const
3030 Glib::Threads::RWLock::ReaderLock lm (lock);
3031 return tempo_at_locked (_metrics, frame);
3035 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3037 Metrics::const_iterator i;
3038 MeterSection* prev = 0;
3040 for (i = metrics.begin(); i != metrics.end(); ++i) {
3043 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3045 if (prev && (*i)->frame() > frame) {
3055 abort(); /*NOTREACHED*/
3063 TempoMap::meter_section_at (framepos_t frame) const
3065 Glib::Threads::RWLock::ReaderLock lm (lock);
3066 return meter_section_at_locked (_metrics, frame);
3070 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3072 MeterSection* prev_m = 0;
3074 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3076 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3077 if (prev_m && m->beat() > beat) {
3088 TempoMap::meter_section_at_beat (double beat) const
3090 Glib::Threads::RWLock::ReaderLock lm (lock);
3091 return meter_section_at_beat_locked (_metrics, beat);
3095 TempoMap::meter_at (framepos_t frame) const
3097 TempoMetric m (metric_at (frame));
3102 TempoMap::fix_legacy_session ()
3104 MeterSection* prev_m = 0;
3105 TempoSection* prev_t = 0;
3107 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3111 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3112 if (!m->movable()) {
3113 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3117 m->set_position_lock_style (AudioTime);
3122 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3123 + (m->bbt().beats - 1)
3124 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3126 m->set_beat (start);
3127 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3128 + (m->bbt().beats - 1)
3129 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3130 m->set_pulse (start_beat / prev_m->note_divisor());
3133 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3139 if (!t->movable()) {
3142 t->set_position_lock_style (AudioTime);
3148 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3149 + (t->legacy_bbt().beats - 1)
3150 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3152 t->set_pulse (beat / prev_m->note_divisor());
3154 /* really shouldn't happen but.. */
3155 t->set_pulse (beat / 4.0);
3164 TempoMap::get_state ()
3166 Metrics::const_iterator i;
3167 XMLNode *root = new XMLNode ("TempoMap");
3170 Glib::Threads::RWLock::ReaderLock lm (lock);
3171 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3172 root->add_child_nocopy ((*i)->get_state());
3180 TempoMap::set_state (const XMLNode& node, int /*version*/)
3183 Glib::Threads::RWLock::WriterLock lm (lock);
3186 XMLNodeConstIterator niter;
3187 Metrics old_metrics (_metrics);
3190 nlist = node.children();
3192 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3193 XMLNode* child = *niter;
3195 if (child->name() == TempoSection::xml_state_node_name) {
3198 TempoSection* ts = new TempoSection (*child);
3199 _metrics.push_back (ts);
3202 catch (failed_constructor& err){
3203 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3204 _metrics = old_metrics;
3208 } else if (child->name() == MeterSection::xml_state_node_name) {
3211 MeterSection* ms = new MeterSection (*child);
3212 _metrics.push_back (ms);
3215 catch (failed_constructor& err) {
3216 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3217 _metrics = old_metrics;
3223 if (niter == nlist.end()) {
3224 MetricSectionSorter cmp;
3225 _metrics.sort (cmp);
3228 /* check for legacy sessions where bbt was the base musical unit for tempo */
3229 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3231 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3232 if (t->legacy_bbt().bars != 0) {
3233 fix_legacy_session();
3240 /* check for multiple tempo/meters at the same location, which
3241 ardour2 somehow allowed.
3244 Metrics::iterator prev = _metrics.end();
3245 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3246 if (prev != _metrics.end()) {
3248 MeterSection* prev_m;
3250 TempoSection* prev_t;
3251 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3252 if (prev_m->pulse() == ms->pulse()) {
3253 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3254 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3257 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3258 if (prev_t->pulse() == ts->pulse()) {
3259 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3260 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3268 recompute_map (_metrics);
3271 PropertyChanged (PropertyChange ());
3277 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3279 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3280 const MeterSection* m;
3281 const TempoSection* t;
3282 const TempoSection* prev_t = 0;
3284 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3286 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3287 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3288 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3289 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3291 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3292 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;
3295 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3296 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3297 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3300 o << "------" << std::endl;
3304 TempoMap::n_tempos() const
3306 Glib::Threads::RWLock::ReaderLock lm (lock);
3309 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3310 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3319 TempoMap::n_meters() const
3321 Glib::Threads::RWLock::ReaderLock lm (lock);
3324 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3325 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3334 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3337 Glib::Threads::RWLock::WriterLock lm (lock);
3338 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3339 if ((*i)->frame() >= where && (*i)->movable ()) {
3340 (*i)->set_frame ((*i)->frame() + amount);
3344 /* now reset the BBT time of all metrics, based on their new
3345 * audio time. This is the only place where we do this reverse
3349 Metrics::iterator i;
3350 const MeterSection* meter;
3351 const TempoSection* tempo;
3355 meter = &first_meter ();
3356 tempo = &first_tempo ();
3361 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3364 MetricSection* prev = 0;
3366 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3369 //TempoMetric metric (*meter, *tempo);
3370 MeterSection* ms = const_cast<MeterSection*>(meter);
3371 TempoSection* ts = const_cast<TempoSection*>(tempo);
3374 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3378 ts->set_pulse (t->pulse());
3380 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3381 ts->set_pulse (m->pulse());
3383 ts->set_frame (prev->frame());
3387 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3388 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3389 ms->set_beat (start);
3390 ms->set_pulse (m->pulse());
3392 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3396 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3397 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3398 ms->set_beat (start);
3399 ms->set_pulse (t->pulse());
3401 ms->set_frame (prev->frame());
3405 // metric will be at frames=0 bbt=1|1|0 by default
3406 // which is correct for our purpose
3409 // cerr << bbt << endl;
3411 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3415 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3417 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3418 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3419 bbt_time (m->frame(), bbt);
3421 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3427 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3428 /* round up to next beat */
3434 if (bbt.beats != 1) {
3435 /* round up to next bar */
3440 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3441 m->set_beat (start);
3442 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3444 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3446 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3447 abort(); /*NOTREACHED*/
3453 recompute_map (_metrics);
3457 PropertyChanged (PropertyChange ());
3460 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3464 std::list<MetricSection*> metric_kill_list;
3466 TempoSection* last_tempo = NULL;
3467 MeterSection* last_meter = NULL;
3468 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3469 bool meter_after = false; // is there a meter marker likewise?
3471 Glib::Threads::RWLock::WriterLock lm (lock);
3472 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3473 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3474 metric_kill_list.push_back(*i);
3475 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3478 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3482 else if ((*i)->frame() >= where) {
3483 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3484 (*i)->set_frame ((*i)->frame() - amount);
3485 if ((*i)->frame() == where) {
3486 // marker was immediately after end of range
3487 tempo_after = dynamic_cast<TempoSection*> (*i);
3488 meter_after = dynamic_cast<MeterSection*> (*i);
3494 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3495 if (last_tempo && !tempo_after) {
3496 metric_kill_list.remove(last_tempo);
3497 last_tempo->set_frame(where);
3500 if (last_meter && !meter_after) {
3501 metric_kill_list.remove(last_meter);
3502 last_meter->set_frame(where);
3506 //remove all the remaining metrics
3507 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3508 _metrics.remove(*i);
3513 recompute_map (_metrics);
3516 PropertyChanged (PropertyChange ());
3520 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3521 * pos can be -ve, if required.
3524 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3526 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3529 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3531 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3533 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3536 /** Add the BBT interval op to pos and return the result */
3538 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3540 Glib::Threads::RWLock::ReaderLock lm (lock);
3542 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3543 pos_bbt.ticks += op.ticks;
3544 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3546 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3548 pos_bbt.beats += op.beats;
3549 /* the meter in effect will start on the bar */
3550 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();
3551 while (pos_bbt.beats >= divisions_per_bar + 1) {
3553 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3554 pos_bbt.beats -= divisions_per_bar;
3556 pos_bbt.bars += op.bars;
3558 return frame_time_locked (_metrics, pos_bbt);
3561 /** Count the number of beats that are equivalent to distance when going forward,
3565 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3567 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3571 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3577 operator<< (std::ostream& o, const Meter& m) {
3578 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3582 operator<< (std::ostream& o, const Tempo& t) {
3583 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3587 operator<< (std::ostream& o, const MetricSection& section) {
3589 o << "MetricSection @ " << section.frame() << ' ';
3591 const TempoSection* ts;
3592 const MeterSection* ms;
3594 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3595 o << *((const Tempo*) ts);
3596 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3597 o << *((const Meter*) ms);