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)
76 : MetricSection (0.0, 0, MusicTime)
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, 0, MusicTime), 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 (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant, AudioTime);
641 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
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_pulse (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_pulse_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_frame (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_frame_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_pulse (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_pulse_locked (tempo, pulse, true, type);
939 PropertyChanged (PropertyChange ());
945 TempoMap::add_tempo_frame (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_frame_locked (tempo, frame, true, type);
954 PropertyChanged (PropertyChange ());
960 TempoMap::add_tempo_pulse_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
962 TempoSection* t = new TempoSection (pulse, 0, tempo.beats_per_minute(), tempo.note_type(), type, MusicTime);
967 solve_map_pulse (_metrics, t, t->pulse());
968 recompute_meters (_metrics);
975 TempoMap::add_tempo_frame_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
977 TempoSection* t = new TempoSection (0.0, frame, tempo.beats_per_minute(), tempo.note_type(), type, AudioTime);
982 solve_map_frame (_metrics, t, t->frame());
983 recompute_meters (_metrics);
990 TempoMap::replace_meter_bbt (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_beat_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_frame (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_frame_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_beat (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_beat_locked (meter, beat, where, true);
1053 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1054 dump (_metrics, std::cerr);
1058 PropertyChanged (PropertyChange ());
1064 TempoMap::add_meter_frame (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_frame_locked (meter, frame, beat, where, true);
1074 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1075 dump (_metrics, std::cerr);
1079 PropertyChanged (PropertyChange ());
1085 TempoMap::add_meter_beat_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, 0, beat, where, meter.divisions_per_bar(), meter.note_divisor(), MusicTime);
1097 do_insert (new_meter);
1100 solve_map_bbt (_metrics, new_meter, where);
1107 TempoMap::add_meter_frame_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_frame_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1112 t->set_locked_to_meter (true);
1115 MeterSection* new_meter = new MeterSection (0.0, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), AudioTime);
1116 new_meter->set_pulse (pulse_at_frame_locked (_metrics, frame));
1118 do_insert (new_meter);
1121 solve_map_frame (_metrics, new_meter, frame);
1127 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1129 Tempo newtempo (beats_per_minute, note_type);
1132 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1133 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1138 Glib::Threads::RWLock::WriterLock lm (lock);
1139 *((Tempo*) t) = newtempo;
1140 recompute_map (_metrics);
1142 PropertyChanged (PropertyChange ());
1149 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1151 Tempo newtempo (beats_per_minute, note_type);
1154 TempoSection* first;
1155 Metrics::iterator i;
1157 /* find the TempoSection immediately preceding "where"
1160 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1162 if ((*i)->frame() > where) {
1168 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1181 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1191 Glib::Threads::RWLock::WriterLock lm (lock);
1192 /* cannot move the first tempo section */
1193 *((Tempo*)prev) = newtempo;
1194 recompute_map (_metrics);
1197 PropertyChanged (PropertyChange ());
1201 TempoMap::first_meter () const
1203 const MeterSection *m = 0;
1205 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1206 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1211 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1212 abort(); /*NOTREACHED*/
1217 TempoMap::first_meter ()
1219 MeterSection *m = 0;
1221 /* CALLER MUST HOLD LOCK */
1223 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1224 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1229 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1230 abort(); /*NOTREACHED*/
1235 TempoMap::first_tempo () const
1237 const TempoSection *t = 0;
1239 /* CALLER MUST HOLD LOCK */
1241 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1242 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1246 if (!t->movable()) {
1252 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1253 abort(); /*NOTREACHED*/
1258 TempoMap::first_tempo ()
1260 TempoSection *t = 0;
1262 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1263 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1267 if (!t->movable()) {
1273 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1274 abort(); /*NOTREACHED*/
1278 TempoMap::recompute_tempos (Metrics& metrics)
1280 TempoSection* prev_t = 0;
1282 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1285 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1289 if (!t->movable()) {
1297 if (t->position_lock_style() == AudioTime) {
1298 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1299 if (!t->locked_to_meter()) {
1300 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1304 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1305 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1312 prev_t->set_c_func (0.0);
1315 /* tempos must be positioned correctly.
1316 the current approach is to use a meter's bbt time as its base position unit.
1317 this means that a meter's beat may change, but its bbt may not.
1318 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1319 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1322 TempoMap::recompute_meters (Metrics& metrics)
1324 MeterSection* meter = 0;
1325 MeterSection* prev_m = 0;
1327 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1328 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1329 if (meter->position_lock_style() == AudioTime) {
1331 pair<double, BBT_Time> b_bbt;
1332 TempoSection* meter_locked_tempo = 0;
1333 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1335 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1336 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1337 meter_locked_tempo = t;
1344 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1345 if (beats + prev_m->beat() != meter->beat()) {
1346 /* reordering caused a bbt change */
1347 b_bbt = make_pair (beats + prev_m->beat()
1348 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1349 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1351 } else if (meter->movable()) {
1352 b_bbt = make_pair (meter->beat(), meter->bbt());
1353 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1356 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1358 if (meter_locked_tempo) {
1359 meter_locked_tempo->set_pulse (pulse);
1361 meter->set_beat (b_bbt);
1362 meter->set_pulse (pulse);
1367 pair<double, BBT_Time> new_beat;
1369 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1370 if (beats + prev_m->beat() != meter->beat()) {
1371 /* reordering caused a bbt change */
1372 new_beat = make_pair (beats + prev_m->beat()
1373 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1375 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1377 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1379 /* shouldn't happen - the first is audio-locked */
1380 pulse = pulse_at_beat_locked (metrics, meter->beat());
1381 new_beat = make_pair (meter->beat(), meter->bbt());
1384 meter->set_beat (new_beat);
1385 meter->set_pulse (pulse);
1386 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1395 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1397 /* CALLER MUST HOLD WRITE LOCK */
1401 /* we will actually stop once we hit
1408 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1411 /* silly call from Session::process() during startup
1416 recompute_tempos (metrics);
1417 recompute_meters (metrics);
1421 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1423 Glib::Threads::RWLock::ReaderLock lm (lock);
1424 TempoMetric m (first_meter(), first_tempo());
1426 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1427 at something, because we insert the default tempo and meter during
1428 TempoMap construction.
1430 now see if we can find better candidates.
1433 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1435 if ((*i)->frame() > frame) {
1449 /* XX meters only */
1451 TempoMap::metric_at (BBT_Time bbt) const
1453 Glib::Threads::RWLock::ReaderLock lm (lock);
1454 TempoMetric m (first_meter(), first_tempo());
1456 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1457 at something, because we insert the default tempo and meter during
1458 TempoMap construction.
1460 now see if we can find better candidates.
1463 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1465 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1466 BBT_Time section_start (mw->bbt());
1468 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1480 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1482 MeterSection* prev_m = 0;
1484 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1486 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1487 if (prev_m && m->beat() > beat) {
1494 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1499 TempoMap::pulse_at_beat (const double& beat) const
1501 Glib::Threads::RWLock::ReaderLock lm (lock);
1502 return pulse_at_beat_locked (_metrics, beat);
1506 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1508 MeterSection* prev_m = 0;
1510 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1512 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1513 if (prev_m && m->pulse() > pulse) {
1514 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1522 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1527 TempoMap::beat_at_pulse (const double& pulse) const
1529 Glib::Threads::RWLock::ReaderLock lm (lock);
1530 return beat_at_pulse_locked (_metrics, pulse);
1533 /* tempo section based */
1535 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1537 /* HOLD (at least) THE READER LOCK */
1538 TempoSection* prev_t = 0;
1540 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1542 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1546 if (prev_t && t->frame() > frame) {
1547 /*the previous ts is the one containing the frame */
1548 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1555 /* treated as constant for this ts */
1556 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1558 return pulses_in_section + prev_t->pulse();
1562 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1564 Glib::Threads::RWLock::ReaderLock lm (lock);
1565 return pulse_at_frame_locked (_metrics, frame);
1568 /* tempo section based */
1570 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1572 /* HOLD THE READER LOCK */
1574 const TempoSection* prev_t = 0;
1576 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1579 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1583 if (prev_t && t->pulse() > pulse) {
1584 return prev_t->frame_at_pulse (pulse, _frame_rate);
1590 /* must be treated as constant, irrespective of _type */
1591 double const pulses_in_section = pulse - prev_t->pulse();
1592 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1594 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1600 TempoMap::frame_at_pulse (const double& pulse) const
1602 Glib::Threads::RWLock::ReaderLock lm (lock);
1603 return frame_at_pulse_locked (_metrics, pulse);
1606 /* meter section based */
1608 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1610 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1611 MeterSection* prev_m = 0;
1612 MeterSection* next_m = 0;
1614 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1616 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1617 if (prev_m && m->frame() > frame) {
1624 if (frame < prev_m->frame()) {
1627 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1629 if (next_m && next_m->beat() < beat) {
1630 return next_m->beat();
1637 TempoMap::beat_at_frame (const framecnt_t& frame) const
1639 Glib::Threads::RWLock::ReaderLock lm (lock);
1640 return beat_at_frame_locked (_metrics, frame);
1643 /* meter section based */
1645 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1647 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1648 MeterSection* prev_m = 0;
1650 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1652 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1653 if (prev_m && m->beat() > beat) {
1660 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1664 TempoMap::frame_at_beat (const double& beat) const
1666 Glib::Threads::RWLock::ReaderLock lm (lock);
1667 return frame_at_beat_locked (_metrics, beat);
1671 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1673 /* CALLER HOLDS READ LOCK */
1675 MeterSection* prev_m = 0;
1677 /* because audio-locked meters have 'fake' integral beats,
1678 there is no pulse offset here.
1680 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1682 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1684 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1685 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1693 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1694 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1695 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1701 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1703 Glib::Threads::RWLock::ReaderLock lm (lock);
1704 return bbt_to_beats_locked (_metrics, bbt);
1708 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1710 /* CALLER HOLDS READ LOCK */
1711 MeterSection* prev_m = 0;
1712 const double beats = (b < 0.0) ? 0.0 : b;
1714 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1715 MeterSection* m = 0;
1717 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1719 if (m->beat() > beats) {
1720 /* this is the meter after the one our beat is on*/
1729 const double beats_in_ms = beats - prev_m->beat();
1730 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1731 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1732 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1733 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1737 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1738 ret.beats = (uint32_t) floor (remaining_beats);
1739 ret.bars = total_bars;
1741 /* 0 0 0 to 1 1 0 - based mapping*/
1745 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1747 ret.ticks -= BBT_Time::ticks_per_beat;
1750 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1759 TempoMap::beats_to_bbt (const double& beats)
1761 Glib::Threads::RWLock::ReaderLock lm (lock);
1762 return beats_to_bbt_locked (_metrics, beats);
1766 TempoMap::pulse_to_bbt (const double& pulse)
1768 Glib::Threads::RWLock::ReaderLock lm (lock);
1769 MeterSection* prev_m = 0;
1771 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1772 MeterSection* m = 0;
1774 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1777 double const pulses_to_m = m->pulse() - prev_m->pulse();
1778 if (prev_m->pulse() + pulses_to_m > pulse) {
1779 /* this is the meter after the one our beat is on*/
1788 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1789 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1790 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1791 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1792 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1796 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1797 ret.beats = (uint32_t) floor (remaining_beats);
1798 ret.bars = total_bars;
1800 /* 0 0 0 to 1 1 0 mapping*/
1804 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1806 ret.ticks -= BBT_Time::ticks_per_beat;
1809 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1818 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1825 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1828 Glib::Threads::RWLock::ReaderLock lm (lock);
1829 const double beat = beat_at_frame_locked (_metrics, frame);
1831 bbt = beats_to_bbt_locked (_metrics, beat);
1834 /* meter section based */
1836 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1838 /* HOLD THE READER LOCK */
1840 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1845 TempoMap::frame_time (const BBT_Time& bbt)
1848 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1852 if (bbt.beats < 1) {
1853 throw std::logic_error ("beats are counted from one");
1855 Glib::Threads::RWLock::ReaderLock lm (lock);
1857 return frame_time_locked (_metrics, bbt);
1861 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1863 TempoSection* prev_t = 0;
1864 MeterSection* prev_m = 0;
1866 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1869 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1874 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1878 if (t->frame() == prev_t->frame()) {
1882 /* precision check ensures pulses and frames align.*/
1883 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1884 if (!t->locked_to_meter()) {
1892 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1893 if (prev_m && m->position_lock_style() == AudioTime) {
1894 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1895 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1896 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1898 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1912 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1914 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1916 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1917 if (!t->movable()) {
1918 t->set_active (true);
1921 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1922 t->set_active (false);
1924 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1925 t->set_active (true);
1926 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1935 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1937 TempoSection* prev_t = 0;
1938 TempoSection* section_prev = 0;
1939 framepos_t first_m_frame = 0;
1941 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1943 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1944 if (!m->movable()) {
1945 first_m_frame = m->frame();
1950 if (section->movable() && frame <= first_m_frame) {
1954 section->set_active (true);
1955 section->set_frame (frame);
1957 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1959 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1966 section_prev = prev_t;
1969 if (t->position_lock_style() == MusicTime) {
1970 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1971 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1973 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1974 if (!t->locked_to_meter()) {
1975 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1984 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1985 if (!section->locked_to_meter()) {
1986 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1990 if (section->position_lock_style() == MusicTime) {
1991 /* we're setting the frame */
1992 section->set_position_lock_style (AudioTime);
1993 recompute_tempos (imaginary);
1994 section->set_position_lock_style (MusicTime);
1996 recompute_tempos (imaginary);
1999 if (check_solved (imaginary, true)) {
2003 MetricSectionFrameSorter fcmp;
2004 imaginary.sort (fcmp);
2005 if (section->position_lock_style() == MusicTime) {
2006 /* we're setting the frame */
2007 section->set_position_lock_style (AudioTime);
2008 recompute_tempos (imaginary);
2009 section->set_position_lock_style (MusicTime);
2011 recompute_tempos (imaginary);
2014 if (check_solved (imaginary, true)) {
2022 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2024 TempoSection* prev_t = 0;
2025 TempoSection* section_prev = 0;
2027 section->set_pulse (pulse);
2029 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2031 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2035 if (!t->movable()) {
2042 section_prev = prev_t;
2045 if (t->position_lock_style() == MusicTime) {
2046 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2047 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2049 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2050 if (!t->locked_to_meter()) {
2051 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2059 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2060 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2063 if (section->position_lock_style() == AudioTime) {
2064 /* we're setting the pulse */
2065 section->set_position_lock_style (MusicTime);
2066 recompute_tempos (imaginary);
2067 section->set_position_lock_style (AudioTime);
2069 recompute_tempos (imaginary);
2072 if (check_solved (imaginary, false)) {
2076 MetricSectionSorter cmp;
2077 imaginary.sort (cmp);
2078 if (section->position_lock_style() == AudioTime) {
2079 /* we're setting the pulse */
2080 section->set_position_lock_style (MusicTime);
2081 recompute_tempos (imaginary);
2082 section->set_position_lock_style (AudioTime);
2084 recompute_tempos (imaginary);
2087 if (check_solved (imaginary, false)) {
2095 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2097 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2098 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2099 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2103 if (!section->movable()) {
2104 /* lock the first tempo to our first meter */
2105 if (!set_active_tempos (imaginary, frame)) {
2110 /* it would make sense to bail out if there is no audio-locked meter,
2111 however it may be desirable to move a music-locked meter by frame at some point.
2113 TempoSection* meter_locked_tempo = 0;
2114 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2116 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2117 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2118 meter_locked_tempo = t;
2124 MeterSection* prev_m = 0;
2126 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2128 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2130 if (prev_m && section->movable()) {
2131 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2132 if (beats + prev_m->beat() < section->beat()) {
2133 /* disallow position change if it will alter our beat
2134 we allow tempo changes to do this in recompute_meters().
2135 blocking this is an option, but i'm not convinced that
2136 this is what the user would actually want.
2137 here we set the frame/pulse corresponding to its musical position.
2140 if (meter_locked_tempo) {
2142 bool solved = false;
2143 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2144 const double new_pulse = ((section->beat() - prev_m->beat())
2145 / prev_m->note_divisor()) + prev_m->pulse();
2146 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2147 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2148 meter_locked_tempo->set_pulse (new_pulse);
2149 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2150 section->set_frame (smallest_frame);
2151 section->set_pulse (new_pulse);
2156 Metrics::const_iterator d = future_map.begin();
2157 while (d != future_map.end()) {
2168 if (meter_locked_tempo) {
2170 bool solved = false;
2172 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2173 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2174 meter_copy->set_frame (frame);
2176 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2177 section->set_frame (frame);
2178 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2179 / prev_m->note_divisor()) + prev_m->pulse());
2180 solve_map_frame (imaginary, meter_locked_tempo, frame);
2185 Metrics::const_iterator d = future_map.begin();
2186 while (d != future_map.end()) {
2197 /* not movable (first meter atm) */
2198 if (meter_locked_tempo) {
2200 bool solved = false;
2201 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2203 tempo_copy->set_frame (frame);
2204 tempo_copy->set_pulse (0.0);
2206 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2207 section->set_frame (frame);
2208 meter_locked_tempo->set_frame (frame);
2209 meter_locked_tempo->set_pulse (0.0);
2210 solve_map_frame (imaginary, meter_locked_tempo, frame);
2215 Metrics::const_iterator d = future_map.begin();
2216 while (d != future_map.end()) {
2228 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2229 section->set_beat (b_bbt);
2230 section->set_pulse (0.0);
2240 MetricSectionFrameSorter fcmp;
2241 imaginary.sort (fcmp);
2242 if (section->position_lock_style() == MusicTime) {
2243 /* we're setting the frame */
2244 section->set_position_lock_style (AudioTime);
2245 recompute_meters (imaginary);
2246 section->set_position_lock_style (MusicTime);
2248 recompute_meters (imaginary);
2255 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2257 /* disallow setting section to an existing meter's bbt */
2258 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2260 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2261 if (m != section && m->bbt().bars == when.bars) {
2267 MeterSection* prev_m = 0;
2268 MeterSection* section_prev = 0;
2270 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2272 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2273 pair<double, BBT_Time> b_bbt;
2274 double new_pulse = 0.0;
2276 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2277 section_prev = prev_m;
2278 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2279 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2280 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2282 section->set_beat (b_bbt);
2283 section->set_pulse (pulse);
2284 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2289 if (m->position_lock_style() == AudioTime) {
2290 TempoSection* meter_locked_tempo = 0;
2291 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2293 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2294 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2295 meter_locked_tempo = t;
2302 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2304 if (beats + prev_m->beat() != m->beat()) {
2305 /* tempo/ meter change caused a change in beat (bar). */
2306 b_bbt = make_pair (beats + prev_m->beat()
2307 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2308 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2309 } else if (m->movable()) {
2310 b_bbt = make_pair (m->beat(), m->bbt());
2311 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2314 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2316 if (meter_locked_tempo) {
2317 meter_locked_tempo->set_pulse (new_pulse);
2318 recompute_tempos (imaginary);
2320 m->set_beat (b_bbt);
2321 m->set_pulse (new_pulse);
2325 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2326 if (beats + prev_m->beat() != m->beat()) {
2327 /* tempo/ meter change caused a change in beat (bar). */
2328 b_bbt = make_pair (beats + prev_m->beat()
2329 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2331 b_bbt = make_pair (beats + prev_m->beat()
2334 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2335 m->set_beat (b_bbt);
2336 m->set_pulse (new_pulse);
2337 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2344 if (!section_prev) {
2346 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2347 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2348 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2350 section->set_beat (b_bbt);
2351 section->set_pulse (pulse);
2352 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2355 MetricSectionSorter cmp;
2356 imaginary.sort (cmp);
2358 if (section->position_lock_style() == AudioTime) {
2359 /* we're setting the pulse */
2360 section->set_position_lock_style (MusicTime);
2361 recompute_meters (imaginary);
2362 section->set_position_lock_style (AudioTime);
2364 recompute_meters (imaginary);
2370 /** places a copy of _metrics into copy and returns a pointer
2371 * to section's equivalent in copy.
2374 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2376 TempoSection* ret = 0;
2378 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2381 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2383 ret = new TempoSection (*t);
2384 copy.push_back (ret);
2388 TempoSection* cp = new TempoSection (*t);
2389 copy.push_back (cp);
2391 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2392 MeterSection* cp = new MeterSection (*m);
2393 copy.push_back (cp);
2401 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2403 MeterSection* ret = 0;
2405 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2408 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2409 TempoSection* cp = new TempoSection (*t);
2410 copy.push_back (cp);
2413 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2415 ret = new MeterSection (*m);
2416 copy.push_back (ret);
2419 MeterSection* cp = new MeterSection (*m);
2420 copy.push_back (cp);
2428 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2431 TempoSection* tempo_copy = 0;
2434 Glib::Threads::RWLock::ReaderLock lm (lock);
2435 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2441 const double beat = bbt_to_beats_locked (copy, bbt);
2442 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2444 Metrics::const_iterator d = copy.begin();
2445 while (d != copy.end()) {
2454 * 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,
2455 * taking any possible reordering as a consequence of this into account.
2456 * @param section - the section to be altered
2457 * @param bpm - the new Tempo
2458 * @param bbt - the bbt where the altered tempo will fall
2459 * @return returns - the position in frames where the new tempo section will lie.
2462 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2464 Glib::Threads::RWLock::ReaderLock lm (lock);
2467 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2471 const double beat = bbt_to_beats_locked (future_map, bbt);
2473 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2474 ret = tempo_copy->frame();
2476 ret = section->frame();
2479 Metrics::const_iterator d = future_map.begin();
2480 while (d != future_map.end()) {
2488 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2490 Glib::Threads::RWLock::ReaderLock lm (lock);
2493 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2495 if (solve_map_frame (future_map, tempo_copy, frame)) {
2496 ret = tempo_copy->pulse();
2498 ret = section->pulse();
2501 Metrics::const_iterator d = future_map.begin();
2502 while (d != future_map.end()) {
2510 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2514 Glib::Threads::RWLock::WriterLock lm (lock);
2515 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2516 if (solve_map_frame (future_map, tempo_copy, frame)) {
2517 solve_map_frame (_metrics, ts, frame);
2518 recompute_meters (_metrics);
2522 Metrics::const_iterator d = future_map.begin();
2523 while (d != future_map.end()) {
2528 MetricPositionChanged (); // Emit Signal
2532 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2536 Glib::Threads::RWLock::WriterLock lm (lock);
2537 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2538 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2539 solve_map_pulse (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2540 recompute_meters (_metrics);
2544 Metrics::const_iterator d = future_map.begin();
2545 while (d != future_map.end()) {
2550 MetricPositionChanged (); // Emit Signal
2554 TempoMap::gui_move_meter_frame (MeterSection* ms, const framepos_t& frame)
2558 Glib::Threads::RWLock::WriterLock lm (lock);
2559 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2560 if (solve_map_frame (future_map, copy, frame)) {
2561 solve_map_frame (_metrics, ms, frame);
2562 recompute_tempos (_metrics);
2566 Metrics::const_iterator d = future_map.begin();
2567 while (d != future_map.end()) {
2572 MetricPositionChanged (); // Emit Signal
2576 TempoMap::gui_move_meter_bbt (MeterSection* ms, const Timecode::BBT_Time& bbt)
2580 Glib::Threads::RWLock::WriterLock lm (lock);
2581 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2582 if (solve_map_bbt (future_map, copy, bbt)) {
2583 solve_map_bbt (_metrics, ms, bbt);
2584 recompute_tempos (_metrics);
2588 Metrics::const_iterator d = future_map.begin();
2589 while (d != future_map.end()) {
2594 MetricPositionChanged (); // Emit Signal
2598 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2601 bool can_solve = false;
2603 Glib::Threads::RWLock::WriterLock lm (lock);
2604 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2605 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2606 recompute_tempos (future_map);
2608 if (check_solved (future_map, true)) {
2609 ts->set_beats_per_minute (bpm.beats_per_minute());
2610 recompute_map (_metrics);
2615 Metrics::const_iterator d = future_map.begin();
2616 while (d != future_map.end()) {
2621 MetricPositionChanged (); // Emit Signal
2627 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2630 TempoSection* ts = 0;
2632 if (frame <= first_meter().frame()) {
2636 if (ms->position_lock_style() == AudioTime) {
2637 /* disabled for now due to faked tempo locked to meter pulse */
2642 Glib::Threads::RWLock::WriterLock lm (lock);
2643 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2647 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2648 TempoSection* prev_to_prev_t = 0;
2649 const frameoffset_t fr_off = frame - ms->frame();
2650 double new_bpm = 0.0;
2652 if (prev_t && prev_t->pulse() > 0.0) {
2653 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2656 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2657 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2659 double contribution = 0.0;
2660 frameoffset_t frame_contribution = 0;
2661 frameoffset_t prev_t_frame_contribution = fr_off;
2663 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2664 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2665 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2666 frame_contribution = contribution * (double) fr_off;
2667 prev_t_frame_contribution = fr_off - frame_contribution;
2670 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2672 if (prev_t->position_lock_style() == MusicTime) {
2673 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2674 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2675 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2678 /* prev to prev is irrelevant */
2679 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2680 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2682 if (frame_pulse != prev_t->pulse()) {
2683 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2685 new_bpm = prev_t->beats_per_minute();
2690 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2691 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2692 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2694 /* prev_to_prev_t is irrelevant */
2696 if (frame != prev_t->frame()) {
2697 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2699 new_bpm = prev_t->beats_per_minute();
2703 } else if (prev_t->c_func() < 0.0) {
2704 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2705 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2707 /* prev_to_prev_t is irrelevant */
2708 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2711 } else if (prev_t->c_func() > 0.0) {
2712 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2713 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2715 /* prev_to_prev_t is irrelevant */
2716 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2720 /* limits - a bit clunky, but meh */
2721 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2722 if (diff > -1.0 && diff < 1.0) {
2723 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2724 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2727 prev_t->set_beats_per_minute (new_bpm);
2728 recompute_tempos (future_map);
2729 recompute_meters (future_map);
2731 if (check_solved (future_map, true)) {
2733 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2734 prev_t->set_beats_per_minute (new_bpm);
2735 recompute_tempos (_metrics);
2737 if (ms->position_lock_style() == AudioTime) {
2738 ms->set_frame (frame);
2741 recompute_meters (_metrics);
2745 Metrics::const_iterator d = future_map.begin();
2746 while (d != future_map.end()) {
2751 MetricPositionChanged (); // Emit Signal
2755 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2758 Ts (future prev_t) Tnext
2761 |----------|----------
2768 Glib::Threads::RWLock::WriterLock lm (lock);
2774 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2775 TempoSection* prev_to_prev_t = 0;
2776 const frameoffset_t fr_off = end_frame - frame;
2778 if (prev_t && prev_t->pulse() > 0.0) {
2779 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2782 TempoSection* next_t = 0;
2783 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2784 TempoSection* t = 0;
2785 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2786 if (t->frame() > ts->frame()) {
2793 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2794 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2796 double contribution = 0.0;
2797 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2799 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2800 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2803 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2804 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2807 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2809 if (prev_t->position_lock_style() == MusicTime) {
2810 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2812 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2813 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2816 /* prev to prev is irrelevant */
2818 if (start_pulse != prev_t->pulse()) {
2819 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2821 new_bpm = prev_t->beats_per_minute();
2826 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2827 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2828 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2830 /* prev_to_prev_t is irrelevant */
2832 if (end_frame != prev_t->frame()) {
2833 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2835 new_bpm = prev_t->beats_per_minute();
2843 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2845 if (prev_to_prev_t) {
2847 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2848 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2851 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2852 pulse_ratio = (start_pulse / end_pulse);
2854 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2857 prev_t->set_beats_per_minute (new_bpm);
2858 recompute_tempos (future_map);
2859 recompute_meters (future_map);
2861 if (check_solved (future_map, true)) {
2862 ts->set_beats_per_minute (new_bpm);
2863 recompute_tempos (_metrics);
2864 recompute_meters (_metrics);
2868 Metrics::const_iterator d = future_map.begin();
2869 while (d != future_map.end()) {
2874 MetricPositionChanged (); // Emit Signal
2878 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2880 Glib::Threads::RWLock::ReaderLock lm (lock);
2882 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2883 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2884 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2886 return frame_at_beat_locked (_metrics, total_beats);
2890 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2892 return round_to_type (fr, dir, Bar);
2896 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2898 return round_to_type (fr, dir, Beat);
2902 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2904 Glib::Threads::RWLock::ReaderLock lm (lock);
2905 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2906 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2907 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2909 ticks -= beats * BBT_Time::ticks_per_beat;
2912 /* round to next (or same iff dir == RoundUpMaybe) */
2914 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2916 if (mod == 0 && dir == RoundUpMaybe) {
2917 /* right on the subdivision, which is fine, so do nothing */
2919 } else if (mod == 0) {
2920 /* right on the subdivision, so the difference is just the subdivision ticks */
2921 ticks += ticks_one_subdivisions_worth;
2924 /* not on subdivision, compute distance to next subdivision */
2926 ticks += ticks_one_subdivisions_worth - mod;
2929 if (ticks >= BBT_Time::ticks_per_beat) {
2930 ticks -= BBT_Time::ticks_per_beat;
2932 } else if (dir < 0) {
2934 /* round to previous (or same iff dir == RoundDownMaybe) */
2936 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2938 if (difference == 0 && dir == RoundDownAlways) {
2939 /* right on the subdivision, but force-rounding down,
2940 so the difference is just the subdivision ticks */
2941 difference = ticks_one_subdivisions_worth;
2944 if (ticks < difference) {
2945 ticks = BBT_Time::ticks_per_beat - ticks;
2947 ticks -= difference;
2951 /* round to nearest */
2954 /* compute the distance to the previous and next subdivision */
2956 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2958 /* closer to the next subdivision, so shift forward */
2960 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2962 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2964 if (ticks > BBT_Time::ticks_per_beat) {
2966 ticks -= BBT_Time::ticks_per_beat;
2967 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2970 } else if (rem > 0) {
2972 /* closer to previous subdivision, so shift backward */
2976 /* can't go backwards past zero, so ... */
2979 /* step back to previous beat */
2981 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2982 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2984 ticks = lrint (ticks - rem);
2985 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2988 /* on the subdivision, do nothing */
2992 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2998 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
3000 if (sub_num == -1) {
3005 } else if (dir < 0) {
3009 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
3010 if ((double) when.beats > bpb / 2.0) {
3019 } else if (sub_num == 0) {
3020 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
3021 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
3023 while ((double) when.beats > bpb) {
3025 when.beats -= (uint32_t) floor (bpb);
3033 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
3036 /* round to next (or same iff dir == RoundUpMaybe) */
3038 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
3040 if (mod == 0 && dir == RoundUpMaybe) {
3041 /* right on the subdivision, which is fine, so do nothing */
3043 } else if (mod == 0) {
3044 /* right on the subdivision, so the difference is just the subdivision ticks */
3045 when.ticks += ticks_one_subdivisions_worth;
3048 /* not on subdivision, compute distance to next subdivision */
3050 when.ticks += ticks_one_subdivisions_worth - mod;
3053 if (when.ticks >= BBT_Time::ticks_per_beat) {
3054 when.ticks -= BBT_Time::ticks_per_beat;
3057 } else if (dir < 0) {
3058 /* round to previous (or same iff dir == RoundDownMaybe) */
3060 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
3062 if (difference == 0 && dir == RoundDownAlways) {
3063 /* right on the subdivision, but force-rounding down,
3064 so the difference is just the subdivision ticks */
3065 difference = ticks_one_subdivisions_worth;
3068 if (when.ticks < difference) {
3069 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
3071 when.ticks -= difference;
3075 /* round to nearest */ double rem;
3076 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
3077 /* closer to the next subdivision, so shift forward */
3079 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
3081 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
3083 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
3086 } else if (rem > 0) {
3087 /* closer to previous subdivision, so shift backward */
3089 if (rem > when.ticks) {
3090 if (when.beats == 0) {
3091 /* can't go backwards past zero, so ... */
3093 /* step back to previous beat */
3095 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
3097 when.ticks = when.ticks - rem;
3104 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3106 Glib::Threads::RWLock::ReaderLock lm (lock);
3108 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3109 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3114 /* find bar previous to 'frame' */
3117 return frame_time_locked (_metrics, bbt);
3119 } else if (dir > 0) {
3120 /* find bar following 'frame' */
3124 return frame_time_locked (_metrics, bbt);
3126 /* true rounding: find nearest bar */
3127 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3130 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3132 framepos_t next_ft = frame_time_locked (_metrics, bbt);
3134 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3145 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3146 } else if (dir > 0) {
3147 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3149 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3158 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3159 framepos_t lower, framepos_t upper)
3161 Glib::Threads::RWLock::ReaderLock lm (lock);
3162 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3164 /* although the map handles negative beats, bbt doesn't. */
3168 while (pos < upper) {
3169 pos = frame_at_beat_locked (_metrics, cnt);
3170 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3171 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3172 const BBT_Time bbt = beats_to_bbt (cnt);
3173 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3179 TempoMap::tempo_section_at (framepos_t frame) const
3181 Glib::Threads::RWLock::ReaderLock lm (lock);
3182 return tempo_section_at_locked (_metrics, frame);
3186 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3188 Metrics::const_iterator i;
3189 TempoSection* prev = 0;
3191 for (i = metrics.begin(); i != metrics.end(); ++i) {
3194 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3198 if (prev && t->frame() > frame) {
3208 abort(); /*NOTREACHED*/
3215 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3217 TempoSection* prev_t = 0;
3218 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3220 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3222 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3223 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3234 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3236 TempoSection* prev_t = 0;
3238 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3240 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3241 if (prev_t && t->pulse() > pulse) {
3251 /* don't use this to calculate length (the tempo is only correct for this frame).
3252 do that stuff based on the beat_at_frame and frame_at_beat api
3255 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3257 Glib::Threads::RWLock::ReaderLock lm (lock);
3259 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3260 const TempoSection* ts_after = 0;
3261 Metrics::const_iterator i;
3263 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3266 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3270 if ((*i)->frame() > frame) {
3278 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3280 /* must be treated as constant tempo */
3281 return ts_at->frames_per_beat (_frame_rate);
3285 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3287 TempoSection* prev_t = 0;
3289 Metrics::const_iterator i;
3291 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3293 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3297 if ((prev_t) && t->frame() > frame) {
3298 /* t is the section past frame */
3299 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3300 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3307 const double ret = prev_t->beats_per_minute();
3308 const Tempo ret_tempo (ret, prev_t->note_type ());
3314 TempoMap::tempo_at (const framepos_t& frame) const
3316 Glib::Threads::RWLock::ReaderLock lm (lock);
3317 return tempo_at_locked (_metrics, frame);
3321 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3323 Metrics::const_iterator i;
3324 MeterSection* prev = 0;
3326 for (i = metrics.begin(); i != metrics.end(); ++i) {
3329 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3331 if (prev && (*i)->frame() > frame) {
3341 abort(); /*NOTREACHED*/
3349 TempoMap::meter_section_at (framepos_t frame) const
3351 Glib::Threads::RWLock::ReaderLock lm (lock);
3352 return meter_section_at_locked (_metrics, frame);
3356 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3358 MeterSection* prev_m = 0;
3360 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3362 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3363 if (prev_m && m->beat() > beat) {
3374 TempoMap::meter_section_at_beat (double beat) const
3376 Glib::Threads::RWLock::ReaderLock lm (lock);
3377 return meter_section_at_beat_locked (_metrics, beat);
3381 TempoMap::meter_at (framepos_t frame) const
3383 TempoMetric m (metric_at (frame));
3388 TempoMap::fix_legacy_session ()
3390 MeterSection* prev_m = 0;
3391 TempoSection* prev_t = 0;
3393 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3397 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3398 if (!m->movable()) {
3399 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3403 m->set_position_lock_style (AudioTime);
3408 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3409 + (m->bbt().beats - 1)
3410 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3412 m->set_beat (start);
3413 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3414 + (m->bbt().beats - 1)
3415 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3416 m->set_pulse (start_beat / prev_m->note_divisor());
3419 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3425 if (!t->movable()) {
3428 t->set_position_lock_style (AudioTime);
3434 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3435 + (t->legacy_bbt().beats - 1)
3436 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3438 t->set_pulse (beat / prev_m->note_divisor());
3440 /* really shouldn't happen but.. */
3441 t->set_pulse (beat / 4.0);
3450 TempoMap::get_state ()
3452 Metrics::const_iterator i;
3453 XMLNode *root = new XMLNode ("TempoMap");
3456 Glib::Threads::RWLock::ReaderLock lm (lock);
3457 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3458 root->add_child_nocopy ((*i)->get_state());
3466 TempoMap::set_state (const XMLNode& node, int /*version*/)
3469 Glib::Threads::RWLock::WriterLock lm (lock);
3472 XMLNodeConstIterator niter;
3473 Metrics old_metrics (_metrics);
3476 nlist = node.children();
3478 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3479 XMLNode* child = *niter;
3481 if (child->name() == TempoSection::xml_state_node_name) {
3484 TempoSection* ts = new TempoSection (*child);
3485 _metrics.push_back (ts);
3488 catch (failed_constructor& err){
3489 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3490 _metrics = old_metrics;
3494 } else if (child->name() == MeterSection::xml_state_node_name) {
3497 MeterSection* ms = new MeterSection (*child);
3498 _metrics.push_back (ms);
3501 catch (failed_constructor& err) {
3502 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3503 _metrics = old_metrics;
3509 if (niter == nlist.end()) {
3510 MetricSectionSorter cmp;
3511 _metrics.sort (cmp);
3514 /* check for legacy sessions where bbt was the base musical unit for tempo */
3515 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3517 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3518 if (t->legacy_bbt().bars != 0) {
3519 fix_legacy_session();
3526 /* check for multiple tempo/meters at the same location, which
3527 ardour2 somehow allowed.
3530 Metrics::iterator prev = _metrics.end();
3531 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3532 if (prev != _metrics.end()) {
3534 MeterSection* prev_m;
3536 TempoSection* prev_t;
3537 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3538 if (prev_m->pulse() == ms->pulse()) {
3539 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3540 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3543 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3544 if (prev_t->pulse() == ts->pulse()) {
3545 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3546 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3554 recompute_map (_metrics);
3557 PropertyChanged (PropertyChange ());
3563 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3565 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3566 const MeterSection* m;
3567 const TempoSection* t;
3568 const TempoSection* prev_t = 0;
3570 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3572 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3573 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3574 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3575 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3577 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3578 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;
3581 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3582 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3583 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3586 o << "------" << std::endl;
3590 TempoMap::n_tempos() const
3592 Glib::Threads::RWLock::ReaderLock lm (lock);
3595 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3596 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3605 TempoMap::n_meters() const
3607 Glib::Threads::RWLock::ReaderLock lm (lock);
3610 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3611 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3620 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3623 Glib::Threads::RWLock::WriterLock lm (lock);
3624 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3625 if ((*i)->frame() >= where && (*i)->movable ()) {
3626 (*i)->set_frame ((*i)->frame() + amount);
3630 /* now reset the BBT time of all metrics, based on their new
3631 * audio time. This is the only place where we do this reverse
3635 Metrics::iterator i;
3636 const MeterSection* meter;
3637 const TempoSection* tempo;
3641 meter = &first_meter ();
3642 tempo = &first_tempo ();
3647 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3650 MetricSection* prev = 0;
3652 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3655 //TempoMetric metric (*meter, *tempo);
3656 MeterSection* ms = const_cast<MeterSection*>(meter);
3657 TempoSection* ts = const_cast<TempoSection*>(tempo);
3660 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3664 ts->set_pulse (t->pulse());
3666 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3667 ts->set_pulse (m->pulse());
3669 ts->set_frame (prev->frame());
3673 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3674 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3675 ms->set_beat (start);
3676 ms->set_pulse (m->pulse());
3678 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3682 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3683 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3684 ms->set_beat (start);
3685 ms->set_pulse (t->pulse());
3687 ms->set_frame (prev->frame());
3691 // metric will be at frames=0 bbt=1|1|0 by default
3692 // which is correct for our purpose
3695 // cerr << bbt << endl;
3697 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3701 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3703 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3704 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3705 bbt_time (m->frame(), bbt);
3707 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3713 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3714 /* round up to next beat */
3720 if (bbt.beats != 1) {
3721 /* round up to next bar */
3726 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3727 m->set_beat (start);
3728 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3730 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3732 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3733 abort(); /*NOTREACHED*/
3739 recompute_map (_metrics);
3743 PropertyChanged (PropertyChange ());
3746 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3750 std::list<MetricSection*> metric_kill_list;
3752 TempoSection* last_tempo = NULL;
3753 MeterSection* last_meter = NULL;
3754 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3755 bool meter_after = false; // is there a meter marker likewise?
3757 Glib::Threads::RWLock::WriterLock lm (lock);
3758 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3759 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3760 metric_kill_list.push_back(*i);
3761 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3764 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3768 else if ((*i)->frame() >= where) {
3769 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3770 (*i)->set_frame ((*i)->frame() - amount);
3771 if ((*i)->frame() == where) {
3772 // marker was immediately after end of range
3773 tempo_after = dynamic_cast<TempoSection*> (*i);
3774 meter_after = dynamic_cast<MeterSection*> (*i);
3780 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3781 if (last_tempo && !tempo_after) {
3782 metric_kill_list.remove(last_tempo);
3783 last_tempo->set_frame(where);
3786 if (last_meter && !meter_after) {
3787 metric_kill_list.remove(last_meter);
3788 last_meter->set_frame(where);
3792 //remove all the remaining metrics
3793 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3794 _metrics.remove(*i);
3799 recompute_map (_metrics);
3802 PropertyChanged (PropertyChange ());
3806 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3807 * pos can be -ve, if required.
3810 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3812 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3815 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3817 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3819 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3822 /** Add the BBT interval op to pos and return the result */
3824 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3826 Glib::Threads::RWLock::ReaderLock lm (lock);
3828 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3829 pos_bbt.ticks += op.ticks;
3830 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3832 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3834 pos_bbt.beats += op.beats;
3835 /* the meter in effect will start on the bar */
3836 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();
3837 while (pos_bbt.beats >= divisions_per_bar + 1) {
3839 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3840 pos_bbt.beats -= divisions_per_bar;
3842 pos_bbt.bars += op.bars;
3844 return frame_time_locked (_metrics, pos_bbt);
3847 /** Count the number of beats that are equivalent to distance when going forward,
3851 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3853 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3857 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3863 operator<< (std::ostream& o, const Meter& m) {
3864 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3868 operator<< (std::ostream& o, const Tempo& t) {
3869 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3873 operator<< (std::ostream& o, const MetricSection& section) {
3875 o << "MetricSection @ " << section.frame() << ' ';
3877 const TempoSection* ts;
3878 const MeterSection* ms;
3880 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3881 o << *((const Tempo*) ts);
3882 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3883 o << *((const Meter*) ms);