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 (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 (framecnt_t) floor ((time * 60.0 * 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 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1305 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1306 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1313 prev_t->set_c_func (0.0);
1316 /* tempos must be positioned correctly */
1318 TempoMap::recompute_meters (Metrics& metrics)
1320 MeterSection* meter = 0;
1321 MeterSection* prev_m = 0;
1323 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1324 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1325 if (meter->position_lock_style() == AudioTime) {
1327 pair<double, BBT_Time> b_bbt;
1328 if (meter->movable()) {
1329 const double beats = floor ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse())
1330 * prev_m->note_divisor() + 0.5);
1332 if (beats + prev_m->beat() < meter->beat()) {
1333 /* tempo change caused a change in beat (bar). */
1334 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1335 b_bbt = make_pair (floor_beats + prev_m->beat()
1336 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1337 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1338 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1339 pulse = true_pulse - pulse_off;
1341 b_bbt = make_pair (meter->beat(), meter->bbt());
1342 pulse = pulse_at_frame_locked (metrics, meter->frame());
1345 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1347 meter->set_beat (b_bbt);
1348 meter->set_pulse (pulse);
1351 pair<double, BBT_Time> new_beat;
1353 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1354 new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1356 /* shouldn't happen - the first is audio-locked */
1357 pulse = pulse_at_beat_locked (metrics, meter->beat());
1358 new_beat = make_pair (pulse, meter->bbt());
1361 meter->set_beat (new_beat);
1362 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1363 meter->set_pulse (pulse);
1369 //dump (_metrics, std::cerr;
1373 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1375 /* CALLER MUST HOLD WRITE LOCK */
1379 /* we will actually stop once we hit
1386 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1389 /* silly call from Session::process() during startup
1394 recompute_tempos (metrics);
1395 recompute_meters (metrics);
1399 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1401 Glib::Threads::RWLock::ReaderLock lm (lock);
1402 TempoMetric m (first_meter(), first_tempo());
1404 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1405 at something, because we insert the default tempo and meter during
1406 TempoMap construction.
1408 now see if we can find better candidates.
1411 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1413 if ((*i)->frame() > frame) {
1427 /* XX meters only */
1429 TempoMap::metric_at (BBT_Time bbt) const
1431 Glib::Threads::RWLock::ReaderLock lm (lock);
1432 TempoMetric m (first_meter(), first_tempo());
1434 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1435 at something, because we insert the default tempo and meter during
1436 TempoMap construction.
1438 now see if we can find better candidates.
1441 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1443 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1444 BBT_Time section_start (mw->bbt());
1446 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1458 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1460 MeterSection* prev_m = 0;
1462 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1464 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1465 if (prev_m && m->beat() > beat) {
1472 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1477 TempoMap::pulse_at_beat (const double& beat) const
1479 Glib::Threads::RWLock::ReaderLock lm (lock);
1480 return pulse_at_beat_locked (_metrics, beat);
1484 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) 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->pulse() > pulse) {
1492 if ((pulse - prev_m->pulse()) * prev_m->note_divisor() < m->beat()) {
1500 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1502 return beats_in_section + prev_m->beat();
1506 TempoMap::beat_at_pulse (const double& pulse) const
1508 Glib::Threads::RWLock::ReaderLock lm (lock);
1509 return beat_at_pulse_locked (_metrics, pulse);
1512 /* tempo section based */
1514 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1516 /* HOLD (at least) THE READER LOCK */
1517 TempoSection* prev_t = 0;
1519 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1521 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1525 if (prev_t && t->frame() > frame) {
1526 /*the previous ts is the one containing the frame */
1527 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1534 /* treated as constant for this ts */
1535 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1537 return pulses_in_section + prev_t->pulse();
1541 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1543 Glib::Threads::RWLock::ReaderLock lm (lock);
1544 return pulse_at_frame_locked (_metrics, frame);
1547 /* tempo section based */
1549 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1551 /* HOLD THE READER LOCK */
1553 const TempoSection* prev_t = 0;
1555 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1558 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1562 if (prev_t && t->pulse() > pulse) {
1563 return prev_t->frame_at_pulse (pulse, _frame_rate);
1569 /* must be treated as constant, irrespective of _type */
1570 double const pulses_in_section = pulse - prev_t->pulse();
1571 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1573 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1579 TempoMap::frame_at_pulse (const double& pulse) const
1581 Glib::Threads::RWLock::ReaderLock lm (lock);
1582 return frame_at_pulse_locked (_metrics, pulse);
1585 /* meter section based */
1587 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1589 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1590 MeterSection* prev_m = 0;
1591 MeterSection* next_m = 0;
1593 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1595 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1596 if (prev_m && m->frame() > frame) {
1603 if (frame < prev_m->frame()) {
1606 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1608 if (next_m && next_m->beat() < beat) {
1609 return next_m->beat();
1616 TempoMap::beat_at_frame (const framecnt_t& frame) const
1618 Glib::Threads::RWLock::ReaderLock lm (lock);
1619 return beat_at_frame_locked (_metrics, frame);
1622 /* meter section based */
1624 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1626 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1627 MeterSection* prev_m = 0;
1629 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1631 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1632 if (prev_m && m->beat() > beat) {
1639 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1643 TempoMap::frame_at_beat (const double& beat) const
1645 Glib::Threads::RWLock::ReaderLock lm (lock);
1646 return frame_at_beat_locked (_metrics, beat);
1650 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1652 /* CALLER HOLDS READ LOCK */
1654 MeterSection* prev_m = 0;
1656 /* because audio-locked meters have 'fake' integral beats,
1657 there is no pulse offset here.
1659 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1661 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1663 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1664 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1672 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1673 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1674 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1680 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1682 Glib::Threads::RWLock::ReaderLock lm (lock);
1683 return bbt_to_beats_locked (_metrics, bbt);
1687 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1689 /* CALLER HOLDS READ LOCK */
1690 MeterSection* prev_m = 0;
1691 const double beats = (b < 0.0) ? 0.0 : b;
1693 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1694 MeterSection* m = 0;
1696 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1698 if (m->beat() > beats) {
1699 /* this is the meter after the one our beat is on*/
1708 const double beats_in_ms = beats - prev_m->beat();
1709 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1710 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1711 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1712 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1716 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1717 ret.beats = (uint32_t) floor (remaining_beats);
1718 ret.bars = total_bars;
1720 /* 0 0 0 to 1 1 0 - based mapping*/
1724 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1726 ret.ticks -= BBT_Time::ticks_per_beat;
1729 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1738 TempoMap::beats_to_bbt (const double& beats)
1740 Glib::Threads::RWLock::ReaderLock lm (lock);
1741 return beats_to_bbt_locked (_metrics, beats);
1745 TempoMap::pulse_to_bbt (const double& pulse)
1747 Glib::Threads::RWLock::ReaderLock lm (lock);
1748 MeterSection* prev_m = 0;
1750 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1751 MeterSection* m = 0;
1753 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1756 double const pulses_to_m = m->pulse() - prev_m->pulse();
1757 if (prev_m->pulse() + pulses_to_m > pulse) {
1758 /* this is the meter after the one our beat is on*/
1767 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1768 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1769 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1770 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1771 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1775 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1776 ret.beats = (uint32_t) floor (remaining_beats);
1777 ret.bars = total_bars;
1779 /* 0 0 0 to 1 1 0 mapping*/
1783 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1785 ret.ticks -= BBT_Time::ticks_per_beat;
1788 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1797 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1804 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1807 Glib::Threads::RWLock::ReaderLock lm (lock);
1808 const double beat = beat_at_frame_locked (_metrics, frame);
1810 bbt = beats_to_bbt_locked (_metrics, beat);
1813 /* meter section based */
1815 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1817 /* HOLD THE READER LOCK */
1819 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1824 TempoMap::frame_time (const BBT_Time& bbt)
1827 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1831 if (bbt.beats < 1) {
1832 throw std::logic_error ("beats are counted from one");
1834 Glib::Threads::RWLock::ReaderLock lm (lock);
1836 return frame_time_locked (_metrics, bbt);
1840 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1842 TempoSection* prev_t = 0;
1844 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1846 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1851 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1855 if (t->frame() == prev_t->frame()) {
1859 /* precision check ensures pulses and frames align.*/
1860 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1872 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1874 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1876 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1877 if (!t->movable()) {
1878 t->set_active (true);
1881 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1882 t->set_active (false);
1884 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1885 t->set_active (true);
1886 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1895 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1897 TempoSection* prev_t = 0;
1898 TempoSection* section_prev = 0;
1899 framepos_t first_m_frame = 0;
1901 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1903 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1904 if (!m->movable()) {
1905 first_m_frame = m->frame();
1910 if (section->movable() && frame <= first_m_frame) {
1914 section->set_active (true);
1915 section->set_frame (frame);
1917 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1919 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1926 section_prev = prev_t;
1929 if (t->position_lock_style() == MusicTime) {
1930 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1931 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1933 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1934 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1942 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1943 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1946 if (section->position_lock_style() == MusicTime) {
1947 /* we're setting the frame */
1948 section->set_position_lock_style (AudioTime);
1949 recompute_tempos (imaginary);
1950 section->set_position_lock_style (MusicTime);
1952 recompute_tempos (imaginary);
1955 if (check_solved (imaginary, true)) {
1959 MetricSectionFrameSorter fcmp;
1960 imaginary.sort (fcmp);
1961 if (section->position_lock_style() == MusicTime) {
1962 /* we're setting the frame */
1963 section->set_position_lock_style (AudioTime);
1964 recompute_tempos (imaginary);
1965 section->set_position_lock_style (MusicTime);
1967 recompute_tempos (imaginary);
1970 if (check_solved (imaginary, true)) {
1974 //dump (imaginary, std::cerr);
1980 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1982 TempoSection* prev_t = 0;
1983 TempoSection* section_prev = 0;
1985 section->set_pulse (pulse);
1987 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1989 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1993 if (!t->movable()) {
2000 section_prev = prev_t;
2003 if (t->position_lock_style() == MusicTime) {
2004 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2005 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2007 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2008 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2015 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2016 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2019 if (section->position_lock_style() == AudioTime) {
2020 /* we're setting the pulse */
2021 section->set_position_lock_style (MusicTime);
2022 recompute_tempos (imaginary);
2023 section->set_position_lock_style (AudioTime);
2025 recompute_tempos (imaginary);
2028 if (check_solved (imaginary, false)) {
2032 MetricSectionSorter cmp;
2033 imaginary.sort (cmp);
2034 if (section->position_lock_style() == AudioTime) {
2035 /* we're setting the pulse */
2036 section->set_position_lock_style (MusicTime);
2037 recompute_tempos (imaginary);
2038 section->set_position_lock_style (AudioTime);
2040 recompute_tempos (imaginary);
2043 if (check_solved (imaginary, false)) {
2047 //dump (imaginary, std::cerr);
2053 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2055 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2056 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2057 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2060 MeterSection* prev_m = 0;
2061 if (!section->movable()) {
2062 /* lock the first tempo to our first meter */
2063 if (!set_active_tempos (imaginary, frame)) {
2066 TempoSection* first_t = &first_tempo();
2068 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2070 new_section->set_frame (frame);
2071 new_section->set_pulse (0.0);
2072 new_section->set_active (true);
2074 if (solve_map (future_map, new_section, frame)) {
2075 first_t->set_frame (frame);
2076 first_t->set_pulse (0.0);
2077 first_t->set_active (true);
2078 solve_map (imaginary, first_t, frame);
2084 TempoSection* meter_locked_tempo = 0;
2086 if ((meter_locked_tempo = const_cast<TempoSection*>(&tempo_section_at_locked (imaginary, section->frame())))->frame() == section->frame()) {
2087 if (meter_locked_tempo->locked_to_meter()) {
2088 std::cout << "locked to meter " << std::endl;
2090 TempoSection* new_section = copy_metrics_and_point (future_map, meter_locked_tempo);
2092 new_section->set_frame (frame);
2093 new_section->set_active (true);
2095 if (solve_map (future_map, new_section, frame)) {
2096 meter_locked_tempo->set_frame (frame);
2097 meter_locked_tempo->set_active (true);
2098 solve_map (imaginary, meter_locked_tempo, frame);
2103 meter_locked_tempo = 0;
2107 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2109 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2111 if (prev_m && section->movable()) {
2112 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2113 if (beats + prev_m->beat() < section->beat()) {
2114 /* disallow position change if it will alter our beat
2115 we allow tempo changes to do this in recompute_meters().
2116 blocking this is an option, but i'm not convinced that
2117 this is what the user would actually want.
2118 here we set the frame/pulse corresponding to its musical position.
2120 const double new_pulse = ((section->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
2121 section->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2122 section->set_pulse (new_pulse);
2123 if (meter_locked_tempo) {
2124 meter_locked_tempo->set_frame (section->frame());
2125 /* XX need to fake the pulse and prevent it from changing */
2126 //meter_locked_tempo->set_pulse (pulse_at_frame_locked (imaginary, section->frame()));
2130 section->set_pulse (pulse_at_frame_locked (imaginary, frame));
2133 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2134 section->set_beat (b_bbt);
2135 section->set_pulse (0.0);
2138 section->set_frame (frame);
2146 MetricSectionFrameSorter fcmp;
2147 imaginary.sort (fcmp);
2148 if (section->position_lock_style() == MusicTime) {
2149 /* we're setting the frame */
2150 section->set_position_lock_style (AudioTime);
2151 recompute_meters (imaginary);
2152 section->set_position_lock_style (MusicTime);
2154 recompute_meters (imaginary);
2156 //dump (imaginary, std::cerr);
2160 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2162 MeterSection* prev_m = 0;
2164 section->set_pulse (pulse);
2166 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2168 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2169 if (prev_m && m == section){
2170 /* the first meter is always audio-locked, so prev_m should exist.
2171 should we allow setting audio locked meters by pulse?
2173 const double beats = ((pulse - prev_m->pulse()) * prev_m->note_divisor());
2174 const int32_t bars = (beats + 1) / prev_m->divisions_per_bar();
2175 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), BBT_Time (bars + prev_m->bbt().bars, 1, 0));
2176 section->set_beat (b_bbt);
2177 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2185 MetricSectionSorter cmp;
2186 imaginary.sort (cmp);
2187 if (section->position_lock_style() == AudioTime) {
2188 /* we're setting the pulse */
2189 section->set_position_lock_style (MusicTime);
2190 recompute_meters (imaginary);
2191 section->set_position_lock_style (AudioTime);
2193 recompute_meters (imaginary);
2197 /** places a copy of _metrics into copy and returns a pointer
2198 * to section's equivalent in copy.
2201 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2205 TempoSection* ret = 0;
2207 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2208 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2210 if (t->position_lock_style() == MusicTime) {
2211 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2212 ret->set_frame (t->frame());
2214 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2215 ret->set_pulse (t->pulse());
2217 ret->set_c_func (t->c_func());
2218 ret->set_active (t->active());
2219 ret->set_movable (t->movable());
2220 copy.push_back (ret);
2223 TempoSection* cp = 0;
2224 if (t->position_lock_style() == MusicTime) {
2225 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2226 cp->set_frame (t->frame());
2228 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2229 cp->set_pulse (t->pulse());
2231 cp->set_c_func (t->c_func());
2232 cp->set_active (t->active());
2233 cp->set_movable (t->movable());
2234 copy.push_back (cp);
2236 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2237 MeterSection* cp = 0;
2238 if (m->position_lock_style() == MusicTime) {
2239 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2240 cp->set_frame (m->frame());
2242 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2243 cp->set_pulse (m->pulse());
2245 cp->set_movable (m->movable());
2246 copy.push_back (cp);
2254 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2257 TempoSection* new_section = 0;
2260 Glib::Threads::RWLock::ReaderLock lm (lock);
2261 new_section = copy_metrics_and_point (copy, ts);
2264 const double beat = bbt_to_beats_locked (copy, bbt);
2265 const bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2267 Metrics::const_iterator d = copy.begin();
2268 while (d != copy.end()) {
2277 * 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,
2278 * taking any possible reordering as a consequence of this into account.
2279 * @param section - the section to be altered
2280 * @param bpm - the new Tempo
2281 * @param bbt - the bbt where the altered tempo will fall
2282 * @return returns - the position in frames where the new tempo section will lie.
2285 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2287 Glib::Threads::RWLock::ReaderLock lm (lock);
2290 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2292 const double beat = bbt_to_beats_locked (future_map, bbt);
2294 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2295 ret = new_section->frame();
2297 ret = frame_at_beat_locked (_metrics, beat);
2300 Metrics::const_iterator d = future_map.begin();
2301 while (d != future_map.end()) {
2309 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2311 Glib::Threads::RWLock::ReaderLock lm (lock);
2314 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2316 if (solve_map (future_map, new_section, frame)) {
2317 ret = new_section->pulse();
2319 ret = pulse_at_frame_locked (_metrics, frame);
2322 Metrics::const_iterator d = future_map.begin();
2323 while (d != future_map.end()) {
2331 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2335 Glib::Threads::RWLock::WriterLock lm (lock);
2336 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2337 if (solve_map (future_map, new_section, frame)) {
2338 solve_map (_metrics, ts, frame);
2339 recompute_meters (_metrics);
2343 Metrics::const_iterator d = future_map.begin();
2344 while (d != future_map.end()) {
2349 MetricPositionChanged (); // Emit Signal
2353 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2357 Glib::Threads::RWLock::WriterLock lm (lock);
2358 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2359 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2360 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2361 recompute_meters (_metrics);
2365 Metrics::const_iterator d = future_map.begin();
2366 while (d != future_map.end()) {
2371 MetricPositionChanged (); // Emit Signal
2375 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2378 Glib::Threads::RWLock::WriterLock lm (lock);
2379 solve_map (_metrics, ms, frame);
2382 MetricPositionChanged (); // Emit Signal
2386 TempoMap::gui_move_meter (MeterSection* ms, const double& pulse)
2389 Glib::Threads::RWLock::WriterLock lm (lock);
2390 solve_map (_metrics, ms, pulse);
2393 MetricPositionChanged (); // Emit Signal
2397 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2400 bool can_solve = false;
2402 Glib::Threads::RWLock::WriterLock lm (lock);
2403 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2404 new_section->set_beats_per_minute (bpm.beats_per_minute());
2405 recompute_tempos (future_map);
2407 if (check_solved (future_map, true)) {
2408 ts->set_beats_per_minute (bpm.beats_per_minute());
2409 recompute_map (_metrics);
2414 Metrics::const_iterator d = future_map.begin();
2415 while (d != future_map.end()) {
2420 MetricPositionChanged (); // Emit Signal
2426 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2429 TempoSection* ts = 0;
2431 Glib::Threads::RWLock::WriterLock lm (lock);
2432 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2433 TempoSection* prev_t = copy_metrics_and_point (future_map, ts);
2434 TempoSection* prev_to_prev_t = 0;
2435 const frameoffset_t fr_off = frame - ms->frame();
2436 double new_bpm = 0.0;
2439 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2442 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2443 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2445 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2446 if (prev_t->position_lock_style() == MusicTime) {
2447 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2448 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2449 const double contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2450 const frameoffset_t frame_contribution = contribution * (double) fr_off;
2451 const frameoffset_t prev_t_frame_contribution = fr_off - frame_contribution;
2452 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2453 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2456 /* prev to prev is irrelevant */
2457 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2458 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2460 if (frame_pulse != prev_t->pulse()) {
2461 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2463 new_bpm = prev_t->beats_per_minute();
2468 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2469 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2470 const double contribution = (prev_t->frame() - prev_to_prev_t->frame())
2471 / (double) (ms->frame() - prev_to_prev_t->frame());
2472 const frameoffset_t frame_contribution = contribution * (double) fr_off;
2473 const frameoffset_t prev_t_frame_contribution = fr_off - frame_contribution;
2474 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2475 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2477 /* prev_to_prev_t is irrelevant */
2479 if (frame != prev_t->frame()) {
2480 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2482 new_bpm = prev_t->beats_per_minute();
2486 } else if (prev_t->c_func() < 0.0) {
2487 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2488 const double contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2489 const frameoffset_t frame_contribution = contribution * (double) fr_off;
2490 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2492 /* prev_to_prev_t is irrelevant */
2493 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2496 const double diff = prev_t->beats_per_minute() - new_bpm;
2497 if (diff > -0.1 && diff < 0.1) {
2498 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2501 } else if (prev_t->c_func() > 0.0) {
2502 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2503 const double contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2504 const frameoffset_t frame_contribution = contribution * (double) fr_off;
2505 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2507 /* prev_to_prev_t is irrelevant */
2508 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2510 /* limits - a bit clunky, but meh */
2511 const double diff = prev_t->beats_per_minute() - new_bpm;
2512 if (diff > -0.1 && diff < 0.1) {
2513 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2517 prev_t->set_beats_per_minute (new_bpm);
2518 recompute_tempos (future_map);
2520 if (check_solved (future_map, true)) {
2522 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2523 prev_t->set_beats_per_minute (new_bpm);
2524 recompute_tempos (_metrics);
2526 if (ms->position_lock_style() == AudioTime) {
2527 ms->set_frame (frame);
2529 ms->set_pulse (prev_t->pulse_at_frame (frame, _frame_rate));
2531 recompute_meters (_metrics);
2535 Metrics::const_iterator d = future_map.begin();
2536 while (d != future_map.end()) {
2541 MetricPositionChanged (); // Emit Signal
2546 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2548 Glib::Threads::RWLock::ReaderLock lm (lock);
2550 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2551 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2552 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2554 return frame_at_beat_locked (_metrics, total_beats);
2558 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2560 return round_to_type (fr, dir, Bar);
2564 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2566 return round_to_type (fr, dir, Beat);
2570 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2572 Glib::Threads::RWLock::ReaderLock lm (lock);
2573 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2574 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2575 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2577 ticks -= beats * BBT_Time::ticks_per_beat;
2580 /* round to next (or same iff dir == RoundUpMaybe) */
2582 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2584 if (mod == 0 && dir == RoundUpMaybe) {
2585 /* right on the subdivision, which is fine, so do nothing */
2587 } else if (mod == 0) {
2588 /* right on the subdivision, so the difference is just the subdivision ticks */
2589 ticks += ticks_one_subdivisions_worth;
2592 /* not on subdivision, compute distance to next subdivision */
2594 ticks += ticks_one_subdivisions_worth - mod;
2597 if (ticks >= BBT_Time::ticks_per_beat) {
2598 ticks -= BBT_Time::ticks_per_beat;
2600 } else if (dir < 0) {
2602 /* round to previous (or same iff dir == RoundDownMaybe) */
2604 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2606 if (difference == 0 && dir == RoundDownAlways) {
2607 /* right on the subdivision, but force-rounding down,
2608 so the difference is just the subdivision ticks */
2609 difference = ticks_one_subdivisions_worth;
2612 if (ticks < difference) {
2613 ticks = BBT_Time::ticks_per_beat - ticks;
2615 ticks -= difference;
2619 /* round to nearest */
2622 /* compute the distance to the previous and next subdivision */
2624 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2626 /* closer to the next subdivision, so shift forward */
2628 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2630 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2632 if (ticks > BBT_Time::ticks_per_beat) {
2634 ticks -= BBT_Time::ticks_per_beat;
2635 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2638 } else if (rem > 0) {
2640 /* closer to previous subdivision, so shift backward */
2644 /* can't go backwards past zero, so ... */
2647 /* step back to previous beat */
2649 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2650 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2652 ticks = lrint (ticks - rem);
2653 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2656 /* on the subdivision, do nothing */
2660 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2666 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2668 if (sub_num == -1) {
2669 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2670 if ((double) when.beats > bpb / 2.0) {
2676 } else if (sub_num == 0) {
2677 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2678 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2680 while ((double) when.beats > bpb) {
2682 when.beats -= (uint32_t) floor (bpb);
2688 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2690 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2691 /* closer to the next subdivision, so shift forward */
2693 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2695 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2697 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2700 } else if (rem > 0) {
2701 /* closer to previous subdivision, so shift backward */
2703 if (rem > when.ticks) {
2704 if (when.beats == 0) {
2705 /* can't go backwards past zero, so ... */
2707 /* step back to previous beat */
2709 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2711 when.ticks = when.ticks - rem;
2717 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2719 Glib::Threads::RWLock::ReaderLock lm (lock);
2721 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2722 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2727 /* find bar previous to 'frame' */
2730 return frame_time_locked (_metrics, bbt);
2732 } else if (dir > 0) {
2733 /* find bar following 'frame' */
2737 return frame_time_locked (_metrics, bbt);
2739 /* true rounding: find nearest bar */
2740 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2743 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2745 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2747 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2758 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2759 } else if (dir > 0) {
2760 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2762 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2771 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2772 framepos_t lower, framepos_t upper)
2774 Glib::Threads::RWLock::ReaderLock lm (lock);
2775 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2777 /* although the map handles negative beats, bbt doesn't. */
2781 while (pos < upper) {
2782 pos = frame_at_beat_locked (_metrics, cnt);
2783 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2784 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2785 const BBT_Time bbt = beats_to_bbt (cnt);
2786 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2792 TempoMap::tempo_section_at (framepos_t frame) const
2794 Glib::Threads::RWLock::ReaderLock lm (lock);
2795 return tempo_section_at_locked (_metrics, frame);
2799 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2801 Metrics::const_iterator i;
2802 TempoSection* prev = 0;
2804 for (i = metrics.begin(); i != metrics.end(); ++i) {
2807 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2811 if (prev && t->frame() > frame) {
2821 abort(); /*NOTREACHED*/
2828 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2830 TempoSection* prev_t = 0;
2831 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2833 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2835 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2836 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2847 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2849 TempoSection* prev_t = 0;
2851 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2853 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2854 if (prev_t && t->pulse() > pulse) {
2864 /* don't use this to calculate length (the tempo is only correct for this frame).
2865 do that stuff based on the beat_at_frame and frame_at_beat api
2868 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2870 Glib::Threads::RWLock::ReaderLock lm (lock);
2872 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
2873 const TempoSection* ts_after = 0;
2874 Metrics::const_iterator i;
2876 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2879 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2883 if ((*i)->frame() > frame) {
2891 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2893 /* must be treated as constant tempo */
2894 return ts_at->frames_per_beat (_frame_rate);
2898 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
2900 TempoSection* prev_t = 0;
2902 Metrics::const_iterator i;
2904 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2906 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2910 if ((prev_t) && t->frame() > frame) {
2911 /* t is the section past frame */
2912 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2913 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2920 const double ret = prev_t->beats_per_minute();
2921 const Tempo ret_tempo (ret, prev_t->note_type ());
2927 TempoMap::tempo_at (const framepos_t& frame) const
2929 Glib::Threads::RWLock::ReaderLock lm (lock);
2930 return tempo_at_locked (_metrics, frame);
2934 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
2936 Metrics::const_iterator i;
2937 MeterSection* prev = 0;
2939 for (i = metrics.begin(); i != metrics.end(); ++i) {
2942 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2944 if (prev && (*i)->frame() > frame) {
2954 abort(); /*NOTREACHED*/
2962 TempoMap::meter_section_at (framepos_t frame) const
2964 Glib::Threads::RWLock::ReaderLock lm (lock);
2965 return meter_section_at_locked (_metrics, frame);
2969 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2971 MeterSection* prev_m = 0;
2973 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2975 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2976 if (prev_m && m->beat() > beat) {
2987 TempoMap::meter_section_at_beat (double beat) const
2989 Glib::Threads::RWLock::ReaderLock lm (lock);
2990 return meter_section_at_beat_locked (_metrics, beat);
2994 TempoMap::meter_at (framepos_t frame) const
2996 TempoMetric m (metric_at (frame));
3001 TempoMap::fix_legacy_session ()
3003 MeterSection* prev_m = 0;
3004 TempoSection* prev_t = 0;
3006 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3010 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3011 if (!m->movable()) {
3012 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3016 m->set_position_lock_style (AudioTime);
3021 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3022 + (m->bbt().beats - 1)
3023 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3025 m->set_beat (start);
3026 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3027 + (m->bbt().beats - 1)
3028 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3029 m->set_pulse (start_beat / prev_m->note_divisor());
3032 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3038 if (!t->movable()) {
3041 t->set_position_lock_style (AudioTime);
3047 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3048 + (t->legacy_bbt().beats - 1)
3049 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3051 t->set_pulse (beat / prev_m->note_divisor());
3053 /* really shouldn't happen but.. */
3054 t->set_pulse (beat / 4.0);
3063 TempoMap::get_state ()
3065 Metrics::const_iterator i;
3066 XMLNode *root = new XMLNode ("TempoMap");
3069 Glib::Threads::RWLock::ReaderLock lm (lock);
3070 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3071 root->add_child_nocopy ((*i)->get_state());
3079 TempoMap::set_state (const XMLNode& node, int /*version*/)
3082 Glib::Threads::RWLock::WriterLock lm (lock);
3085 XMLNodeConstIterator niter;
3086 Metrics old_metrics (_metrics);
3089 nlist = node.children();
3091 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3092 XMLNode* child = *niter;
3094 if (child->name() == TempoSection::xml_state_node_name) {
3097 TempoSection* ts = new TempoSection (*child);
3098 _metrics.push_back (ts);
3101 catch (failed_constructor& err){
3102 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3103 _metrics = old_metrics;
3107 } else if (child->name() == MeterSection::xml_state_node_name) {
3110 MeterSection* ms = new MeterSection (*child);
3111 _metrics.push_back (ms);
3114 catch (failed_constructor& err) {
3115 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3116 _metrics = old_metrics;
3122 if (niter == nlist.end()) {
3123 MetricSectionSorter cmp;
3124 _metrics.sort (cmp);
3127 /* check for legacy sessions where bbt was the base musical unit for tempo */
3128 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3130 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3131 if (t->legacy_bbt().bars != 0) {
3132 fix_legacy_session();
3139 /* check for multiple tempo/meters at the same location, which
3140 ardour2 somehow allowed.
3143 Metrics::iterator prev = _metrics.end();
3144 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3145 if (prev != _metrics.end()) {
3147 MeterSection* prev_m;
3149 TempoSection* prev_t;
3150 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3151 if (prev_m->pulse() == ms->pulse()) {
3152 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3153 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3156 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3157 if (prev_t->pulse() == ts->pulse()) {
3158 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3159 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3167 recompute_map (_metrics);
3170 PropertyChanged (PropertyChange ());
3176 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3178 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3179 const MeterSection* m;
3180 const TempoSection* t;
3181 const TempoSection* prev_t = 0;
3183 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3185 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3186 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3187 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3188 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3190 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3191 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;
3194 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3195 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3196 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3199 o << "------" << std::endl;
3203 TempoMap::n_tempos() const
3205 Glib::Threads::RWLock::ReaderLock lm (lock);
3208 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3209 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3218 TempoMap::n_meters() const
3220 Glib::Threads::RWLock::ReaderLock lm (lock);
3223 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3224 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3233 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3236 Glib::Threads::RWLock::WriterLock lm (lock);
3237 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3238 if ((*i)->frame() >= where && (*i)->movable ()) {
3239 (*i)->set_frame ((*i)->frame() + amount);
3243 /* now reset the BBT time of all metrics, based on their new
3244 * audio time. This is the only place where we do this reverse
3248 Metrics::iterator i;
3249 const MeterSection* meter;
3250 const TempoSection* tempo;
3254 meter = &first_meter ();
3255 tempo = &first_tempo ();
3260 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3263 MetricSection* prev = 0;
3265 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3268 //TempoMetric metric (*meter, *tempo);
3269 MeterSection* ms = const_cast<MeterSection*>(meter);
3270 TempoSection* ts = const_cast<TempoSection*>(tempo);
3273 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3277 ts->set_pulse (t->pulse());
3279 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3280 ts->set_pulse (m->pulse());
3282 ts->set_frame (prev->frame());
3286 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3287 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3288 ms->set_beat (start);
3289 ms->set_pulse (m->pulse());
3291 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3295 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3296 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3297 ms->set_beat (start);
3298 ms->set_pulse (t->pulse());
3300 ms->set_frame (prev->frame());
3304 // metric will be at frames=0 bbt=1|1|0 by default
3305 // which is correct for our purpose
3308 // cerr << bbt << endl;
3310 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3314 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3316 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3317 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3318 bbt_time (m->frame(), bbt);
3320 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3326 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3327 /* round up to next beat */
3333 if (bbt.beats != 1) {
3334 /* round up to next bar */
3339 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3340 m->set_beat (start);
3341 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3343 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3345 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3346 abort(); /*NOTREACHED*/
3352 recompute_map (_metrics);
3356 PropertyChanged (PropertyChange ());
3359 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3363 std::list<MetricSection*> metric_kill_list;
3365 TempoSection* last_tempo = NULL;
3366 MeterSection* last_meter = NULL;
3367 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3368 bool meter_after = false; // is there a meter marker likewise?
3370 Glib::Threads::RWLock::WriterLock lm (lock);
3371 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3372 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3373 metric_kill_list.push_back(*i);
3374 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3377 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3381 else if ((*i)->frame() >= where) {
3382 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3383 (*i)->set_frame ((*i)->frame() - amount);
3384 if ((*i)->frame() == where) {
3385 // marker was immediately after end of range
3386 tempo_after = dynamic_cast<TempoSection*> (*i);
3387 meter_after = dynamic_cast<MeterSection*> (*i);
3393 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3394 if (last_tempo && !tempo_after) {
3395 metric_kill_list.remove(last_tempo);
3396 last_tempo->set_frame(where);
3399 if (last_meter && !meter_after) {
3400 metric_kill_list.remove(last_meter);
3401 last_meter->set_frame(where);
3405 //remove all the remaining metrics
3406 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3407 _metrics.remove(*i);
3412 recompute_map (_metrics);
3415 PropertyChanged (PropertyChange ());
3419 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3420 * pos can be -ve, if required.
3423 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3425 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3428 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3430 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3432 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3435 /** Add the BBT interval op to pos and return the result */
3437 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3439 Glib::Threads::RWLock::ReaderLock lm (lock);
3441 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3442 pos_bbt.ticks += op.ticks;
3443 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3445 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3447 pos_bbt.beats += op.beats;
3448 /* the meter in effect will start on the bar */
3449 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();
3450 while (pos_bbt.beats >= divisions_per_bar + 1) {
3452 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3453 pos_bbt.beats -= divisions_per_bar;
3455 pos_bbt.bars += op.bars;
3457 return frame_time_locked (_metrics, pos_bbt);
3460 /** Count the number of beats that are equivalent to distance when going forward,
3464 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3466 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3470 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3476 operator<< (std::ostream& o, const Meter& m) {
3477 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3481 operator<< (std::ostream& o, const Tempo& t) {
3482 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3486 operator<< (std::ostream& o, const MetricSection& section) {
3488 o << "MetricSection @ " << section.frame() << ' ';
3490 const TempoSection* ts;
3491 const MeterSection* ms;
3493 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3494 o << *((const Tempo*) ts);
3495 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3496 o << *((const Meter*) ms);