2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 const XMLProperty *prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses per minute occurs in this section.
222 beat b is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_pulse_tempo (ppm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
461 const XMLProperty *prop;
465 framepos_t frame = 0;
466 pair<double, BBT_Time> start;
468 if ((prop = node.property ("start")) != 0) {
469 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
473 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
475 /* legacy session - start used to be in bbt*/
476 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
481 if ((prop = node.property ("pulse")) != 0) {
482 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
483 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
488 if ((prop = node.property ("beat")) != 0) {
489 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
490 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
496 if ((prop = node.property ("bbt")) == 0) {
497 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
498 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
502 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
503 throw failed_constructor();
509 if ((prop = node.property ("frame")) != 0) {
510 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
511 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
517 /* beats-per-bar is old; divisions-per-bar is new */
519 if ((prop = node.property ("divisions-per-bar")) == 0) {
520 if ((prop = node.property ("beats-per-bar")) == 0) {
521 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
522 throw failed_constructor();
525 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
526 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
527 throw failed_constructor();
530 if ((prop = node.property ("note-type")) == 0) {
531 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
532 throw failed_constructor();
534 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
535 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
536 throw failed_constructor();
539 if ((prop = node.property ("movable")) == 0) {
540 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
541 throw failed_constructor();
544 set_movable (string_is_affirmative (prop->value()));
546 if ((prop = node.property ("lock-style")) == 0) {
547 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
549 set_position_lock_style (MusicTime);
551 set_position_lock_style (AudioTime);
554 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
559 MeterSection::get_state() const
561 XMLNode *root = new XMLNode (xml_state_node_name);
565 snprintf (buf, sizeof (buf), "%lf", pulse());
566 root->add_property ("pulse", buf);
567 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
571 root->add_property ("bbt", buf);
572 snprintf (buf, sizeof (buf), "%lf", beat());
573 root->add_property ("beat", buf);
574 snprintf (buf, sizeof (buf), "%f", _note_type);
575 root->add_property ("note-type", buf);
576 snprintf (buf, sizeof (buf), "%li", frame());
577 root->add_property ("frame", buf);
578 root->add_property ("lock-style", enum_2_string (position_lock_style()));
579 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
580 root->add_property ("divisions-per-bar", buf);
581 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
582 root->add_property ("movable", buf);
587 /***********************************************************************/
591 Tempo can be thought of as a source of the musical pulse.
592 Meters divide that pulse into measures and beats.
593 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
594 at any particular time.
595 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
596 It should rather be thought of as tempo note divisions per minute.
598 TempoSections, which are nice to think of in whole pulses per minute,
599 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
600 and beats (via note_divisor) are used to form a tempo map.
601 TempoSections and MeterSections may be locked to either audio or music (position lock style).
602 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
603 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
605 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 Both tempos and meters have a pulse position and a frame position.
612 Meters also have a beat position, which is always 0.0 for the first meter.
614 A tempo locked to music is locked to musical pulses.
615 A meter locked to music is locked to beats.
617 Recomputing the tempo map is the process where the 'missing' position
618 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
620 It is important to keep the _metrics in an order that makes sense.
621 Because ramped MusicTime and AudioTime tempos can interact with each other,
622 reordering is frequent. Care must be taken to keep _metrics in a solved state.
623 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
625 struct MetricSectionSorter {
626 bool operator() (const MetricSection* a, const MetricSection* b) {
627 return a->pulse() < b->pulse();
631 struct MetricSectionFrameSorter {
632 bool operator() (const MetricSection* a, const MetricSection* b) {
633 return a->frame() < b->frame();
637 TempoMap::TempoMap (framecnt_t fr)
640 BBT_Time start (1, 1, 0);
642 TempoSection *t = new TempoSection ((framepos_t) 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
643 MeterSection *m = new MeterSection ((framepos_t) 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
645 t->set_movable (false);
646 m->set_movable (false);
648 /* note: frame time is correct (zero) for both of these */
650 _metrics.push_back (t);
651 _metrics.push_back (m);
655 TempoMap::~TempoMap ()
657 Metrics::const_iterator d = _metrics.begin();
658 while (d != _metrics.end()) {
666 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
668 bool removed = false;
671 Glib::Threads::RWLock::WriterLock lm (lock);
672 if ((removed = remove_tempo_locked (tempo))) {
673 if (complete_operation) {
674 recompute_map (_metrics);
679 if (removed && complete_operation) {
680 PropertyChanged (PropertyChange ());
685 TempoMap::remove_tempo_locked (const TempoSection& tempo)
689 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
690 if (dynamic_cast<TempoSection*> (*i) != 0) {
691 if (tempo.frame() == (*i)->frame()) {
692 if ((*i)->movable()) {
705 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
707 bool removed = false;
710 Glib::Threads::RWLock::WriterLock lm (lock);
711 if ((removed = remove_meter_locked (tempo))) {
712 if (complete_operation) {
713 recompute_map (_metrics);
718 if (removed && complete_operation) {
719 PropertyChanged (PropertyChange ());
724 TempoMap::remove_meter_locked (const MeterSection& meter)
728 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
730 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
731 if (meter.frame() == (*i)->frame()) {
732 if (t->locked_to_meter()) {
741 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
742 if (dynamic_cast<MeterSection*> (*i) != 0) {
743 if (meter.frame() == (*i)->frame()) {
744 if ((*i)->movable()) {
757 TempoMap::do_insert (MetricSection* section)
759 bool need_add = true;
760 /* we only allow new meters to be inserted on beat 1 of an existing
764 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
766 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
768 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
769 corrected.second.beats = 1;
770 corrected.second.ticks = 0;
771 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
772 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
773 m->bbt(), corrected.second) << endmsg;
774 //m->set_pulse (corrected);
778 /* Look for any existing MetricSection that is of the same type and
779 in the same bar as the new one, and remove it before adding
780 the new one. Note that this means that if we find a matching,
781 existing section, we can break out of the loop since we're
782 guaranteed that there is only one such match.
785 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
787 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
788 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
789 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
790 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
792 if (tempo && insert_tempo) {
795 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
796 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
798 if (!tempo->movable()) {
800 /* can't (re)move this section, so overwrite
801 * its data content (but not its properties as
805 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
806 (*i)->set_position_lock_style (AudioTime);
808 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
809 t->set_type (insert_tempo->type());
819 } else if (meter && insert_meter) {
823 bool const ipm = insert_meter->position_lock_style() == MusicTime;
825 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
827 if (!meter->movable()) {
829 /* can't (re)move this section, so overwrite
830 * its data content (but not its properties as
834 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
835 (*i)->set_position_lock_style (AudioTime);
845 /* non-matching types, so we don't care */
849 /* Add the given MetricSection, if we didn't just reset an existing
854 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
855 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
858 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
859 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
862 bool const ipm = insert_meter->position_lock_style() == MusicTime;
863 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
868 } else if (insert_tempo) {
869 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
870 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
873 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
874 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
881 _metrics.insert (i, section);
882 //dump (_metrics, std::cout);
887 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
890 Glib::Threads::RWLock::WriterLock lm (lock);
891 TempoSection& first (first_tempo());
892 if (ts.pulse() != first.pulse()) {
893 remove_tempo_locked (ts);
894 add_tempo_locked (tempo, pulse, true, type);
896 first.set_type (type);
898 /* cannot move the first tempo section */
899 *static_cast<Tempo*>(&first) = tempo;
900 recompute_map (_metrics);
905 PropertyChanged (PropertyChange ());
909 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
912 Glib::Threads::RWLock::WriterLock lm (lock);
913 TempoSection& first (first_tempo());
914 if (ts.frame() != first.frame()) {
915 remove_tempo_locked (ts);
916 add_tempo_locked (tempo, frame, true, type);
918 first.set_type (type);
919 first.set_pulse (0.0);
920 first.set_position_lock_style (AudioTime);
922 /* cannot move the first tempo section */
923 *static_cast<Tempo*>(&first) = tempo;
924 recompute_map (_metrics);
929 PropertyChanged (PropertyChange ());
933 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
935 TempoSection* ts = 0;
937 Glib::Threads::RWLock::WriterLock lm (lock);
938 ts = add_tempo_locked (tempo, pulse, true, type);
941 PropertyChanged (PropertyChange ());
947 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
949 TempoSection* ts = 0;
951 Glib::Threads::RWLock::WriterLock lm (lock);
952 ts = add_tempo_locked (tempo, frame, true, type);
956 PropertyChanged (PropertyChange ());
962 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
964 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
969 solve_map (_metrics, t, t->pulse());
970 recompute_meters (_metrics);
977 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
979 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
984 solve_map (_metrics, t, t->frame());
985 recompute_meters (_metrics);
992 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
995 Glib::Threads::RWLock::WriterLock lm (lock);
998 remove_meter_locked (ms);
999 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
1001 MeterSection& first (first_meter());
1002 /* cannot move the first meter section */
1003 *static_cast<Meter*>(&first) = meter;
1004 first.set_position_lock_style (AudioTime);
1006 recompute_map (_metrics);
1009 PropertyChanged (PropertyChange ());
1013 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1016 Glib::Threads::RWLock::WriterLock lm (lock);
1018 const double beat = ms.beat();
1019 const BBT_Time bbt = ms.bbt();
1022 remove_meter_locked (ms);
1023 add_meter_locked (meter, frame, beat, bbt, true);
1025 MeterSection& first (first_meter());
1026 TempoSection& first_t (first_tempo());
1027 /* cannot move the first meter section */
1028 *static_cast<Meter*>(&first) = meter;
1029 first.set_position_lock_style (AudioTime);
1030 first.set_pulse (0.0);
1031 first.set_frame (frame);
1032 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1033 first.set_beat (beat);
1034 first_t.set_frame (first.frame());
1035 first_t.set_pulse (0.0);
1036 first_t.set_position_lock_style (AudioTime);
1038 recompute_map (_metrics);
1040 PropertyChanged (PropertyChange ());
1045 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1047 MeterSection* m = 0;
1049 Glib::Threads::RWLock::WriterLock lm (lock);
1050 m = add_meter_locked (meter, beat, where, true);
1055 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1056 dump (_metrics, std::cerr);
1060 PropertyChanged (PropertyChange ());
1066 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1068 MeterSection* m = 0;
1070 Glib::Threads::RWLock::WriterLock lm (lock);
1071 m = add_meter_locked (meter, frame, beat, where, true);
1076 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1077 dump (_metrics, std::cerr);
1081 PropertyChanged (PropertyChange ());
1087 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, bool recompute)
1089 /* a new meter always starts a new bar on the first beat. so
1090 round the start time appropriately. remember that
1091 `where' is based on the existing tempo map, not
1092 the result after we insert the new meter.
1096 const double pulse = pulse_at_beat_locked (_metrics, beat);
1097 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1099 do_insert (new_meter);
1102 solve_map (_metrics, new_meter, where);
1109 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, const Timecode::BBT_Time& where, bool recompute)
1111 /* add meter-locked tempo */
1112 TempoSection* t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1114 t->set_locked_to_meter (true);
1117 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1118 new_meter->set_pulse (pulse_at_frame_locked (_metrics, frame));
1120 do_insert (new_meter);
1123 solve_map (_metrics, new_meter, frame);
1130 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1132 Tempo newtempo (beats_per_minute, note_type);
1135 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1136 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1141 Glib::Threads::RWLock::WriterLock lm (lock);
1142 *((Tempo*) t) = newtempo;
1143 recompute_map (_metrics);
1145 PropertyChanged (PropertyChange ());
1152 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1154 Tempo newtempo (beats_per_minute, note_type);
1157 TempoSection* first;
1158 Metrics::iterator i;
1160 /* find the TempoSection immediately preceding "where"
1163 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1165 if ((*i)->frame() > where) {
1171 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1184 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1194 Glib::Threads::RWLock::WriterLock lm (lock);
1195 /* cannot move the first tempo section */
1196 *((Tempo*)prev) = newtempo;
1197 recompute_map (_metrics);
1200 PropertyChanged (PropertyChange ());
1204 TempoMap::first_meter () const
1206 const MeterSection *m = 0;
1208 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1209 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1214 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1215 abort(); /*NOTREACHED*/
1220 TempoMap::first_meter ()
1222 MeterSection *m = 0;
1224 /* CALLER MUST HOLD LOCK */
1226 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1227 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1232 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1233 abort(); /*NOTREACHED*/
1238 TempoMap::first_tempo () const
1240 const TempoSection *t = 0;
1242 /* CALLER MUST HOLD LOCK */
1244 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1245 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1249 if (!t->movable()) {
1255 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1256 abort(); /*NOTREACHED*/
1261 TempoMap::first_tempo ()
1263 TempoSection *t = 0;
1265 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1266 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1270 if (!t->movable()) {
1276 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1277 abort(); /*NOTREACHED*/
1281 TempoMap::recompute_tempos (Metrics& metrics)
1283 TempoSection* prev_t = 0;
1285 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1288 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1292 if (!t->movable()) {
1300 if (t->position_lock_style() == AudioTime) {
1301 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1302 if (!t->locked_to_meter()) {
1303 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1307 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1308 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1315 prev_t->set_c_func (0.0);
1318 /* tempos must be positioned correctly.
1319 the current approach is to use a meter's bbt time as its base position unit.
1320 this means that a meter's beat may change, but its bbt may not.
1321 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1322 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1325 TempoMap::recompute_meters (Metrics& metrics)
1327 MeterSection* meter = 0;
1328 MeterSection* prev_m = 0;
1330 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1331 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1332 if (meter->position_lock_style() == AudioTime) {
1334 pair<double, BBT_Time> b_bbt;
1335 TempoSection* meter_locked_tempo = 0;
1336 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1338 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1339 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1340 meter_locked_tempo = t;
1347 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1348 if (beats + prev_m->beat() != meter->beat()) {
1349 /* reordering caused a bbt change */
1350 b_bbt = make_pair (beats + prev_m->beat()
1351 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1352 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1354 } else if (meter->movable()) {
1355 b_bbt = make_pair (meter->beat(), meter->bbt());
1356 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1359 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1361 if (meter_locked_tempo) {
1362 meter_locked_tempo->set_pulse (pulse);
1364 meter->set_beat (b_bbt);
1365 meter->set_pulse (pulse);
1370 pair<double, BBT_Time> new_beat;
1372 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1373 if (beats + prev_m->beat() != meter->beat()) {
1374 /* reordering caused a bbt change */
1375 new_beat = make_pair (beats + prev_m->beat()
1376 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1378 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1380 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1382 /* shouldn't happen - the first is audio-locked */
1383 pulse = pulse_at_beat_locked (metrics, meter->beat());
1384 new_beat = make_pair (meter->beat(), meter->bbt());
1387 meter->set_beat (new_beat);
1388 meter->set_pulse (pulse);
1389 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1398 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1400 /* CALLER MUST HOLD WRITE LOCK */
1404 /* we will actually stop once we hit
1411 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1414 /* silly call from Session::process() during startup
1419 recompute_tempos (metrics);
1420 recompute_meters (metrics);
1424 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1426 Glib::Threads::RWLock::ReaderLock lm (lock);
1427 TempoMetric m (first_meter(), first_tempo());
1429 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1430 at something, because we insert the default tempo and meter during
1431 TempoMap construction.
1433 now see if we can find better candidates.
1436 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1438 if ((*i)->frame() > frame) {
1452 /* XX meters only */
1454 TempoMap::metric_at (BBT_Time bbt) const
1456 Glib::Threads::RWLock::ReaderLock lm (lock);
1457 TempoMetric m (first_meter(), first_tempo());
1459 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1460 at something, because we insert the default tempo and meter during
1461 TempoMap construction.
1463 now see if we can find better candidates.
1466 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1468 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1469 BBT_Time section_start (mw->bbt());
1471 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1483 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1485 MeterSection* prev_m = 0;
1487 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1489 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1490 if (prev_m && m->beat() > beat) {
1497 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1502 TempoMap::pulse_at_beat (const double& beat) const
1504 Glib::Threads::RWLock::ReaderLock lm (lock);
1505 return pulse_at_beat_locked (_metrics, beat);
1509 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1511 MeterSection* prev_m = 0;
1513 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1515 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1516 if (prev_m && m->pulse() > pulse) {
1517 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1525 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1530 TempoMap::beat_at_pulse (const double& pulse) const
1532 Glib::Threads::RWLock::ReaderLock lm (lock);
1533 return beat_at_pulse_locked (_metrics, pulse);
1536 /* tempo section based */
1538 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1540 /* HOLD (at least) THE READER LOCK */
1541 TempoSection* prev_t = 0;
1543 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1545 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1549 if (prev_t && t->frame() > frame) {
1550 /*the previous ts is the one containing the frame */
1551 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1558 /* treated as constant for this ts */
1559 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1561 return pulses_in_section + prev_t->pulse();
1565 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1567 Glib::Threads::RWLock::ReaderLock lm (lock);
1568 return pulse_at_frame_locked (_metrics, frame);
1571 /* tempo section based */
1573 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1575 /* HOLD THE READER LOCK */
1577 const TempoSection* prev_t = 0;
1579 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1582 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1586 if (prev_t && t->pulse() > pulse) {
1587 return prev_t->frame_at_pulse (pulse, _frame_rate);
1593 /* must be treated as constant, irrespective of _type */
1594 double const pulses_in_section = pulse - prev_t->pulse();
1595 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1597 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1603 TempoMap::frame_at_pulse (const double& pulse) const
1605 Glib::Threads::RWLock::ReaderLock lm (lock);
1606 return frame_at_pulse_locked (_metrics, pulse);
1609 /* meter section based */
1611 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1613 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1614 MeterSection* prev_m = 0;
1615 MeterSection* next_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->frame() > frame) {
1627 if (frame < prev_m->frame()) {
1630 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1632 if (next_m && next_m->beat() < beat) {
1633 return next_m->beat();
1640 TempoMap::beat_at_frame (const framecnt_t& frame) const
1642 Glib::Threads::RWLock::ReaderLock lm (lock);
1643 return beat_at_frame_locked (_metrics, frame);
1646 /* meter section based */
1648 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1650 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1651 MeterSection* prev_m = 0;
1653 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1655 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1656 if (prev_m && m->beat() > beat) {
1663 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1667 TempoMap::frame_at_beat (const double& beat) const
1669 Glib::Threads::RWLock::ReaderLock lm (lock);
1670 return frame_at_beat_locked (_metrics, beat);
1674 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1676 /* CALLER HOLDS READ LOCK */
1678 MeterSection* prev_m = 0;
1680 /* because audio-locked meters have 'fake' integral beats,
1681 there is no pulse offset here.
1683 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1685 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1687 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1688 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1696 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1697 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1698 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1704 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1706 Glib::Threads::RWLock::ReaderLock lm (lock);
1707 return bbt_to_beats_locked (_metrics, bbt);
1711 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1713 /* CALLER HOLDS READ LOCK */
1714 MeterSection* prev_m = 0;
1715 const double beats = (b < 0.0) ? 0.0 : b;
1717 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1718 MeterSection* m = 0;
1720 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1722 if (m->beat() > beats) {
1723 /* this is the meter after the one our beat is on*/
1732 const double beats_in_ms = beats - prev_m->beat();
1733 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1734 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1735 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1736 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1740 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1741 ret.beats = (uint32_t) floor (remaining_beats);
1742 ret.bars = total_bars;
1744 /* 0 0 0 to 1 1 0 - based mapping*/
1748 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1750 ret.ticks -= BBT_Time::ticks_per_beat;
1753 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1762 TempoMap::beats_to_bbt (const double& beats)
1764 Glib::Threads::RWLock::ReaderLock lm (lock);
1765 return beats_to_bbt_locked (_metrics, beats);
1769 TempoMap::pulse_to_bbt (const double& pulse)
1771 Glib::Threads::RWLock::ReaderLock lm (lock);
1772 MeterSection* prev_m = 0;
1774 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1775 MeterSection* m = 0;
1777 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1780 double const pulses_to_m = m->pulse() - prev_m->pulse();
1781 if (prev_m->pulse() + pulses_to_m > pulse) {
1782 /* this is the meter after the one our beat is on*/
1791 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1792 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1793 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1794 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1795 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1799 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1800 ret.beats = (uint32_t) floor (remaining_beats);
1801 ret.bars = total_bars;
1803 /* 0 0 0 to 1 1 0 mapping*/
1807 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1809 ret.ticks -= BBT_Time::ticks_per_beat;
1812 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1821 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1828 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1831 Glib::Threads::RWLock::ReaderLock lm (lock);
1832 const double beat = beat_at_frame_locked (_metrics, frame);
1834 bbt = beats_to_bbt_locked (_metrics, beat);
1837 /* meter section based */
1839 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1841 /* HOLD THE READER LOCK */
1843 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1848 TempoMap::frame_time (const BBT_Time& bbt)
1851 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1855 if (bbt.beats < 1) {
1856 throw std::logic_error ("beats are counted from one");
1858 Glib::Threads::RWLock::ReaderLock lm (lock);
1860 return frame_time_locked (_metrics, bbt);
1864 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1866 TempoSection* prev_t = 0;
1867 MeterSection* prev_m = 0;
1869 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1872 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1877 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1881 if (t->frame() == prev_t->frame()) {
1885 /* precision check ensures pulses and frames align.*/
1886 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1887 if (!t->locked_to_meter()) {
1895 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1896 if (prev_m && m->position_lock_style() == AudioTime) {
1897 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1898 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1899 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1901 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1915 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1917 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1919 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1920 if (!t->movable()) {
1921 t->set_active (true);
1924 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1925 t->set_active (false);
1927 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1928 t->set_active (true);
1929 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1938 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1940 TempoSection* prev_t = 0;
1941 TempoSection* section_prev = 0;
1942 framepos_t first_m_frame = 0;
1944 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1946 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1947 if (!m->movable()) {
1948 first_m_frame = m->frame();
1953 if (section->movable() && frame <= first_m_frame) {
1957 section->set_active (true);
1958 section->set_frame (frame);
1960 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1962 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1969 section_prev = prev_t;
1972 if (t->position_lock_style() == MusicTime) {
1973 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1974 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1976 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1977 if (!t->locked_to_meter()) {
1978 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1987 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1988 if (!section->locked_to_meter()) {
1989 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1993 if (section->position_lock_style() == MusicTime) {
1994 /* we're setting the frame */
1995 section->set_position_lock_style (AudioTime);
1996 recompute_tempos (imaginary);
1997 section->set_position_lock_style (MusicTime);
1999 recompute_tempos (imaginary);
2002 if (check_solved (imaginary, true)) {
2006 MetricSectionFrameSorter fcmp;
2007 imaginary.sort (fcmp);
2008 if (section->position_lock_style() == MusicTime) {
2009 /* we're setting the frame */
2010 section->set_position_lock_style (AudioTime);
2011 recompute_tempos (imaginary);
2012 section->set_position_lock_style (MusicTime);
2014 recompute_tempos (imaginary);
2017 if (check_solved (imaginary, true)) {
2025 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
2027 TempoSection* prev_t = 0;
2028 TempoSection* section_prev = 0;
2030 section->set_pulse (pulse);
2032 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2034 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2038 if (!t->movable()) {
2045 section_prev = prev_t;
2048 if (t->position_lock_style() == MusicTime) {
2049 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2050 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2052 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2053 if (!t->locked_to_meter()) {
2054 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2062 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2063 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2066 if (section->position_lock_style() == AudioTime) {
2067 /* we're setting the pulse */
2068 section->set_position_lock_style (MusicTime);
2069 recompute_tempos (imaginary);
2070 section->set_position_lock_style (AudioTime);
2072 recompute_tempos (imaginary);
2075 if (check_solved (imaginary, false)) {
2079 MetricSectionSorter cmp;
2080 imaginary.sort (cmp);
2081 if (section->position_lock_style() == AudioTime) {
2082 /* we're setting the pulse */
2083 section->set_position_lock_style (MusicTime);
2084 recompute_tempos (imaginary);
2085 section->set_position_lock_style (AudioTime);
2087 recompute_tempos (imaginary);
2090 if (check_solved (imaginary, false)) {
2098 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2100 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2101 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2102 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2106 if (!section->movable()) {
2107 /* lock the first tempo to our first meter */
2108 if (!set_active_tempos (imaginary, frame)) {
2113 /* it would make sense to bail out if there is no audio-locked meter,
2114 however it may be desirable to move a music-locked meter by frame at some point.
2116 TempoSection* meter_locked_tempo = 0;
2117 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2119 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2120 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2121 meter_locked_tempo = t;
2127 MeterSection* prev_m = 0;
2129 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2131 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2133 if (prev_m && section->movable()) {
2134 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2135 if (beats + prev_m->beat() < section->beat()) {
2136 /* disallow position change if it will alter our beat
2137 we allow tempo changes to do this in recompute_meters().
2138 blocking this is an option, but i'm not convinced that
2139 this is what the user would actually want.
2140 here we set the frame/pulse corresponding to its musical position.
2143 if (meter_locked_tempo) {
2145 bool solved = false;
2146 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2147 const double new_pulse = ((section->beat() - prev_m->beat())
2148 / prev_m->note_divisor()) + prev_m->pulse();
2149 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2150 if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
2151 meter_locked_tempo->set_pulse (new_pulse);
2152 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2153 section->set_frame (smallest_frame);
2154 section->set_pulse (new_pulse);
2159 Metrics::const_iterator d = future_map.begin();
2160 while (d != future_map.end()) {
2171 if (meter_locked_tempo) {
2173 bool solved = false;
2175 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2176 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2177 meter_copy->set_frame (frame);
2179 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2180 section->set_frame (frame);
2181 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2182 / prev_m->note_divisor()) + prev_m->pulse());
2183 solve_map (imaginary, meter_locked_tempo, frame);
2188 Metrics::const_iterator d = future_map.begin();
2189 while (d != future_map.end()) {
2200 /* not movable (first meter atm) */
2201 if (meter_locked_tempo) {
2203 bool solved = false;
2204 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2206 tempo_copy->set_frame (frame);
2207 tempo_copy->set_pulse (0.0);
2209 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2210 section->set_frame (frame);
2211 meter_locked_tempo->set_frame (frame);
2212 meter_locked_tempo->set_pulse (0.0);
2213 solve_map (imaginary, meter_locked_tempo, frame);
2218 Metrics::const_iterator d = future_map.begin();
2219 while (d != future_map.end()) {
2231 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2232 section->set_beat (b_bbt);
2233 section->set_pulse (0.0);
2243 MetricSectionFrameSorter fcmp;
2244 imaginary.sort (fcmp);
2245 if (section->position_lock_style() == MusicTime) {
2246 /* we're setting the frame */
2247 section->set_position_lock_style (AudioTime);
2248 recompute_meters (imaginary);
2249 section->set_position_lock_style (MusicTime);
2251 recompute_meters (imaginary);
2258 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2260 /* disallow setting section to an existing meter's bbt */
2261 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2263 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2264 if (m != section && m->bbt().bars == when.bars) {
2270 MeterSection* prev_m = 0;
2271 MeterSection* section_prev = 0;
2273 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2275 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2276 pair<double, BBT_Time> b_bbt;
2277 double new_pulse = 0.0;
2279 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2280 section_prev = prev_m;
2281 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2282 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2283 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2285 section->set_beat (b_bbt);
2286 section->set_pulse (pulse);
2287 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2292 if (m->position_lock_style() == AudioTime) {
2293 TempoSection* meter_locked_tempo = 0;
2294 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2296 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2297 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2298 meter_locked_tempo = t;
2305 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2307 if (beats + prev_m->beat() != m->beat()) {
2308 /* tempo/ meter change caused a change in beat (bar). */
2309 b_bbt = make_pair (beats + prev_m->beat()
2310 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2311 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2312 } else if (m->movable()) {
2313 b_bbt = make_pair (m->beat(), m->bbt());
2314 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2317 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2319 if (meter_locked_tempo) {
2320 meter_locked_tempo->set_pulse (new_pulse);
2321 recompute_tempos (imaginary);
2323 m->set_beat (b_bbt);
2324 m->set_pulse (new_pulse);
2328 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2329 if (beats + prev_m->beat() != m->beat()) {
2330 /* tempo/ meter change caused a change in beat (bar). */
2331 b_bbt = make_pair (beats + prev_m->beat()
2332 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2334 b_bbt = make_pair (beats + prev_m->beat()
2337 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2338 m->set_beat (b_bbt);
2339 m->set_pulse (new_pulse);
2340 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2347 if (!section_prev) {
2349 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2350 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2351 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2353 section->set_beat (b_bbt);
2354 section->set_pulse (pulse);
2355 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2358 MetricSectionSorter cmp;
2359 imaginary.sort (cmp);
2361 if (section->position_lock_style() == AudioTime) {
2362 /* we're setting the pulse */
2363 section->set_position_lock_style (MusicTime);
2364 recompute_meters (imaginary);
2365 section->set_position_lock_style (AudioTime);
2367 recompute_meters (imaginary);
2373 /** places a copy of _metrics into copy and returns a pointer
2374 * to section's equivalent in copy.
2377 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2379 TempoSection* ret = 0;
2381 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2384 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2386 ret = new TempoSection (*t);
2387 copy.push_back (ret);
2391 TempoSection* cp = new TempoSection (*t);
2392 copy.push_back (cp);
2394 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2395 MeterSection* cp = new MeterSection (*m);
2396 copy.push_back (cp);
2404 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2406 MeterSection* ret = 0;
2408 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2411 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2412 TempoSection* cp = new TempoSection (*t);
2413 copy.push_back (cp);
2416 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2418 ret = new MeterSection (*m);
2419 copy.push_back (ret);
2422 MeterSection* cp = new MeterSection (*m);
2423 copy.push_back (cp);
2431 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2434 TempoSection* tempo_copy = 0;
2437 Glib::Threads::RWLock::ReaderLock lm (lock);
2438 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2444 const double beat = bbt_to_beats_locked (copy, bbt);
2445 const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2447 Metrics::const_iterator d = copy.begin();
2448 while (d != copy.end()) {
2457 * 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,
2458 * taking any possible reordering as a consequence of this into account.
2459 * @param section - the section to be altered
2460 * @param bpm - the new Tempo
2461 * @param bbt - the bbt where the altered tempo will fall
2462 * @return returns - the position in frames where the new tempo section will lie.
2465 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2467 Glib::Threads::RWLock::ReaderLock lm (lock);
2470 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2474 const double beat = bbt_to_beats_locked (future_map, bbt);
2476 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2477 ret = tempo_copy->frame();
2479 ret = section->frame();
2482 Metrics::const_iterator d = future_map.begin();
2483 while (d != future_map.end()) {
2491 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2493 Glib::Threads::RWLock::ReaderLock lm (lock);
2496 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2498 if (solve_map (future_map, tempo_copy, frame)) {
2499 ret = tempo_copy->pulse();
2501 ret = section->pulse();
2504 Metrics::const_iterator d = future_map.begin();
2505 while (d != future_map.end()) {
2513 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2517 Glib::Threads::RWLock::WriterLock lm (lock);
2518 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2519 if (solve_map (future_map, tempo_copy, frame)) {
2520 solve_map (_metrics, ts, frame);
2521 recompute_meters (_metrics);
2525 Metrics::const_iterator d = future_map.begin();
2526 while (d != future_map.end()) {
2531 MetricPositionChanged (); // Emit Signal
2535 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2539 Glib::Threads::RWLock::WriterLock lm (lock);
2540 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2541 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2542 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2543 recompute_meters (_metrics);
2547 Metrics::const_iterator d = future_map.begin();
2548 while (d != future_map.end()) {
2553 MetricPositionChanged (); // Emit Signal
2557 TempoMap::gui_move_tempo_pulse (TempoSection* ts, const double& pulse)
2561 Glib::Threads::RWLock::WriterLock lm (lock);
2562 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2563 if (solve_map (future_map, tempo_copy, pulse)) {
2564 solve_map (_metrics, ts, pulse);
2565 recompute_meters (_metrics);
2569 Metrics::const_iterator d = future_map.begin();
2570 while (d != future_map.end()) {
2575 MetricPositionChanged (); // Emit Signal
2579 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2583 Glib::Threads::RWLock::WriterLock lm (lock);
2584 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2585 if (solve_map (future_map, copy, frame)) {
2586 solve_map (_metrics, ms, frame);
2587 recompute_tempos (_metrics);
2591 Metrics::const_iterator d = future_map.begin();
2592 while (d != future_map.end()) {
2597 MetricPositionChanged (); // Emit Signal
2601 TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
2605 Glib::Threads::RWLock::WriterLock lm (lock);
2606 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2607 if (solve_map (future_map, copy, bbt)) {
2608 solve_map (_metrics, ms, bbt);
2609 recompute_tempos (_metrics);
2613 Metrics::const_iterator d = future_map.begin();
2614 while (d != future_map.end()) {
2619 MetricPositionChanged (); // Emit Signal
2623 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2626 bool can_solve = false;
2628 Glib::Threads::RWLock::WriterLock lm (lock);
2629 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2630 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2631 recompute_tempos (future_map);
2633 if (check_solved (future_map, true)) {
2634 ts->set_beats_per_minute (bpm.beats_per_minute());
2635 recompute_map (_metrics);
2640 Metrics::const_iterator d = future_map.begin();
2641 while (d != future_map.end()) {
2646 MetricPositionChanged (); // Emit Signal
2652 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2655 TempoSection* ts = 0;
2657 if (frame <= first_meter().frame()) {
2661 if (ms->position_lock_style() == AudioTime) {
2662 /* disabled for now due to faked tempo locked to meter pulse */
2667 Glib::Threads::RWLock::WriterLock lm (lock);
2668 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2672 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2673 TempoSection* prev_to_prev_t = 0;
2674 const frameoffset_t fr_off = frame - ms->frame();
2675 double new_bpm = 0.0;
2677 if (prev_t && prev_t->pulse() > 0.0) {
2678 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2681 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2682 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2684 double contribution = 0.0;
2685 frameoffset_t frame_contribution = 0;
2686 frameoffset_t prev_t_frame_contribution = fr_off;
2688 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2689 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2690 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2691 frame_contribution = contribution * (double) fr_off;
2692 prev_t_frame_contribution = fr_off - frame_contribution;
2695 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2697 if (prev_t->position_lock_style() == MusicTime) {
2698 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2699 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2700 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2703 /* prev to prev is irrelevant */
2704 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2705 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2707 if (frame_pulse != prev_t->pulse()) {
2708 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2710 new_bpm = prev_t->beats_per_minute();
2715 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2716 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2717 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2719 /* prev_to_prev_t is irrelevant */
2721 if (frame != prev_t->frame()) {
2722 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2724 new_bpm = prev_t->beats_per_minute();
2728 } else if (prev_t->c_func() < 0.0) {
2729 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2730 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2732 /* prev_to_prev_t is irrelevant */
2733 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2736 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2737 if (diff > -0.1 && diff < 0.1) {
2738 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2739 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2742 } else if (prev_t->c_func() > 0.0) {
2743 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2744 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2746 /* prev_to_prev_t is irrelevant */
2747 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2750 /* limits - a bit clunky, but meh */
2751 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2752 if (diff > -0.1 && diff < 0.1) {
2753 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2754 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2758 prev_t->set_beats_per_minute (new_bpm);
2759 recompute_tempos (future_map);
2760 recompute_meters (future_map);
2762 if (check_solved (future_map, true)) {
2764 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2765 prev_t->set_beats_per_minute (new_bpm);
2766 recompute_tempos (_metrics);
2768 if (ms->position_lock_style() == AudioTime) {
2769 ms->set_frame (frame);
2772 recompute_meters (_metrics);
2776 Metrics::const_iterator d = future_map.begin();
2777 while (d != future_map.end()) {
2782 MetricPositionChanged (); // Emit Signal
2786 TempoMap::gui_dilate_next_tempo (const framepos_t& frame, const framepos_t& end_frame)
2789 TempoSection* ts = 0;
2792 Glib::Threads::RWLock::WriterLock lm (lock);
2794 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2795 TempoSection* t = 0;
2796 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2797 if (t->frame() > end_frame) {
2805 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
2808 TempoSection* next_t = copy_metrics_and_point (_metrics, future_map, ts);
2809 TempoSection* prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, next_t->frame() - 1));
2810 TempoSection* prev_to_prev_t = 0;
2811 const frameoffset_t fr_off = end_frame - frame;
2813 if (prev_t && prev_t->pulse() > 0.0) {
2814 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2818 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2819 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2821 double contribution = 0.0;
2823 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2824 contribution = prev_t->beats_per_minute() / (prev_t->beats_per_minute() + next_t->beats_per_minute());
2827 frameoffset_t prev_t_frame_contribution = fr_off;
2829 const double start_tempo = prev_t->tempo_at_frame (frame, _frame_rate);
2830 const double end_tempo = prev_t->tempo_at_frame (frame + prev_t_frame_contribution, _frame_rate);
2831 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2832 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2835 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2837 if (prev_t->position_lock_style() == MusicTime) {
2838 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2839 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2840 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2843 /* prev to prev is irrelevant */
2845 if (start_pulse != prev_t->pulse()) {
2846 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2848 new_bpm = prev_t->beats_per_minute();
2853 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2854 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2855 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2857 /* prev_to_prev_t is irrelevant */
2859 if (end_frame != prev_t->frame()) {
2860 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2862 new_bpm = prev_t->beats_per_minute();
2867 const double end_minute = ((next_t->frame() + prev_t_frame_contribution - prev_t->frame()) / (double) _frame_rate) / 60.0;
2868 const double pulse_delta_at_next = prev_t->pulse_at_frame ((next_t->frame()) + prev_t_frame_contribution, _frame_rate) - next_t->pulse();
2869 const double target_pulse = (next_t->pulse() - prev_t->pulse()) + (pulse_delta_at_next);
2871 new_bpm = (prev_t->tempo_at_frame (next_t->frame() - prev_t_frame_contribution, _frame_rate)) * (double) prev_t->note_type();
2875 next_t->set_beats_per_minute (new_bpm);
2876 recompute_tempos (future_map);
2877 recompute_meters (future_map);
2879 if (check_solved (future_map, true)) {
2880 ts->set_beats_per_minute (new_bpm);
2881 recompute_tempos (_metrics);
2882 recompute_meters (_metrics);
2886 Metrics::const_iterator d = future_map.begin();
2887 while (d != future_map.end()) {
2892 MetricPositionChanged (); // Emit Signal
2896 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
2901 Glib::Threads::RWLock::WriterLock lm (lock);
2907 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2908 TempoSection* prev_to_prev_t = 0;
2909 const frameoffset_t fr_off = end_frame - frame;
2911 if (prev_t && prev_t->pulse() > 0.0) {
2912 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2914 TempoSection* next_t = 0;
2915 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2916 TempoSection* t = 0;
2917 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2918 if (t->frame() > ts->frame()) {
2925 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2926 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2928 double contribution = 0.0;
2930 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2931 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2934 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2935 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2936 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2939 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2941 if (prev_t->position_lock_style() == MusicTime) {
2942 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2943 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2944 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2947 /* prev to prev is irrelevant */
2949 if (start_pulse != prev_t->pulse()) {
2950 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2952 new_bpm = prev_t->beats_per_minute();
2957 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2958 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2959 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2961 /* prev_to_prev_t is irrelevant */
2963 if (end_frame != prev_t->frame()) {
2964 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2966 new_bpm = prev_t->beats_per_minute();
2971 const frameoffset_t halfway = ((next_t->frame() + prev_t->frame()) / 2.0);
2972 const frameoffset_t halfway_off = halfway + prev_t_frame_contribution;
2973 const double halfway_pulse = prev_t->pulse_at_frame (halfway, _frame_rate);
2974 const double halfway_off_minute = ((halfway_off - prev_t->frame()) / (double) _frame_rate) / 60.0;
2976 new_bpm = (((halfway_pulse - prev_t->pulse()) * prev_t->c_func())
2977 / (exp (halfway_off_minute * prev_t->c_func()) - 1.0)) * (double) prev_t->note_type();
2981 prev_t->set_beats_per_minute (new_bpm);
2982 recompute_tempos (future_map);
2983 recompute_meters (future_map);
2985 if (check_solved (future_map, true)) {
2986 ts->set_beats_per_minute (new_bpm);
2987 recompute_tempos (_metrics);
2988 recompute_meters (_metrics);
2992 Metrics::const_iterator d = future_map.begin();
2993 while (d != future_map.end()) {
2998 MetricPositionChanged (); // Emit Signal
3002 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3004 Glib::Threads::RWLock::ReaderLock lm (lock);
3006 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
3007 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
3008 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
3010 return frame_at_beat_locked (_metrics, total_beats);
3014 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3016 return round_to_type (fr, dir, Bar);
3020 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3022 return round_to_type (fr, dir, Beat);
3026 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3028 Glib::Threads::RWLock::ReaderLock lm (lock);
3029 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
3030 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3031 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3033 ticks -= beats * BBT_Time::ticks_per_beat;
3036 /* round to next (or same iff dir == RoundUpMaybe) */
3038 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3040 if (mod == 0 && dir == RoundUpMaybe) {
3041 /* right on the subdivision, which is fine, so do nothing */
3043 } else if (mod == 0) {
3044 /* right on the subdivision, so the difference is just the subdivision ticks */
3045 ticks += ticks_one_subdivisions_worth;
3048 /* not on subdivision, compute distance to next subdivision */
3050 ticks += ticks_one_subdivisions_worth - mod;
3053 if (ticks >= BBT_Time::ticks_per_beat) {
3054 ticks -= BBT_Time::ticks_per_beat;
3056 } else if (dir < 0) {
3058 /* round to previous (or same iff dir == RoundDownMaybe) */
3060 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3062 if (difference == 0 && dir == RoundDownAlways) {
3063 /* right on the subdivision, but force-rounding down,
3064 so the difference is just the subdivision ticks */
3065 difference = ticks_one_subdivisions_worth;
3068 if (ticks < difference) {
3069 ticks = BBT_Time::ticks_per_beat - ticks;
3071 ticks -= difference;
3075 /* round to nearest */
3078 /* compute the distance to the previous and next subdivision */
3080 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3082 /* closer to the next subdivision, so shift forward */
3084 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3086 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3088 if (ticks > BBT_Time::ticks_per_beat) {
3090 ticks -= BBT_Time::ticks_per_beat;
3091 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3094 } else if (rem > 0) {
3096 /* closer to previous subdivision, so shift backward */
3100 /* can't go backwards past zero, so ... */
3103 /* step back to previous beat */
3105 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3106 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3108 ticks = lrint (ticks - rem);
3109 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3112 /* on the subdivision, do nothing */
3116 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3122 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
3124 if (sub_num == -1) {
3129 } else if (dir < 0) {
3133 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
3134 if ((double) when.beats > bpb / 2.0) {
3143 } else if (sub_num == 0) {
3144 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
3145 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
3147 while ((double) when.beats > bpb) {
3149 when.beats -= (uint32_t) floor (bpb);
3157 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
3160 /* round to next (or same iff dir == RoundUpMaybe) */
3162 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
3164 if (mod == 0 && dir == RoundUpMaybe) {
3165 /* right on the subdivision, which is fine, so do nothing */
3167 } else if (mod == 0) {
3168 /* right on the subdivision, so the difference is just the subdivision ticks */
3169 when.ticks += ticks_one_subdivisions_worth;
3172 /* not on subdivision, compute distance to next subdivision */
3174 when.ticks += ticks_one_subdivisions_worth - mod;
3177 if (when.ticks >= BBT_Time::ticks_per_beat) {
3178 when.ticks -= BBT_Time::ticks_per_beat;
3181 } else if (dir < 0) {
3182 /* round to previous (or same iff dir == RoundDownMaybe) */
3184 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
3186 if (difference == 0 && dir == RoundDownAlways) {
3187 /* right on the subdivision, but force-rounding down,
3188 so the difference is just the subdivision ticks */
3189 difference = ticks_one_subdivisions_worth;
3192 if (when.ticks < difference) {
3193 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
3195 when.ticks -= difference;
3199 /* round to nearest */ double rem;
3200 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
3201 /* closer to the next subdivision, so shift forward */
3203 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
3205 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
3207 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
3210 } else if (rem > 0) {
3211 /* closer to previous subdivision, so shift backward */
3213 if (rem > when.ticks) {
3214 if (when.beats == 0) {
3215 /* can't go backwards past zero, so ... */
3217 /* step back to previous beat */
3219 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
3221 when.ticks = when.ticks - rem;
3228 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3230 Glib::Threads::RWLock::ReaderLock lm (lock);
3232 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3233 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3238 /* find bar previous to 'frame' */
3241 return frame_time_locked (_metrics, bbt);
3243 } else if (dir > 0) {
3244 /* find bar following 'frame' */
3248 return frame_time_locked (_metrics, bbt);
3250 /* true rounding: find nearest bar */
3251 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3254 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3256 framepos_t next_ft = frame_time_locked (_metrics, bbt);
3258 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3269 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3270 } else if (dir > 0) {
3271 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3273 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3282 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3283 framepos_t lower, framepos_t upper)
3285 Glib::Threads::RWLock::ReaderLock lm (lock);
3286 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3288 /* although the map handles negative beats, bbt doesn't. */
3292 while (pos < upper) {
3293 pos = frame_at_beat_locked (_metrics, cnt);
3294 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3295 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3296 const BBT_Time bbt = beats_to_bbt (cnt);
3297 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3303 TempoMap::tempo_section_at (framepos_t frame) const
3305 Glib::Threads::RWLock::ReaderLock lm (lock);
3306 return tempo_section_at_locked (_metrics, frame);
3310 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3312 Metrics::const_iterator i;
3313 TempoSection* prev = 0;
3315 for (i = metrics.begin(); i != metrics.end(); ++i) {
3318 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3322 if (prev && t->frame() > frame) {
3332 abort(); /*NOTREACHED*/
3339 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3341 TempoSection* prev_t = 0;
3342 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3344 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3346 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3347 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3358 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3360 TempoSection* prev_t = 0;
3362 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3364 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3365 if (prev_t && t->pulse() > pulse) {
3375 /* don't use this to calculate length (the tempo is only correct for this frame).
3376 do that stuff based on the beat_at_frame and frame_at_beat api
3379 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3381 Glib::Threads::RWLock::ReaderLock lm (lock);
3383 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3384 const TempoSection* ts_after = 0;
3385 Metrics::const_iterator i;
3387 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3390 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3394 if ((*i)->frame() > frame) {
3402 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3404 /* must be treated as constant tempo */
3405 return ts_at->frames_per_beat (_frame_rate);
3409 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3411 TempoSection* prev_t = 0;
3413 Metrics::const_iterator i;
3415 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3417 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3421 if ((prev_t) && t->frame() > frame) {
3422 /* t is the section past frame */
3423 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3424 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3431 const double ret = prev_t->beats_per_minute();
3432 const Tempo ret_tempo (ret, prev_t->note_type ());
3438 TempoMap::tempo_at (const framepos_t& frame) const
3440 Glib::Threads::RWLock::ReaderLock lm (lock);
3441 return tempo_at_locked (_metrics, frame);
3445 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3447 Metrics::const_iterator i;
3448 MeterSection* prev = 0;
3450 for (i = metrics.begin(); i != metrics.end(); ++i) {
3453 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3455 if (prev && (*i)->frame() > frame) {
3465 abort(); /*NOTREACHED*/
3473 TempoMap::meter_section_at (framepos_t frame) const
3475 Glib::Threads::RWLock::ReaderLock lm (lock);
3476 return meter_section_at_locked (_metrics, frame);
3480 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3482 MeterSection* prev_m = 0;
3484 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3486 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3487 if (prev_m && m->beat() > beat) {
3498 TempoMap::meter_section_at_beat (double beat) const
3500 Glib::Threads::RWLock::ReaderLock lm (lock);
3501 return meter_section_at_beat_locked (_metrics, beat);
3505 TempoMap::meter_at (framepos_t frame) const
3507 TempoMetric m (metric_at (frame));
3512 TempoMap::fix_legacy_session ()
3514 MeterSection* prev_m = 0;
3515 TempoSection* prev_t = 0;
3517 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3521 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3522 if (!m->movable()) {
3523 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3527 m->set_position_lock_style (AudioTime);
3532 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3533 + (m->bbt().beats - 1)
3534 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3536 m->set_beat (start);
3537 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3538 + (m->bbt().beats - 1)
3539 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3540 m->set_pulse (start_beat / prev_m->note_divisor());
3543 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3549 if (!t->movable()) {
3552 t->set_position_lock_style (AudioTime);
3558 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3559 + (t->legacy_bbt().beats - 1)
3560 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3562 t->set_pulse (beat / prev_m->note_divisor());
3564 /* really shouldn't happen but.. */
3565 t->set_pulse (beat / 4.0);
3574 TempoMap::get_state ()
3576 Metrics::const_iterator i;
3577 XMLNode *root = new XMLNode ("TempoMap");
3580 Glib::Threads::RWLock::ReaderLock lm (lock);
3581 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3582 root->add_child_nocopy ((*i)->get_state());
3590 TempoMap::set_state (const XMLNode& node, int /*version*/)
3593 Glib::Threads::RWLock::WriterLock lm (lock);
3596 XMLNodeConstIterator niter;
3597 Metrics old_metrics (_metrics);
3600 nlist = node.children();
3602 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3603 XMLNode* child = *niter;
3605 if (child->name() == TempoSection::xml_state_node_name) {
3608 TempoSection* ts = new TempoSection (*child);
3609 _metrics.push_back (ts);
3612 catch (failed_constructor& err){
3613 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3614 _metrics = old_metrics;
3618 } else if (child->name() == MeterSection::xml_state_node_name) {
3621 MeterSection* ms = new MeterSection (*child);
3622 _metrics.push_back (ms);
3625 catch (failed_constructor& err) {
3626 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3627 _metrics = old_metrics;
3633 if (niter == nlist.end()) {
3634 MetricSectionSorter cmp;
3635 _metrics.sort (cmp);
3638 /* check for legacy sessions where bbt was the base musical unit for tempo */
3639 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3641 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3642 if (t->legacy_bbt().bars != 0) {
3643 fix_legacy_session();
3650 /* check for multiple tempo/meters at the same location, which
3651 ardour2 somehow allowed.
3654 Metrics::iterator prev = _metrics.end();
3655 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3656 if (prev != _metrics.end()) {
3658 MeterSection* prev_m;
3660 TempoSection* prev_t;
3661 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3662 if (prev_m->pulse() == ms->pulse()) {
3663 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3664 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3667 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3668 if (prev_t->pulse() == ts->pulse()) {
3669 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3670 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3678 recompute_map (_metrics);
3681 PropertyChanged (PropertyChange ());
3687 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3689 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3690 const MeterSection* m;
3691 const TempoSection* t;
3692 const TempoSection* prev_t = 0;
3694 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3696 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3697 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3698 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3699 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3701 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3702 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;
3705 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3706 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3707 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3710 o << "------" << std::endl;
3714 TempoMap::n_tempos() const
3716 Glib::Threads::RWLock::ReaderLock lm (lock);
3719 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3720 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3729 TempoMap::n_meters() const
3731 Glib::Threads::RWLock::ReaderLock lm (lock);
3734 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3735 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3744 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3747 Glib::Threads::RWLock::WriterLock lm (lock);
3748 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3749 if ((*i)->frame() >= where && (*i)->movable ()) {
3750 (*i)->set_frame ((*i)->frame() + amount);
3754 /* now reset the BBT time of all metrics, based on their new
3755 * audio time. This is the only place where we do this reverse
3759 Metrics::iterator i;
3760 const MeterSection* meter;
3761 const TempoSection* tempo;
3765 meter = &first_meter ();
3766 tempo = &first_tempo ();
3771 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3774 MetricSection* prev = 0;
3776 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3779 //TempoMetric metric (*meter, *tempo);
3780 MeterSection* ms = const_cast<MeterSection*>(meter);
3781 TempoSection* ts = const_cast<TempoSection*>(tempo);
3784 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3788 ts->set_pulse (t->pulse());
3790 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3791 ts->set_pulse (m->pulse());
3793 ts->set_frame (prev->frame());
3797 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3798 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3799 ms->set_beat (start);
3800 ms->set_pulse (m->pulse());
3802 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3806 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3807 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3808 ms->set_beat (start);
3809 ms->set_pulse (t->pulse());
3811 ms->set_frame (prev->frame());
3815 // metric will be at frames=0 bbt=1|1|0 by default
3816 // which is correct for our purpose
3819 // cerr << bbt << endl;
3821 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3825 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3827 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3828 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3829 bbt_time (m->frame(), bbt);
3831 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3837 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3838 /* round up to next beat */
3844 if (bbt.beats != 1) {
3845 /* round up to next bar */
3850 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3851 m->set_beat (start);
3852 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3854 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3856 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3857 abort(); /*NOTREACHED*/
3863 recompute_map (_metrics);
3867 PropertyChanged (PropertyChange ());
3870 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3874 std::list<MetricSection*> metric_kill_list;
3876 TempoSection* last_tempo = NULL;
3877 MeterSection* last_meter = NULL;
3878 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3879 bool meter_after = false; // is there a meter marker likewise?
3881 Glib::Threads::RWLock::WriterLock lm (lock);
3882 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3883 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3884 metric_kill_list.push_back(*i);
3885 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3888 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3892 else if ((*i)->frame() >= where) {
3893 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3894 (*i)->set_frame ((*i)->frame() - amount);
3895 if ((*i)->frame() == where) {
3896 // marker was immediately after end of range
3897 tempo_after = dynamic_cast<TempoSection*> (*i);
3898 meter_after = dynamic_cast<MeterSection*> (*i);
3904 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3905 if (last_tempo && !tempo_after) {
3906 metric_kill_list.remove(last_tempo);
3907 last_tempo->set_frame(where);
3910 if (last_meter && !meter_after) {
3911 metric_kill_list.remove(last_meter);
3912 last_meter->set_frame(where);
3916 //remove all the remaining metrics
3917 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3918 _metrics.remove(*i);
3923 recompute_map (_metrics);
3926 PropertyChanged (PropertyChange ());
3930 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3931 * pos can be -ve, if required.
3934 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3936 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3939 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3941 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3943 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3946 /** Add the BBT interval op to pos and return the result */
3948 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3950 Glib::Threads::RWLock::ReaderLock lm (lock);
3952 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3953 pos_bbt.ticks += op.ticks;
3954 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3956 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3958 pos_bbt.beats += op.beats;
3959 /* the meter in effect will start on the bar */
3960 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();
3961 while (pos_bbt.beats >= divisions_per_bar + 1) {
3963 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3964 pos_bbt.beats -= divisions_per_bar;
3966 pos_bbt.bars += op.bars;
3968 return frame_time_locked (_metrics, pos_bbt);
3971 /** Count the number of beats that are equivalent to distance when going forward,
3975 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3977 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3981 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3987 operator<< (std::ostream& o, const Meter& m) {
3988 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3992 operator<< (std::ostream& o, const Tempo& t) {
3993 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3997 operator<< (std::ostream& o, const MetricSection& section) {
3999 o << "MetricSection @ " << section.frame() << ' ';
4001 const TempoSection* ts;
4002 const MeterSection* ms;
4004 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4005 o << *((const Tempo*) ts);
4006 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4007 o << *((const Meter*) ms);