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 XMLProperty const * 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;
463 framepos_t frame = 0;
464 pair<double, BBT_Time> start;
466 if ((prop = node.property ("start")) != 0) {
467 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
471 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
473 /* legacy session - start used to be in bbt*/
474 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
479 if ((prop = node.property ("pulse")) != 0) {
480 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
481 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
486 if ((prop = node.property ("beat")) != 0) {
487 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
488 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
494 if ((prop = node.property ("bbt")) == 0) {
495 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
496 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
500 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
501 throw failed_constructor();
507 if ((prop = node.property ("frame")) != 0) {
508 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
509 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
515 /* beats-per-bar is old; divisions-per-bar is new */
517 if ((prop = node.property ("divisions-per-bar")) == 0) {
518 if ((prop = node.property ("beats-per-bar")) == 0) {
519 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
520 throw failed_constructor();
523 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
524 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
525 throw failed_constructor();
528 if ((prop = node.property ("note-type")) == 0) {
529 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
530 throw failed_constructor();
532 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
533 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
534 throw failed_constructor();
537 if ((prop = node.property ("movable")) == 0) {
538 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
539 throw failed_constructor();
542 set_movable (string_is_affirmative (prop->value()));
544 if ((prop = node.property ("lock-style")) == 0) {
545 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
547 set_position_lock_style (MusicTime);
549 set_position_lock_style (AudioTime);
552 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
557 MeterSection::get_state() const
559 XMLNode *root = new XMLNode (xml_state_node_name);
563 snprintf (buf, sizeof (buf), "%lf", pulse());
564 root->add_property ("pulse", buf);
565 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
569 root->add_property ("bbt", buf);
570 snprintf (buf, sizeof (buf), "%lf", beat());
571 root->add_property ("beat", buf);
572 snprintf (buf, sizeof (buf), "%f", _note_type);
573 root->add_property ("note-type", buf);
574 snprintf (buf, sizeof (buf), "%li", frame());
575 root->add_property ("frame", buf);
576 root->add_property ("lock-style", enum_2_string (position_lock_style()));
577 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
578 root->add_property ("divisions-per-bar", buf);
579 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
580 root->add_property ("movable", buf);
585 /***********************************************************************/
589 Tempo can be thought of as a source of the musical pulse.
590 Meters divide that pulse into measures and beats.
591 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
592 at any particular time.
593 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
594 It should rather be thought of as tempo note divisions per minute.
596 TempoSections, which are nice to think of in whole pulses per minute,
597 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
598 and beats (via note_divisor) are used to form a tempo map.
599 TempoSections and MeterSections may be locked to either audio or music (position lock style).
600 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
601 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
603 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
605 The first tempo and first meter are special. they must move together, and must be locked to audio.
606 Audio locked tempos which lie before the first meter are made inactive.
607 They will be re-activated if the first meter is again placed before them.
609 Both tempos and meters have a pulse position and a frame position.
610 Meters also have a beat position, which is always 0.0 for the first meter.
612 A tempo locked to music is locked to musical pulses.
613 A meter locked to music is locked to beats.
615 Recomputing the tempo map is the process where the 'missing' position
616 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
618 It is important to keep the _metrics in an order that makes sense.
619 Because ramped MusicTime and AudioTime tempos can interact with each other,
620 reordering is frequent. Care must be taken to keep _metrics in a solved state.
621 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
623 struct MetricSectionSorter {
624 bool operator() (const MetricSection* a, const MetricSection* b) {
625 return a->pulse() < b->pulse();
629 struct MetricSectionFrameSorter {
630 bool operator() (const MetricSection* a, const MetricSection* b) {
631 return a->frame() < b->frame();
635 TempoMap::TempoMap (framecnt_t fr)
638 BBT_Time start (1, 1, 0);
640 TempoSection *t = new TempoSection ((framepos_t) 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
641 MeterSection *m = new MeterSection ((framepos_t) 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
643 t->set_movable (false);
644 m->set_movable (false);
646 /* note: frame time is correct (zero) for both of these */
648 _metrics.push_back (t);
649 _metrics.push_back (m);
653 TempoMap::~TempoMap ()
655 Metrics::const_iterator d = _metrics.begin();
656 while (d != _metrics.end()) {
664 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
666 bool removed = false;
669 Glib::Threads::RWLock::WriterLock lm (lock);
670 if ((removed = remove_tempo_locked (tempo))) {
671 if (complete_operation) {
672 recompute_map (_metrics);
677 if (removed && complete_operation) {
678 PropertyChanged (PropertyChange ());
683 TempoMap::remove_tempo_locked (const TempoSection& tempo)
687 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
688 if (dynamic_cast<TempoSection*> (*i) != 0) {
689 if (tempo.frame() == (*i)->frame()) {
690 if ((*i)->movable()) {
703 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
705 bool removed = false;
708 Glib::Threads::RWLock::WriterLock lm (lock);
709 if ((removed = remove_meter_locked (tempo))) {
710 if (complete_operation) {
711 recompute_map (_metrics);
716 if (removed && complete_operation) {
717 PropertyChanged (PropertyChange ());
722 TempoMap::remove_meter_locked (const MeterSection& meter)
726 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
728 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
729 if (meter.frame() == (*i)->frame()) {
730 if (t->locked_to_meter()) {
739 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
740 if (dynamic_cast<MeterSection*> (*i) != 0) {
741 if (meter.frame() == (*i)->frame()) {
742 if ((*i)->movable()) {
755 TempoMap::do_insert (MetricSection* section)
757 bool need_add = true;
758 /* we only allow new meters to be inserted on beat 1 of an existing
762 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
764 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
766 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
767 corrected.second.beats = 1;
768 corrected.second.ticks = 0;
769 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
770 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
771 m->bbt(), corrected.second) << endmsg;
772 //m->set_pulse (corrected);
776 /* Look for any existing MetricSection that is of the same type and
777 in the same bar as the new one, and remove it before adding
778 the new one. Note that this means that if we find a matching,
779 existing section, we can break out of the loop since we're
780 guaranteed that there is only one such match.
783 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
785 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
786 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
787 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
788 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
790 if (tempo && insert_tempo) {
793 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
794 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
796 if (!tempo->movable()) {
798 /* can't (re)move this section, so overwrite
799 * its data content (but not its properties as
803 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
804 (*i)->set_position_lock_style (AudioTime);
806 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
807 t->set_type (insert_tempo->type());
817 } else if (meter && insert_meter) {
821 bool const ipm = insert_meter->position_lock_style() == MusicTime;
823 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
825 if (!meter->movable()) {
827 /* can't (re)move this section, so overwrite
828 * its data content (but not its properties as
832 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
833 (*i)->set_position_lock_style (AudioTime);
843 /* non-matching types, so we don't care */
847 /* Add the given MetricSection, if we didn't just reset an existing
852 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
853 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
856 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
857 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
860 bool const ipm = insert_meter->position_lock_style() == MusicTime;
861 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
866 } else if (insert_tempo) {
867 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
868 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
871 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
872 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
879 _metrics.insert (i, section);
880 //dump (_metrics, std::cout);
885 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
888 Glib::Threads::RWLock::WriterLock lm (lock);
889 TempoSection& first (first_tempo());
890 if (ts.pulse() != first.pulse()) {
891 remove_tempo_locked (ts);
892 add_tempo_locked (tempo, pulse, true, type);
894 first.set_type (type);
896 /* cannot move the first tempo section */
897 *static_cast<Tempo*>(&first) = tempo;
898 recompute_map (_metrics);
903 PropertyChanged (PropertyChange ());
907 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
910 Glib::Threads::RWLock::WriterLock lm (lock);
911 TempoSection& first (first_tempo());
912 if (ts.frame() != first.frame()) {
913 remove_tempo_locked (ts);
914 add_tempo_locked (tempo, frame, true, type);
916 first.set_type (type);
917 first.set_pulse (0.0);
918 first.set_position_lock_style (AudioTime);
920 /* cannot move the first tempo section */
921 *static_cast<Tempo*>(&first) = tempo;
922 recompute_map (_metrics);
927 PropertyChanged (PropertyChange ());
931 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
933 TempoSection* ts = 0;
935 Glib::Threads::RWLock::WriterLock lm (lock);
936 ts = add_tempo_locked (tempo, pulse, true, type);
939 PropertyChanged (PropertyChange ());
945 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
947 TempoSection* ts = 0;
949 Glib::Threads::RWLock::WriterLock lm (lock);
950 ts = add_tempo_locked (tempo, frame, true, type);
954 PropertyChanged (PropertyChange ());
960 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
962 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
967 solve_map (_metrics, t, t->pulse());
968 recompute_meters (_metrics);
975 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
977 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
982 solve_map (_metrics, t, t->frame());
983 recompute_meters (_metrics);
990 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
993 Glib::Threads::RWLock::WriterLock lm (lock);
996 remove_meter_locked (ms);
997 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
999 MeterSection& first (first_meter());
1000 /* cannot move the first meter section */
1001 *static_cast<Meter*>(&first) = meter;
1002 first.set_position_lock_style (AudioTime);
1004 recompute_map (_metrics);
1007 PropertyChanged (PropertyChange ());
1011 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1014 Glib::Threads::RWLock::WriterLock lm (lock);
1016 const double beat = ms.beat();
1017 const BBT_Time bbt = ms.bbt();
1020 remove_meter_locked (ms);
1021 add_meter_locked (meter, frame, beat, bbt, true);
1023 MeterSection& first (first_meter());
1024 TempoSection& first_t (first_tempo());
1025 /* cannot move the first meter section */
1026 *static_cast<Meter*>(&first) = meter;
1027 first.set_position_lock_style (AudioTime);
1028 first.set_pulse (0.0);
1029 first.set_frame (frame);
1030 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1031 first.set_beat (beat);
1032 first_t.set_frame (first.frame());
1033 first_t.set_pulse (0.0);
1034 first_t.set_position_lock_style (AudioTime);
1036 recompute_map (_metrics);
1038 PropertyChanged (PropertyChange ());
1043 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1045 MeterSection* m = 0;
1047 Glib::Threads::RWLock::WriterLock lm (lock);
1048 m = add_meter_locked (meter, beat, where, true);
1053 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1054 dump (_metrics, std::cerr);
1058 PropertyChanged (PropertyChange ());
1064 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1066 MeterSection* m = 0;
1068 Glib::Threads::RWLock::WriterLock lm (lock);
1069 m = add_meter_locked (meter, frame, beat, where, true);
1074 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1075 dump (_metrics, std::cerr);
1079 PropertyChanged (PropertyChange ());
1085 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, bool recompute)
1087 /* a new meter always starts a new bar on the first beat. so
1088 round the start time appropriately. remember that
1089 `where' is based on the existing tempo map, not
1090 the result after we insert the new meter.
1094 const double pulse = pulse_at_beat_locked (_metrics, beat);
1095 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1097 do_insert (new_meter);
1100 solve_map (_metrics, new_meter, where);
1107 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, const Timecode::BBT_Time& where, bool recompute)
1109 /* add meter-locked tempo */
1110 TempoSection* t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1112 t->set_locked_to_meter (true);
1115 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1116 new_meter->set_pulse (pulse_at_frame_locked (_metrics, frame));
1118 do_insert (new_meter);
1121 solve_map (_metrics, new_meter, frame);
1128 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1130 Tempo newtempo (beats_per_minute, note_type);
1133 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1134 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1139 Glib::Threads::RWLock::WriterLock lm (lock);
1140 *((Tempo*) t) = newtempo;
1141 recompute_map (_metrics);
1143 PropertyChanged (PropertyChange ());
1150 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1152 Tempo newtempo (beats_per_minute, note_type);
1155 TempoSection* first;
1156 Metrics::iterator i;
1158 /* find the TempoSection immediately preceding "where"
1161 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1163 if ((*i)->frame() > where) {
1169 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1182 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1192 Glib::Threads::RWLock::WriterLock lm (lock);
1193 /* cannot move the first tempo section */
1194 *((Tempo*)prev) = newtempo;
1195 recompute_map (_metrics);
1198 PropertyChanged (PropertyChange ());
1202 TempoMap::first_meter () const
1204 const MeterSection *m = 0;
1206 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1207 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1212 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1213 abort(); /*NOTREACHED*/
1218 TempoMap::first_meter ()
1220 MeterSection *m = 0;
1222 /* CALLER MUST HOLD LOCK */
1224 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1225 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1230 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1231 abort(); /*NOTREACHED*/
1236 TempoMap::first_tempo () const
1238 const TempoSection *t = 0;
1240 /* CALLER MUST HOLD LOCK */
1242 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1243 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1247 if (!t->movable()) {
1253 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1254 abort(); /*NOTREACHED*/
1259 TempoMap::first_tempo ()
1261 TempoSection *t = 0;
1263 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1264 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1268 if (!t->movable()) {
1274 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1275 abort(); /*NOTREACHED*/
1279 TempoMap::recompute_tempos (Metrics& metrics)
1281 TempoSection* prev_t = 0;
1283 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1286 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1290 if (!t->movable()) {
1298 if (t->position_lock_style() == AudioTime) {
1299 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1300 if (!t->locked_to_meter()) {
1301 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1305 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1306 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1313 prev_t->set_c_func (0.0);
1316 /* tempos must be positioned correctly.
1317 the current approach is to use a meter's bbt time as its base position unit.
1318 this means that a meter's beat may change, but its bbt may not.
1319 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1320 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1323 TempoMap::recompute_meters (Metrics& metrics)
1325 MeterSection* meter = 0;
1326 MeterSection* prev_m = 0;
1328 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1329 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1330 if (meter->position_lock_style() == AudioTime) {
1332 pair<double, BBT_Time> b_bbt;
1333 TempoSection* meter_locked_tempo = 0;
1334 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1336 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1337 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1338 meter_locked_tempo = t;
1345 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1346 if (beats + prev_m->beat() != meter->beat()) {
1347 /* reordering caused a bbt change */
1348 b_bbt = make_pair (beats + prev_m->beat()
1349 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1350 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1352 } else if (meter->movable()) {
1353 b_bbt = make_pair (meter->beat(), meter->bbt());
1354 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1357 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1359 if (meter_locked_tempo) {
1360 meter_locked_tempo->set_pulse (pulse);
1362 meter->set_beat (b_bbt);
1363 meter->set_pulse (pulse);
1368 pair<double, BBT_Time> new_beat;
1370 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1371 if (beats + prev_m->beat() != meter->beat()) {
1372 /* reordering caused a bbt change */
1373 new_beat = make_pair (beats + prev_m->beat()
1374 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1376 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1378 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1380 /* shouldn't happen - the first is audio-locked */
1381 pulse = pulse_at_beat_locked (metrics, meter->beat());
1382 new_beat = make_pair (meter->beat(), meter->bbt());
1385 meter->set_beat (new_beat);
1386 meter->set_pulse (pulse);
1387 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1396 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1398 /* CALLER MUST HOLD WRITE LOCK */
1402 /* we will actually stop once we hit
1409 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1412 /* silly call from Session::process() during startup
1417 recompute_tempos (metrics);
1418 recompute_meters (metrics);
1422 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1424 Glib::Threads::RWLock::ReaderLock lm (lock);
1425 TempoMetric m (first_meter(), first_tempo());
1427 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1428 at something, because we insert the default tempo and meter during
1429 TempoMap construction.
1431 now see if we can find better candidates.
1434 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1436 if ((*i)->frame() > frame) {
1450 /* XX meters only */
1452 TempoMap::metric_at (BBT_Time bbt) const
1454 Glib::Threads::RWLock::ReaderLock lm (lock);
1455 TempoMetric m (first_meter(), first_tempo());
1457 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1458 at something, because we insert the default tempo and meter during
1459 TempoMap construction.
1461 now see if we can find better candidates.
1464 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1466 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1467 BBT_Time section_start (mw->bbt());
1469 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1481 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1483 MeterSection* prev_m = 0;
1485 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1487 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1488 if (prev_m && m->beat() > beat) {
1495 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1500 TempoMap::pulse_at_beat (const double& beat) const
1502 Glib::Threads::RWLock::ReaderLock lm (lock);
1503 return pulse_at_beat_locked (_metrics, beat);
1507 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1509 MeterSection* prev_m = 0;
1511 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1513 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1514 if (prev_m && m->pulse() > pulse) {
1515 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1523 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1528 TempoMap::beat_at_pulse (const double& pulse) const
1530 Glib::Threads::RWLock::ReaderLock lm (lock);
1531 return beat_at_pulse_locked (_metrics, pulse);
1534 /* tempo section based */
1536 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1538 /* HOLD (at least) THE READER LOCK */
1539 TempoSection* prev_t = 0;
1541 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1543 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1547 if (prev_t && t->frame() > frame) {
1548 /*the previous ts is the one containing the frame */
1549 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1556 /* treated as constant for this ts */
1557 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1559 return pulses_in_section + prev_t->pulse();
1563 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1565 Glib::Threads::RWLock::ReaderLock lm (lock);
1566 return pulse_at_frame_locked (_metrics, frame);
1569 /* tempo section based */
1571 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1573 /* HOLD THE READER LOCK */
1575 const TempoSection* prev_t = 0;
1577 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1580 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1584 if (prev_t && t->pulse() > pulse) {
1585 return prev_t->frame_at_pulse (pulse, _frame_rate);
1591 /* must be treated as constant, irrespective of _type */
1592 double const pulses_in_section = pulse - prev_t->pulse();
1593 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1595 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1601 TempoMap::frame_at_pulse (const double& pulse) const
1603 Glib::Threads::RWLock::ReaderLock lm (lock);
1604 return frame_at_pulse_locked (_metrics, pulse);
1607 /* meter section based */
1609 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1611 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1612 MeterSection* prev_m = 0;
1613 MeterSection* next_m = 0;
1615 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1617 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1618 if (prev_m && m->frame() > frame) {
1625 if (frame < prev_m->frame()) {
1628 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1630 if (next_m && next_m->beat() < beat) {
1631 return next_m->beat();
1638 TempoMap::beat_at_frame (const framecnt_t& frame) const
1640 Glib::Threads::RWLock::ReaderLock lm (lock);
1641 return beat_at_frame_locked (_metrics, frame);
1644 /* meter section based */
1646 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1648 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1649 MeterSection* prev_m = 0;
1651 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1653 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1654 if (prev_m && m->beat() > beat) {
1661 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1665 TempoMap::frame_at_beat (const double& beat) const
1667 Glib::Threads::RWLock::ReaderLock lm (lock);
1668 return frame_at_beat_locked (_metrics, beat);
1672 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1674 /* CALLER HOLDS READ LOCK */
1676 MeterSection* prev_m = 0;
1678 /* because audio-locked meters have 'fake' integral beats,
1679 there is no pulse offset here.
1681 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1683 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1685 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1686 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1694 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1695 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1696 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1702 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1704 Glib::Threads::RWLock::ReaderLock lm (lock);
1705 return bbt_to_beats_locked (_metrics, bbt);
1709 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1711 /* CALLER HOLDS READ LOCK */
1712 MeterSection* prev_m = 0;
1713 const double beats = (b < 0.0) ? 0.0 : b;
1715 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1716 MeterSection* m = 0;
1718 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1720 if (m->beat() > beats) {
1721 /* this is the meter after the one our beat is on*/
1730 const double beats_in_ms = beats - prev_m->beat();
1731 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1732 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1733 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1734 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1738 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1739 ret.beats = (uint32_t) floor (remaining_beats);
1740 ret.bars = total_bars;
1742 /* 0 0 0 to 1 1 0 - based mapping*/
1746 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1748 ret.ticks -= BBT_Time::ticks_per_beat;
1751 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1760 TempoMap::beats_to_bbt (const double& beats)
1762 Glib::Threads::RWLock::ReaderLock lm (lock);
1763 return beats_to_bbt_locked (_metrics, beats);
1767 TempoMap::pulse_to_bbt (const double& pulse)
1769 Glib::Threads::RWLock::ReaderLock lm (lock);
1770 MeterSection* prev_m = 0;
1772 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1773 MeterSection* m = 0;
1775 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1778 double const pulses_to_m = m->pulse() - prev_m->pulse();
1779 if (prev_m->pulse() + pulses_to_m > pulse) {
1780 /* this is the meter after the one our beat is on*/
1789 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1790 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1791 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1792 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1793 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1797 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1798 ret.beats = (uint32_t) floor (remaining_beats);
1799 ret.bars = total_bars;
1801 /* 0 0 0 to 1 1 0 mapping*/
1805 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1807 ret.ticks -= BBT_Time::ticks_per_beat;
1810 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1819 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1826 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1829 Glib::Threads::RWLock::ReaderLock lm (lock);
1830 const double beat = beat_at_frame_locked (_metrics, frame);
1832 bbt = beats_to_bbt_locked (_metrics, beat);
1835 /* meter section based */
1837 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1839 /* HOLD THE READER LOCK */
1841 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1846 TempoMap::frame_time (const BBT_Time& bbt)
1849 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1853 if (bbt.beats < 1) {
1854 throw std::logic_error ("beats are counted from one");
1856 Glib::Threads::RWLock::ReaderLock lm (lock);
1858 return frame_time_locked (_metrics, bbt);
1862 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1864 TempoSection* prev_t = 0;
1865 MeterSection* prev_m = 0;
1867 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1870 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1875 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1879 if (t->frame() == prev_t->frame()) {
1883 /* precision check ensures pulses and frames align.*/
1884 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1885 if (!t->locked_to_meter()) {
1893 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1894 if (prev_m && m->position_lock_style() == AudioTime) {
1895 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1896 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1897 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1899 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1913 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1915 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1917 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1918 if (!t->movable()) {
1919 t->set_active (true);
1922 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1923 t->set_active (false);
1925 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1926 t->set_active (true);
1927 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1936 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1938 TempoSection* prev_t = 0;
1939 TempoSection* section_prev = 0;
1940 framepos_t first_m_frame = 0;
1942 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1944 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1945 if (!m->movable()) {
1946 first_m_frame = m->frame();
1951 if (section->movable() && frame <= first_m_frame) {
1955 section->set_active (true);
1956 section->set_frame (frame);
1958 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1960 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1967 section_prev = prev_t;
1970 if (t->position_lock_style() == MusicTime) {
1971 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1972 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1974 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1975 if (!t->locked_to_meter()) {
1976 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1985 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1986 if (!section->locked_to_meter()) {
1987 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1991 if (section->position_lock_style() == MusicTime) {
1992 /* we're setting the frame */
1993 section->set_position_lock_style (AudioTime);
1994 recompute_tempos (imaginary);
1995 section->set_position_lock_style (MusicTime);
1997 recompute_tempos (imaginary);
2000 if (check_solved (imaginary, true)) {
2004 MetricSectionFrameSorter fcmp;
2005 imaginary.sort (fcmp);
2006 if (section->position_lock_style() == MusicTime) {
2007 /* we're setting the frame */
2008 section->set_position_lock_style (AudioTime);
2009 recompute_tempos (imaginary);
2010 section->set_position_lock_style (MusicTime);
2012 recompute_tempos (imaginary);
2015 if (check_solved (imaginary, true)) {
2023 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
2025 TempoSection* prev_t = 0;
2026 TempoSection* section_prev = 0;
2028 section->set_pulse (pulse);
2030 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2032 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2036 if (!t->movable()) {
2043 section_prev = prev_t;
2046 if (t->position_lock_style() == MusicTime) {
2047 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2048 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2050 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2051 if (!t->locked_to_meter()) {
2052 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2060 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2061 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2064 if (section->position_lock_style() == AudioTime) {
2065 /* we're setting the pulse */
2066 section->set_position_lock_style (MusicTime);
2067 recompute_tempos (imaginary);
2068 section->set_position_lock_style (AudioTime);
2070 recompute_tempos (imaginary);
2073 if (check_solved (imaginary, false)) {
2077 MetricSectionSorter cmp;
2078 imaginary.sort (cmp);
2079 if (section->position_lock_style() == AudioTime) {
2080 /* we're setting the pulse */
2081 section->set_position_lock_style (MusicTime);
2082 recompute_tempos (imaginary);
2083 section->set_position_lock_style (AudioTime);
2085 recompute_tempos (imaginary);
2088 if (check_solved (imaginary, false)) {
2096 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2098 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2099 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2100 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2104 if (!section->movable()) {
2105 /* lock the first tempo to our first meter */
2106 if (!set_active_tempos (imaginary, frame)) {
2111 /* it would make sense to bail out if there is no audio-locked meter,
2112 however it may be desirable to move a music-locked meter by frame at some point.
2114 TempoSection* meter_locked_tempo = 0;
2115 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2117 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2118 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2119 meter_locked_tempo = t;
2125 MeterSection* prev_m = 0;
2127 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2129 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2131 if (prev_m && section->movable()) {
2132 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2133 if (beats + prev_m->beat() < section->beat()) {
2134 /* disallow position change if it will alter our beat
2135 we allow tempo changes to do this in recompute_meters().
2136 blocking this is an option, but i'm not convinced that
2137 this is what the user would actually want.
2138 here we set the frame/pulse corresponding to its musical position.
2141 if (meter_locked_tempo) {
2143 bool solved = false;
2144 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2145 const double new_pulse = ((section->beat() - prev_m->beat())
2146 / prev_m->note_divisor()) + prev_m->pulse();
2147 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2148 if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
2149 meter_locked_tempo->set_pulse (new_pulse);
2150 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2151 section->set_frame (smallest_frame);
2152 section->set_pulse (new_pulse);
2157 Metrics::const_iterator d = future_map.begin();
2158 while (d != future_map.end()) {
2169 if (meter_locked_tempo) {
2171 bool solved = false;
2173 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2174 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2175 meter_copy->set_frame (frame);
2177 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2178 section->set_frame (frame);
2179 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2180 / prev_m->note_divisor()) + prev_m->pulse());
2181 solve_map (imaginary, meter_locked_tempo, frame);
2186 Metrics::const_iterator d = future_map.begin();
2187 while (d != future_map.end()) {
2198 /* not movable (first meter atm) */
2199 if (meter_locked_tempo) {
2201 bool solved = false;
2202 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2204 tempo_copy->set_frame (frame);
2205 tempo_copy->set_pulse (0.0);
2207 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2208 section->set_frame (frame);
2209 meter_locked_tempo->set_frame (frame);
2210 meter_locked_tempo->set_pulse (0.0);
2211 solve_map (imaginary, meter_locked_tempo, frame);
2216 Metrics::const_iterator d = future_map.begin();
2217 while (d != future_map.end()) {
2229 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2230 section->set_beat (b_bbt);
2231 section->set_pulse (0.0);
2241 MetricSectionFrameSorter fcmp;
2242 imaginary.sort (fcmp);
2243 if (section->position_lock_style() == MusicTime) {
2244 /* we're setting the frame */
2245 section->set_position_lock_style (AudioTime);
2246 recompute_meters (imaginary);
2247 section->set_position_lock_style (MusicTime);
2249 recompute_meters (imaginary);
2256 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2258 /* disallow setting section to an existing meter's bbt */
2259 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2261 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2262 if (m != section && m->bbt().bars == when.bars) {
2268 MeterSection* prev_m = 0;
2269 MeterSection* section_prev = 0;
2271 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2273 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2274 pair<double, BBT_Time> b_bbt;
2275 double new_pulse = 0.0;
2277 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2278 section_prev = prev_m;
2279 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2280 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2281 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2283 section->set_beat (b_bbt);
2284 section->set_pulse (pulse);
2285 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2290 if (m->position_lock_style() == AudioTime) {
2291 TempoSection* meter_locked_tempo = 0;
2292 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2294 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2295 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2296 meter_locked_tempo = t;
2303 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2305 if (beats + prev_m->beat() != m->beat()) {
2306 /* tempo/ meter change caused a change in beat (bar). */
2307 b_bbt = make_pair (beats + prev_m->beat()
2308 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2309 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2310 } else if (m->movable()) {
2311 b_bbt = make_pair (m->beat(), m->bbt());
2312 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2315 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2317 if (meter_locked_tempo) {
2318 meter_locked_tempo->set_pulse (new_pulse);
2319 recompute_tempos (imaginary);
2321 m->set_beat (b_bbt);
2322 m->set_pulse (new_pulse);
2326 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2327 if (beats + prev_m->beat() != m->beat()) {
2328 /* tempo/ meter change caused a change in beat (bar). */
2329 b_bbt = make_pair (beats + prev_m->beat()
2330 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2332 b_bbt = make_pair (beats + prev_m->beat()
2335 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2336 m->set_beat (b_bbt);
2337 m->set_pulse (new_pulse);
2338 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2345 if (!section_prev) {
2347 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2348 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2349 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2351 section->set_beat (b_bbt);
2352 section->set_pulse (pulse);
2353 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2356 MetricSectionSorter cmp;
2357 imaginary.sort (cmp);
2359 if (section->position_lock_style() == AudioTime) {
2360 /* we're setting the pulse */
2361 section->set_position_lock_style (MusicTime);
2362 recompute_meters (imaginary);
2363 section->set_position_lock_style (AudioTime);
2365 recompute_meters (imaginary);
2371 /** places a copy of _metrics into copy and returns a pointer
2372 * to section's equivalent in copy.
2375 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2377 TempoSection* ret = 0;
2379 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2382 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2384 ret = new TempoSection (*t);
2385 copy.push_back (ret);
2389 TempoSection* cp = new TempoSection (*t);
2390 copy.push_back (cp);
2392 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2393 MeterSection* cp = new MeterSection (*m);
2394 copy.push_back (cp);
2402 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2404 MeterSection* ret = 0;
2406 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2409 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2410 TempoSection* cp = new TempoSection (*t);
2411 copy.push_back (cp);
2414 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2416 ret = new MeterSection (*m);
2417 copy.push_back (ret);
2420 MeterSection* cp = new MeterSection (*m);
2421 copy.push_back (cp);
2429 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2432 TempoSection* tempo_copy = 0;
2435 Glib::Threads::RWLock::ReaderLock lm (lock);
2436 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2442 const double beat = bbt_to_beats_locked (copy, bbt);
2443 const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2445 Metrics::const_iterator d = copy.begin();
2446 while (d != copy.end()) {
2455 * 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,
2456 * taking any possible reordering as a consequence of this into account.
2457 * @param section - the section to be altered
2458 * @param bpm - the new Tempo
2459 * @param bbt - the bbt where the altered tempo will fall
2460 * @return returns - the position in frames where the new tempo section will lie.
2463 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2465 Glib::Threads::RWLock::ReaderLock lm (lock);
2468 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2472 const double beat = bbt_to_beats_locked (future_map, bbt);
2474 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2475 ret = tempo_copy->frame();
2477 ret = section->frame();
2480 Metrics::const_iterator d = future_map.begin();
2481 while (d != future_map.end()) {
2489 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2491 Glib::Threads::RWLock::ReaderLock lm (lock);
2494 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2496 if (solve_map (future_map, tempo_copy, frame)) {
2497 ret = tempo_copy->pulse();
2499 ret = section->pulse();
2502 Metrics::const_iterator d = future_map.begin();
2503 while (d != future_map.end()) {
2511 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2515 Glib::Threads::RWLock::WriterLock lm (lock);
2516 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2517 if (solve_map (future_map, tempo_copy, frame)) {
2518 solve_map (_metrics, ts, frame);
2519 recompute_meters (_metrics);
2523 Metrics::const_iterator d = future_map.begin();
2524 while (d != future_map.end()) {
2529 MetricPositionChanged (); // Emit Signal
2533 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2537 Glib::Threads::RWLock::WriterLock lm (lock);
2538 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2539 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2540 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2541 recompute_meters (_metrics);
2545 Metrics::const_iterator d = future_map.begin();
2546 while (d != future_map.end()) {
2551 MetricPositionChanged (); // Emit Signal
2555 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2559 Glib::Threads::RWLock::WriterLock lm (lock);
2560 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2561 if (solve_map (future_map, copy, frame)) {
2562 solve_map (_metrics, ms, frame);
2563 recompute_tempos (_metrics);
2567 Metrics::const_iterator d = future_map.begin();
2568 while (d != future_map.end()) {
2573 MetricPositionChanged (); // Emit Signal
2577 TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
2581 Glib::Threads::RWLock::WriterLock lm (lock);
2582 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2583 if (solve_map (future_map, copy, bbt)) {
2584 solve_map (_metrics, ms, bbt);
2585 recompute_tempos (_metrics);
2589 Metrics::const_iterator d = future_map.begin();
2590 while (d != future_map.end()) {
2595 MetricPositionChanged (); // Emit Signal
2599 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2602 bool can_solve = false;
2604 Glib::Threads::RWLock::WriterLock lm (lock);
2605 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2606 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2607 recompute_tempos (future_map);
2609 if (check_solved (future_map, true)) {
2610 ts->set_beats_per_minute (bpm.beats_per_minute());
2611 recompute_map (_metrics);
2616 Metrics::const_iterator d = future_map.begin();
2617 while (d != future_map.end()) {
2622 MetricPositionChanged (); // Emit Signal
2628 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2631 TempoSection* ts = 0;
2633 if (frame <= first_meter().frame()) {
2637 if (ms->position_lock_style() == AudioTime) {
2638 /* disabled for now due to faked tempo locked to meter pulse */
2643 Glib::Threads::RWLock::WriterLock lm (lock);
2644 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2648 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2649 TempoSection* prev_to_prev_t = 0;
2650 const frameoffset_t fr_off = frame - ms->frame();
2651 double new_bpm = 0.0;
2653 if (prev_t && prev_t->pulse() > 0.0) {
2654 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2657 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2658 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2660 double contribution = 0.0;
2661 frameoffset_t frame_contribution = 0;
2662 frameoffset_t prev_t_frame_contribution = fr_off;
2664 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2665 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2666 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2667 frame_contribution = contribution * (double) fr_off;
2668 prev_t_frame_contribution = fr_off - frame_contribution;
2671 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2673 if (prev_t->position_lock_style() == MusicTime) {
2674 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2675 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2676 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2679 /* prev to prev is irrelevant */
2680 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2681 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2683 if (frame_pulse != prev_t->pulse()) {
2684 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2686 new_bpm = prev_t->beats_per_minute();
2691 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2692 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2693 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2695 /* prev_to_prev_t is irrelevant */
2697 if (frame != prev_t->frame()) {
2698 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2700 new_bpm = prev_t->beats_per_minute();
2704 } else if (prev_t->c_func() < 0.0) {
2705 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2706 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2708 /* prev_to_prev_t is irrelevant */
2709 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2712 } else if (prev_t->c_func() > 0.0) {
2713 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2714 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2716 /* prev_to_prev_t is irrelevant */
2717 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2721 /* limits - a bit clunky, but meh */
2722 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2723 if (diff > -1.0 && diff < 1.0) {
2724 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2725 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2728 prev_t->set_beats_per_minute (new_bpm);
2729 recompute_tempos (future_map);
2730 recompute_meters (future_map);
2732 if (check_solved (future_map, true)) {
2734 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2735 prev_t->set_beats_per_minute (new_bpm);
2736 recompute_tempos (_metrics);
2738 if (ms->position_lock_style() == AudioTime) {
2739 ms->set_frame (frame);
2742 recompute_meters (_metrics);
2746 Metrics::const_iterator d = future_map.begin();
2747 while (d != future_map.end()) {
2752 MetricPositionChanged (); // Emit Signal
2756 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2759 Ts (future prev_t) Tnext
2762 |----------|----------
2769 Glib::Threads::RWLock::WriterLock lm (lock);
2775 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2776 TempoSection* prev_to_prev_t = 0;
2777 const frameoffset_t fr_off = end_frame - frame;
2779 if (prev_t && prev_t->pulse() > 0.0) {
2780 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2783 TempoSection* next_t = 0;
2784 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2785 TempoSection* t = 0;
2786 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2787 if (t->frame() > ts->frame()) {
2794 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2795 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2797 double contribution = 0.0;
2798 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2800 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2801 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2804 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2805 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2808 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2810 if (prev_t->position_lock_style() == MusicTime) {
2811 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2813 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2814 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2817 /* prev to prev is irrelevant */
2819 if (start_pulse != prev_t->pulse()) {
2820 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2822 new_bpm = prev_t->beats_per_minute();
2827 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2828 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2829 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2831 /* prev_to_prev_t is irrelevant */
2833 if (end_frame != prev_t->frame()) {
2834 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2836 new_bpm = prev_t->beats_per_minute();
2844 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2846 if (prev_to_prev_t) {
2848 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2849 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2852 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2853 pulse_ratio = (start_pulse / end_pulse);
2855 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2858 prev_t->set_beats_per_minute (new_bpm);
2859 recompute_tempos (future_map);
2860 recompute_meters (future_map);
2862 if (check_solved (future_map, true)) {
2863 ts->set_beats_per_minute (new_bpm);
2864 recompute_tempos (_metrics);
2865 recompute_meters (_metrics);
2869 Metrics::const_iterator d = future_map.begin();
2870 while (d != future_map.end()) {
2875 MetricPositionChanged (); // Emit Signal
2879 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2881 Glib::Threads::RWLock::ReaderLock lm (lock);
2883 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2884 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2885 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2887 return frame_at_beat_locked (_metrics, total_beats);
2891 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2893 return round_to_type (fr, dir, Bar);
2897 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2899 return round_to_type (fr, dir, Beat);
2903 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2905 Glib::Threads::RWLock::ReaderLock lm (lock);
2906 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2907 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2908 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2910 ticks -= beats * BBT_Time::ticks_per_beat;
2913 /* round to next (or same iff dir == RoundUpMaybe) */
2915 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2917 if (mod == 0 && dir == RoundUpMaybe) {
2918 /* right on the subdivision, which is fine, so do nothing */
2920 } else if (mod == 0) {
2921 /* right on the subdivision, so the difference is just the subdivision ticks */
2922 ticks += ticks_one_subdivisions_worth;
2925 /* not on subdivision, compute distance to next subdivision */
2927 ticks += ticks_one_subdivisions_worth - mod;
2930 if (ticks >= BBT_Time::ticks_per_beat) {
2931 ticks -= BBT_Time::ticks_per_beat;
2933 } else if (dir < 0) {
2935 /* round to previous (or same iff dir == RoundDownMaybe) */
2937 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2939 if (difference == 0 && dir == RoundDownAlways) {
2940 /* right on the subdivision, but force-rounding down,
2941 so the difference is just the subdivision ticks */
2942 difference = ticks_one_subdivisions_worth;
2945 if (ticks < difference) {
2946 ticks = BBT_Time::ticks_per_beat - ticks;
2948 ticks -= difference;
2952 /* round to nearest */
2955 /* compute the distance to the previous and next subdivision */
2957 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2959 /* closer to the next subdivision, so shift forward */
2961 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2963 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2965 if (ticks > BBT_Time::ticks_per_beat) {
2967 ticks -= BBT_Time::ticks_per_beat;
2968 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2971 } else if (rem > 0) {
2973 /* closer to previous subdivision, so shift backward */
2977 /* can't go backwards past zero, so ... */
2980 /* step back to previous beat */
2982 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2983 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2985 ticks = lrint (ticks - rem);
2986 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2989 /* on the subdivision, do nothing */
2993 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2999 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
3001 if (sub_num == -1) {
3006 } else if (dir < 0) {
3010 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
3011 if ((double) when.beats > bpb / 2.0) {
3020 } else if (sub_num == 0) {
3021 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
3022 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
3024 while ((double) when.beats > bpb) {
3026 when.beats -= (uint32_t) floor (bpb);
3034 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
3037 /* round to next (or same iff dir == RoundUpMaybe) */
3039 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
3041 if (mod == 0 && dir == RoundUpMaybe) {
3042 /* right on the subdivision, which is fine, so do nothing */
3044 } else if (mod == 0) {
3045 /* right on the subdivision, so the difference is just the subdivision ticks */
3046 when.ticks += ticks_one_subdivisions_worth;
3049 /* not on subdivision, compute distance to next subdivision */
3051 when.ticks += ticks_one_subdivisions_worth - mod;
3054 if (when.ticks >= BBT_Time::ticks_per_beat) {
3055 when.ticks -= BBT_Time::ticks_per_beat;
3058 } else if (dir < 0) {
3059 /* round to previous (or same iff dir == RoundDownMaybe) */
3061 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
3063 if (difference == 0 && dir == RoundDownAlways) {
3064 /* right on the subdivision, but force-rounding down,
3065 so the difference is just the subdivision ticks */
3066 difference = ticks_one_subdivisions_worth;
3069 if (when.ticks < difference) {
3070 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
3072 when.ticks -= difference;
3076 /* round to nearest */ double rem;
3077 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
3078 /* closer to the next subdivision, so shift forward */
3080 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
3082 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
3084 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
3087 } else if (rem > 0) {
3088 /* closer to previous subdivision, so shift backward */
3090 if (rem > when.ticks) {
3091 if (when.beats == 0) {
3092 /* can't go backwards past zero, so ... */
3094 /* step back to previous beat */
3096 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
3098 when.ticks = when.ticks - rem;
3105 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3107 Glib::Threads::RWLock::ReaderLock lm (lock);
3109 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3110 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3115 /* find bar previous to 'frame' */
3118 return frame_time_locked (_metrics, bbt);
3120 } else if (dir > 0) {
3121 /* find bar following 'frame' */
3125 return frame_time_locked (_metrics, bbt);
3127 /* true rounding: find nearest bar */
3128 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3131 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3133 framepos_t next_ft = frame_time_locked (_metrics, bbt);
3135 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3146 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3147 } else if (dir > 0) {
3148 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3150 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3159 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3160 framepos_t lower, framepos_t upper)
3162 Glib::Threads::RWLock::ReaderLock lm (lock);
3163 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3165 /* although the map handles negative beats, bbt doesn't. */
3169 while (pos < upper) {
3170 pos = frame_at_beat_locked (_metrics, cnt);
3171 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3172 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3173 const BBT_Time bbt = beats_to_bbt (cnt);
3174 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3180 TempoMap::tempo_section_at (framepos_t frame) const
3182 Glib::Threads::RWLock::ReaderLock lm (lock);
3183 return tempo_section_at_locked (_metrics, frame);
3187 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3189 Metrics::const_iterator i;
3190 TempoSection* prev = 0;
3192 for (i = metrics.begin(); i != metrics.end(); ++i) {
3195 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3199 if (prev && t->frame() > frame) {
3209 abort(); /*NOTREACHED*/
3216 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3218 TempoSection* prev_t = 0;
3219 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3221 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3223 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3224 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3235 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3237 TempoSection* prev_t = 0;
3239 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3241 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3242 if (prev_t && t->pulse() > pulse) {
3252 /* don't use this to calculate length (the tempo is only correct for this frame).
3253 do that stuff based on the beat_at_frame and frame_at_beat api
3256 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3258 Glib::Threads::RWLock::ReaderLock lm (lock);
3260 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3261 const TempoSection* ts_after = 0;
3262 Metrics::const_iterator i;
3264 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3267 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3271 if ((*i)->frame() > frame) {
3279 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3281 /* must be treated as constant tempo */
3282 return ts_at->frames_per_beat (_frame_rate);
3286 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3288 TempoSection* prev_t = 0;
3290 Metrics::const_iterator i;
3292 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3294 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3298 if ((prev_t) && t->frame() > frame) {
3299 /* t is the section past frame */
3300 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3301 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3308 const double ret = prev_t->beats_per_minute();
3309 const Tempo ret_tempo (ret, prev_t->note_type ());
3315 TempoMap::tempo_at (const framepos_t& frame) const
3317 Glib::Threads::RWLock::ReaderLock lm (lock);
3318 return tempo_at_locked (_metrics, frame);
3322 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3324 Metrics::const_iterator i;
3325 MeterSection* prev = 0;
3327 for (i = metrics.begin(); i != metrics.end(); ++i) {
3330 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3332 if (prev && (*i)->frame() > frame) {
3342 abort(); /*NOTREACHED*/
3350 TempoMap::meter_section_at (framepos_t frame) const
3352 Glib::Threads::RWLock::ReaderLock lm (lock);
3353 return meter_section_at_locked (_metrics, frame);
3357 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3359 MeterSection* prev_m = 0;
3361 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3363 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3364 if (prev_m && m->beat() > beat) {
3375 TempoMap::meter_section_at_beat (double beat) const
3377 Glib::Threads::RWLock::ReaderLock lm (lock);
3378 return meter_section_at_beat_locked (_metrics, beat);
3382 TempoMap::meter_at (framepos_t frame) const
3384 TempoMetric m (metric_at (frame));
3389 TempoMap::fix_legacy_session ()
3391 MeterSection* prev_m = 0;
3392 TempoSection* prev_t = 0;
3394 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3398 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3399 if (!m->movable()) {
3400 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3404 m->set_position_lock_style (AudioTime);
3409 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3410 + (m->bbt().beats - 1)
3411 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3413 m->set_beat (start);
3414 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3415 + (m->bbt().beats - 1)
3416 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3417 m->set_pulse (start_beat / prev_m->note_divisor());
3420 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3426 if (!t->movable()) {
3429 t->set_position_lock_style (AudioTime);
3435 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3436 + (t->legacy_bbt().beats - 1)
3437 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3439 t->set_pulse (beat / prev_m->note_divisor());
3441 /* really shouldn't happen but.. */
3442 t->set_pulse (beat / 4.0);
3451 TempoMap::get_state ()
3453 Metrics::const_iterator i;
3454 XMLNode *root = new XMLNode ("TempoMap");
3457 Glib::Threads::RWLock::ReaderLock lm (lock);
3458 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3459 root->add_child_nocopy ((*i)->get_state());
3467 TempoMap::set_state (const XMLNode& node, int /*version*/)
3470 Glib::Threads::RWLock::WriterLock lm (lock);
3473 XMLNodeConstIterator niter;
3474 Metrics old_metrics (_metrics);
3477 nlist = node.children();
3479 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3480 XMLNode* child = *niter;
3482 if (child->name() == TempoSection::xml_state_node_name) {
3485 TempoSection* ts = new TempoSection (*child);
3486 _metrics.push_back (ts);
3489 catch (failed_constructor& err){
3490 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3491 _metrics = old_metrics;
3495 } else if (child->name() == MeterSection::xml_state_node_name) {
3498 MeterSection* ms = new MeterSection (*child);
3499 _metrics.push_back (ms);
3502 catch (failed_constructor& err) {
3503 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3504 _metrics = old_metrics;
3510 if (niter == nlist.end()) {
3511 MetricSectionSorter cmp;
3512 _metrics.sort (cmp);
3515 /* check for legacy sessions where bbt was the base musical unit for tempo */
3516 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3518 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3519 if (t->legacy_bbt().bars != 0) {
3520 fix_legacy_session();
3527 /* check for multiple tempo/meters at the same location, which
3528 ardour2 somehow allowed.
3531 Metrics::iterator prev = _metrics.end();
3532 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3533 if (prev != _metrics.end()) {
3535 MeterSection* prev_m;
3537 TempoSection* prev_t;
3538 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3539 if (prev_m->pulse() == ms->pulse()) {
3540 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3541 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3544 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3545 if (prev_t->pulse() == ts->pulse()) {
3546 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3547 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3555 recompute_map (_metrics);
3558 PropertyChanged (PropertyChange ());
3564 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3566 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3567 const MeterSection* m;
3568 const TempoSection* t;
3569 const TempoSection* prev_t = 0;
3571 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3573 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3574 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3575 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3576 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3578 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3579 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;
3582 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3583 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3584 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3587 o << "------" << std::endl;
3591 TempoMap::n_tempos() const
3593 Glib::Threads::RWLock::ReaderLock lm (lock);
3596 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3597 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3606 TempoMap::n_meters() const
3608 Glib::Threads::RWLock::ReaderLock lm (lock);
3611 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3612 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3621 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3624 Glib::Threads::RWLock::WriterLock lm (lock);
3625 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3626 if ((*i)->frame() >= where && (*i)->movable ()) {
3627 (*i)->set_frame ((*i)->frame() + amount);
3631 /* now reset the BBT time of all metrics, based on their new
3632 * audio time. This is the only place where we do this reverse
3636 Metrics::iterator i;
3637 const MeterSection* meter;
3638 const TempoSection* tempo;
3642 meter = &first_meter ();
3643 tempo = &first_tempo ();
3648 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3651 MetricSection* prev = 0;
3653 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3656 //TempoMetric metric (*meter, *tempo);
3657 MeterSection* ms = const_cast<MeterSection*>(meter);
3658 TempoSection* ts = const_cast<TempoSection*>(tempo);
3661 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3665 ts->set_pulse (t->pulse());
3667 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3668 ts->set_pulse (m->pulse());
3670 ts->set_frame (prev->frame());
3674 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3675 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3676 ms->set_beat (start);
3677 ms->set_pulse (m->pulse());
3679 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3683 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3684 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3685 ms->set_beat (start);
3686 ms->set_pulse (t->pulse());
3688 ms->set_frame (prev->frame());
3692 // metric will be at frames=0 bbt=1|1|0 by default
3693 // which is correct for our purpose
3696 // cerr << bbt << endl;
3698 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3702 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3704 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3705 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3706 bbt_time (m->frame(), bbt);
3708 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3714 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3715 /* round up to next beat */
3721 if (bbt.beats != 1) {
3722 /* round up to next bar */
3727 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3728 m->set_beat (start);
3729 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3731 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3733 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3734 abort(); /*NOTREACHED*/
3740 recompute_map (_metrics);
3744 PropertyChanged (PropertyChange ());
3747 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3751 std::list<MetricSection*> metric_kill_list;
3753 TempoSection* last_tempo = NULL;
3754 MeterSection* last_meter = NULL;
3755 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3756 bool meter_after = false; // is there a meter marker likewise?
3758 Glib::Threads::RWLock::WriterLock lm (lock);
3759 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3760 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3761 metric_kill_list.push_back(*i);
3762 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3765 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3769 else if ((*i)->frame() >= where) {
3770 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3771 (*i)->set_frame ((*i)->frame() - amount);
3772 if ((*i)->frame() == where) {
3773 // marker was immediately after end of range
3774 tempo_after = dynamic_cast<TempoSection*> (*i);
3775 meter_after = dynamic_cast<MeterSection*> (*i);
3781 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3782 if (last_tempo && !tempo_after) {
3783 metric_kill_list.remove(last_tempo);
3784 last_tempo->set_frame(where);
3787 if (last_meter && !meter_after) {
3788 metric_kill_list.remove(last_meter);
3789 last_meter->set_frame(where);
3793 //remove all the remaining metrics
3794 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3795 _metrics.remove(*i);
3800 recompute_map (_metrics);
3803 PropertyChanged (PropertyChange ());
3807 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3808 * pos can be -ve, if required.
3811 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3813 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3816 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3818 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3820 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3823 /** Add the BBT interval op to pos and return the result */
3825 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3827 Glib::Threads::RWLock::ReaderLock lm (lock);
3829 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3830 pos_bbt.ticks += op.ticks;
3831 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3833 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3835 pos_bbt.beats += op.beats;
3836 /* the meter in effect will start on the bar */
3837 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();
3838 while (pos_bbt.beats >= divisions_per_bar + 1) {
3840 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3841 pos_bbt.beats -= divisions_per_bar;
3843 pos_bbt.bars += op.bars;
3845 return frame_time_locked (_metrics, pos_bbt);
3848 /** Count the number of beats that are equivalent to distance when going forward,
3852 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3854 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3858 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3864 operator<< (std::ostream& o, const Meter& m) {
3865 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3869 operator<< (std::ostream& o, const Tempo& t) {
3870 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3874 operator<< (std::ostream& o, const MetricSection& section) {
3876 o << "MetricSection @ " << section.frame() << ' ';
3878 const TempoSection* ts;
3879 const MeterSection* ms;
3881 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3882 o << *((const Tempo*) ts);
3883 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3884 o << *((const Meter*) ms);