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 pulse p 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& p, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((p - 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 is the rate of the musical pulse.
590 Meters divide the pulses into measures and beats.
592 TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
593 and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
594 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
596 MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
598 Both tempos and meters have a pulse position and a frame position.
599 Meters also have a beat position, which is always 0.0 for the first meter.
600 TempoSections and MeterSections may be locked to either audio or music (position lock style).
601 The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
603 The first tempo and first meter are special. they must move together, and must be locked to audio.
604 Audio locked tempos which lie before the first meter are made inactive.
605 They will be re-activated if the first meter is again placed before them.
607 With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
608 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
609 sb->beat() - meter->beat() / meter->note_divisor().
610 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
611 two meters is of course
612 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
614 Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
615 Beat to frame conversion of course requires the use of meter and tempo.
617 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
618 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
620 Recomputing the map is the process where the 'missing' position
621 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
622 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
623 We then use this tempo map (really just the tempos) to find the pulse or frame position of each meter (again depending on lock style).
625 Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
626 and querying its appropriate meter/tempo.
628 It is important to keep the _metrics in an order that makes sense.
629 Because ramped MusicTime and AudioTime tempos can interact with each other,
630 reordering is frequent. Care must be taken to keep _metrics in a solved state.
631 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
633 struct MetricSectionSorter {
634 bool operator() (const MetricSection* a, const MetricSection* b) {
635 return a->pulse() < b->pulse();
639 struct MetricSectionFrameSorter {
640 bool operator() (const MetricSection* a, const MetricSection* b) {
641 return a->frame() < b->frame();
645 TempoMap::TempoMap (framecnt_t fr)
648 BBT_Time start (1, 1, 0);
650 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
651 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
653 t->set_movable (false);
654 m->set_movable (false);
656 /* note: frame time is correct (zero) for both of these */
658 _metrics.push_back (t);
659 _metrics.push_back (m);
663 TempoMap::~TempoMap ()
665 Metrics::const_iterator d = _metrics.begin();
666 while (d != _metrics.end()) {
674 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
676 bool removed = false;
679 Glib::Threads::RWLock::WriterLock lm (lock);
680 if ((removed = remove_tempo_locked (tempo))) {
681 if (complete_operation) {
682 recompute_map (_metrics);
687 if (removed && complete_operation) {
688 PropertyChanged (PropertyChange ());
693 TempoMap::remove_tempo_locked (const TempoSection& tempo)
697 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
698 if (dynamic_cast<TempoSection*> (*i) != 0) {
699 if (tempo.frame() == (*i)->frame()) {
700 if ((*i)->movable()) {
713 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
715 bool removed = false;
718 Glib::Threads::RWLock::WriterLock lm (lock);
719 if ((removed = remove_meter_locked (tempo))) {
720 if (complete_operation) {
721 recompute_map (_metrics);
726 if (removed && complete_operation) {
727 PropertyChanged (PropertyChange ());
732 TempoMap::remove_meter_locked (const MeterSection& meter)
736 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
738 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
739 if (meter.frame() == (*i)->frame()) {
740 if (t->locked_to_meter()) {
749 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
750 if (dynamic_cast<MeterSection*> (*i) != 0) {
751 if (meter.frame() == (*i)->frame()) {
752 if ((*i)->movable()) {
765 TempoMap::do_insert (MetricSection* section)
767 bool need_add = true;
768 /* we only allow new meters to be inserted on beat 1 of an existing
772 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
774 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
776 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
777 corrected.second.beats = 1;
778 corrected.second.ticks = 0;
779 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
780 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
781 m->bbt(), corrected.second) << endmsg;
782 //m->set_pulse (corrected);
786 /* Look for any existing MetricSection that is of the same type and
787 in the same bar as the new one, and remove it before adding
788 the new one. Note that this means that if we find a matching,
789 existing section, we can break out of the loop since we're
790 guaranteed that there is only one such match.
793 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
795 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
796 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
797 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
798 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
800 if (tempo && insert_tempo) {
803 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
804 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
806 if (!tempo->movable()) {
808 /* can't (re)move this section, so overwrite
809 * its data content (but not its properties as
813 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
814 (*i)->set_position_lock_style (AudioTime);
816 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
817 t->set_type (insert_tempo->type());
827 } else if (meter && insert_meter) {
831 bool const ipm = insert_meter->position_lock_style() == MusicTime;
833 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
835 if (!meter->movable()) {
837 /* can't (re)move this section, so overwrite
838 * its data content (but not its properties as
842 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
843 (*i)->set_position_lock_style (AudioTime);
853 /* non-matching types, so we don't care */
857 /* Add the given MetricSection, if we didn't just reset an existing
862 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
863 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
866 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
867 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
870 bool const ipm = insert_meter->position_lock_style() == MusicTime;
871 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
876 } else if (insert_tempo) {
877 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
878 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
881 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
882 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
889 _metrics.insert (i, section);
890 //dump (_metrics, std::cout);
895 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
897 TempoSection* ts = 0;
899 Glib::Threads::RWLock::WriterLock lm (lock);
900 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
904 PropertyChanged (PropertyChange ());
910 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
912 const bool locked_to_meter = ts.locked_to_meter();
915 Glib::Threads::RWLock::WriterLock lm (lock);
916 TempoSection& first (first_tempo());
917 if (ts.frame() != first.frame()) {
918 remove_tempo_locked (ts);
919 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
921 first.set_type (type);
922 first.set_pulse (0.0);
923 first.set_frame (frame);
924 first.set_position_lock_style (AudioTime);
926 /* cannot move the first tempo section */
927 *static_cast<Tempo*>(&first) = tempo;
928 recompute_map (_metrics);
933 PropertyChanged (PropertyChange ());
937 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
938 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
940 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
941 t->set_locked_to_meter (locked_to_meter);
946 if (pls == AudioTime) {
947 solve_map_frame (_metrics, t, t->frame());
949 solve_map_pulse (_metrics, t, t->pulse());
951 recompute_meters (_metrics);
958 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
962 Glib::Threads::RWLock::WriterLock lm (lock);
963 m = add_meter_locked (meter, beat, where, frame, pls, true);
968 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
969 dump (_metrics, std::cerr);
973 PropertyChanged (PropertyChange ());
978 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
981 Glib::Threads::RWLock::WriterLock lm (lock);
982 const double beat = beat_at_bbt_locked (_metrics, where);
985 remove_meter_locked (ms);
986 add_meter_locked (meter, beat, where, frame, pls, true);
988 MeterSection& first (first_meter());
989 TempoSection& first_t (first_tempo());
990 /* cannot move the first meter section */
991 *static_cast<Meter*>(&first) = meter;
992 first.set_position_lock_style (AudioTime);
993 first.set_pulse (0.0);
994 first.set_frame (frame);
995 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
996 first.set_beat (beat);
997 first_t.set_frame (first.frame());
998 first_t.set_pulse (0.0);
999 first_t.set_position_lock_style (AudioTime);
1000 recompute_map (_metrics);
1004 PropertyChanged (PropertyChange ());
1008 TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1010 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1011 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1013 if (pls == AudioTime) {
1014 /* add meter-locked tempo */
1015 add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1018 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1020 do_insert (new_meter);
1024 if (pls == AudioTime) {
1025 solve_map_frame (_metrics, new_meter, frame);
1027 solve_map_bbt (_metrics, new_meter, where);
1035 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1037 Tempo newtempo (beats_per_minute, note_type);
1040 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1041 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1046 Glib::Threads::RWLock::WriterLock lm (lock);
1047 *((Tempo*) t) = newtempo;
1048 recompute_map (_metrics);
1050 PropertyChanged (PropertyChange ());
1057 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1059 Tempo newtempo (beats_per_minute, note_type);
1062 TempoSection* first;
1063 Metrics::iterator i;
1065 /* find the TempoSection immediately preceding "where"
1068 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1070 if ((*i)->frame() > where) {
1076 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1089 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1099 Glib::Threads::RWLock::WriterLock lm (lock);
1100 /* cannot move the first tempo section */
1101 *((Tempo*)prev) = newtempo;
1102 recompute_map (_metrics);
1105 PropertyChanged (PropertyChange ());
1109 TempoMap::first_meter () const
1111 const MeterSection *m = 0;
1113 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1114 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1119 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1120 abort(); /*NOTREACHED*/
1125 TempoMap::first_meter ()
1127 MeterSection *m = 0;
1129 /* CALLER MUST HOLD LOCK */
1131 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1132 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1137 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1138 abort(); /*NOTREACHED*/
1143 TempoMap::first_tempo () const
1145 const TempoSection *t = 0;
1147 /* CALLER MUST HOLD LOCK */
1149 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1150 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1154 if (!t->movable()) {
1160 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1161 abort(); /*NOTREACHED*/
1166 TempoMap::first_tempo ()
1168 TempoSection *t = 0;
1170 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1171 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1175 if (!t->movable()) {
1181 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1182 abort(); /*NOTREACHED*/
1186 TempoMap::recompute_tempos (Metrics& metrics)
1188 TempoSection* prev_t = 0;
1190 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1193 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1197 if (!t->movable()) {
1205 if (t->position_lock_style() == AudioTime) {
1206 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1207 if (!t->locked_to_meter()) {
1208 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1212 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1213 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1220 prev_t->set_c_func (0.0);
1223 /* tempos must be positioned correctly.
1224 the current approach is to use a meter's bbt time as its base position unit.
1225 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1226 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1229 TempoMap::recompute_meters (Metrics& metrics)
1231 MeterSection* meter = 0;
1232 MeterSection* prev_m = 0;
1234 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1235 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1236 if (meter->position_lock_style() == AudioTime) {
1238 pair<double, BBT_Time> b_bbt;
1239 TempoSection* meter_locked_tempo = 0;
1240 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1242 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1243 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1244 meter_locked_tempo = t;
1251 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1252 if (beats + prev_m->beat() != meter->beat()) {
1253 /* reordering caused a bbt change */
1254 b_bbt = make_pair (beats + prev_m->beat()
1255 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1256 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1258 } else if (meter->movable()) {
1259 b_bbt = make_pair (meter->beat(), meter->bbt());
1260 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1263 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1265 if (meter_locked_tempo) {
1266 meter_locked_tempo->set_pulse (pulse);
1268 meter->set_beat (b_bbt);
1269 meter->set_pulse (pulse);
1274 pair<double, BBT_Time> b_bbt;
1276 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1277 if (beats + prev_m->beat() != meter->beat()) {
1278 /* reordering caused a bbt change */
1279 b_bbt = make_pair (beats + prev_m->beat()
1280 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1282 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1284 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1286 /* shouldn't happen - the first is audio-locked */
1287 pulse = pulse_at_beat_locked (metrics, meter->beat());
1288 b_bbt = make_pair (meter->beat(), meter->bbt());
1291 meter->set_beat (b_bbt);
1292 meter->set_pulse (pulse);
1293 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1302 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1304 /* CALLER MUST HOLD WRITE LOCK */
1308 /* we will actually stop once we hit
1315 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1318 /* silly call from Session::process() during startup
1323 recompute_tempos (metrics);
1324 recompute_meters (metrics);
1328 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1330 Glib::Threads::RWLock::ReaderLock lm (lock);
1331 TempoMetric m (first_meter(), first_tempo());
1333 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1334 at something, because we insert the default tempo and meter during
1335 TempoMap construction.
1337 now see if we can find better candidates.
1340 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1342 if ((*i)->frame() > frame) {
1356 /* XX meters only */
1358 TempoMap::metric_at (BBT_Time bbt) const
1360 Glib::Threads::RWLock::ReaderLock lm (lock);
1361 TempoMetric m (first_meter(), first_tempo());
1363 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1364 at something, because we insert the default tempo and meter during
1365 TempoMap construction.
1367 now see if we can find better candidates.
1370 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1372 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1373 BBT_Time section_start (mw->bbt());
1375 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1387 TempoMap::beat_at_frame (const framecnt_t& frame) const
1389 Glib::Threads::RWLock::ReaderLock lm (lock);
1390 return beat_at_frame_locked (_metrics, frame);
1393 /* meter / tempo section based */
1395 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1397 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1398 MeterSection* prev_m = 0;
1399 MeterSection* next_m = 0;
1401 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1403 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1404 if (prev_m && m->frame() > frame) {
1411 if (frame < prev_m->frame()) {
1414 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1416 /* audio locked meters fake their beat */
1417 if (next_m && next_m->beat() < beat) {
1418 return next_m->beat();
1425 TempoMap::frame_at_beat (const double& beat) const
1427 Glib::Threads::RWLock::ReaderLock lm (lock);
1428 return frame_at_beat_locked (_metrics, beat);
1431 /* meter section based */
1433 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1435 const TempoSection* prev_t = &tempo_section_at_beat_locked (metrics, beat);
1436 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1438 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1442 TempoMap::tempo_at_frame (const framepos_t& frame) const
1444 Glib::Threads::RWLock::ReaderLock lm (lock);
1445 return tempo_at_frame_locked (_metrics, frame);
1449 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1451 TempoSection* prev_t = 0;
1453 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1455 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1459 if ((prev_t) && t->frame() > frame) {
1460 /* t is the section past frame */
1461 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1462 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1469 const double ret = prev_t->beats_per_minute();
1470 const Tempo ret_tempo (ret, prev_t->note_type ());
1475 /** returns the frame at which the supplied tempo occurs, or
1476 * the frame of the last tempo section (search exhausted)
1477 * only the position of the first occurence will be returned
1481 TempoMap::frame_at_tempo (const Tempo& tempo) const
1483 Glib::Threads::RWLock::ReaderLock lm (lock);
1484 return frame_at_tempo_locked (_metrics, tempo);
1489 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1491 TempoSection* prev_t = 0;
1492 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1494 Metrics::const_iterator i;
1496 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1498 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1504 const double t_ppm = t->beats_per_minute() / t->note_type();
1506 if (t_ppm == tempo_ppm) {
1511 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1513 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1514 const framepos_t ret_frame = prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1522 return prev_t->frame();
1525 /** more precise than doing tempo_at_frame (frame_at_beat (b)),
1526 * as there is no intermediate frame rounding.
1529 TempoMap::tempo_at_beat (const double& beat) const
1531 Glib::Threads::RWLock::ReaderLock lm (lock);
1532 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1533 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1534 const double note_type = prev_t->note_type();
1536 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1540 TempoMap::pulse_at_beat (const double& beat) const
1542 Glib::Threads::RWLock::ReaderLock lm (lock);
1543 return pulse_at_beat_locked (_metrics, beat);
1547 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1549 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1551 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1555 TempoMap::beat_at_pulse (const double& pulse) const
1557 Glib::Threads::RWLock::ReaderLock lm (lock);
1558 return beat_at_pulse_locked (_metrics, pulse);
1562 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1564 MeterSection* prev_m = 0;
1566 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1568 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1569 if (prev_m && m->pulse() > pulse) {
1570 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1578 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1583 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1585 Glib::Threads::RWLock::ReaderLock lm (lock);
1586 return pulse_at_frame_locked (_metrics, frame);
1589 /* tempo section based */
1591 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1593 /* HOLD (at least) THE READER LOCK */
1594 TempoSection* prev_t = 0;
1596 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1598 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1602 if (prev_t && t->frame() > frame) {
1603 /*the previous ts is the one containing the frame */
1604 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1611 /* treated as constant for this ts */
1612 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1614 return pulses_in_section + prev_t->pulse();
1618 TempoMap::frame_at_pulse (const double& pulse) const
1620 Glib::Threads::RWLock::ReaderLock lm (lock);
1621 return frame_at_pulse_locked (_metrics, pulse);
1624 /* tempo section based */
1626 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1628 /* HOLD THE READER LOCK */
1630 const TempoSection* prev_t = 0;
1632 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1635 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1639 if (prev_t && t->pulse() > pulse) {
1640 return prev_t->frame_at_pulse (pulse, _frame_rate);
1646 /* must be treated as constant, irrespective of _type */
1647 double const pulses_in_section = pulse - prev_t->pulse();
1648 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1650 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1656 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1658 Glib::Threads::RWLock::ReaderLock lm (lock);
1659 return beat_at_bbt_locked (_metrics, bbt);
1664 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1666 /* CALLER HOLDS READ LOCK */
1668 MeterSection* prev_m = 0;
1670 /* because audio-locked meters have 'fake' integral beats,
1671 there is no pulse offset here.
1673 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1675 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1677 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1678 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1686 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1687 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1688 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1694 TempoMap::bbt_at_beat (const double& beats)
1696 Glib::Threads::RWLock::ReaderLock lm (lock);
1697 return bbt_at_beat_locked (_metrics, beats);
1701 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1703 /* CALLER HOLDS READ LOCK */
1704 MeterSection* prev_m = 0;
1705 const double beats = max (0.0, b);
1707 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1708 MeterSection* m = 0;
1710 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1712 if (m->beat() > beats) {
1713 /* this is the meter after the one our beat is on*/
1722 const double beats_in_ms = beats - prev_m->beat();
1723 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1724 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1725 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1726 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1730 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1731 ret.beats = (uint32_t) floor (remaining_beats);
1732 ret.bars = total_bars;
1734 /* 0 0 0 to 1 1 0 - based mapping*/
1738 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1740 ret.ticks -= BBT_Time::ticks_per_beat;
1743 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1752 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1754 Glib::Threads::RWLock::ReaderLock lm (lock);
1756 return pulse_at_bbt_locked (_metrics, bbt);
1760 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1762 /* CALLER HOLDS READ LOCK */
1764 MeterSection* prev_m = 0;
1766 /* because audio-locked meters have 'fake' integral beats,
1767 there is no pulse offset here.
1769 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1771 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1773 if (m->bbt().bars > bbt.bars) {
1781 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1782 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1783 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1789 TempoMap::bbt_at_pulse (const double& pulse)
1791 Glib::Threads::RWLock::ReaderLock lm (lock);
1793 return bbt_at_pulse_locked (_metrics, pulse);
1797 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1799 MeterSection* prev_m = 0;
1801 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1802 MeterSection* m = 0;
1804 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1807 double const pulses_to_m = m->pulse() - prev_m->pulse();
1808 if (prev_m->pulse() + pulses_to_m > pulse) {
1809 /* this is the meter after the one our beat is on*/
1818 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1819 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1820 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1821 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1822 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1826 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1827 ret.beats = (uint32_t) floor (remaining_beats);
1828 ret.bars = total_bars;
1830 /* 0 0 0 to 1 1 0 mapping*/
1834 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1836 ret.ticks -= BBT_Time::ticks_per_beat;
1839 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1848 TempoMap::bbt_at_frame (framepos_t frame)
1855 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1858 Glib::Threads::RWLock::ReaderLock lm (lock);
1860 return bbt_at_frame_locked (_metrics, frame);
1864 TempoMap::bbt_at_frame_rt (framepos_t frame)
1866 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1869 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1872 return bbt_at_frame_locked (_metrics, frame);
1876 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1883 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1886 const double beat = beat_at_frame_locked (metrics, frame);
1888 return bbt_at_beat_locked (metrics, beat);
1892 TempoMap::frame_at_bbt (const BBT_Time& bbt)
1895 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1899 if (bbt.beats < 1) {
1900 throw std::logic_error ("beats are counted from one");
1902 Glib::Threads::RWLock::ReaderLock lm (lock);
1904 return frame_at_bbt_locked (_metrics, bbt);
1907 /* meter & tempo section based */
1909 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
1911 /* HOLD THE READER LOCK */
1913 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
1918 TempoMap::check_solved (const Metrics& metrics) const
1920 TempoSection* prev_t = 0;
1921 MeterSection* prev_m = 0;
1923 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1926 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1931 /* check ordering */
1932 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
1936 /* precision check ensures tempo and frames align.*/
1937 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
1938 if (!t->locked_to_meter()) {
1943 /* gradient limit - who knows what it should be?
1944 things are also ok (if a little chaotic) without this
1946 if (fabs (prev_t->c_func()) > 1000.0) {
1947 //std::cout << "c : " << prev_t->c_func() << std::endl;
1954 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1955 if (prev_m && m->position_lock_style() == AudioTime) {
1956 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (metrics, m->frame() - 1));
1957 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1958 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1960 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1974 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1976 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1978 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1979 if (!t->movable()) {
1980 t->set_active (true);
1983 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1984 t->set_active (false);
1986 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1987 t->set_active (true);
1988 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1997 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1999 TempoSection* prev_t = 0;
2000 TempoSection* section_prev = 0;
2001 framepos_t first_m_frame = 0;
2003 /* can't move a tempo before the first meter */
2004 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2006 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2007 if (!m->movable()) {
2008 first_m_frame = m->frame();
2013 if (section->movable() && frame <= first_m_frame) {
2017 section->set_active (true);
2018 section->set_frame (frame);
2020 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2022 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2029 section_prev = prev_t;
2030 if (t->locked_to_meter()) {
2035 if (t->position_lock_style() == MusicTime) {
2036 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2037 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2039 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2040 if (!t->locked_to_meter()) {
2041 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2050 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2051 if (!section->locked_to_meter()) {
2052 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2057 recompute_tempos (imaginary);
2059 if (check_solved (imaginary)) {
2062 dunp (imaginary, std::cout);
2066 MetricSectionFrameSorter fcmp;
2067 imaginary.sort (fcmp);
2069 recompute_tempos (imaginary);
2071 if (check_solved (imaginary)) {
2079 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2081 TempoSection* prev_t = 0;
2082 TempoSection* section_prev = 0;
2084 section->set_pulse (pulse);
2086 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2088 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2092 if (!t->movable()) {
2099 section_prev = prev_t;
2102 if (t->position_lock_style() == MusicTime) {
2103 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2104 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2106 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2107 if (!t->locked_to_meter()) {
2108 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2117 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2118 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2122 recompute_tempos (imaginary);
2124 if (check_solved (imaginary)) {
2127 dunp (imaginary, std::cout);
2131 MetricSectionSorter cmp;
2132 imaginary.sort (cmp);
2134 recompute_tempos (imaginary);
2136 * XX need a restriction here, but only for this case,
2137 * as audio locked tempos don't interact in the same way.
2139 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2141 * |50 bpm |250 bpm |60 bpm
2142 * drag 250 to the pulse after 60->
2143 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2145 if (check_solved (imaginary)) {
2153 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2155 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2156 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2157 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2161 if (!section->movable()) {
2162 /* lock the first tempo to our first meter */
2163 if (!set_active_tempos (imaginary, frame)) {
2168 TempoSection* meter_locked_tempo = 0;
2170 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2172 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2173 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2174 meter_locked_tempo = t;
2180 if (!meter_locked_tempo) {
2184 MeterSection* prev_m = 0;
2186 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2187 bool solved = false;
2189 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2191 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2193 if (prev_m && section->movable()) {
2194 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2195 if (beats + prev_m->beat() < section->beat()) {
2196 /* set the frame/pulse corresponding to its musical position,
2197 * as an earlier time than this has been requested.
2199 const double new_pulse = ((section->beat() - prev_m->beat())
2200 / prev_m->note_divisor()) + prev_m->pulse();
2202 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2204 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2205 meter_locked_tempo->set_pulse (new_pulse);
2206 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2207 section->set_frame (smallest_frame);
2208 section->set_pulse (new_pulse);
2213 Metrics::const_iterator d = future_map.begin();
2214 while (d != future_map.end()) {
2223 /* all is ok. set section's locked tempo if allowed.
2224 possibly disallowed if there is an adjacent audio-locked tempo.
2225 XX this check could possibly go. its never actually happened here.
2227 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2228 meter_copy->set_frame (frame);
2230 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2231 section->set_frame (frame);
2232 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2233 / prev_m->note_divisor()) + prev_m->pulse());
2234 solve_map_frame (imaginary, meter_locked_tempo, frame);
2239 Metrics::const_iterator d = future_map.begin();
2240 while (d != future_map.end()) {
2250 /* not movable (first meter atm) */
2252 tempo_copy->set_frame (frame);
2253 tempo_copy->set_pulse (0.0);
2255 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2256 section->set_frame (frame);
2257 meter_locked_tempo->set_frame (frame);
2258 meter_locked_tempo->set_pulse (0.0);
2259 solve_map_frame (imaginary, meter_locked_tempo, frame);
2264 Metrics::const_iterator d = future_map.begin();
2265 while (d != future_map.end()) {
2274 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2275 section->set_beat (b_bbt);
2276 section->set_pulse (0.0);
2286 MetricSectionFrameSorter fcmp;
2287 imaginary.sort (fcmp);
2289 recompute_meters (imaginary);
2295 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2297 /* disallow setting section to an existing meter's bbt */
2298 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2300 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2301 if (m != section && m->bbt().bars == when.bars) {
2307 MeterSection* prev_m = 0;
2308 MeterSection* section_prev = 0;
2310 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2312 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2313 pair<double, BBT_Time> b_bbt;
2314 double new_pulse = 0.0;
2316 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2317 section_prev = prev_m;
2318 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2319 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2320 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2322 section->set_beat (b_bbt);
2323 section->set_pulse (pulse);
2324 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2329 if (m->position_lock_style() == AudioTime) {
2330 TempoSection* meter_locked_tempo = 0;
2332 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2334 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2335 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2336 meter_locked_tempo = t;
2342 if (!meter_locked_tempo) {
2347 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2349 if (beats + prev_m->beat() != m->beat()) {
2350 /* tempo/ meter change caused a change in beat (bar). */
2351 b_bbt = make_pair (beats + prev_m->beat()
2352 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2353 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2354 } else if (m->movable()) {
2355 b_bbt = make_pair (m->beat(), m->bbt());
2356 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2359 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2362 meter_locked_tempo->set_pulse (new_pulse);
2363 m->set_beat (b_bbt);
2364 m->set_pulse (new_pulse);
2368 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2369 if (beats + prev_m->beat() != m->beat()) {
2370 /* tempo/ meter change caused a change in beat (bar). */
2371 b_bbt = make_pair (beats + prev_m->beat()
2372 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2374 b_bbt = make_pair (beats + prev_m->beat()
2377 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2378 m->set_beat (b_bbt);
2379 m->set_pulse (new_pulse);
2380 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2387 if (!section_prev) {
2389 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2390 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2391 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2393 section->set_beat (b_bbt);
2394 section->set_pulse (pulse);
2395 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2398 MetricSectionSorter cmp;
2399 imaginary.sort (cmp);
2401 recompute_meters (imaginary);
2406 /** places a copy of _metrics into copy and returns a pointer
2407 * to section's equivalent in copy.
2410 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2412 TempoSection* ret = 0;
2414 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2417 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2419 ret = new TempoSection (*t);
2420 copy.push_back (ret);
2424 TempoSection* cp = new TempoSection (*t);
2425 copy.push_back (cp);
2427 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2428 MeterSection* cp = new MeterSection (*m);
2429 copy.push_back (cp);
2437 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2439 MeterSection* ret = 0;
2441 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2444 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2445 TempoSection* cp = new TempoSection (*t);
2446 copy.push_back (cp);
2449 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2451 ret = new MeterSection (*m);
2452 copy.push_back (ret);
2455 MeterSection* cp = new MeterSection (*m);
2456 copy.push_back (cp);
2463 /** answers the question "is this a valid beat position for this tempo section?".
2464 * it returns true if the tempo section can be moved to the requested bbt position,
2465 * leaving the tempo map in a solved state.
2466 * @param section the tempo section to be moved
2467 * @param bbt the requested new position for the tempo section
2468 * @return true if the tempo section can be moved to the position, otherwise false.
2471 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2474 TempoSection* tempo_copy = 0;
2477 Glib::Threads::RWLock::ReaderLock lm (lock);
2478 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2484 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2486 Metrics::const_iterator d = copy.begin();
2487 while (d != copy.end()) {
2496 * This is for a gui that needs to know the pulse or frame of a tempo section if it were to be moved to some bbt time,
2497 * taking any possible reordering as a consequence of this into account.
2498 * @param section - the section to be altered
2499 * @param bbt - the bbt where the altered tempo will fall
2500 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2502 pair<double, framepos_t>
2503 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2506 pair<double, framepos_t> ret = make_pair (0.0, 0);
2508 Glib::Threads::RWLock::ReaderLock lm (lock);
2510 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2512 const double beat = beat_at_bbt_locked (future_map, bbt);
2514 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2515 ret.first = tempo_copy->pulse();
2516 ret.second = tempo_copy->frame();
2518 ret.first = section->pulse();
2519 ret.second = section->frame();
2522 Metrics::const_iterator d = future_map.begin();
2523 while (d != future_map.end()) {
2531 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
2534 bool was_musical = ts->position_lock_style() == MusicTime;
2536 if (sub_num == 0 && was_musical) {
2537 /* if we're not snapping to music,
2538 AudioTime and MusicTime may be treated identically.
2540 ts->set_position_lock_style (AudioTime);
2543 if (ts->position_lock_style() == MusicTime) {
2545 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
2546 Glib::Threads::RWLock::WriterLock lm (lock);
2547 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2548 double beat = beat_at_frame_locked (future_map, frame);
2551 beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
2552 } else if (sub_num == 1) {
2554 beat = floor (beat + 0.5);
2557 double pulse = pulse_at_beat_locked (future_map, beat);
2559 if (sub_num == -1) {
2561 pulse = floor (pulse + 0.5);
2564 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2565 solve_map_pulse (_metrics, ts, pulse);
2566 recompute_meters (_metrics);
2573 Glib::Threads::RWLock::WriterLock lm (lock);
2574 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2575 if (solve_map_frame (future_map, tempo_copy, frame)) {
2576 solve_map_frame (_metrics, ts, frame);
2577 recompute_meters (_metrics);
2582 if (sub_num == 0 && was_musical) {
2583 ts->set_position_lock_style (MusicTime);
2586 Metrics::const_iterator d = future_map.begin();
2587 while (d != future_map.end()) {
2592 MetricPositionChanged (); // Emit Signal
2596 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2600 if (ms->position_lock_style() == AudioTime) {
2603 Glib::Threads::RWLock::WriterLock lm (lock);
2604 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2606 if (solve_map_frame (future_map, copy, frame)) {
2607 solve_map_frame (_metrics, ms, frame);
2608 recompute_tempos (_metrics);
2613 Glib::Threads::RWLock::WriterLock lm (lock);
2614 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2616 const double beat = beat_at_frame_locked (_metrics, frame);
2617 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2619 if (solve_map_bbt (future_map, copy, bbt)) {
2620 solve_map_bbt (_metrics, ms, bbt);
2621 recompute_tempos (_metrics);
2626 Metrics::const_iterator d = future_map.begin();
2627 while (d != future_map.end()) {
2632 MetricPositionChanged (); // Emit Signal
2636 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2639 bool can_solve = false;
2641 Glib::Threads::RWLock::WriterLock lm (lock);
2642 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2643 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2644 recompute_tempos (future_map);
2646 if (check_solved (future_map)) {
2647 ts->set_beats_per_minute (bpm.beats_per_minute());
2648 recompute_map (_metrics);
2653 Metrics::const_iterator d = future_map.begin();
2654 while (d != future_map.end()) {
2659 MetricPositionChanged (); // Emit Signal
2665 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2668 Ts (future prev_t) Tnext
2671 |----------|----------
2678 Glib::Threads::RWLock::WriterLock lm (lock);
2684 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2685 TempoSection* prev_to_prev_t = 0;
2686 const frameoffset_t fr_off = end_frame - frame;
2688 if (prev_t && prev_t->pulse() > 0.0) {
2689 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2692 TempoSection* next_t = 0;
2693 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2694 TempoSection* t = 0;
2695 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2696 if (t->frame() > ts->frame()) {
2702 /* minimum allowed measurement distance in frames */
2703 const framepos_t min_dframe = 2;
2705 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2706 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2708 double contribution = 0.0;
2710 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2711 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2714 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2716 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2717 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2721 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2723 if (prev_t->position_lock_style() == MusicTime) {
2724 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2725 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
2727 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2728 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2730 new_bpm = prev_t->beats_per_minute();
2733 /* prev to prev is irrelevant */
2735 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
2736 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2738 new_bpm = prev_t->beats_per_minute();
2743 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2744 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
2746 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2747 / (double) ((end_frame) - prev_to_prev_t->frame()));
2749 new_bpm = prev_t->beats_per_minute();
2752 /* prev_to_prev_t is irrelevant */
2754 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
2755 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2757 new_bpm = prev_t->beats_per_minute();
2763 double frame_ratio = 1.0;
2764 double pulse_ratio = 1.0;
2765 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2767 if (prev_to_prev_t) {
2768 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
2769 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2771 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
2772 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2775 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
2776 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2778 pulse_ratio = (start_pulse / end_pulse);
2780 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2783 /* don't clamp and proceed here.
2784 testing has revealed that this can go negative,
2785 which is an entirely different thing to just being too low.
2787 if (new_bpm < 0.5) {
2790 new_bpm = min (new_bpm, (double) 1000.0);
2791 prev_t->set_beats_per_minute (new_bpm);
2792 recompute_tempos (future_map);
2793 recompute_meters (future_map);
2795 if (check_solved (future_map)) {
2796 ts->set_beats_per_minute (new_bpm);
2797 recompute_tempos (_metrics);
2798 recompute_meters (_metrics);
2802 Metrics::const_iterator d = future_map.begin();
2803 while (d != future_map.end()) {
2808 MetricPositionChanged (); // Emit Signal
2812 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2814 Glib::Threads::RWLock::ReaderLock lm (lock);
2816 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2817 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2818 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2820 return frame_at_beat_locked (_metrics, total_beats);
2824 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2826 return round_to_type (fr, dir, Bar);
2830 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2832 return round_to_type (fr, dir, Beat);
2836 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2838 Glib::Threads::RWLock::ReaderLock lm (lock);
2839 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2840 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2841 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2843 ticks -= beats * BBT_Time::ticks_per_beat;
2846 /* round to next (or same iff dir == RoundUpMaybe) */
2848 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2850 if (mod == 0 && dir == RoundUpMaybe) {
2851 /* right on the subdivision, which is fine, so do nothing */
2853 } else if (mod == 0) {
2854 /* right on the subdivision, so the difference is just the subdivision ticks */
2855 ticks += ticks_one_subdivisions_worth;
2858 /* not on subdivision, compute distance to next subdivision */
2860 ticks += ticks_one_subdivisions_worth - mod;
2863 if (ticks >= BBT_Time::ticks_per_beat) {
2864 ticks -= BBT_Time::ticks_per_beat;
2866 } else if (dir < 0) {
2868 /* round to previous (or same iff dir == RoundDownMaybe) */
2870 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2872 if (difference == 0 && dir == RoundDownAlways) {
2873 /* right on the subdivision, but force-rounding down,
2874 so the difference is just the subdivision ticks */
2875 difference = ticks_one_subdivisions_worth;
2878 if (ticks < difference) {
2879 ticks = BBT_Time::ticks_per_beat - ticks;
2881 ticks -= difference;
2885 /* round to nearest */
2888 /* compute the distance to the previous and next subdivision */
2890 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2892 /* closer to the next subdivision, so shift forward */
2894 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2896 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2898 if (ticks > BBT_Time::ticks_per_beat) {
2900 ticks -= BBT_Time::ticks_per_beat;
2901 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2904 } else if (rem > 0) {
2906 /* closer to previous subdivision, so shift backward */
2910 /* can't go backwards past zero, so ... */
2913 /* step back to previous beat */
2915 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2916 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2918 ticks = lrint (ticks - rem);
2919 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2922 /* on the subdivision, do nothing */
2926 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2932 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2934 Glib::Threads::RWLock::ReaderLock lm (lock);
2936 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2937 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
2942 /* find bar previous to 'frame' */
2945 return frame_at_bbt_locked (_metrics, bbt);
2947 } else if (dir > 0) {
2948 /* find bar following 'frame' */
2952 return frame_at_bbt_locked (_metrics, bbt);
2954 /* true rounding: find nearest bar */
2955 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
2958 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
2960 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
2962 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2973 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2974 } else if (dir > 0) {
2975 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2977 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2986 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2987 framepos_t lower, framepos_t upper)
2989 Glib::Threads::RWLock::ReaderLock lm (lock);
2990 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2992 /* although the map handles negative beats, bbt doesn't. */
2997 if (frame_at_beat_locked (_metrics, cnt) >= upper) {
3001 while (pos < upper) {
3002 pos = frame_at_beat_locked (_metrics, cnt);
3003 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
3004 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
3005 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3006 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3012 TempoMap::tempo_section_at_frame (framepos_t frame) const
3014 Glib::Threads::RWLock::ReaderLock lm (lock);
3015 return tempo_section_at_frame_locked (_metrics, frame);
3019 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3021 Metrics::const_iterator i;
3022 TempoSection* prev = 0;
3024 for (i = metrics.begin(); i != metrics.end(); ++i) {
3027 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3031 if (prev && t->frame() > frame) {
3041 abort(); /*NOTREACHED*/
3048 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3050 TempoSection* prev_t = 0;
3051 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3053 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3055 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3056 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3066 /* don't use this to calculate length (the tempo is only correct for this frame).
3067 do that stuff based on the beat_at_frame and frame_at_beat api
3070 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3072 Glib::Threads::RWLock::ReaderLock lm (lock);
3074 const TempoSection* ts_at = &tempo_section_at_frame_locked (_metrics, frame);
3075 const TempoSection* ts_after = 0;
3076 Metrics::const_iterator i;
3078 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3081 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3085 if ((*i)->frame() > frame) {
3093 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
3095 /* must be treated as constant tempo */
3096 return ts_at->frames_per_beat (_frame_rate);
3100 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3102 Metrics::const_iterator i;
3103 MeterSection* prev = 0;
3105 for (i = metrics.begin(); i != metrics.end(); ++i) {
3108 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3110 if (prev && (*i)->frame() > frame) {
3120 abort(); /*NOTREACHED*/
3128 TempoMap::meter_section_at_frame (framepos_t frame) const
3130 Glib::Threads::RWLock::ReaderLock lm (lock);
3131 return meter_section_at_frame_locked (_metrics, frame);
3135 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3137 MeterSection* prev_m = 0;
3139 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3141 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3142 if (prev_m && m->beat() > beat) {
3153 TempoMap::meter_section_at_beat (double beat) const
3155 Glib::Threads::RWLock::ReaderLock lm (lock);
3156 return meter_section_at_beat_locked (_metrics, beat);
3160 TempoMap::meter_at_frame (framepos_t frame) const
3162 TempoMetric m (metric_at (frame));
3167 TempoMap::fix_legacy_session ()
3169 MeterSection* prev_m = 0;
3170 TempoSection* prev_t = 0;
3172 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3176 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3177 if (!m->movable()) {
3178 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3182 m->set_position_lock_style (AudioTime);
3187 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3188 + (m->bbt().beats - 1)
3189 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3191 m->set_beat (start);
3192 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3193 + (m->bbt().beats - 1)
3194 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3195 m->set_pulse (start_beat / prev_m->note_divisor());
3198 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3204 if (!t->movable()) {
3207 t->set_position_lock_style (AudioTime);
3213 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3214 + (t->legacy_bbt().beats - 1)
3215 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3217 t->set_pulse (beat / prev_m->note_divisor());
3219 /* really shouldn't happen but.. */
3220 t->set_pulse (beat / 4.0);
3229 TempoMap::get_state ()
3231 Metrics::const_iterator i;
3232 XMLNode *root = new XMLNode ("TempoMap");
3235 Glib::Threads::RWLock::ReaderLock lm (lock);
3236 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3237 root->add_child_nocopy ((*i)->get_state());
3245 TempoMap::set_state (const XMLNode& node, int /*version*/)
3248 Glib::Threads::RWLock::WriterLock lm (lock);
3251 XMLNodeConstIterator niter;
3252 Metrics old_metrics (_metrics);
3255 nlist = node.children();
3257 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3258 XMLNode* child = *niter;
3260 if (child->name() == TempoSection::xml_state_node_name) {
3263 TempoSection* ts = new TempoSection (*child);
3264 _metrics.push_back (ts);
3267 catch (failed_constructor& err){
3268 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3269 _metrics = old_metrics;
3273 } else if (child->name() == MeterSection::xml_state_node_name) {
3276 MeterSection* ms = new MeterSection (*child);
3277 _metrics.push_back (ms);
3280 catch (failed_constructor& err) {
3281 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3282 _metrics = old_metrics;
3288 if (niter == nlist.end()) {
3289 MetricSectionSorter cmp;
3290 _metrics.sort (cmp);
3293 /* check for legacy sessions where bbt was the base musical unit for tempo */
3294 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3296 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3297 if (t->legacy_bbt().bars != 0) {
3298 fix_legacy_session();
3305 /* check for multiple tempo/meters at the same location, which
3306 ardour2 somehow allowed.
3309 Metrics::iterator prev = _metrics.end();
3310 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3311 if (prev != _metrics.end()) {
3313 MeterSection* prev_m;
3315 TempoSection* prev_t;
3316 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3317 if (prev_m->pulse() == ms->pulse()) {
3318 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3319 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3322 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3323 if (prev_t->pulse() == ts->pulse()) {
3324 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3325 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3333 recompute_map (_metrics);
3336 PropertyChanged (PropertyChange ());
3342 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3344 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3345 const MeterSection* m;
3346 const TempoSection* t;
3347 const TempoSection* prev_t = 0;
3349 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3351 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3352 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3353 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3354 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3356 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3357 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;
3360 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3361 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3362 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3365 o << "------" << std::endl;
3369 TempoMap::n_tempos() const
3371 Glib::Threads::RWLock::ReaderLock lm (lock);
3374 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3375 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3384 TempoMap::n_meters() const
3386 Glib::Threads::RWLock::ReaderLock lm (lock);
3389 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3390 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3399 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3402 Glib::Threads::RWLock::WriterLock lm (lock);
3403 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3404 if ((*i)->frame() >= where && (*i)->movable ()) {
3405 (*i)->set_frame ((*i)->frame() + amount);
3409 /* now reset the BBT time of all metrics, based on their new
3410 * audio time. This is the only place where we do this reverse
3414 Metrics::iterator i;
3415 const MeterSection* meter;
3416 const TempoSection* tempo;
3420 meter = &first_meter ();
3421 tempo = &first_tempo ();
3426 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3429 MetricSection* prev = 0;
3431 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3434 //TempoMetric metric (*meter, *tempo);
3435 MeterSection* ms = const_cast<MeterSection*>(meter);
3436 TempoSection* ts = const_cast<TempoSection*>(tempo);
3439 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3443 ts->set_pulse (t->pulse());
3445 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3446 ts->set_pulse (m->pulse());
3448 ts->set_frame (prev->frame());
3452 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3453 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3454 ms->set_beat (start);
3455 ms->set_pulse (m->pulse());
3457 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3461 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3462 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3463 ms->set_beat (start);
3464 ms->set_pulse (t->pulse());
3466 ms->set_frame (prev->frame());
3470 // metric will be at frames=0 bbt=1|1|0 by default
3471 // which is correct for our purpose
3474 // cerr << bbt << endl;
3476 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3480 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3482 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3483 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3484 bbt = bbt_at_frame_locked (_metrics, m->frame());
3486 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3492 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3493 /* round up to next beat */
3499 if (bbt.beats != 1) {
3500 /* round up to next bar */
3505 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3506 m->set_beat (start);
3507 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3509 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3511 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3512 abort(); /*NOTREACHED*/
3518 recompute_map (_metrics);
3522 PropertyChanged (PropertyChange ());
3525 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3529 std::list<MetricSection*> metric_kill_list;
3531 TempoSection* last_tempo = NULL;
3532 MeterSection* last_meter = NULL;
3533 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3534 bool meter_after = false; // is there a meter marker likewise?
3536 Glib::Threads::RWLock::WriterLock lm (lock);
3537 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3538 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3539 metric_kill_list.push_back(*i);
3540 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3543 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3547 else if ((*i)->frame() >= where) {
3548 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3549 (*i)->set_frame ((*i)->frame() - amount);
3550 if ((*i)->frame() == where) {
3551 // marker was immediately after end of range
3552 tempo_after = dynamic_cast<TempoSection*> (*i);
3553 meter_after = dynamic_cast<MeterSection*> (*i);
3559 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3560 if (last_tempo && !tempo_after) {
3561 metric_kill_list.remove(last_tempo);
3562 last_tempo->set_frame(where);
3565 if (last_meter && !meter_after) {
3566 metric_kill_list.remove(last_meter);
3567 last_meter->set_frame(where);
3571 //remove all the remaining metrics
3572 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3573 _metrics.remove(*i);
3578 recompute_map (_metrics);
3581 PropertyChanged (PropertyChange ());
3585 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3586 * pos can be -ve, if required.
3589 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3591 Glib::Threads::RWLock::ReaderLock lm (lock);
3593 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) + beats.to_double());
3596 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3598 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3600 Glib::Threads::RWLock::ReaderLock lm (lock);
3602 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
3605 /** Add the BBT interval op to pos and return the result */
3607 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3609 Glib::Threads::RWLock::ReaderLock lm (lock);
3611 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3612 pos_bbt.ticks += op.ticks;
3613 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3615 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3617 pos_bbt.beats += op.beats;
3618 /* the meter in effect will start on the bar */
3619 double divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3620 while (pos_bbt.beats >= divisions_per_bar + 1) {
3622 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3623 pos_bbt.beats -= divisions_per_bar;
3625 pos_bbt.bars += op.bars;
3627 return frame_at_bbt_locked (_metrics, pos_bbt);
3630 /** Count the number of beats that are equivalent to distance when going forward,
3634 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3636 Glib::Threads::RWLock::ReaderLock lm (lock);
3638 return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
3642 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3648 operator<< (std::ostream& o, const Meter& m) {
3649 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3653 operator<< (std::ostream& o, const Tempo& t) {
3654 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3658 operator<< (std::ostream& o, const MetricSection& section) {
3660 o << "MetricSection @ " << section.frame() << ' ';
3662 const TempoSection* ts;
3663 const MeterSection* ms;
3665 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3666 o << *((const Tempo*) ts);
3667 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3668 o << *((const Meter*) ms);