2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
76 : MetricSection (0.0, 0, MusicTime)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 XMLProperty const * prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses per minute occurs in this section.
222 beat b is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_pulse_tempo (ppm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0, 0, MusicTime), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
463 framepos_t frame = 0;
464 pair<double, BBT_Time> start;
466 if ((prop = node.property ("start")) != 0) {
467 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
471 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
473 /* legacy session - start used to be in bbt*/
474 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
479 if ((prop = node.property ("pulse")) != 0) {
480 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
481 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
486 if ((prop = node.property ("beat")) != 0) {
487 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
488 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
494 if ((prop = node.property ("bbt")) == 0) {
495 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
496 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
500 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
501 throw failed_constructor();
507 if ((prop = node.property ("frame")) != 0) {
508 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
509 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
515 /* beats-per-bar is old; divisions-per-bar is new */
517 if ((prop = node.property ("divisions-per-bar")) == 0) {
518 if ((prop = node.property ("beats-per-bar")) == 0) {
519 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
520 throw failed_constructor();
523 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
524 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
525 throw failed_constructor();
528 if ((prop = node.property ("note-type")) == 0) {
529 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
530 throw failed_constructor();
532 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
533 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
534 throw failed_constructor();
537 if ((prop = node.property ("movable")) == 0) {
538 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
539 throw failed_constructor();
542 set_movable (string_is_affirmative (prop->value()));
544 if ((prop = node.property ("lock-style")) == 0) {
545 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
547 set_position_lock_style (MusicTime);
549 set_position_lock_style (AudioTime);
552 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
557 MeterSection::get_state() const
559 XMLNode *root = new XMLNode (xml_state_node_name);
563 snprintf (buf, sizeof (buf), "%lf", pulse());
564 root->add_property ("pulse", buf);
565 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
569 root->add_property ("bbt", buf);
570 snprintf (buf, sizeof (buf), "%lf", beat());
571 root->add_property ("beat", buf);
572 snprintf (buf, sizeof (buf), "%f", _note_type);
573 root->add_property ("note-type", buf);
574 snprintf (buf, sizeof (buf), "%li", frame());
575 root->add_property ("frame", buf);
576 root->add_property ("lock-style", enum_2_string (position_lock_style()));
577 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
578 root->add_property ("divisions-per-bar", buf);
579 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
580 root->add_property ("movable", buf);
585 /***********************************************************************/
589 Tempo is the rate of the musical pulse.
590 Meters divide the pulses into measures and beats.
592 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
593 It should rather be thought of as tempo note divisions per minute.
595 TempoSections, which are nice to think of in whole pulses per minute,
596 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
597 and beats (via note_divisor) are used to form a tempo map.
598 TempoSections and MeterSections may be locked to either audio or music (position lock style).
599 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
600 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
602 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
604 With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
605 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
606 sb->beat() - meter->beat() / meter->note_divisor().
607 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
608 two meters is of course
609 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
611 Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
612 Beat to frame conversion of course requires the use of meter and tempo.
614 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
615 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
617 The first tempo and first meter are special. they must move together, and must be locked to audio.
618 Audio locked tempos which lie before the first meter are made inactive.
619 They will be re-activated if the first meter is again placed before them.
621 Both tempos and meters have a pulse position and a frame position.
622 Meters also have a beat position, which is always 0.0 for the first meter.
624 A tempo locked to music is locked to musical pulses.
625 A meter locked to music is locked to beats.
627 Recomputing the tempo map is the process where the 'missing' position
628 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
630 It is important to keep the _metrics in an order that makes sense.
631 Because ramped MusicTime and AudioTime tempos can interact with each other,
632 reordering is frequent. Care must be taken to keep _metrics in a solved state.
633 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
635 struct MetricSectionSorter {
636 bool operator() (const MetricSection* a, const MetricSection* b) {
637 return a->pulse() < b->pulse();
641 struct MetricSectionFrameSorter {
642 bool operator() (const MetricSection* a, const MetricSection* b) {
643 return a->frame() < b->frame();
647 TempoMap::TempoMap (framecnt_t fr)
650 BBT_Time start (1, 1, 0);
652 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant, AudioTime);
653 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
655 t->set_movable (false);
656 m->set_movable (false);
658 /* note: frame time is correct (zero) for both of these */
660 _metrics.push_back (t);
661 _metrics.push_back (m);
665 TempoMap::~TempoMap ()
667 Metrics::const_iterator d = _metrics.begin();
668 while (d != _metrics.end()) {
676 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
678 bool removed = false;
681 Glib::Threads::RWLock::WriterLock lm (lock);
682 if ((removed = remove_tempo_locked (tempo))) {
683 if (complete_operation) {
684 recompute_map (_metrics);
689 if (removed && complete_operation) {
690 PropertyChanged (PropertyChange ());
695 TempoMap::remove_tempo_locked (const TempoSection& tempo)
699 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
700 if (dynamic_cast<TempoSection*> (*i) != 0) {
701 if (tempo.frame() == (*i)->frame()) {
702 if ((*i)->movable()) {
715 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
717 bool removed = false;
720 Glib::Threads::RWLock::WriterLock lm (lock);
721 if ((removed = remove_meter_locked (tempo))) {
722 if (complete_operation) {
723 recompute_map (_metrics);
728 if (removed && complete_operation) {
729 PropertyChanged (PropertyChange ());
734 TempoMap::remove_meter_locked (const MeterSection& meter)
738 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
740 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
741 if (meter.frame() == (*i)->frame()) {
742 if (t->locked_to_meter()) {
751 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
752 if (dynamic_cast<MeterSection*> (*i) != 0) {
753 if (meter.frame() == (*i)->frame()) {
754 if ((*i)->movable()) {
767 TempoMap::do_insert (MetricSection* section)
769 bool need_add = true;
770 /* we only allow new meters to be inserted on beat 1 of an existing
774 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
776 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
778 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
779 corrected.second.beats = 1;
780 corrected.second.ticks = 0;
781 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
782 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
783 m->bbt(), corrected.second) << endmsg;
784 //m->set_pulse (corrected);
788 /* Look for any existing MetricSection that is of the same type and
789 in the same bar as the new one, and remove it before adding
790 the new one. Note that this means that if we find a matching,
791 existing section, we can break out of the loop since we're
792 guaranteed that there is only one such match.
795 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
797 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
798 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
799 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
800 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
802 if (tempo && insert_tempo) {
805 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
806 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
808 if (!tempo->movable()) {
810 /* can't (re)move this section, so overwrite
811 * its data content (but not its properties as
815 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
816 (*i)->set_position_lock_style (AudioTime);
818 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
819 t->set_type (insert_tempo->type());
829 } else if (meter && insert_meter) {
833 bool const ipm = insert_meter->position_lock_style() == MusicTime;
835 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
837 if (!meter->movable()) {
839 /* can't (re)move this section, so overwrite
840 * its data content (but not its properties as
844 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
845 (*i)->set_position_lock_style (AudioTime);
855 /* non-matching types, so we don't care */
859 /* Add the given MetricSection, if we didn't just reset an existing
864 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
865 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
868 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
869 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
872 bool const ipm = insert_meter->position_lock_style() == MusicTime;
873 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
878 } else if (insert_tempo) {
879 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
880 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
883 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
884 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
891 _metrics.insert (i, section);
892 //dump (_metrics, std::cout);
897 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
899 TempoSection* ts = 0;
901 Glib::Threads::RWLock::WriterLock lm (lock);
902 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
906 PropertyChanged (PropertyChange ());
912 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
914 const bool locked_to_meter = ts.locked_to_meter();
917 Glib::Threads::RWLock::WriterLock lm (lock);
918 TempoSection& first (first_tempo());
919 if (ts.frame() != first.frame()) {
920 remove_tempo_locked (ts);
921 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
923 first.set_type (type);
924 first.set_pulse (0.0);
925 first.set_frame (frame);
926 first.set_position_lock_style (AudioTime);
928 /* cannot move the first tempo section */
929 *static_cast<Tempo*>(&first) = tempo;
930 recompute_map (_metrics);
935 PropertyChanged (PropertyChange ());
939 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
940 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
942 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
943 t->set_locked_to_meter (locked_to_meter);
948 if (pls == AudioTime) {
949 solve_map_frame (_metrics, t, t->frame());
951 solve_map_pulse (_metrics, t, t->pulse());
953 recompute_meters (_metrics);
960 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
964 Glib::Threads::RWLock::WriterLock lm (lock);
965 m = add_meter_locked (meter, beat, where, frame, pls, true);
970 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
971 dump (_metrics, std::cerr);
975 PropertyChanged (PropertyChange ());
980 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
983 Glib::Threads::RWLock::WriterLock lm (lock);
984 const double beat = beat_at_bbt_locked (_metrics, where);
987 remove_meter_locked (ms);
988 add_meter_locked (meter, beat, where, frame, pls, true);
990 MeterSection& first (first_meter());
991 TempoSection& first_t (first_tempo());
992 /* cannot move the first meter section */
993 *static_cast<Meter*>(&first) = meter;
994 first.set_position_lock_style (AudioTime);
995 first.set_pulse (0.0);
996 first.set_frame (frame);
997 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
998 first.set_beat (beat);
999 first_t.set_frame (first.frame());
1000 first_t.set_pulse (0.0);
1001 first_t.set_position_lock_style (AudioTime);
1003 recompute_map (_metrics);
1005 PropertyChanged (PropertyChange ());
1009 TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1011 const MeterSection& prev_m = meter_section_at_locked (_metrics, frame - 1);
1012 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1014 if (pls == AudioTime) {
1015 /* add meter-locked tempo */
1016 add_tempo_locked (tempo_at_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1019 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1021 do_insert (new_meter);
1025 if (pls == AudioTime) {
1026 solve_map_frame (_metrics, new_meter, frame);
1028 solve_map_bbt (_metrics, new_meter, where);
1036 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1038 Tempo newtempo (beats_per_minute, note_type);
1041 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1042 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1047 Glib::Threads::RWLock::WriterLock lm (lock);
1048 *((Tempo*) t) = newtempo;
1049 recompute_map (_metrics);
1051 PropertyChanged (PropertyChange ());
1058 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1060 Tempo newtempo (beats_per_minute, note_type);
1063 TempoSection* first;
1064 Metrics::iterator i;
1066 /* find the TempoSection immediately preceding "where"
1069 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1071 if ((*i)->frame() > where) {
1077 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1090 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1100 Glib::Threads::RWLock::WriterLock lm (lock);
1101 /* cannot move the first tempo section */
1102 *((Tempo*)prev) = newtempo;
1103 recompute_map (_metrics);
1106 PropertyChanged (PropertyChange ());
1110 TempoMap::first_meter () const
1112 const MeterSection *m = 0;
1114 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1115 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1120 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1121 abort(); /*NOTREACHED*/
1126 TempoMap::first_meter ()
1128 MeterSection *m = 0;
1130 /* CALLER MUST HOLD LOCK */
1132 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1133 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1138 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1139 abort(); /*NOTREACHED*/
1144 TempoMap::first_tempo () const
1146 const TempoSection *t = 0;
1148 /* CALLER MUST HOLD LOCK */
1150 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1151 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1155 if (!t->movable()) {
1161 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1162 abort(); /*NOTREACHED*/
1167 TempoMap::first_tempo ()
1169 TempoSection *t = 0;
1171 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1172 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1176 if (!t->movable()) {
1182 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1183 abort(); /*NOTREACHED*/
1187 TempoMap::recompute_tempos (Metrics& metrics)
1189 TempoSection* prev_t = 0;
1191 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1194 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1198 if (!t->movable()) {
1206 if (t->position_lock_style() == AudioTime) {
1207 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1208 if (!t->locked_to_meter()) {
1209 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1213 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1214 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1221 prev_t->set_c_func (0.0);
1224 /* tempos must be positioned correctly.
1225 the current approach is to use a meter's bbt time as its base position unit.
1226 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1227 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1230 TempoMap::recompute_meters (Metrics& metrics)
1232 MeterSection* meter = 0;
1233 MeterSection* prev_m = 0;
1235 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1236 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1237 if (meter->position_lock_style() == AudioTime) {
1239 pair<double, BBT_Time> b_bbt;
1240 TempoSection* meter_locked_tempo = 0;
1241 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1243 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1244 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1245 meter_locked_tempo = t;
1252 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1253 if (beats + prev_m->beat() != meter->beat()) {
1254 /* reordering caused a bbt change */
1255 b_bbt = make_pair (beats + prev_m->beat()
1256 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1257 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1259 } else if (meter->movable()) {
1260 b_bbt = make_pair (meter->beat(), meter->bbt());
1261 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1264 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1266 if (meter_locked_tempo) {
1267 meter_locked_tempo->set_pulse (pulse);
1269 meter->set_beat (b_bbt);
1270 meter->set_pulse (pulse);
1275 pair<double, BBT_Time> new_beat;
1277 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1278 if (beats + prev_m->beat() != meter->beat()) {
1279 /* reordering caused a bbt change */
1280 new_beat = make_pair (beats + prev_m->beat()
1281 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1283 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1285 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1287 /* shouldn't happen - the first is audio-locked */
1288 pulse = pulse_at_beat_locked (metrics, meter->beat());
1289 new_beat = make_pair (meter->beat(), meter->bbt());
1292 meter->set_beat (new_beat);
1293 meter->set_pulse (pulse);
1294 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1303 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1305 /* CALLER MUST HOLD WRITE LOCK */
1309 /* we will actually stop once we hit
1316 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1319 /* silly call from Session::process() during startup
1324 recompute_tempos (metrics);
1325 recompute_meters (metrics);
1329 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1331 Glib::Threads::RWLock::ReaderLock lm (lock);
1332 TempoMetric m (first_meter(), first_tempo());
1334 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1335 at something, because we insert the default tempo and meter during
1336 TempoMap construction.
1338 now see if we can find better candidates.
1341 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1343 if ((*i)->frame() > frame) {
1357 /* XX meters only */
1359 TempoMap::metric_at (BBT_Time bbt) const
1361 Glib::Threads::RWLock::ReaderLock lm (lock);
1362 TempoMetric m (first_meter(), first_tempo());
1364 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1365 at something, because we insert the default tempo and meter during
1366 TempoMap construction.
1368 now see if we can find better candidates.
1371 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1373 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1374 BBT_Time section_start (mw->bbt());
1376 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1388 TempoMap::pulse_at_beat (const double& beat) const
1390 Glib::Threads::RWLock::ReaderLock lm (lock);
1391 return pulse_at_beat_locked (_metrics, beat);
1395 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1397 MeterSection* prev_m = 0;
1399 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1401 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1402 if (prev_m && m->beat() > beat) {
1409 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1414 TempoMap::beat_at_pulse (const double& pulse) const
1416 Glib::Threads::RWLock::ReaderLock lm (lock);
1417 return beat_at_pulse_locked (_metrics, pulse);
1421 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1423 MeterSection* prev_m = 0;
1425 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1427 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1428 if (prev_m && m->pulse() > pulse) {
1429 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1437 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1442 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1444 Glib::Threads::RWLock::ReaderLock lm (lock);
1445 return pulse_at_frame_locked (_metrics, frame);
1448 /* tempo section based */
1450 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1452 /* HOLD (at least) THE READER LOCK */
1453 TempoSection* prev_t = 0;
1455 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1457 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1461 if (prev_t && t->frame() > frame) {
1462 /*the previous ts is the one containing the frame */
1463 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1470 /* treated as constant for this ts */
1471 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1473 return pulses_in_section + prev_t->pulse();
1477 TempoMap::frame_at_pulse (const double& pulse) const
1479 Glib::Threads::RWLock::ReaderLock lm (lock);
1480 return frame_at_pulse_locked (_metrics, pulse);
1483 /* tempo section based */
1485 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1487 /* HOLD THE READER LOCK */
1489 const TempoSection* prev_t = 0;
1491 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1494 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1498 if (prev_t && t->pulse() > pulse) {
1499 return prev_t->frame_at_pulse (pulse, _frame_rate);
1505 /* must be treated as constant, irrespective of _type */
1506 double const pulses_in_section = pulse - prev_t->pulse();
1507 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1509 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1515 TempoMap::beat_at_frame (const framecnt_t& frame) const
1517 Glib::Threads::RWLock::ReaderLock lm (lock);
1518 return beat_at_frame_locked (_metrics, frame);
1521 /* meter / tempo section based */
1523 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1525 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1526 MeterSection* prev_m = 0;
1527 MeterSection* next_m = 0;
1529 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1531 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1532 if (prev_m && m->frame() > frame) {
1539 if (frame < prev_m->frame()) {
1542 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1544 if (next_m && next_m->beat() < beat) {
1545 return next_m->beat();
1552 TempoMap::frame_at_beat (const double& beat) const
1554 Glib::Threads::RWLock::ReaderLock lm (lock);
1555 return frame_at_beat_locked (_metrics, beat);
1558 /* meter section based */
1560 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1562 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1563 MeterSection* prev_m = 0;
1565 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1567 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1568 if (prev_m && m->beat() > beat) {
1575 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1579 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1581 Glib::Threads::RWLock::ReaderLock lm (lock);
1582 return beat_at_bbt_locked (_metrics, bbt);
1587 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1589 /* CALLER HOLDS READ LOCK */
1591 MeterSection* prev_m = 0;
1593 /* because audio-locked meters have 'fake' integral beats,
1594 there is no pulse offset here.
1596 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1598 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1600 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1601 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1609 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1610 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1611 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1617 TempoMap::bbt_at_beat (const double& beats)
1619 Glib::Threads::RWLock::ReaderLock lm (lock);
1620 return bbt_at_beat_locked (_metrics, beats);
1624 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1626 /* CALLER HOLDS READ LOCK */
1627 MeterSection* prev_m = 0;
1628 const double beats = max (0.0, b);
1630 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1631 MeterSection* m = 0;
1633 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1635 if (m->beat() > beats) {
1636 /* this is the meter after the one our beat is on*/
1645 const double beats_in_ms = beats - prev_m->beat();
1646 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1647 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1648 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1649 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1653 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1654 ret.beats = (uint32_t) floor (remaining_beats);
1655 ret.bars = total_bars;
1657 /* 0 0 0 to 1 1 0 - based mapping*/
1661 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1663 ret.ticks -= BBT_Time::ticks_per_beat;
1666 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1675 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1677 Glib::Threads::RWLock::ReaderLock lm (lock);
1679 return pulse_at_bbt_locked (_metrics, bbt);
1683 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1685 /* CALLER HOLDS READ LOCK */
1687 MeterSection* prev_m = 0;
1689 /* because audio-locked meters have 'fake' integral beats,
1690 there is no pulse offset here.
1692 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1694 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1696 if (m->bbt().bars > bbt.bars) {
1704 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1705 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1706 const double ret = remaining_pulses + prev_m->pulse();
1712 TempoMap::bbt_at_pulse (const double& pulse)
1714 Glib::Threads::RWLock::ReaderLock lm (lock);
1716 return bbt_at_pulse_locked (_metrics, pulse);
1720 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1722 MeterSection* prev_m = 0;
1724 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1725 MeterSection* m = 0;
1727 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1730 double const pulses_to_m = m->pulse() - prev_m->pulse();
1731 if (prev_m->pulse() + pulses_to_m > pulse) {
1732 /* this is the meter after the one our beat is on*/
1741 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1742 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1743 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1744 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1745 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1749 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1750 ret.beats = (uint32_t) floor (remaining_beats);
1751 ret.bars = total_bars;
1753 /* 0 0 0 to 1 1 0 mapping*/
1757 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1759 ret.ticks -= BBT_Time::ticks_per_beat;
1762 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1771 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1778 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1781 Glib::Threads::RWLock::ReaderLock lm (lock);
1782 const double beat = beat_at_frame_locked (_metrics, frame);
1784 bbt = bbt_at_beat_locked (_metrics, beat);
1788 TempoMap::frame_time (const BBT_Time& bbt)
1791 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1795 if (bbt.beats < 1) {
1796 throw std::logic_error ("beats are counted from one");
1798 Glib::Threads::RWLock::ReaderLock lm (lock);
1800 return frame_time_locked (_metrics, bbt);
1803 /* meter section based */
1805 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1807 /* HOLD THE READER LOCK */
1809 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
1814 TempoMap::check_solved (const Metrics& metrics) const
1816 TempoSection* prev_t = 0;
1817 MeterSection* prev_m = 0;
1819 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1822 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1827 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
1831 /* precision check ensures pulses and frames align.*/
1832 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1833 if (!t->locked_to_meter()) {
1841 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1842 if (prev_m && m->position_lock_style() == AudioTime) {
1843 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1844 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1845 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1847 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1861 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1863 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1865 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1866 if (!t->movable()) {
1867 t->set_active (true);
1870 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1871 t->set_active (false);
1873 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1874 t->set_active (true);
1875 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1884 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1886 TempoSection* prev_t = 0;
1887 TempoSection* section_prev = 0;
1888 framepos_t first_m_frame = 0;
1890 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1892 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1893 if (!m->movable()) {
1894 first_m_frame = m->frame();
1899 if (section->movable() && frame <= first_m_frame) {
1903 section->set_active (true);
1904 section->set_frame (frame);
1906 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1908 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1915 section_prev = prev_t;
1918 if (t->position_lock_style() == MusicTime) {
1919 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1920 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1922 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1923 if (!t->locked_to_meter()) {
1924 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1933 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1934 if (!section->locked_to_meter()) {
1935 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1939 recompute_tempos (imaginary);
1941 if (check_solved (imaginary)) {
1945 MetricSectionFrameSorter fcmp;
1946 imaginary.sort (fcmp);
1948 recompute_tempos (imaginary);
1950 if (check_solved (imaginary)) {
1958 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
1960 TempoSection* prev_t = 0;
1961 TempoSection* section_prev = 0;
1963 section->set_pulse (pulse);
1965 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1967 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1971 if (!t->movable()) {
1978 section_prev = prev_t;
1981 if (t->position_lock_style() == MusicTime) {
1982 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1983 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1985 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1986 if (!t->locked_to_meter()) {
1987 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1996 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1997 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2000 recompute_tempos (imaginary);
2002 if (check_solved (imaginary)) {
2006 MetricSectionSorter cmp;
2007 imaginary.sort (cmp);
2009 recompute_tempos (imaginary);
2011 * XX need a restriction here, but only for this case,
2012 * as audio locked tempos don't interact in the same way.
2014 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2016 * |50 bpm |250 bpm |60 bpm
2017 * drag 250 to the pulse after 60->
2018 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2020 if (check_solved (imaginary)) {
2028 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2030 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2031 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2032 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2036 if (!section->movable()) {
2037 /* lock the first tempo to our first meter */
2038 if (!set_active_tempos (imaginary, frame)) {
2043 /* it would make sense to bail out if there is no audio-locked meter,
2044 however it may be desirable to move a music-locked meter by frame at some point.
2046 TempoSection* meter_locked_tempo = 0;
2047 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2049 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2050 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2051 meter_locked_tempo = t;
2057 if (!meter_locked_tempo) {
2061 MeterSection* prev_m = 0;
2063 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2064 bool solved = false;
2066 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2068 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2070 if (prev_m && section->movable()) {
2071 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2072 if (beats + prev_m->beat() < section->beat()) {
2073 /* set the frame/pulse corresponding to its musical position,
2074 * as an earlier time than this has been requested.
2076 const double new_pulse = ((section->beat() - prev_m->beat())
2077 / prev_m->note_divisor()) + prev_m->pulse();
2079 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2081 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2082 meter_locked_tempo->set_pulse (new_pulse);
2083 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2084 section->set_frame (smallest_frame);
2085 section->set_pulse (new_pulse);
2090 Metrics::const_iterator d = future_map.begin();
2091 while (d != future_map.end()) {
2100 /* all is ok. set section's tempo */
2101 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2102 meter_copy->set_frame (frame);
2104 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2105 section->set_frame (frame);
2106 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2107 / prev_m->note_divisor()) + prev_m->pulse());
2108 solve_map_frame (imaginary, meter_locked_tempo, frame);
2113 Metrics::const_iterator d = future_map.begin();
2114 while (d != future_map.end()) {
2124 /* not movable (first meter atm) */
2126 tempo_copy->set_frame (frame);
2127 tempo_copy->set_pulse (0.0);
2129 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2130 section->set_frame (frame);
2131 meter_locked_tempo->set_frame (frame);
2132 meter_locked_tempo->set_pulse (0.0);
2133 solve_map_frame (imaginary, meter_locked_tempo, frame);
2138 Metrics::const_iterator d = future_map.begin();
2139 while (d != future_map.end()) {
2148 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2149 section->set_beat (b_bbt);
2150 section->set_pulse (0.0);
2160 MetricSectionFrameSorter fcmp;
2161 imaginary.sort (fcmp);
2163 recompute_meters (imaginary);
2169 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2171 /* disallow setting section to an existing meter's bbt */
2172 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2174 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2175 if (m != section && m->bbt().bars == when.bars) {
2181 MeterSection* prev_m = 0;
2182 MeterSection* section_prev = 0;
2184 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2186 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2187 pair<double, BBT_Time> b_bbt;
2188 double new_pulse = 0.0;
2190 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2191 section_prev = prev_m;
2192 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2193 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2194 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2196 section->set_beat (b_bbt);
2197 section->set_pulse (pulse);
2198 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2203 if (m->position_lock_style() == AudioTime) {
2204 TempoSection* meter_locked_tempo = 0;
2206 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2208 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2209 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2210 meter_locked_tempo = t;
2216 if (!meter_locked_tempo) {
2221 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2223 if (beats + prev_m->beat() != m->beat()) {
2224 /* tempo/ meter change caused a change in beat (bar). */
2225 b_bbt = make_pair (beats + prev_m->beat()
2226 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2227 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2228 } else if (m->movable()) {
2229 b_bbt = make_pair (m->beat(), m->bbt());
2230 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2233 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2236 meter_locked_tempo->set_pulse (new_pulse);
2237 m->set_beat (b_bbt);
2238 m->set_pulse (new_pulse);
2242 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2243 if (beats + prev_m->beat() != m->beat()) {
2244 /* tempo/ meter change caused a change in beat (bar). */
2245 b_bbt = make_pair (beats + prev_m->beat()
2246 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2248 b_bbt = make_pair (beats + prev_m->beat()
2251 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2252 m->set_beat (b_bbt);
2253 m->set_pulse (new_pulse);
2254 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2261 if (!section_prev) {
2263 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2264 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2265 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2267 section->set_beat (b_bbt);
2268 section->set_pulse (pulse);
2269 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2272 MetricSectionSorter cmp;
2273 imaginary.sort (cmp);
2275 recompute_meters (imaginary);
2280 /** places a copy of _metrics into copy and returns a pointer
2281 * to section's equivalent in copy.
2284 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2286 TempoSection* ret = 0;
2288 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2291 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2293 ret = new TempoSection (*t);
2294 copy.push_back (ret);
2298 TempoSection* cp = new TempoSection (*t);
2299 copy.push_back (cp);
2301 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2302 MeterSection* cp = new MeterSection (*m);
2303 copy.push_back (cp);
2311 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2313 MeterSection* ret = 0;
2315 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2318 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2319 TempoSection* cp = new TempoSection (*t);
2320 copy.push_back (cp);
2323 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2325 ret = new MeterSection (*m);
2326 copy.push_back (ret);
2329 MeterSection* cp = new MeterSection (*m);
2330 copy.push_back (cp);
2338 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2341 TempoSection* tempo_copy = 0;
2344 Glib::Threads::RWLock::ReaderLock lm (lock);
2345 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2351 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2353 Metrics::const_iterator d = copy.begin();
2354 while (d != copy.end()) {
2363 * 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,
2364 * taking any possible reordering as a consequence of this into account.
2365 * @param section - the section to be altered
2366 * @param bbt - the bbt where the altered tempo will fall
2367 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2369 pair<double, framepos_t>
2370 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2373 pair<double, framepos_t> ret = make_pair (0.0, 0);
2375 Glib::Threads::RWLock::ReaderLock lm (lock);
2377 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2379 const double beat = beat_at_bbt_locked (future_map, bbt);
2381 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2382 ret.first = tempo_copy->pulse();
2383 ret.second = tempo_copy->frame();
2385 ret.first = section->pulse();
2386 ret.second = section->frame();
2389 Metrics::const_iterator d = future_map.begin();
2390 while (d != future_map.end()) {
2398 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame)
2402 if (ts->position_lock_style() == MusicTime) {
2404 Glib::Threads::RWLock::WriterLock lm (lock);
2405 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2406 const double pulse = pulse_at_frame_locked (future_map, frame);
2407 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2408 solve_map_pulse (_metrics, ts, pulse);
2409 recompute_meters (_metrics);
2416 Glib::Threads::RWLock::WriterLock lm (lock);
2417 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2418 if (solve_map_frame (future_map, tempo_copy, frame)) {
2419 solve_map_frame (_metrics, ts, frame);
2420 recompute_meters (_metrics);
2425 Metrics::const_iterator d = future_map.begin();
2426 while (d != future_map.end()) {
2431 MetricPositionChanged (); // Emit Signal
2435 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2439 if (ms->position_lock_style() == AudioTime) {
2442 Glib::Threads::RWLock::WriterLock lm (lock);
2443 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2445 if (solve_map_frame (future_map, copy, frame)) {
2446 solve_map_frame (_metrics, ms, frame);
2447 recompute_tempos (_metrics);
2452 Glib::Threads::RWLock::WriterLock lm (lock);
2453 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2455 const double beat = beat_at_frame_locked (_metrics, frame);
2456 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2458 if (solve_map_bbt (future_map, copy, bbt)) {
2459 solve_map_bbt (_metrics, ms, bbt);
2460 recompute_tempos (_metrics);
2465 Metrics::const_iterator d = future_map.begin();
2466 while (d != future_map.end()) {
2471 MetricPositionChanged (); // Emit Signal
2475 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2478 bool can_solve = false;
2480 Glib::Threads::RWLock::WriterLock lm (lock);
2481 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2482 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2483 recompute_tempos (future_map);
2485 if (check_solved (future_map)) {
2486 ts->set_beats_per_minute (bpm.beats_per_minute());
2487 recompute_map (_metrics);
2492 Metrics::const_iterator d = future_map.begin();
2493 while (d != future_map.end()) {
2498 MetricPositionChanged (); // Emit Signal
2504 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2507 Ts (future prev_t) Tnext
2510 |----------|----------
2517 Glib::Threads::RWLock::WriterLock lm (lock);
2523 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2524 TempoSection* prev_to_prev_t = 0;
2525 const frameoffset_t fr_off = end_frame - frame;
2527 if (prev_t && prev_t->pulse() > 0.0) {
2528 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2531 TempoSection* next_t = 0;
2532 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2533 TempoSection* t = 0;
2534 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2535 if (t->frame() > ts->frame()) {
2542 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2543 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2545 double contribution = 0.0;
2546 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2548 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2549 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2552 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2553 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2556 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2558 if (prev_t->position_lock_style() == MusicTime) {
2559 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2561 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2562 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2565 /* prev to prev is irrelevant */
2567 if (start_pulse != prev_t->pulse()) {
2568 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2570 new_bpm = prev_t->beats_per_minute();
2575 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2576 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2577 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2579 /* prev_to_prev_t is irrelevant */
2581 if (end_frame != prev_t->frame()) {
2582 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2584 new_bpm = prev_t->beats_per_minute();
2592 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2594 if (prev_to_prev_t) {
2596 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2597 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2600 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2601 pulse_ratio = (start_pulse / end_pulse);
2603 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2606 prev_t->set_beats_per_minute (new_bpm);
2607 recompute_tempos (future_map);
2608 recompute_meters (future_map);
2610 if (check_solved (future_map)) {
2611 ts->set_beats_per_minute (new_bpm);
2612 recompute_tempos (_metrics);
2613 recompute_meters (_metrics);
2617 Metrics::const_iterator d = future_map.begin();
2618 while (d != future_map.end()) {
2623 MetricPositionChanged (); // Emit Signal
2627 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2629 Glib::Threads::RWLock::ReaderLock lm (lock);
2631 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2632 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2633 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2635 return frame_at_beat_locked (_metrics, total_beats);
2639 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2641 return round_to_type (fr, dir, Bar);
2645 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2647 return round_to_type (fr, dir, Beat);
2651 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2653 Glib::Threads::RWLock::ReaderLock lm (lock);
2654 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2655 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2656 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2658 ticks -= beats * BBT_Time::ticks_per_beat;
2661 /* round to next (or same iff dir == RoundUpMaybe) */
2663 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2665 if (mod == 0 && dir == RoundUpMaybe) {
2666 /* right on the subdivision, which is fine, so do nothing */
2668 } else if (mod == 0) {
2669 /* right on the subdivision, so the difference is just the subdivision ticks */
2670 ticks += ticks_one_subdivisions_worth;
2673 /* not on subdivision, compute distance to next subdivision */
2675 ticks += ticks_one_subdivisions_worth - mod;
2678 if (ticks >= BBT_Time::ticks_per_beat) {
2679 ticks -= BBT_Time::ticks_per_beat;
2681 } else if (dir < 0) {
2683 /* round to previous (or same iff dir == RoundDownMaybe) */
2685 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2687 if (difference == 0 && dir == RoundDownAlways) {
2688 /* right on the subdivision, but force-rounding down,
2689 so the difference is just the subdivision ticks */
2690 difference = ticks_one_subdivisions_worth;
2693 if (ticks < difference) {
2694 ticks = BBT_Time::ticks_per_beat - ticks;
2696 ticks -= difference;
2700 /* round to nearest */
2703 /* compute the distance to the previous and next subdivision */
2705 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2707 /* closer to the next subdivision, so shift forward */
2709 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2711 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2713 if (ticks > BBT_Time::ticks_per_beat) {
2715 ticks -= BBT_Time::ticks_per_beat;
2716 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2719 } else if (rem > 0) {
2721 /* closer to previous subdivision, so shift backward */
2725 /* can't go backwards past zero, so ... */
2728 /* step back to previous beat */
2730 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2731 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2733 ticks = lrint (ticks - rem);
2734 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2737 /* on the subdivision, do nothing */
2741 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2747 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
2749 if (sub_num == -1) {
2754 } else if (dir < 0) {
2758 const double bpb = meter_section_at_beat (beat_at_bbt_locked (_metrics, when)).divisions_per_bar();
2759 if ((double) when.beats > bpb / 2.0) {
2768 } else if (sub_num == 0) {
2769 const double bpb = meter_section_at_beat (beat_at_bbt_locked (_metrics, when)).divisions_per_bar();
2770 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2772 while ((double) when.beats > bpb) {
2774 when.beats -= (uint32_t) floor (bpb);
2782 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2785 /* round to next (or same iff dir == RoundUpMaybe) */
2787 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
2789 if (mod == 0 && dir == RoundUpMaybe) {
2790 /* right on the subdivision, which is fine, so do nothing */
2792 } else if (mod == 0) {
2793 /* right on the subdivision, so the difference is just the subdivision ticks */
2794 when.ticks += ticks_one_subdivisions_worth;
2797 /* not on subdivision, compute distance to next subdivision */
2799 when.ticks += ticks_one_subdivisions_worth - mod;
2802 if (when.ticks >= BBT_Time::ticks_per_beat) {
2804 const double bpb = meter_section_at_beat (beat_at_bbt_locked (_metrics, when)).divisions_per_bar();
2805 if ((double) when.beats > bpb) {
2809 when.ticks -= BBT_Time::ticks_per_beat;
2812 } else if (dir < 0) {
2813 /* round to previous (or same iff dir == RoundDownMaybe) */
2815 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
2817 if (difference == 0 && dir == RoundDownAlways) {
2818 /* right on the subdivision, but force-rounding down,
2819 so the difference is just the subdivision ticks */
2820 difference = ticks_one_subdivisions_worth;
2823 if (when.ticks < difference) {
2825 const double bpb = meter_section_at_beat (beat_at_bbt_locked (_metrics, when)).divisions_per_bar();
2826 if ((double) when.beats < bpb) {
2830 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
2832 when.ticks -= difference;
2836 /* round to nearest */ double rem;
2837 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2838 /* closer to the next subdivision, so shift forward */
2840 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2842 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2844 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2847 } else if (rem > 0) {
2848 /* closer to previous subdivision, so shift backward */
2850 if (rem > when.ticks) {
2851 if (when.beats == 0) {
2852 /* can't go backwards past zero, so ... */
2854 /* step back to previous beat */
2856 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2858 when.ticks = when.ticks - rem;
2865 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2867 Glib::Threads::RWLock::ReaderLock lm (lock);
2869 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2870 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
2875 /* find bar previous to 'frame' */
2878 return frame_time_locked (_metrics, bbt);
2880 } else if (dir > 0) {
2881 /* find bar following 'frame' */
2885 return frame_time_locked (_metrics, bbt);
2887 /* true rounding: find nearest bar */
2888 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2891 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2893 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2895 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2906 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2907 } else if (dir > 0) {
2908 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2910 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2919 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2920 framepos_t lower, framepos_t upper)
2922 Glib::Threads::RWLock::ReaderLock lm (lock);
2923 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2925 /* although the map handles negative beats, bbt doesn't. */
2929 while (pos < upper) {
2930 pos = frame_at_beat_locked (_metrics, cnt);
2931 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2932 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2933 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
2934 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2940 TempoMap::tempo_section_at (framepos_t frame) const
2942 Glib::Threads::RWLock::ReaderLock lm (lock);
2943 return tempo_section_at_locked (_metrics, frame);
2947 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2949 Metrics::const_iterator i;
2950 TempoSection* prev = 0;
2952 for (i = metrics.begin(); i != metrics.end(); ++i) {
2955 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2959 if (prev && t->frame() > frame) {
2969 abort(); /*NOTREACHED*/
2976 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2978 TempoSection* prev_t = 0;
2979 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2981 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2983 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2984 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2995 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2997 TempoSection* prev_t = 0;
2999 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3001 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3002 if (prev_t && t->pulse() > pulse) {
3012 /* don't use this to calculate length (the tempo is only correct for this frame).
3013 do that stuff based on the beat_at_frame and frame_at_beat api
3016 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3018 Glib::Threads::RWLock::ReaderLock lm (lock);
3020 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3021 const TempoSection* ts_after = 0;
3022 Metrics::const_iterator i;
3024 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3027 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3031 if ((*i)->frame() > frame) {
3039 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3041 /* must be treated as constant tempo */
3042 return ts_at->frames_per_beat (_frame_rate);
3046 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3048 TempoSection* prev_t = 0;
3050 Metrics::const_iterator i;
3052 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3054 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3058 if ((prev_t) && t->frame() > frame) {
3059 /* t is the section past frame */
3060 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3061 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3068 const double ret = prev_t->beats_per_minute();
3069 const Tempo ret_tempo (ret, prev_t->note_type ());
3075 TempoMap::tempo_at (const framepos_t& frame) const
3077 Glib::Threads::RWLock::ReaderLock lm (lock);
3078 return tempo_at_locked (_metrics, frame);
3082 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3084 Metrics::const_iterator i;
3085 MeterSection* prev = 0;
3087 for (i = metrics.begin(); i != metrics.end(); ++i) {
3090 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3092 if (prev && (*i)->frame() > frame) {
3102 abort(); /*NOTREACHED*/
3110 TempoMap::meter_section_at (framepos_t frame) const
3112 Glib::Threads::RWLock::ReaderLock lm (lock);
3113 return meter_section_at_locked (_metrics, frame);
3117 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3119 MeterSection* prev_m = 0;
3121 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3123 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3124 if (prev_m && m->beat() > beat) {
3135 TempoMap::meter_section_at_beat (double beat) const
3137 Glib::Threads::RWLock::ReaderLock lm (lock);
3138 return meter_section_at_beat_locked (_metrics, beat);
3142 TempoMap::meter_at (framepos_t frame) const
3144 TempoMetric m (metric_at (frame));
3149 TempoMap::fix_legacy_session ()
3151 MeterSection* prev_m = 0;
3152 TempoSection* prev_t = 0;
3154 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3158 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3159 if (!m->movable()) {
3160 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3164 m->set_position_lock_style (AudioTime);
3169 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3170 + (m->bbt().beats - 1)
3171 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3173 m->set_beat (start);
3174 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3175 + (m->bbt().beats - 1)
3176 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3177 m->set_pulse (start_beat / prev_m->note_divisor());
3180 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3186 if (!t->movable()) {
3189 t->set_position_lock_style (AudioTime);
3195 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3196 + (t->legacy_bbt().beats - 1)
3197 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3199 t->set_pulse (beat / prev_m->note_divisor());
3201 /* really shouldn't happen but.. */
3202 t->set_pulse (beat / 4.0);
3211 TempoMap::get_state ()
3213 Metrics::const_iterator i;
3214 XMLNode *root = new XMLNode ("TempoMap");
3217 Glib::Threads::RWLock::ReaderLock lm (lock);
3218 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3219 root->add_child_nocopy ((*i)->get_state());
3227 TempoMap::set_state (const XMLNode& node, int /*version*/)
3230 Glib::Threads::RWLock::WriterLock lm (lock);
3233 XMLNodeConstIterator niter;
3234 Metrics old_metrics (_metrics);
3237 nlist = node.children();
3239 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3240 XMLNode* child = *niter;
3242 if (child->name() == TempoSection::xml_state_node_name) {
3245 TempoSection* ts = new TempoSection (*child);
3246 _metrics.push_back (ts);
3249 catch (failed_constructor& err){
3250 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3251 _metrics = old_metrics;
3255 } else if (child->name() == MeterSection::xml_state_node_name) {
3258 MeterSection* ms = new MeterSection (*child);
3259 _metrics.push_back (ms);
3262 catch (failed_constructor& err) {
3263 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3264 _metrics = old_metrics;
3270 if (niter == nlist.end()) {
3271 MetricSectionSorter cmp;
3272 _metrics.sort (cmp);
3275 /* check for legacy sessions where bbt was the base musical unit for tempo */
3276 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3278 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3279 if (t->legacy_bbt().bars != 0) {
3280 fix_legacy_session();
3287 /* check for multiple tempo/meters at the same location, which
3288 ardour2 somehow allowed.
3291 Metrics::iterator prev = _metrics.end();
3292 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3293 if (prev != _metrics.end()) {
3295 MeterSection* prev_m;
3297 TempoSection* prev_t;
3298 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3299 if (prev_m->pulse() == ms->pulse()) {
3300 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3301 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3304 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3305 if (prev_t->pulse() == ts->pulse()) {
3306 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3307 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3315 recompute_map (_metrics);
3318 PropertyChanged (PropertyChange ());
3324 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3326 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3327 const MeterSection* m;
3328 const TempoSection* t;
3329 const TempoSection* prev_t = 0;
3331 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3333 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3334 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3335 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3336 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3338 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3339 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;
3342 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3343 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3344 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3347 o << "------" << std::endl;
3351 TempoMap::n_tempos() const
3353 Glib::Threads::RWLock::ReaderLock lm (lock);
3356 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3357 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3366 TempoMap::n_meters() const
3368 Glib::Threads::RWLock::ReaderLock lm (lock);
3371 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3372 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3381 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3384 Glib::Threads::RWLock::WriterLock lm (lock);
3385 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3386 if ((*i)->frame() >= where && (*i)->movable ()) {
3387 (*i)->set_frame ((*i)->frame() + amount);
3391 /* now reset the BBT time of all metrics, based on their new
3392 * audio time. This is the only place where we do this reverse
3396 Metrics::iterator i;
3397 const MeterSection* meter;
3398 const TempoSection* tempo;
3402 meter = &first_meter ();
3403 tempo = &first_tempo ();
3408 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3411 MetricSection* prev = 0;
3413 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3416 //TempoMetric metric (*meter, *tempo);
3417 MeterSection* ms = const_cast<MeterSection*>(meter);
3418 TempoSection* ts = const_cast<TempoSection*>(tempo);
3421 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3425 ts->set_pulse (t->pulse());
3427 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3428 ts->set_pulse (m->pulse());
3430 ts->set_frame (prev->frame());
3434 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3435 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3436 ms->set_beat (start);
3437 ms->set_pulse (m->pulse());
3439 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3443 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3444 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3445 ms->set_beat (start);
3446 ms->set_pulse (t->pulse());
3448 ms->set_frame (prev->frame());
3452 // metric will be at frames=0 bbt=1|1|0 by default
3453 // which is correct for our purpose
3456 // cerr << bbt << endl;
3458 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3462 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3464 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3465 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3466 bbt_time (m->frame(), bbt);
3468 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3474 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3475 /* round up to next beat */
3481 if (bbt.beats != 1) {
3482 /* round up to next bar */
3487 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3488 m->set_beat (start);
3489 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3491 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3493 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3494 abort(); /*NOTREACHED*/
3500 recompute_map (_metrics);
3504 PropertyChanged (PropertyChange ());
3507 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3511 std::list<MetricSection*> metric_kill_list;
3513 TempoSection* last_tempo = NULL;
3514 MeterSection* last_meter = NULL;
3515 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3516 bool meter_after = false; // is there a meter marker likewise?
3518 Glib::Threads::RWLock::WriterLock lm (lock);
3519 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3520 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3521 metric_kill_list.push_back(*i);
3522 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3525 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3529 else if ((*i)->frame() >= where) {
3530 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3531 (*i)->set_frame ((*i)->frame() - amount);
3532 if ((*i)->frame() == where) {
3533 // marker was immediately after end of range
3534 tempo_after = dynamic_cast<TempoSection*> (*i);
3535 meter_after = dynamic_cast<MeterSection*> (*i);
3541 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3542 if (last_tempo && !tempo_after) {
3543 metric_kill_list.remove(last_tempo);
3544 last_tempo->set_frame(where);
3547 if (last_meter && !meter_after) {
3548 metric_kill_list.remove(last_meter);
3549 last_meter->set_frame(where);
3553 //remove all the remaining metrics
3554 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3555 _metrics.remove(*i);
3560 recompute_map (_metrics);
3563 PropertyChanged (PropertyChange ());
3567 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3568 * pos can be -ve, if required.
3571 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3573 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3576 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3578 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3580 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3583 /** Add the BBT interval op to pos and return the result */
3585 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3587 Glib::Threads::RWLock::ReaderLock lm (lock);
3589 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3590 pos_bbt.ticks += op.ticks;
3591 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3593 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3595 pos_bbt.beats += op.beats;
3596 /* the meter in effect will start on the bar */
3597 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();
3598 while (pos_bbt.beats >= divisions_per_bar + 1) {
3600 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3601 pos_bbt.beats -= divisions_per_bar;
3603 pos_bbt.bars += op.bars;
3605 return frame_time_locked (_metrics, pos_bbt);
3608 /** Count the number of beats that are equivalent to distance when going forward,
3612 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3614 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3618 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3624 operator<< (std::ostream& o, const Meter& m) {
3625 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3629 operator<< (std::ostream& o, const Tempo& t) {
3630 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3634 operator<< (std::ostream& o, const MetricSection& section) {
3636 o << "MetricSection @ " << section.frame() << ' ';
3638 const TempoSection* ts;
3639 const MeterSection* ms;
3641 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3642 o << *((const Tempo*) ts);
3643 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3644 o << *((const Meter*) ms);