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::cerr);
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, 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 if (where.beats != 1) {
1100 /* new meters *always* start on a beat. */
1102 const double pulse = pulse_at_beat_locked (_metrics, beat);
1103 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1104 do_insert (new_meter);
1107 solve_map (_metrics, new_meter, where);
1114 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1116 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1117 TempoSection* t = 0;
1118 double pulse = pulse_at_frame_locked (_metrics, frame);
1119 new_meter->set_pulse (pulse);
1121 do_insert (new_meter);
1123 /* add meter-locked tempo */
1124 t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1125 t->set_locked_to_meter (true);
1128 solve_map (_metrics, new_meter, frame);
1135 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1137 Tempo newtempo (beats_per_minute, note_type);
1140 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1141 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1146 Glib::Threads::RWLock::WriterLock lm (lock);
1147 *((Tempo*) t) = newtempo;
1148 recompute_map (_metrics);
1150 PropertyChanged (PropertyChange ());
1157 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1159 Tempo newtempo (beats_per_minute, note_type);
1162 TempoSection* first;
1163 Metrics::iterator i;
1165 /* find the TempoSection immediately preceding "where"
1168 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1170 if ((*i)->frame() > where) {
1176 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1189 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1199 Glib::Threads::RWLock::WriterLock lm (lock);
1200 /* cannot move the first tempo section */
1201 *((Tempo*)prev) = newtempo;
1202 recompute_map (_metrics);
1205 PropertyChanged (PropertyChange ());
1209 TempoMap::first_meter () const
1211 const MeterSection *m = 0;
1213 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1214 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1219 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1220 abort(); /*NOTREACHED*/
1225 TempoMap::first_meter ()
1227 MeterSection *m = 0;
1229 /* CALLER MUST HOLD LOCK */
1231 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1232 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1237 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1238 abort(); /*NOTREACHED*/
1243 TempoMap::first_tempo () const
1245 const TempoSection *t = 0;
1247 /* CALLER MUST HOLD LOCK */
1249 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1250 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1254 if (!t->movable()) {
1260 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1261 abort(); /*NOTREACHED*/
1266 TempoMap::first_tempo ()
1268 TempoSection *t = 0;
1270 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1271 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1275 if (!t->movable()) {
1281 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1282 abort(); /*NOTREACHED*/
1286 TempoMap::recompute_tempos (Metrics& metrics)
1288 TempoSection* prev_t = 0;
1290 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1293 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1297 if (!t->movable()) {
1305 if (t->position_lock_style() == AudioTime) {
1306 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1307 if (!t->locked_to_meter()) {
1308 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1312 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1313 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1320 prev_t->set_c_func (0.0);
1323 /* tempos must be positioned correctly.
1324 the current approach is to use a meter's bbt time as its base position unit.
1325 this means that a meter's beat may change, but its bbt may not.
1326 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1327 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1330 TempoMap::recompute_meters (Metrics& metrics)
1332 MeterSection* meter = 0;
1333 MeterSection* prev_m = 0;
1335 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1336 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1337 if (meter->position_lock_style() == AudioTime) {
1339 pair<double, BBT_Time> b_bbt;
1340 TempoSection* meter_locked_tempo = 0;
1341 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1343 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1344 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1345 meter_locked_tempo = t;
1352 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1353 if (beats + prev_m->beat() != meter->beat()) {
1354 /* reordering caused a bbt change */
1355 b_bbt = make_pair (beats + prev_m->beat()
1356 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1357 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1359 } else if (meter->movable()) {
1360 b_bbt = make_pair (meter->beat(), meter->bbt());
1361 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1364 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1366 if (meter_locked_tempo) {
1367 meter_locked_tempo->set_pulse (pulse);
1368 recompute_tempos (metrics);
1370 meter->set_beat (b_bbt);
1371 meter->set_pulse (pulse);
1376 pair<double, BBT_Time> new_beat;
1378 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1379 if (beats + prev_m->beat() != meter->beat()) {
1380 /* reordering caused a bbt change */
1381 new_beat = make_pair (beats + prev_m->beat()
1382 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1384 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1386 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1388 /* shouldn't happen - the first is audio-locked */
1389 pulse = pulse_at_beat_locked (metrics, meter->beat());
1390 new_beat = make_pair (meter->beat(), meter->bbt());
1393 meter->set_beat (new_beat);
1394 meter->set_pulse (pulse);
1395 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1401 //dump (_metrics, std::cerr;
1405 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1407 /* CALLER MUST HOLD WRITE LOCK */
1411 /* we will actually stop once we hit
1418 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1421 /* silly call from Session::process() during startup
1426 recompute_tempos (metrics);
1427 recompute_meters (metrics);
1431 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1433 Glib::Threads::RWLock::ReaderLock lm (lock);
1434 TempoMetric m (first_meter(), first_tempo());
1436 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1437 at something, because we insert the default tempo and meter during
1438 TempoMap construction.
1440 now see if we can find better candidates.
1443 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1445 if ((*i)->frame() > frame) {
1459 /* XX meters only */
1461 TempoMap::metric_at (BBT_Time bbt) const
1463 Glib::Threads::RWLock::ReaderLock lm (lock);
1464 TempoMetric m (first_meter(), first_tempo());
1466 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1467 at something, because we insert the default tempo and meter during
1468 TempoMap construction.
1470 now see if we can find better candidates.
1473 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1475 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1476 BBT_Time section_start (mw->bbt());
1478 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1490 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1492 MeterSection* prev_m = 0;
1494 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1496 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1497 if (prev_m && m->beat() > beat) {
1504 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1509 TempoMap::pulse_at_beat (const double& beat) const
1511 Glib::Threads::RWLock::ReaderLock lm (lock);
1512 return pulse_at_beat_locked (_metrics, beat);
1516 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1518 MeterSection* prev_m = 0;
1520 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1522 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1523 if (prev_m && m->pulse() > pulse) {
1524 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1532 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1537 TempoMap::beat_at_pulse (const double& pulse) const
1539 Glib::Threads::RWLock::ReaderLock lm (lock);
1540 return beat_at_pulse_locked (_metrics, pulse);
1543 /* tempo section based */
1545 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1547 /* HOLD (at least) THE READER LOCK */
1548 TempoSection* prev_t = 0;
1550 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1552 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1556 if (prev_t && t->frame() > frame) {
1557 /*the previous ts is the one containing the frame */
1558 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1565 /* treated as constant for this ts */
1566 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1568 return pulses_in_section + prev_t->pulse();
1572 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1574 Glib::Threads::RWLock::ReaderLock lm (lock);
1575 return pulse_at_frame_locked (_metrics, frame);
1578 /* tempo section based */
1580 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1582 /* HOLD THE READER LOCK */
1584 const TempoSection* prev_t = 0;
1586 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1589 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1593 if (prev_t && t->pulse() > pulse) {
1594 return prev_t->frame_at_pulse (pulse, _frame_rate);
1600 /* must be treated as constant, irrespective of _type */
1601 double const pulses_in_section = pulse - prev_t->pulse();
1602 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1604 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1610 TempoMap::frame_at_pulse (const double& pulse) const
1612 Glib::Threads::RWLock::ReaderLock lm (lock);
1613 return frame_at_pulse_locked (_metrics, pulse);
1616 /* meter section based */
1618 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1620 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1621 MeterSection* prev_m = 0;
1622 MeterSection* next_m = 0;
1624 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1626 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1627 if (prev_m && m->frame() > frame) {
1634 if (frame < prev_m->frame()) {
1637 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1639 if (next_m && next_m->beat() < beat) {
1640 return next_m->beat();
1647 TempoMap::beat_at_frame (const framecnt_t& frame) const
1649 Glib::Threads::RWLock::ReaderLock lm (lock);
1650 return beat_at_frame_locked (_metrics, frame);
1653 /* meter section based */
1655 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1657 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1658 MeterSection* prev_m = 0;
1660 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1662 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1663 if (prev_m && m->beat() > beat) {
1670 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1674 TempoMap::frame_at_beat (const double& beat) const
1676 Glib::Threads::RWLock::ReaderLock lm (lock);
1677 return frame_at_beat_locked (_metrics, beat);
1681 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1683 /* CALLER HOLDS READ LOCK */
1685 MeterSection* prev_m = 0;
1687 /* because audio-locked meters have 'fake' integral beats,
1688 there is no pulse offset here.
1690 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1692 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1694 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1695 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1703 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1704 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1705 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1711 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1713 Glib::Threads::RWLock::ReaderLock lm (lock);
1714 return bbt_to_beats_locked (_metrics, bbt);
1718 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1720 /* CALLER HOLDS READ LOCK */
1721 MeterSection* prev_m = 0;
1722 const double beats = (b < 0.0) ? 0.0 : b;
1724 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1725 MeterSection* m = 0;
1727 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1729 if (m->beat() > beats) {
1730 /* this is the meter after the one our beat is on*/
1739 const double beats_in_ms = beats - prev_m->beat();
1740 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1741 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1742 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1743 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1747 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1748 ret.beats = (uint32_t) floor (remaining_beats);
1749 ret.bars = total_bars;
1751 /* 0 0 0 to 1 1 0 - based mapping*/
1755 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1757 ret.ticks -= BBT_Time::ticks_per_beat;
1760 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1769 TempoMap::beats_to_bbt (const double& beats)
1771 Glib::Threads::RWLock::ReaderLock lm (lock);
1772 return beats_to_bbt_locked (_metrics, beats);
1776 TempoMap::pulse_to_bbt (const double& pulse)
1778 Glib::Threads::RWLock::ReaderLock lm (lock);
1779 MeterSection* prev_m = 0;
1781 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1782 MeterSection* m = 0;
1784 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1787 double const pulses_to_m = m->pulse() - prev_m->pulse();
1788 if (prev_m->pulse() + pulses_to_m > pulse) {
1789 /* this is the meter after the one our beat is on*/
1798 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1799 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1800 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1801 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1802 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1806 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1807 ret.beats = (uint32_t) floor (remaining_beats);
1808 ret.bars = total_bars;
1810 /* 0 0 0 to 1 1 0 mapping*/
1814 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1816 ret.ticks -= BBT_Time::ticks_per_beat;
1819 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1828 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1835 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1838 Glib::Threads::RWLock::ReaderLock lm (lock);
1839 const double beat = beat_at_frame_locked (_metrics, frame);
1841 bbt = beats_to_bbt_locked (_metrics, beat);
1844 /* meter section based */
1846 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1848 /* HOLD THE READER LOCK */
1850 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1855 TempoMap::frame_time (const BBT_Time& bbt)
1858 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1862 if (bbt.beats < 1) {
1863 throw std::logic_error ("beats are counted from one");
1865 Glib::Threads::RWLock::ReaderLock lm (lock);
1867 return frame_time_locked (_metrics, bbt);
1871 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1873 TempoSection* prev_t = 0;
1874 MeterSection* prev_m = 0;
1876 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1879 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1884 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1888 if (t->frame() == prev_t->frame()) {
1892 /* precision check ensures pulses and frames align.*/
1893 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1894 if (!t->locked_to_meter()) {
1902 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1903 if (prev_m && m->position_lock_style() == AudioTime) {
1904 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1905 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1906 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1908 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1922 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1924 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1926 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1927 if (!t->movable()) {
1928 t->set_active (true);
1931 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1932 t->set_active (false);
1934 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1935 t->set_active (true);
1936 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1945 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1947 TempoSection* prev_t = 0;
1948 TempoSection* section_prev = 0;
1949 framepos_t first_m_frame = 0;
1951 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1953 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1954 if (!m->movable()) {
1955 first_m_frame = m->frame();
1960 if (section->movable() && frame <= first_m_frame) {
1964 section->set_active (true);
1965 section->set_frame (frame);
1967 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1969 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1976 section_prev = prev_t;
1979 if (t->position_lock_style() == MusicTime) {
1980 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1981 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1983 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1984 if (!t->locked_to_meter()) {
1985 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1994 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1995 if (!section->locked_to_meter()) {
1996 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2000 if (section->position_lock_style() == MusicTime) {
2001 /* we're setting the frame */
2002 section->set_position_lock_style (AudioTime);
2003 recompute_tempos (imaginary);
2004 section->set_position_lock_style (MusicTime);
2006 recompute_tempos (imaginary);
2009 if (check_solved (imaginary, true)) {
2013 MetricSectionFrameSorter fcmp;
2014 imaginary.sort (fcmp);
2015 if (section->position_lock_style() == MusicTime) {
2016 /* we're setting the frame */
2017 section->set_position_lock_style (AudioTime);
2018 recompute_tempos (imaginary);
2019 section->set_position_lock_style (MusicTime);
2021 recompute_tempos (imaginary);
2024 if (check_solved (imaginary, true)) {
2028 //dump (imaginary, std::cerr);
2034 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
2036 TempoSection* prev_t = 0;
2037 TempoSection* section_prev = 0;
2039 section->set_pulse (pulse);
2041 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2043 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2047 if (!t->movable()) {
2054 section_prev = prev_t;
2057 if (t->position_lock_style() == MusicTime) {
2058 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2059 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2061 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2062 if (!t->locked_to_meter()) {
2063 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2071 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2072 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2075 if (section->position_lock_style() == AudioTime) {
2076 /* we're setting the pulse */
2077 section->set_position_lock_style (MusicTime);
2078 recompute_tempos (imaginary);
2079 section->set_position_lock_style (AudioTime);
2081 recompute_tempos (imaginary);
2084 if (check_solved (imaginary, false)) {
2088 MetricSectionSorter cmp;
2089 imaginary.sort (cmp);
2090 if (section->position_lock_style() == AudioTime) {
2091 /* we're setting the pulse */
2092 section->set_position_lock_style (MusicTime);
2093 recompute_tempos (imaginary);
2094 section->set_position_lock_style (AudioTime);
2096 recompute_tempos (imaginary);
2099 if (check_solved (imaginary, false)) {
2103 //dump (imaginary, std::cerr);
2109 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2111 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2112 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2113 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2117 if (!section->movable()) {
2118 /* lock the first tempo to our first meter */
2119 if (!set_active_tempos (imaginary, frame)) {
2124 /* it would make sense to bail out if there is no audio-locked meter,
2125 however it may be desirable to move a music-locked meter by frame at some point.
2127 TempoSection* meter_locked_tempo = 0;
2128 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2130 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2131 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2132 meter_locked_tempo = t;
2138 MeterSection* prev_m = 0;
2140 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2142 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2144 if (prev_m && section->movable()) {
2145 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2146 if (beats + prev_m->beat() < section->beat()) {
2147 /* disallow position change if it will alter our beat
2148 we allow tempo changes to do this in recompute_meters().
2149 blocking this is an option, but i'm not convinced that
2150 this is what the user would actually want.
2151 here we set the frame/pulse corresponding to its musical position.
2154 if (meter_locked_tempo) {
2156 bool solved = false;
2157 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2158 const double new_pulse = ((section->beat() - prev_m->beat())
2159 / prev_m->note_divisor()) + prev_m->pulse();
2160 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2161 if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
2162 meter_locked_tempo->set_pulse (new_pulse);
2163 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2164 section->set_frame (smallest_frame);
2165 section->set_pulse (new_pulse);
2170 Metrics::const_iterator d = future_map.begin();
2171 while (d != future_map.end()) {
2182 if (meter_locked_tempo) {
2184 bool solved = false;
2186 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2187 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2188 meter_copy->set_frame (frame);
2190 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2191 section->set_frame (frame);
2192 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2193 / prev_m->note_divisor()) + prev_m->pulse());
2194 solve_map (imaginary, meter_locked_tempo, frame);
2199 Metrics::const_iterator d = future_map.begin();
2200 while (d != future_map.end()) {
2211 /* not movable (first meter atm) */
2212 if (meter_locked_tempo) {
2214 bool solved = false;
2215 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2217 tempo_copy->set_frame (frame);
2218 tempo_copy->set_pulse (0.0);
2220 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2221 section->set_frame (frame);
2222 meter_locked_tempo->set_frame (frame);
2223 meter_locked_tempo->set_pulse (0.0);
2224 solve_map (imaginary, meter_locked_tempo, frame);
2229 Metrics::const_iterator d = future_map.begin();
2230 while (d != future_map.end()) {
2242 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2243 section->set_beat (b_bbt);
2244 section->set_pulse (0.0);
2254 MetricSectionFrameSorter fcmp;
2255 imaginary.sort (fcmp);
2256 if (section->position_lock_style() == MusicTime) {
2257 /* we're setting the frame */
2258 section->set_position_lock_style (AudioTime);
2259 recompute_meters (imaginary);
2260 section->set_position_lock_style (MusicTime);
2262 recompute_meters (imaginary);
2264 //dump (imaginary, std::cerr);
2269 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2271 /* disallow setting section to an existing meter's bbt */
2272 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2274 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2275 if (m->bbt().bars == when.bars) {
2281 MeterSection* prev_m = 0;
2282 MeterSection* section_prev = 0;
2284 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2286 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2287 pair<double, BBT_Time> b_bbt;
2288 double new_pulse = 0.0;
2290 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2291 section_prev = prev_m;
2292 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2293 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2294 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2296 section->set_beat (b_bbt);
2297 section->set_pulse (pulse);
2298 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2303 if (m->position_lock_style() == AudioTime) {
2304 TempoSection* meter_locked_tempo = 0;
2305 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2307 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2308 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2309 meter_locked_tempo = t;
2316 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2318 if (beats + prev_m->beat() != m->beat()) {
2319 /* tempo/ meter change caused a change in beat (bar). */
2320 b_bbt = make_pair (beats + prev_m->beat()
2321 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2322 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2323 } else if (m->movable()) {
2324 b_bbt = make_pair (m->beat(), m->bbt());
2325 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2328 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2330 if (meter_locked_tempo) {
2331 meter_locked_tempo->set_pulse (new_pulse);
2332 recompute_tempos (imaginary);
2334 m->set_beat (b_bbt);
2335 m->set_pulse (new_pulse);
2339 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2340 if (beats + prev_m->beat() != m->beat()) {
2341 /* tempo/ meter change caused a change in beat (bar). */
2342 b_bbt = make_pair (beats + prev_m->beat()
2343 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2345 b_bbt = make_pair (beats + prev_m->beat()
2348 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2349 m->set_beat (b_bbt);
2350 m->set_pulse (new_pulse);
2351 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2358 if (!section_prev) {
2360 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2361 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2362 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2364 section->set_beat (b_bbt);
2365 section->set_pulse (pulse);
2366 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2369 MetricSectionSorter cmp;
2370 imaginary.sort (cmp);
2371 if (section->position_lock_style() == AudioTime) {
2372 /* we're setting the pulse */
2373 section->set_position_lock_style (MusicTime);
2374 recompute_meters (imaginary);
2375 section->set_position_lock_style (AudioTime);
2377 recompute_meters (imaginary);
2383 /** places a copy of _metrics into copy and returns a pointer
2384 * to section's equivalent in copy.
2387 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2389 TempoSection* ret = 0;
2391 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2394 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2396 ret = new TempoSection (*t);
2397 copy.push_back (ret);
2401 TempoSection* cp = new TempoSection (*t);
2402 copy.push_back (cp);
2404 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2405 MeterSection* cp = new MeterSection (*m);
2406 copy.push_back (cp);
2414 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2416 MeterSection* ret = 0;
2418 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2421 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2422 TempoSection* cp = new TempoSection (*t);
2423 copy.push_back (cp);
2426 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2428 ret = new MeterSection (*m);
2429 copy.push_back (ret);
2432 MeterSection* cp = new MeterSection (*m);
2433 copy.push_back (cp);
2441 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2444 TempoSection* tempo_copy = 0;
2447 Glib::Threads::RWLock::ReaderLock lm (lock);
2448 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2454 const double beat = bbt_to_beats_locked (copy, bbt);
2455 const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2457 Metrics::const_iterator d = copy.begin();
2458 while (d != copy.end()) {
2467 * 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,
2468 * taking any possible reordering as a consequence of this into account.
2469 * @param section - the section to be altered
2470 * @param bpm - the new Tempo
2471 * @param bbt - the bbt where the altered tempo will fall
2472 * @return returns - the position in frames where the new tempo section will lie.
2475 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2477 Glib::Threads::RWLock::ReaderLock lm (lock);
2480 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2484 const double beat = bbt_to_beats_locked (future_map, bbt);
2486 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2487 ret = tempo_copy->frame();
2489 ret = section->frame();
2492 Metrics::const_iterator d = future_map.begin();
2493 while (d != future_map.end()) {
2501 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2503 Glib::Threads::RWLock::ReaderLock lm (lock);
2506 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2508 if (solve_map (future_map, tempo_copy, frame)) {
2509 ret = tempo_copy->pulse();
2511 ret = section->pulse();
2514 Metrics::const_iterator d = future_map.begin();
2515 while (d != future_map.end()) {
2523 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2527 Glib::Threads::RWLock::WriterLock lm (lock);
2528 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2529 if (solve_map (future_map, tempo_copy, frame)) {
2530 solve_map (_metrics, ts, frame);
2531 recompute_meters (_metrics);
2535 Metrics::const_iterator d = future_map.begin();
2536 while (d != future_map.end()) {
2541 MetricPositionChanged (); // Emit Signal
2545 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2549 Glib::Threads::RWLock::WriterLock lm (lock);
2550 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2551 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2552 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2553 recompute_meters (_metrics);
2557 Metrics::const_iterator d = future_map.begin();
2558 while (d != future_map.end()) {
2563 MetricPositionChanged (); // Emit Signal
2567 TempoMap::gui_move_tempo_pulse (TempoSection* ts, const double& pulse)
2571 Glib::Threads::RWLock::WriterLock lm (lock);
2572 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2573 if (solve_map (future_map, tempo_copy, pulse)) {
2574 solve_map (_metrics, ts, pulse);
2575 recompute_meters (_metrics);
2579 Metrics::const_iterator d = future_map.begin();
2580 while (d != future_map.end()) {
2585 MetricPositionChanged (); // Emit Signal
2589 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2593 Glib::Threads::RWLock::WriterLock lm (lock);
2594 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2595 if (solve_map (future_map, copy, frame)) {
2596 solve_map (_metrics, ms, frame);
2597 recompute_tempos (_metrics);
2601 Metrics::const_iterator d = future_map.begin();
2602 while (d != future_map.end()) {
2607 MetricPositionChanged (); // Emit Signal
2611 TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
2615 Glib::Threads::RWLock::WriterLock lm (lock);
2616 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2617 if (solve_map (future_map, copy, bbt)) {
2618 solve_map (_metrics, ms, bbt);
2619 recompute_tempos (_metrics);
2623 Metrics::const_iterator d = future_map.begin();
2624 while (d != future_map.end()) {
2629 MetricPositionChanged (); // Emit Signal
2633 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2636 bool can_solve = false;
2638 Glib::Threads::RWLock::WriterLock lm (lock);
2639 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2640 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2641 recompute_tempos (future_map);
2643 if (check_solved (future_map, true)) {
2644 ts->set_beats_per_minute (bpm.beats_per_minute());
2645 recompute_map (_metrics);
2650 Metrics::const_iterator d = future_map.begin();
2651 while (d != future_map.end()) {
2656 MetricPositionChanged (); // Emit Signal
2662 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2665 TempoSection* ts = 0;
2667 if (ms->position_lock_style() == AudioTime) {
2668 /* disabled for now due to faked tempo locked to meter pulse */
2673 Glib::Threads::RWLock::WriterLock lm (lock);
2674 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2678 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2679 TempoSection* prev_to_prev_t = 0;
2680 const frameoffset_t fr_off = frame - ms->frame();
2681 double new_bpm = 0.0;
2683 if (prev_t && prev_t->pulse() > 0.0) {
2684 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2687 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2688 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2690 double contribution = 0.0;
2691 frameoffset_t frame_contribution = 0.0;
2692 frameoffset_t prev_t_frame_contribution = 0.0;
2694 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2695 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2696 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2697 frame_contribution = contribution * (double) fr_off;
2698 prev_t_frame_contribution = fr_off - frame_contribution;
2701 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2703 if (prev_t->position_lock_style() == MusicTime) {
2704 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2705 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2706 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2709 /* prev to prev is irrelevant */
2710 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2711 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2713 if (frame_pulse != prev_t->pulse()) {
2714 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2716 new_bpm = prev_t->beats_per_minute();
2721 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2722 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2723 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2725 /* prev_to_prev_t is irrelevant */
2727 if (frame != prev_t->frame()) {
2728 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2730 new_bpm = prev_t->beats_per_minute();
2734 } else if (prev_t->c_func() < 0.0) {
2735 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2736 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2738 /* prev_to_prev_t is irrelevant */
2739 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2742 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2743 if (diff > -0.1 && diff < 0.1) {
2744 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2745 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2748 } else if (prev_t->c_func() > 0.0) {
2749 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2750 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2752 /* prev_to_prev_t is irrelevant */
2753 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2756 /* limits - a bit clunky, but meh */
2757 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2758 if (diff > -0.1 && diff < 0.1) {
2759 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2760 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2764 prev_t->set_beats_per_minute (new_bpm);
2765 recompute_tempos (future_map);
2766 recompute_meters (future_map);
2768 if (check_solved (future_map, true)) {
2770 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2771 prev_t->set_beats_per_minute (new_bpm);
2772 recompute_tempos (_metrics);
2774 if (ms->position_lock_style() == AudioTime) {
2775 ms->set_frame (frame);
2778 recompute_meters (_metrics);
2782 Metrics::const_iterator d = future_map.begin();
2783 while (d != future_map.end()) {
2788 MetricPositionChanged (); // Emit Signal
2792 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2794 Glib::Threads::RWLock::ReaderLock lm (lock);
2796 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2797 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2798 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2800 return frame_at_beat_locked (_metrics, total_beats);
2804 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2806 return round_to_type (fr, dir, Bar);
2810 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2812 return round_to_type (fr, dir, Beat);
2816 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2818 Glib::Threads::RWLock::ReaderLock lm (lock);
2819 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2820 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2821 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2823 ticks -= beats * BBT_Time::ticks_per_beat;
2826 /* round to next (or same iff dir == RoundUpMaybe) */
2828 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2830 if (mod == 0 && dir == RoundUpMaybe) {
2831 /* right on the subdivision, which is fine, so do nothing */
2833 } else if (mod == 0) {
2834 /* right on the subdivision, so the difference is just the subdivision ticks */
2835 ticks += ticks_one_subdivisions_worth;
2838 /* not on subdivision, compute distance to next subdivision */
2840 ticks += ticks_one_subdivisions_worth - mod;
2843 if (ticks >= BBT_Time::ticks_per_beat) {
2844 ticks -= BBT_Time::ticks_per_beat;
2846 } else if (dir < 0) {
2848 /* round to previous (or same iff dir == RoundDownMaybe) */
2850 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2852 if (difference == 0 && dir == RoundDownAlways) {
2853 /* right on the subdivision, but force-rounding down,
2854 so the difference is just the subdivision ticks */
2855 difference = ticks_one_subdivisions_worth;
2858 if (ticks < difference) {
2859 ticks = BBT_Time::ticks_per_beat - ticks;
2861 ticks -= difference;
2865 /* round to nearest */
2868 /* compute the distance to the previous and next subdivision */
2870 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2872 /* closer to the next subdivision, so shift forward */
2874 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2876 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2878 if (ticks > BBT_Time::ticks_per_beat) {
2880 ticks -= BBT_Time::ticks_per_beat;
2881 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2884 } else if (rem > 0) {
2886 /* closer to previous subdivision, so shift backward */
2890 /* can't go backwards past zero, so ... */
2893 /* step back to previous beat */
2895 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2896 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2898 ticks = lrint (ticks - rem);
2899 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2902 /* on the subdivision, do nothing */
2906 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2912 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
2914 if (sub_num == -1) {
2919 } else if (dir < 0) {
2923 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2924 if ((double) when.beats > bpb / 2.0) {
2933 } else if (sub_num == 0) {
2934 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2935 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2937 while ((double) when.beats > bpb) {
2939 when.beats -= (uint32_t) floor (bpb);
2947 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2950 /* round to next (or same iff dir == RoundUpMaybe) */
2952 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
2954 if (mod == 0 && dir == RoundUpMaybe) {
2955 /* right on the subdivision, which is fine, so do nothing */
2957 } else if (mod == 0) {
2958 /* right on the subdivision, so the difference is just the subdivision ticks */
2959 when.ticks += ticks_one_subdivisions_worth;
2962 /* not on subdivision, compute distance to next subdivision */
2964 when.ticks += ticks_one_subdivisions_worth - mod;
2967 if (when.ticks >= BBT_Time::ticks_per_beat) {
2968 when.ticks -= BBT_Time::ticks_per_beat;
2971 } else if (dir < 0) {
2972 /* round to previous (or same iff dir == RoundDownMaybe) */
2974 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
2976 if (difference == 0 && dir == RoundDownAlways) {
2977 /* right on the subdivision, but force-rounding down,
2978 so the difference is just the subdivision ticks */
2979 difference = ticks_one_subdivisions_worth;
2982 if (when.ticks < difference) {
2983 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
2985 when.ticks -= difference;
2989 /* round to nearest */ double rem;
2990 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2991 /* closer to the next subdivision, so shift forward */
2993 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2995 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2997 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
3000 } else if (rem > 0) {
3001 /* closer to previous subdivision, so shift backward */
3003 if (rem > when.ticks) {
3004 if (when.beats == 0) {
3005 /* can't go backwards past zero, so ... */
3007 /* step back to previous beat */
3009 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
3011 when.ticks = when.ticks - rem;
3018 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3020 Glib::Threads::RWLock::ReaderLock lm (lock);
3022 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3023 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3028 /* find bar previous to 'frame' */
3031 return frame_time_locked (_metrics, bbt);
3033 } else if (dir > 0) {
3034 /* find bar following 'frame' */
3038 return frame_time_locked (_metrics, bbt);
3040 /* true rounding: find nearest bar */
3041 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3044 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3046 framepos_t next_ft = frame_time_locked (_metrics, bbt);
3048 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3059 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3060 } else if (dir > 0) {
3061 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3063 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3072 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3073 framepos_t lower, framepos_t upper)
3075 Glib::Threads::RWLock::ReaderLock lm (lock);
3076 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3078 /* although the map handles negative beats, bbt doesn't. */
3082 while (pos < upper) {
3083 pos = frame_at_beat_locked (_metrics, cnt);
3084 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3085 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3086 const BBT_Time bbt = beats_to_bbt (cnt);
3087 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3093 TempoMap::tempo_section_at (framepos_t frame) const
3095 Glib::Threads::RWLock::ReaderLock lm (lock);
3096 return tempo_section_at_locked (_metrics, frame);
3100 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3102 Metrics::const_iterator i;
3103 TempoSection* prev = 0;
3105 for (i = metrics.begin(); i != metrics.end(); ++i) {
3108 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3112 if (prev && t->frame() > frame) {
3122 abort(); /*NOTREACHED*/
3129 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3131 TempoSection* prev_t = 0;
3132 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3134 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3136 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3137 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3148 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3150 TempoSection* prev_t = 0;
3152 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3154 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3155 if (prev_t && t->pulse() > pulse) {
3165 /* don't use this to calculate length (the tempo is only correct for this frame).
3166 do that stuff based on the beat_at_frame and frame_at_beat api
3169 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3171 Glib::Threads::RWLock::ReaderLock lm (lock);
3173 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3174 const TempoSection* ts_after = 0;
3175 Metrics::const_iterator i;
3177 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3180 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3184 if ((*i)->frame() > frame) {
3192 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3194 /* must be treated as constant tempo */
3195 return ts_at->frames_per_beat (_frame_rate);
3199 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3201 TempoSection* prev_t = 0;
3203 Metrics::const_iterator i;
3205 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3207 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3211 if ((prev_t) && t->frame() > frame) {
3212 /* t is the section past frame */
3213 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3214 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3221 const double ret = prev_t->beats_per_minute();
3222 const Tempo ret_tempo (ret, prev_t->note_type ());
3228 TempoMap::tempo_at (const framepos_t& frame) const
3230 Glib::Threads::RWLock::ReaderLock lm (lock);
3231 return tempo_at_locked (_metrics, frame);
3235 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3237 Metrics::const_iterator i;
3238 MeterSection* prev = 0;
3240 for (i = metrics.begin(); i != metrics.end(); ++i) {
3243 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3245 if (prev && (*i)->frame() > frame) {
3255 abort(); /*NOTREACHED*/
3263 TempoMap::meter_section_at (framepos_t frame) const
3265 Glib::Threads::RWLock::ReaderLock lm (lock);
3266 return meter_section_at_locked (_metrics, frame);
3270 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3272 MeterSection* prev_m = 0;
3274 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3276 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3277 if (prev_m && m->beat() > beat) {
3288 TempoMap::meter_section_at_beat (double beat) const
3290 Glib::Threads::RWLock::ReaderLock lm (lock);
3291 return meter_section_at_beat_locked (_metrics, beat);
3295 TempoMap::meter_at (framepos_t frame) const
3297 TempoMetric m (metric_at (frame));
3302 TempoMap::fix_legacy_session ()
3304 MeterSection* prev_m = 0;
3305 TempoSection* prev_t = 0;
3307 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3311 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3312 if (!m->movable()) {
3313 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3317 m->set_position_lock_style (AudioTime);
3322 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3323 + (m->bbt().beats - 1)
3324 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3326 m->set_beat (start);
3327 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3328 + (m->bbt().beats - 1)
3329 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3330 m->set_pulse (start_beat / prev_m->note_divisor());
3333 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3339 if (!t->movable()) {
3342 t->set_position_lock_style (AudioTime);
3348 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3349 + (t->legacy_bbt().beats - 1)
3350 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3352 t->set_pulse (beat / prev_m->note_divisor());
3354 /* really shouldn't happen but.. */
3355 t->set_pulse (beat / 4.0);
3364 TempoMap::get_state ()
3366 Metrics::const_iterator i;
3367 XMLNode *root = new XMLNode ("TempoMap");
3370 Glib::Threads::RWLock::ReaderLock lm (lock);
3371 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3372 root->add_child_nocopy ((*i)->get_state());
3380 TempoMap::set_state (const XMLNode& node, int /*version*/)
3383 Glib::Threads::RWLock::WriterLock lm (lock);
3386 XMLNodeConstIterator niter;
3387 Metrics old_metrics (_metrics);
3390 nlist = node.children();
3392 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3393 XMLNode* child = *niter;
3395 if (child->name() == TempoSection::xml_state_node_name) {
3398 TempoSection* ts = new TempoSection (*child);
3399 _metrics.push_back (ts);
3402 catch (failed_constructor& err){
3403 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3404 _metrics = old_metrics;
3408 } else if (child->name() == MeterSection::xml_state_node_name) {
3411 MeterSection* ms = new MeterSection (*child);
3412 _metrics.push_back (ms);
3415 catch (failed_constructor& err) {
3416 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3417 _metrics = old_metrics;
3423 if (niter == nlist.end()) {
3424 MetricSectionSorter cmp;
3425 _metrics.sort (cmp);
3428 /* check for legacy sessions where bbt was the base musical unit for tempo */
3429 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3431 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3432 if (t->legacy_bbt().bars != 0) {
3433 fix_legacy_session();
3440 /* check for multiple tempo/meters at the same location, which
3441 ardour2 somehow allowed.
3444 Metrics::iterator prev = _metrics.end();
3445 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3446 if (prev != _metrics.end()) {
3448 MeterSection* prev_m;
3450 TempoSection* prev_t;
3451 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3452 if (prev_m->pulse() == ms->pulse()) {
3453 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3454 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3457 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3458 if (prev_t->pulse() == ts->pulse()) {
3459 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3460 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3468 recompute_map (_metrics);
3471 PropertyChanged (PropertyChange ());
3477 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3479 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3480 const MeterSection* m;
3481 const TempoSection* t;
3482 const TempoSection* prev_t = 0;
3484 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3486 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3487 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3488 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3489 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3491 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3492 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;
3495 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3496 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3497 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3500 o << "------" << std::endl;
3504 TempoMap::n_tempos() const
3506 Glib::Threads::RWLock::ReaderLock lm (lock);
3509 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3510 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3519 TempoMap::n_meters() const
3521 Glib::Threads::RWLock::ReaderLock lm (lock);
3524 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3525 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3534 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3537 Glib::Threads::RWLock::WriterLock lm (lock);
3538 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3539 if ((*i)->frame() >= where && (*i)->movable ()) {
3540 (*i)->set_frame ((*i)->frame() + amount);
3544 /* now reset the BBT time of all metrics, based on their new
3545 * audio time. This is the only place where we do this reverse
3549 Metrics::iterator i;
3550 const MeterSection* meter;
3551 const TempoSection* tempo;
3555 meter = &first_meter ();
3556 tempo = &first_tempo ();
3561 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3564 MetricSection* prev = 0;
3566 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3569 //TempoMetric metric (*meter, *tempo);
3570 MeterSection* ms = const_cast<MeterSection*>(meter);
3571 TempoSection* ts = const_cast<TempoSection*>(tempo);
3574 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3578 ts->set_pulse (t->pulse());
3580 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3581 ts->set_pulse (m->pulse());
3583 ts->set_frame (prev->frame());
3587 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3588 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3589 ms->set_beat (start);
3590 ms->set_pulse (m->pulse());
3592 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3596 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3597 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3598 ms->set_beat (start);
3599 ms->set_pulse (t->pulse());
3601 ms->set_frame (prev->frame());
3605 // metric will be at frames=0 bbt=1|1|0 by default
3606 // which is correct for our purpose
3609 // cerr << bbt << endl;
3611 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3615 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3617 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3618 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3619 bbt_time (m->frame(), bbt);
3621 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3627 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3628 /* round up to next beat */
3634 if (bbt.beats != 1) {
3635 /* round up to next bar */
3640 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3641 m->set_beat (start);
3642 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3644 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3646 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3647 abort(); /*NOTREACHED*/
3653 recompute_map (_metrics);
3657 PropertyChanged (PropertyChange ());
3660 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3664 std::list<MetricSection*> metric_kill_list;
3666 TempoSection* last_tempo = NULL;
3667 MeterSection* last_meter = NULL;
3668 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3669 bool meter_after = false; // is there a meter marker likewise?
3671 Glib::Threads::RWLock::WriterLock lm (lock);
3672 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3673 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3674 metric_kill_list.push_back(*i);
3675 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3678 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3682 else if ((*i)->frame() >= where) {
3683 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3684 (*i)->set_frame ((*i)->frame() - amount);
3685 if ((*i)->frame() == where) {
3686 // marker was immediately after end of range
3687 tempo_after = dynamic_cast<TempoSection*> (*i);
3688 meter_after = dynamic_cast<MeterSection*> (*i);
3694 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3695 if (last_tempo && !tempo_after) {
3696 metric_kill_list.remove(last_tempo);
3697 last_tempo->set_frame(where);
3700 if (last_meter && !meter_after) {
3701 metric_kill_list.remove(last_meter);
3702 last_meter->set_frame(where);
3706 //remove all the remaining metrics
3707 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3708 _metrics.remove(*i);
3713 recompute_map (_metrics);
3716 PropertyChanged (PropertyChange ());
3720 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3721 * pos can be -ve, if required.
3724 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3726 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3729 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3731 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3733 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3736 /** Add the BBT interval op to pos and return the result */
3738 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3740 Glib::Threads::RWLock::ReaderLock lm (lock);
3742 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3743 pos_bbt.ticks += op.ticks;
3744 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3746 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3748 pos_bbt.beats += op.beats;
3749 /* the meter in effect will start on the bar */
3750 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();
3751 while (pos_bbt.beats >= divisions_per_bar + 1) {
3753 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3754 pos_bbt.beats -= divisions_per_bar;
3756 pos_bbt.bars += op.bars;
3758 return frame_time_locked (_metrics, pos_bbt);
3761 /** Count the number of beats that are equivalent to distance when going forward,
3765 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3767 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3771 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3777 operator<< (std::ostream& o, const Meter& m) {
3778 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3782 operator<< (std::ostream& o, const Tempo& t) {
3783 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3787 operator<< (std::ostream& o, const MetricSection& section) {
3789 o << "MetricSection @ " << section.frame() << ' ';
3791 const TempoSection* ts;
3792 const MeterSection* ms;
3794 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3795 o << *((const Tempo*) ts);
3796 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3797 o << *((const Meter*) ms);