2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 const XMLProperty *prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const frameoffset_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 - (frameoffset_t) 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 (frameoffset_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 (frameoffset_t) floor ((time * 60.0 * (frameoffset_t) frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const frameoffset_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
461 const XMLProperty *prop;
465 framepos_t frame = 0;
466 pair<double, BBT_Time> start;
468 if ((prop = node.property ("start")) != 0) {
469 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
473 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
475 /* legacy session - start used to be in bbt*/
476 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
481 if ((prop = node.property ("pulse")) != 0) {
482 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
483 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
488 if ((prop = node.property ("beat")) != 0) {
489 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
490 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
496 if ((prop = node.property ("bbt")) == 0) {
497 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
498 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
502 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
503 throw failed_constructor();
509 if ((prop = node.property ("frame")) != 0) {
510 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
511 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
517 /* beats-per-bar is old; divisions-per-bar is new */
519 if ((prop = node.property ("divisions-per-bar")) == 0) {
520 if ((prop = node.property ("beats-per-bar")) == 0) {
521 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
522 throw failed_constructor();
525 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
526 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
527 throw failed_constructor();
530 if ((prop = node.property ("note-type")) == 0) {
531 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
532 throw failed_constructor();
534 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
535 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
536 throw failed_constructor();
539 if ((prop = node.property ("movable")) == 0) {
540 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
541 throw failed_constructor();
544 set_movable (string_is_affirmative (prop->value()));
546 if ((prop = node.property ("lock-style")) == 0) {
547 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
549 set_position_lock_style (MusicTime);
551 set_position_lock_style (AudioTime);
554 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
559 MeterSection::get_state() const
561 XMLNode *root = new XMLNode (xml_state_node_name);
565 snprintf (buf, sizeof (buf), "%lf", pulse());
566 root->add_property ("pulse", buf);
567 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
571 root->add_property ("bbt", buf);
572 snprintf (buf, sizeof (buf), "%lf", beat());
573 root->add_property ("beat", buf);
574 snprintf (buf, sizeof (buf), "%f", _note_type);
575 root->add_property ("note-type", buf);
576 snprintf (buf, sizeof (buf), "%li", frame());
577 root->add_property ("frame", buf);
578 root->add_property ("lock-style", enum_2_string (position_lock_style()));
579 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
580 root->add_property ("divisions-per-bar", buf);
581 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
582 root->add_property ("movable", buf);
587 /***********************************************************************/
591 Tempo can be thought of as a source of the musical pulse.
592 Meters divide that pulse into measures and beats.
593 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
594 at any particular time.
595 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
596 It should rather be thought of as tempo note divisions per minute.
598 TempoSections, which are nice to think of in whole pulses per minute,
599 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
600 and beats (via note_divisor) are used to form a tempo map.
601 TempoSections and MeterSections may be locked to either audio or music (position lock style).
602 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
603 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
605 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 Both tempos and meters have a pulse position and a frame position.
612 Meters also have a beat position, which is always 0.0 for the first meter.
614 A tempo locked to music is locked to musical pulses.
615 A meter locked to music is locked to beats.
617 Recomputing the tempo map is the process where the 'missing' position
618 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
620 It is important to keep the _metrics in an order that makes sense.
621 Because ramped MusicTime and AudioTime tempos can interact with each other,
622 reordering is frequent. Care must be taken to keep _metrics in a solved state.
623 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
625 struct MetricSectionSorter {
626 bool operator() (const MetricSection* a, const MetricSection* b) {
627 return a->pulse() < b->pulse();
631 struct MetricSectionFrameSorter {
632 bool operator() (const MetricSection* a, const MetricSection* b) {
633 return a->frame() < b->frame();
637 TempoMap::TempoMap (framecnt_t fr)
640 BBT_Time start (1, 1, 0);
642 TempoSection *t = new TempoSection ((framepos_t) 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
643 MeterSection *m = new MeterSection ((framepos_t) 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
645 t->set_movable (false);
646 m->set_movable (false);
648 /* note: frame time is correct (zero) for both of these */
650 _metrics.push_back (t);
651 _metrics.push_back (m);
655 TempoMap::~TempoMap ()
657 Metrics::const_iterator d = _metrics.begin();
658 while (d != _metrics.end()) {
666 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
668 bool removed = false;
671 Glib::Threads::RWLock::WriterLock lm (lock);
672 if ((removed = remove_tempo_locked (tempo))) {
673 if (complete_operation) {
674 recompute_map (_metrics);
679 if (removed && complete_operation) {
680 PropertyChanged (PropertyChange ());
685 TempoMap::remove_tempo_locked (const TempoSection& tempo)
689 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
690 if (dynamic_cast<TempoSection*> (*i) != 0) {
691 if (tempo.frame() == (*i)->frame()) {
692 if ((*i)->movable()) {
705 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
707 bool removed = false;
710 Glib::Threads::RWLock::WriterLock lm (lock);
711 if ((removed = remove_meter_locked (tempo))) {
712 if (complete_operation) {
713 recompute_map (_metrics);
718 if (removed && complete_operation) {
719 PropertyChanged (PropertyChange ());
724 TempoMap::remove_meter_locked (const MeterSection& meter)
728 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
730 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
731 if (meter.frame() == (*i)->frame()) {
732 if (t->locked_to_meter()) {
741 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
742 if (dynamic_cast<MeterSection*> (*i) != 0) {
743 if (meter.frame() == (*i)->frame()) {
744 if ((*i)->movable()) {
757 TempoMap::do_insert (MetricSection* section)
759 bool need_add = true;
760 /* we only allow new meters to be inserted on beat 1 of an existing
764 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
765 //assert (m->bbt().ticks == 0);
767 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
769 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
770 corrected.second.beats = 1;
771 corrected.second.ticks = 0;
772 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
773 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
774 m->bbt(), corrected.second) << endmsg;
775 //m->set_pulse (corrected);
779 /* Look for any existing MetricSection that is of the same type and
780 in the same bar as the new one, and remove it before adding
781 the new one. Note that this means that if we find a matching,
782 existing section, we can break out of the loop since we're
783 guaranteed that there is only one such match.
786 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
788 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
789 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
790 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
791 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
793 if (tempo && insert_tempo) {
796 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
797 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
799 if (!tempo->movable()) {
801 /* can't (re)move this section, so overwrite
802 * its data content (but not its properties as
806 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
807 (*i)->set_position_lock_style (AudioTime);
809 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
810 t->set_type (insert_tempo->type());
820 } else if (meter && insert_meter) {
824 bool const ipm = insert_meter->position_lock_style() == MusicTime;
826 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
828 if (!meter->movable()) {
830 /* can't (re)move this section, so overwrite
831 * its data content (but not its properties as
835 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
836 (*i)->set_position_lock_style (AudioTime);
846 /* non-matching types, so we don't care */
850 /* Add the given MetricSection, if we didn't just reset an existing
855 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
856 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
859 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
860 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
863 bool const ipm = insert_meter->position_lock_style() == MusicTime;
864 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
869 } else if (insert_tempo) {
870 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
871 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
874 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
875 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
882 _metrics.insert (i, section);
883 //dump (_metrics, std::cerr);
888 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
891 Glib::Threads::RWLock::WriterLock lm (lock);
892 TempoSection& first (first_tempo());
893 if (ts.pulse() != first.pulse()) {
894 remove_tempo_locked (ts);
895 add_tempo_locked (tempo, pulse, true, type);
897 first.set_type (type);
899 /* cannot move the first tempo section */
900 *static_cast<Tempo*>(&first) = tempo;
901 recompute_map (_metrics);
906 PropertyChanged (PropertyChange ());
910 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
913 Glib::Threads::RWLock::WriterLock lm (lock);
914 TempoSection& first (first_tempo());
915 if (ts.frame() != first.frame()) {
916 remove_tempo_locked (ts);
917 add_tempo_locked (tempo, frame, true, type);
919 first.set_type (type);
920 first.set_pulse (0.0);
921 first.set_position_lock_style (AudioTime);
923 /* cannot move the first tempo section */
924 *static_cast<Tempo*>(&first) = tempo;
925 recompute_map (_metrics);
930 PropertyChanged (PropertyChange ());
934 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
936 TempoSection* ts = 0;
938 Glib::Threads::RWLock::WriterLock lm (lock);
939 ts = add_tempo_locked (tempo, pulse, true, type);
942 PropertyChanged (PropertyChange ());
948 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
950 TempoSection* ts = 0;
952 Glib::Threads::RWLock::WriterLock lm (lock);
953 ts = add_tempo_locked (tempo, frame, true, type);
957 PropertyChanged (PropertyChange ());
963 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
965 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
970 solve_map (_metrics, t, t->pulse());
971 recompute_meters (_metrics);
978 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
980 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
985 solve_map (_metrics, t, t->frame());
986 recompute_meters (_metrics);
993 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
996 Glib::Threads::RWLock::WriterLock lm (lock);
999 remove_meter_locked (ms);
1000 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
1002 MeterSection& first (first_meter());
1003 /* cannot move the first meter section */
1004 *static_cast<Meter*>(&first) = meter;
1005 first.set_position_lock_style (AudioTime);
1007 recompute_map (_metrics);
1010 PropertyChanged (PropertyChange ());
1014 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1017 Glib::Threads::RWLock::WriterLock lm (lock);
1019 const double beat = ms.beat();
1020 const BBT_Time bbt = ms.bbt();
1023 remove_meter_locked (ms);
1024 add_meter_locked (meter, frame, beat, bbt, true);
1026 MeterSection& first (first_meter());
1027 TempoSection& first_t (first_tempo());
1028 /* cannot move the first meter section */
1029 *static_cast<Meter*>(&first) = meter;
1030 first.set_position_lock_style (AudioTime);
1031 first.set_pulse (0.0);
1032 first.set_frame (frame);
1033 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1034 first.set_beat (beat);
1035 first_t.set_frame (first.frame());
1036 first_t.set_pulse (0.0);
1037 first_t.set_position_lock_style (AudioTime);
1039 recompute_map (_metrics);
1041 PropertyChanged (PropertyChange ());
1046 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1048 MeterSection* m = 0;
1050 Glib::Threads::RWLock::WriterLock lm (lock);
1051 m = add_meter_locked (meter, beat, where, true);
1056 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1057 dump (_metrics, std::cerr);
1061 PropertyChanged (PropertyChange ());
1067 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1069 MeterSection* m = 0;
1071 Glib::Threads::RWLock::WriterLock lm (lock);
1072 m = add_meter_locked (meter, frame, beat, where, true);
1077 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1078 dump (_metrics, std::cerr);
1082 PropertyChanged (PropertyChange ());
1088 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1090 /* a new meter always starts a new bar on the first beat. so
1091 round the start time appropriately. remember that
1092 `where' is based on the existing tempo map, not
1093 the result after we insert the new meter.
1097 if (where.beats != 1) {
1101 /* new meters *always* start on a beat. */
1103 const double pulse = pulse_at_beat_locked (_metrics, beat);
1104 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1105 do_insert (new_meter);
1108 solve_map (_metrics, new_meter, pulse);
1115 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1117 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1118 TempoSection* t = 0;
1119 double pulse = pulse_at_frame_locked (_metrics, frame);
1120 new_meter->set_pulse (pulse);
1122 do_insert (new_meter);
1124 /* add meter-locked tempo */
1125 t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1126 t->set_locked_to_meter (true);
1129 solve_map (_metrics, new_meter, frame);
1136 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1138 Tempo newtempo (beats_per_minute, note_type);
1141 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1142 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1147 Glib::Threads::RWLock::WriterLock lm (lock);
1148 *((Tempo*) t) = newtempo;
1149 recompute_map (_metrics);
1151 PropertyChanged (PropertyChange ());
1158 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1160 Tempo newtempo (beats_per_minute, note_type);
1163 TempoSection* first;
1164 Metrics::iterator i;
1166 /* find the TempoSection immediately preceding "where"
1169 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1171 if ((*i)->frame() > where) {
1177 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1190 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1200 Glib::Threads::RWLock::WriterLock lm (lock);
1201 /* cannot move the first tempo section */
1202 *((Tempo*)prev) = newtempo;
1203 recompute_map (_metrics);
1206 PropertyChanged (PropertyChange ());
1210 TempoMap::first_meter () const
1212 const MeterSection *m = 0;
1214 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1215 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1220 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1221 abort(); /*NOTREACHED*/
1226 TempoMap::first_meter ()
1228 MeterSection *m = 0;
1230 /* CALLER MUST HOLD LOCK */
1232 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1233 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1238 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1239 abort(); /*NOTREACHED*/
1244 TempoMap::first_tempo () const
1246 const TempoSection *t = 0;
1248 /* CALLER MUST HOLD LOCK */
1250 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1251 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1255 if (!t->movable()) {
1261 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1262 abort(); /*NOTREACHED*/
1267 TempoMap::first_tempo ()
1269 TempoSection *t = 0;
1271 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1272 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1276 if (!t->movable()) {
1282 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1283 abort(); /*NOTREACHED*/
1287 TempoMap::recompute_tempos (Metrics& metrics)
1289 TempoSection* prev_t = 0;
1291 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1294 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1298 if (!t->movable()) {
1306 if (t->position_lock_style() == AudioTime) {
1307 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1308 if (!t->locked_to_meter()) {
1309 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1313 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1314 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1321 prev_t->set_c_func (0.0);
1324 /* tempos must be positioned correctly */
1326 TempoMap::recompute_meters (Metrics& metrics)
1328 MeterSection* meter = 0;
1329 MeterSection* prev_m = 0;
1331 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1332 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1333 if (meter->position_lock_style() == AudioTime) {
1335 pair<double, BBT_Time> b_bbt;
1336 if (meter->movable()) {
1337 b_bbt = make_pair (meter->beat(), meter->bbt());
1338 pulse = pulse_at_frame_locked (metrics, meter->frame());
1340 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1342 meter->set_beat (b_bbt);
1343 meter->set_pulse (pulse);
1346 pair<double, BBT_Time> new_beat;
1348 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1349 //new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1351 /* shouldn't happen - the first is audio-locked */
1352 pulse = pulse_at_beat_locked (metrics, meter->beat());
1353 new_beat = make_pair (meter->beat(), meter->bbt());
1356 //meter->set_beat (new_beat);
1357 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1358 meter->set_pulse (pulse);
1364 //dump (_metrics, std::cerr;
1368 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1370 /* CALLER MUST HOLD WRITE LOCK */
1374 /* we will actually stop once we hit
1381 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1384 /* silly call from Session::process() during startup
1389 recompute_tempos (metrics);
1390 recompute_meters (metrics);
1394 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1396 Glib::Threads::RWLock::ReaderLock lm (lock);
1397 TempoMetric m (first_meter(), first_tempo());
1399 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1400 at something, because we insert the default tempo and meter during
1401 TempoMap construction.
1403 now see if we can find better candidates.
1406 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1408 if ((*i)->frame() > frame) {
1422 /* XX meters only */
1424 TempoMap::metric_at (BBT_Time bbt) const
1426 Glib::Threads::RWLock::ReaderLock lm (lock);
1427 TempoMetric m (first_meter(), first_tempo());
1429 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1430 at something, because we insert the default tempo and meter during
1431 TempoMap construction.
1433 now see if we can find better candidates.
1436 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1438 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1439 BBT_Time section_start (mw->bbt());
1441 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1453 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1455 MeterSection* prev_m = 0;
1457 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1459 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1460 if (prev_m && m->beat() > beat) {
1467 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1472 TempoMap::pulse_at_beat (const double& beat) const
1474 Glib::Threads::RWLock::ReaderLock lm (lock);
1475 return pulse_at_beat_locked (_metrics, beat);
1479 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1481 MeterSection* prev_m = 0;
1483 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1485 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1486 if (prev_m && m->pulse() > pulse) {
1487 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1495 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1500 TempoMap::beat_at_pulse (const double& pulse) const
1502 Glib::Threads::RWLock::ReaderLock lm (lock);
1503 return beat_at_pulse_locked (_metrics, pulse);
1506 /* tempo section based */
1508 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1510 /* HOLD (at least) THE READER LOCK */
1511 TempoSection* prev_t = 0;
1513 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1515 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1519 if (prev_t && t->frame() > frame) {
1520 /*the previous ts is the one containing the frame */
1521 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1528 /* treated as constant for this ts */
1529 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1531 return pulses_in_section + prev_t->pulse();
1535 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1537 Glib::Threads::RWLock::ReaderLock lm (lock);
1538 return pulse_at_frame_locked (_metrics, frame);
1541 /* tempo section based */
1543 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1545 /* HOLD THE READER LOCK */
1547 const TempoSection* prev_t = 0;
1549 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1552 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1556 if (prev_t && t->pulse() > pulse) {
1557 return prev_t->frame_at_pulse (pulse, _frame_rate);
1563 /* must be treated as constant, irrespective of _type */
1564 double const pulses_in_section = pulse - prev_t->pulse();
1565 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1567 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1573 TempoMap::frame_at_pulse (const double& pulse) const
1575 Glib::Threads::RWLock::ReaderLock lm (lock);
1576 return frame_at_pulse_locked (_metrics, pulse);
1579 /* meter section based */
1581 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1583 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1584 MeterSection* prev_m = 0;
1585 MeterSection* next_m = 0;
1587 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1589 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1590 if (prev_m && m->frame() > frame) {
1597 if (frame < prev_m->frame()) {
1600 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1602 if (next_m && next_m->beat() < beat) {
1603 return next_m->beat();
1610 TempoMap::beat_at_frame (const framecnt_t& frame) const
1612 Glib::Threads::RWLock::ReaderLock lm (lock);
1613 return beat_at_frame_locked (_metrics, frame);
1616 /* meter section based */
1618 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1620 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1621 MeterSection* prev_m = 0;
1623 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1625 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1626 if (prev_m && m->beat() > beat) {
1633 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1637 TempoMap::frame_at_beat (const double& beat) const
1639 Glib::Threads::RWLock::ReaderLock lm (lock);
1640 return frame_at_beat_locked (_metrics, beat);
1644 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1646 /* CALLER HOLDS READ LOCK */
1648 MeterSection* prev_m = 0;
1650 /* because audio-locked meters have 'fake' integral beats,
1651 there is no pulse offset here.
1653 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1655 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1657 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1658 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1666 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1667 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1668 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1674 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1676 Glib::Threads::RWLock::ReaderLock lm (lock);
1677 return bbt_to_beats_locked (_metrics, bbt);
1681 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1683 /* CALLER HOLDS READ LOCK */
1684 MeterSection* prev_m = 0;
1685 const double beats = (b < 0.0) ? 0.0 : b;
1687 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1688 MeterSection* m = 0;
1690 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1692 if (m->beat() > beats) {
1693 /* this is the meter after the one our beat is on*/
1702 const double beats_in_ms = beats - prev_m->beat();
1703 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1704 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1705 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1706 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1710 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1711 ret.beats = (uint32_t) floor (remaining_beats);
1712 ret.bars = total_bars;
1714 /* 0 0 0 to 1 1 0 - based mapping*/
1718 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1720 ret.ticks -= BBT_Time::ticks_per_beat;
1723 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1732 TempoMap::beats_to_bbt (const double& beats)
1734 Glib::Threads::RWLock::ReaderLock lm (lock);
1735 return beats_to_bbt_locked (_metrics, beats);
1739 TempoMap::pulse_to_bbt (const double& pulse)
1741 Glib::Threads::RWLock::ReaderLock lm (lock);
1742 MeterSection* prev_m = 0;
1744 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1745 MeterSection* m = 0;
1747 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1750 double const pulses_to_m = m->pulse() - prev_m->pulse();
1751 if (prev_m->pulse() + pulses_to_m > pulse) {
1752 /* this is the meter after the one our beat is on*/
1761 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1762 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1763 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1764 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1765 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1769 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1770 ret.beats = (uint32_t) floor (remaining_beats);
1771 ret.bars = total_bars;
1773 /* 0 0 0 to 1 1 0 mapping*/
1777 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1779 ret.ticks -= BBT_Time::ticks_per_beat;
1782 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1791 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1798 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1801 Glib::Threads::RWLock::ReaderLock lm (lock);
1802 const double beat = beat_at_frame_locked (_metrics, frame);
1804 bbt = beats_to_bbt_locked (_metrics, beat);
1807 /* meter section based */
1809 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1811 /* HOLD THE READER LOCK */
1813 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1818 TempoMap::frame_time (const BBT_Time& bbt)
1821 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1825 if (bbt.beats < 1) {
1826 throw std::logic_error ("beats are counted from one");
1828 Glib::Threads::RWLock::ReaderLock lm (lock);
1830 return frame_time_locked (_metrics, bbt);
1834 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1836 TempoSection* prev_t = 0;
1837 MeterSection* prev_m = 0;
1839 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1842 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1847 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1851 if (t->frame() == prev_t->frame()) {
1855 /* precision check ensures pulses and frames align.*/
1856 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1857 if (!t->locked_to_meter()) {
1865 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1866 if (prev_m && m->position_lock_style() == AudioTime) {
1867 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1868 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1869 const frameoffset_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1870 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1884 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1886 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1888 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1889 if (!t->movable()) {
1890 t->set_active (true);
1893 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1894 t->set_active (false);
1896 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1897 t->set_active (true);
1898 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1907 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1909 TempoSection* prev_t = 0;
1910 TempoSection* section_prev = 0;
1911 framepos_t first_m_frame = 0;
1913 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1915 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1916 if (!m->movable()) {
1917 first_m_frame = m->frame();
1922 if (section->movable() && frame <= first_m_frame) {
1926 section->set_active (true);
1927 section->set_frame (frame);
1929 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1931 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1938 section_prev = prev_t;
1941 if (t->position_lock_style() == MusicTime) {
1942 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1943 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1945 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1946 if (!t->locked_to_meter()) {
1947 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1956 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1957 if (!section->locked_to_meter()) {
1958 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1962 if (section->position_lock_style() == MusicTime) {
1963 /* we're setting the frame */
1964 section->set_position_lock_style (AudioTime);
1965 recompute_tempos (imaginary);
1966 section->set_position_lock_style (MusicTime);
1968 recompute_tempos (imaginary);
1971 recompute_meters (imaginary);
1972 if (check_solved (imaginary, true)) {
1976 MetricSectionFrameSorter fcmp;
1977 imaginary.sort (fcmp);
1978 if (section->position_lock_style() == MusicTime) {
1979 /* we're setting the frame */
1980 section->set_position_lock_style (AudioTime);
1981 recompute_tempos (imaginary);
1982 section->set_position_lock_style (MusicTime);
1984 recompute_tempos (imaginary);
1987 recompute_meters (imaginary);
1988 if (check_solved (imaginary, true)) {
1992 //dump (imaginary, std::cerr);
1998 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
2000 TempoSection* prev_t = 0;
2001 TempoSection* section_prev = 0;
2003 section->set_pulse (pulse);
2005 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2007 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2011 if (!t->movable()) {
2018 section_prev = prev_t;
2021 if (t->position_lock_style() == MusicTime) {
2022 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2023 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2025 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2026 if (!t->locked_to_meter()) {
2027 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2035 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2036 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2039 if (section->position_lock_style() == AudioTime) {
2040 /* we're setting the pulse */
2041 section->set_position_lock_style (MusicTime);
2042 recompute_tempos (imaginary);
2043 section->set_position_lock_style (AudioTime);
2045 recompute_tempos (imaginary);
2048 recompute_meters (imaginary);
2049 if (check_solved (imaginary, false)) {
2053 MetricSectionSorter cmp;
2054 imaginary.sort (cmp);
2055 if (section->position_lock_style() == AudioTime) {
2056 /* we're setting the pulse */
2057 section->set_position_lock_style (MusicTime);
2058 recompute_tempos (imaginary);
2059 section->set_position_lock_style (AudioTime);
2061 recompute_tempos (imaginary);
2064 recompute_meters (imaginary);
2065 if (check_solved (imaginary, false)) {
2069 //dump (imaginary, std::cerr);
2075 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2077 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2078 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2079 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2083 if (!section->movable()) {
2084 /* lock the first tempo to our first meter */
2085 if (!set_active_tempos (imaginary, frame)) {
2090 TempoSection* meter_locked_tempo = 0;
2091 for (Metrics::const_iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2093 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2094 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2095 if (t->frame() == section->frame()) {
2096 meter_locked_tempo = t;
2103 MeterSection* prev_m = 0;
2105 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2107 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2109 if (prev_m && section->movable()) {
2110 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2111 if (beats + prev_m->beat() < section->beat()) {
2112 /* disallow position change if it will alter our beat
2113 we allow tempo changes to do this in recompute_meters().
2114 blocking this is an option, but i'm not convinced that
2115 this is what the user would actually want.
2116 here we set the frame/pulse corresponding to its musical position.
2119 if (meter_locked_tempo) {
2121 bool solved = false;
2122 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2123 const double new_pulse = ((section->beat() - prev_m->beat())
2124 / prev_m->note_divisor()) + prev_m->pulse();
2125 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2126 if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
2127 meter_locked_tempo->set_pulse (new_pulse);
2128 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2129 section->set_frame (smallest_frame);
2130 section->set_pulse (new_pulse);
2135 Metrics::const_iterator d = future_map.begin();
2136 while (d != future_map.end()) {
2147 if (meter_locked_tempo) {
2149 bool solved = false;
2151 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2152 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2153 meter_copy->set_frame (frame);
2155 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2156 section->set_frame (frame);
2157 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2158 / prev_m->note_divisor()) + prev_m->pulse());
2159 solve_map (imaginary, meter_locked_tempo, frame);
2164 Metrics::const_iterator d = future_map.begin();
2165 while (d != future_map.end()) {
2176 /* not movable (first meter atm) */
2177 if (meter_locked_tempo) {
2179 bool solved = false;
2180 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2182 tempo_copy->set_frame (frame);
2183 tempo_copy->set_pulse (0.0);
2185 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2186 section->set_frame (frame);
2187 meter_locked_tempo->set_frame (frame);
2188 meter_locked_tempo->set_pulse (0.0);
2189 solve_map (imaginary, meter_locked_tempo, frame);
2194 Metrics::const_iterator d = future_map.begin();
2195 while (d != future_map.end()) {
2207 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2208 section->set_beat (b_bbt);
2209 section->set_pulse (0.0);
2212 //section->set_frame (frame);
2220 MetricSectionFrameSorter fcmp;
2221 imaginary.sort (fcmp);
2222 if (section->position_lock_style() == MusicTime) {
2223 /* we're setting the frame */
2224 section->set_position_lock_style (AudioTime);
2225 recompute_meters (imaginary);
2226 section->set_position_lock_style (MusicTime);
2228 recompute_meters (imaginary);
2230 //dump (imaginary, std::cerr);
2235 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2237 MeterSection* prev_m = 0;
2239 section->set_pulse (pulse);
2241 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2243 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2244 double new_pulse = 0.0;
2245 pair<double, BBT_Time> b_bbt;
2247 if (prev_m && m == section){
2248 /* the first meter is always audio-locked, so prev_m should exist.
2249 should we allow setting audio locked meters by pulse?
2251 const double beats = floor (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + 0.5);
2252 const int32_t bars = (beats) / prev_m->divisions_per_bar();
2253 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), BBT_Time (bars + prev_m->bbt().bars, 1, 0));
2254 section->set_beat (b_bbt);
2255 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2259 if (m->position_lock_style() == AudioTime) {
2261 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2262 if (beats + prev_m->beat() != m->beat()) {
2263 /* tempo/ meter change caused a change in beat (bar). */
2264 b_bbt = make_pair (beats + prev_m->beat()
2265 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2266 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2268 b_bbt = make_pair (m->beat(), m->bbt());
2269 new_pulse = pulse_at_frame_locked (imaginary, m->frame());
2272 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2275 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2276 b_bbt = make_pair (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2278 m->set_beat (b_bbt);
2279 m->set_pulse (new_pulse);
2284 MetricSectionSorter cmp;
2285 imaginary.sort (cmp);
2286 if (section->position_lock_style() == AudioTime) {
2287 /* we're setting the pulse */
2288 section->set_position_lock_style (MusicTime);
2289 recompute_meters (imaginary);
2290 section->set_position_lock_style (AudioTime);
2292 recompute_meters (imaginary);
2297 /** places a copy of _metrics into copy and returns a pointer
2298 * to section's equivalent in copy.
2301 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2303 TempoSection* ret = 0;
2305 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2308 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2310 ret = new TempoSection (*t);
2311 copy.push_back (ret);
2315 TempoSection* cp = new TempoSection (*t);
2316 copy.push_back (cp);
2318 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2319 MeterSection* cp = new MeterSection (*m);
2320 copy.push_back (cp);
2328 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2330 MeterSection* ret = 0;
2332 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2335 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2336 TempoSection* cp = new TempoSection (*t);
2337 copy.push_back (cp);
2340 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2342 ret = new MeterSection (*m);
2343 copy.push_back (ret);
2346 MeterSection* cp = new MeterSection (*m);
2347 copy.push_back (cp);
2355 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2358 TempoSection* tempo_copy = 0;
2361 Glib::Threads::RWLock::ReaderLock lm (lock);
2362 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2368 const double beat = bbt_to_beats_locked (copy, bbt);
2369 const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2371 Metrics::const_iterator d = copy.begin();
2372 while (d != copy.end()) {
2381 * This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
2382 * taking any possible reordering as a consequence of this into account.
2383 * @param section - the section to be altered
2384 * @param bpm - the new Tempo
2385 * @param bbt - the bbt where the altered tempo will fall
2386 * @return returns - the position in frames where the new tempo section will lie.
2389 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2391 Glib::Threads::RWLock::ReaderLock lm (lock);
2394 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2398 const double beat = bbt_to_beats_locked (future_map, bbt);
2400 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2401 ret = tempo_copy->frame();
2403 ret = frame_at_beat_locked (_metrics, beat);
2406 Metrics::const_iterator d = future_map.begin();
2407 while (d != future_map.end()) {
2415 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2417 Glib::Threads::RWLock::ReaderLock lm (lock);
2420 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2422 if (solve_map (future_map, tempo_copy, frame)) {
2423 ret = tempo_copy->pulse();
2425 ret = pulse_at_frame_locked (_metrics, frame);
2428 Metrics::const_iterator d = future_map.begin();
2429 while (d != future_map.end()) {
2437 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2441 Glib::Threads::RWLock::WriterLock lm (lock);
2442 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2443 if (solve_map (future_map, tempo_copy, frame)) {
2444 solve_map (_metrics, ts, frame);
2445 recompute_meters (_metrics);
2449 Metrics::const_iterator d = future_map.begin();
2450 while (d != future_map.end()) {
2455 MetricPositionChanged (); // Emit Signal
2459 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2463 Glib::Threads::RWLock::WriterLock lm (lock);
2464 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2465 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2466 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2467 recompute_meters (_metrics);
2471 Metrics::const_iterator d = future_map.begin();
2472 while (d != future_map.end()) {
2477 MetricPositionChanged (); // Emit Signal
2481 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2485 Glib::Threads::RWLock::WriterLock lm (lock);
2486 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2487 if (solve_map (future_map, copy, frame)) {
2488 solve_map (_metrics, ms, frame);
2492 Metrics::const_iterator d = future_map.begin();
2493 while (d != future_map.end()) {
2498 MetricPositionChanged (); // Emit Signal
2502 TempoMap::gui_move_meter (MeterSection* ms, const double& pulse)
2506 Glib::Threads::RWLock::WriterLock lm (lock);
2507 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2508 if (solve_map (future_map, copy, pulse)) {
2509 solve_map (_metrics, ms, pulse);
2513 Metrics::const_iterator d = future_map.begin();
2514 while (d != future_map.end()) {
2519 MetricPositionChanged (); // Emit Signal
2523 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2526 bool can_solve = false;
2528 Glib::Threads::RWLock::WriterLock lm (lock);
2529 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2530 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2531 recompute_tempos (future_map);
2533 if (check_solved (future_map, true)) {
2534 ts->set_beats_per_minute (bpm.beats_per_minute());
2535 recompute_map (_metrics);
2540 Metrics::const_iterator d = future_map.begin();
2541 while (d != future_map.end()) {
2546 MetricPositionChanged (); // Emit Signal
2552 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2555 TempoSection* ts = 0;
2557 if (ms->position_lock_style() == AudioTime) {
2558 /* disabled for now due to faked tempo locked to meter pulse */
2562 Glib::Threads::RWLock::WriterLock lm (lock);
2563 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2567 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2568 TempoSection* prev_to_prev_t = 0;
2569 const frameoffset_t fr_off = frame - ms->frame();
2570 double new_bpm = 0.0;
2573 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2576 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2577 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2579 double contribution = 0.0;
2580 frameoffset_t frame_contribution = 0.0;
2581 frameoffset_t prev_t_frame_contribution = 0.0;
2583 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2584 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2585 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2586 frame_contribution = contribution * (double) fr_off;
2587 prev_t_frame_contribution = fr_off - frame_contribution;
2590 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2592 if (prev_t->position_lock_style() == MusicTime) {
2593 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2594 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2595 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2598 /* prev to prev is irrelevant */
2599 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2600 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2602 if (frame_pulse != prev_t->pulse()) {
2603 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2605 new_bpm = prev_t->beats_per_minute();
2610 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2611 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2612 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2614 /* prev_to_prev_t is irrelevant */
2616 if (frame != prev_t->frame()) {
2617 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2619 new_bpm = prev_t->beats_per_minute();
2623 } else if (prev_t->c_func() < 0.0) {
2624 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2625 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2627 /* prev_to_prev_t is irrelevant */
2628 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2631 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2632 if (diff > -0.1 && diff < 0.1) {
2633 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2634 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2637 } else if (prev_t->c_func() > 0.0) {
2638 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2639 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2641 /* prev_to_prev_t is irrelevant */
2642 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2645 /* limits - a bit clunky, but meh */
2646 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2647 if (diff > -0.1 && diff < 0.1) {
2648 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2649 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2653 prev_t->set_beats_per_minute (new_bpm);
2654 recompute_tempos (future_map);
2655 recompute_meters (future_map);
2657 if (check_solved (future_map, true)) {
2659 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2660 prev_t->set_beats_per_minute (new_bpm);
2661 recompute_tempos (_metrics);
2663 if (ms->position_lock_style() == AudioTime) {
2664 ms->set_frame (frame);
2667 recompute_meters (_metrics);
2671 Metrics::const_iterator d = future_map.begin();
2672 while (d != future_map.end()) {
2677 MetricPositionChanged (); // Emit Signal
2682 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2684 Glib::Threads::RWLock::ReaderLock lm (lock);
2686 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2687 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2688 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2690 return frame_at_beat_locked (_metrics, total_beats);
2694 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2696 return round_to_type (fr, dir, Bar);
2700 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2702 return round_to_type (fr, dir, Beat);
2706 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2708 Glib::Threads::RWLock::ReaderLock lm (lock);
2709 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2710 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2711 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2713 ticks -= beats * BBT_Time::ticks_per_beat;
2716 /* round to next (or same iff dir == RoundUpMaybe) */
2718 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2720 if (mod == 0 && dir == RoundUpMaybe) {
2721 /* right on the subdivision, which is fine, so do nothing */
2723 } else if (mod == 0) {
2724 /* right on the subdivision, so the difference is just the subdivision ticks */
2725 ticks += ticks_one_subdivisions_worth;
2728 /* not on subdivision, compute distance to next subdivision */
2730 ticks += ticks_one_subdivisions_worth - mod;
2733 if (ticks >= BBT_Time::ticks_per_beat) {
2734 ticks -= BBT_Time::ticks_per_beat;
2736 } else if (dir < 0) {
2738 /* round to previous (or same iff dir == RoundDownMaybe) */
2740 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2742 if (difference == 0 && dir == RoundDownAlways) {
2743 /* right on the subdivision, but force-rounding down,
2744 so the difference is just the subdivision ticks */
2745 difference = ticks_one_subdivisions_worth;
2748 if (ticks < difference) {
2749 ticks = BBT_Time::ticks_per_beat - ticks;
2751 ticks -= difference;
2755 /* round to nearest */
2758 /* compute the distance to the previous and next subdivision */
2760 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2762 /* closer to the next subdivision, so shift forward */
2764 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2766 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2768 if (ticks > BBT_Time::ticks_per_beat) {
2770 ticks -= BBT_Time::ticks_per_beat;
2771 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2774 } else if (rem > 0) {
2776 /* closer to previous subdivision, so shift backward */
2780 /* can't go backwards past zero, so ... */
2783 /* step back to previous beat */
2785 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2786 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2788 ticks = lrint (ticks - rem);
2789 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2792 /* on the subdivision, do nothing */
2796 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2802 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2804 if (sub_num == -1) {
2805 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2806 if ((double) when.beats > bpb / 2.0) {
2812 } else if (sub_num == 0) {
2813 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2814 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2816 while ((double) when.beats > bpb) {
2818 when.beats -= (uint32_t) floor (bpb);
2824 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2826 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2827 /* closer to the next subdivision, so shift forward */
2829 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2831 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2833 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2836 } else if (rem > 0) {
2837 /* closer to previous subdivision, so shift backward */
2839 if (rem > when.ticks) {
2840 if (when.beats == 0) {
2841 /* can't go backwards past zero, so ... */
2843 /* step back to previous beat */
2845 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2847 when.ticks = when.ticks - rem;
2853 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2855 Glib::Threads::RWLock::ReaderLock lm (lock);
2857 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2858 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2863 /* find bar previous to 'frame' */
2866 return frame_time_locked (_metrics, bbt);
2868 } else if (dir > 0) {
2869 /* find bar following 'frame' */
2873 return frame_time_locked (_metrics, bbt);
2875 /* true rounding: find nearest bar */
2876 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2879 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2881 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2883 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2894 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2895 } else if (dir > 0) {
2896 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2898 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2907 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2908 framepos_t lower, framepos_t upper)
2910 Glib::Threads::RWLock::ReaderLock lm (lock);
2911 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2913 /* although the map handles negative beats, bbt doesn't. */
2917 while (pos < upper) {
2918 pos = frame_at_beat_locked (_metrics, cnt);
2919 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2920 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2921 const BBT_Time bbt = beats_to_bbt (cnt);
2922 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2928 TempoMap::tempo_section_at (framepos_t frame) const
2930 Glib::Threads::RWLock::ReaderLock lm (lock);
2931 return tempo_section_at_locked (_metrics, frame);
2935 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2937 Metrics::const_iterator i;
2938 TempoSection* prev = 0;
2940 for (i = metrics.begin(); i != metrics.end(); ++i) {
2943 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2947 if (prev && t->frame() > frame) {
2957 abort(); /*NOTREACHED*/
2964 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2966 TempoSection* prev_t = 0;
2967 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2969 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2971 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2972 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2983 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2985 TempoSection* prev_t = 0;
2987 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2989 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2990 if (prev_t && t->pulse() > pulse) {
3000 /* don't use this to calculate length (the tempo is only correct for this frame).
3001 do that stuff based on the beat_at_frame and frame_at_beat api
3004 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3006 Glib::Threads::RWLock::ReaderLock lm (lock);
3008 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3009 const TempoSection* ts_after = 0;
3010 Metrics::const_iterator i;
3012 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3015 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3019 if ((*i)->frame() > frame) {
3027 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3029 /* must be treated as constant tempo */
3030 return ts_at->frames_per_beat (_frame_rate);
3034 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3036 TempoSection* prev_t = 0;
3038 Metrics::const_iterator i;
3040 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3042 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3046 if ((prev_t) && t->frame() > frame) {
3047 /* t is the section past frame */
3048 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3049 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3056 const double ret = prev_t->beats_per_minute();
3057 const Tempo ret_tempo (ret, prev_t->note_type ());
3063 TempoMap::tempo_at (const framepos_t& frame) const
3065 Glib::Threads::RWLock::ReaderLock lm (lock);
3066 return tempo_at_locked (_metrics, frame);
3070 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3072 Metrics::const_iterator i;
3073 MeterSection* prev = 0;
3075 for (i = metrics.begin(); i != metrics.end(); ++i) {
3078 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3080 if (prev && (*i)->frame() > frame) {
3090 abort(); /*NOTREACHED*/
3098 TempoMap::meter_section_at (framepos_t frame) const
3100 Glib::Threads::RWLock::ReaderLock lm (lock);
3101 return meter_section_at_locked (_metrics, frame);
3105 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3107 MeterSection* prev_m = 0;
3109 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3111 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3112 if (prev_m && m->beat() > beat) {
3123 TempoMap::meter_section_at_beat (double beat) const
3125 Glib::Threads::RWLock::ReaderLock lm (lock);
3126 return meter_section_at_beat_locked (_metrics, beat);
3130 TempoMap::meter_at (framepos_t frame) const
3132 TempoMetric m (metric_at (frame));
3137 TempoMap::fix_legacy_session ()
3139 MeterSection* prev_m = 0;
3140 TempoSection* prev_t = 0;
3142 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3146 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3147 if (!m->movable()) {
3148 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3152 m->set_position_lock_style (AudioTime);
3157 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3158 + (m->bbt().beats - 1)
3159 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3161 m->set_beat (start);
3162 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3163 + (m->bbt().beats - 1)
3164 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3165 m->set_pulse (start_beat / prev_m->note_divisor());
3168 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3174 if (!t->movable()) {
3177 t->set_position_lock_style (AudioTime);
3183 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3184 + (t->legacy_bbt().beats - 1)
3185 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3187 t->set_pulse (beat / prev_m->note_divisor());
3189 /* really shouldn't happen but.. */
3190 t->set_pulse (beat / 4.0);
3199 TempoMap::get_state ()
3201 Metrics::const_iterator i;
3202 XMLNode *root = new XMLNode ("TempoMap");
3205 Glib::Threads::RWLock::ReaderLock lm (lock);
3206 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3207 root->add_child_nocopy ((*i)->get_state());
3215 TempoMap::set_state (const XMLNode& node, int /*version*/)
3218 Glib::Threads::RWLock::WriterLock lm (lock);
3221 XMLNodeConstIterator niter;
3222 Metrics old_metrics (_metrics);
3225 nlist = node.children();
3227 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3228 XMLNode* child = *niter;
3230 if (child->name() == TempoSection::xml_state_node_name) {
3233 TempoSection* ts = new TempoSection (*child);
3234 _metrics.push_back (ts);
3237 catch (failed_constructor& err){
3238 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3239 _metrics = old_metrics;
3243 } else if (child->name() == MeterSection::xml_state_node_name) {
3246 MeterSection* ms = new MeterSection (*child);
3247 _metrics.push_back (ms);
3250 catch (failed_constructor& err) {
3251 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3252 _metrics = old_metrics;
3258 if (niter == nlist.end()) {
3259 MetricSectionSorter cmp;
3260 _metrics.sort (cmp);
3263 /* check for legacy sessions where bbt was the base musical unit for tempo */
3264 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3266 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3267 if (t->legacy_bbt().bars != 0) {
3268 fix_legacy_session();
3275 /* check for multiple tempo/meters at the same location, which
3276 ardour2 somehow allowed.
3279 Metrics::iterator prev = _metrics.end();
3280 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3281 if (prev != _metrics.end()) {
3283 MeterSection* prev_m;
3285 TempoSection* prev_t;
3286 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3287 if (prev_m->pulse() == ms->pulse()) {
3288 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3289 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3292 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3293 if (prev_t->pulse() == ts->pulse()) {
3294 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3295 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3303 recompute_map (_metrics);
3306 PropertyChanged (PropertyChange ());
3312 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3314 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3315 const MeterSection* m;
3316 const TempoSection* t;
3317 const TempoSection* prev_t = 0;
3319 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3321 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3322 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3323 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3324 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3326 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3327 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;
3330 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3331 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3332 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3335 o << "------" << std::endl;
3339 TempoMap::n_tempos() const
3341 Glib::Threads::RWLock::ReaderLock lm (lock);
3344 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3345 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3354 TempoMap::n_meters() const
3356 Glib::Threads::RWLock::ReaderLock lm (lock);
3359 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3360 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3369 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3372 Glib::Threads::RWLock::WriterLock lm (lock);
3373 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3374 if ((*i)->frame() >= where && (*i)->movable ()) {
3375 (*i)->set_frame ((*i)->frame() + amount);
3379 /* now reset the BBT time of all metrics, based on their new
3380 * audio time. This is the only place where we do this reverse
3384 Metrics::iterator i;
3385 const MeterSection* meter;
3386 const TempoSection* tempo;
3390 meter = &first_meter ();
3391 tempo = &first_tempo ();
3396 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3399 MetricSection* prev = 0;
3401 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3404 //TempoMetric metric (*meter, *tempo);
3405 MeterSection* ms = const_cast<MeterSection*>(meter);
3406 TempoSection* ts = const_cast<TempoSection*>(tempo);
3409 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3413 ts->set_pulse (t->pulse());
3415 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3416 ts->set_pulse (m->pulse());
3418 ts->set_frame (prev->frame());
3422 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3423 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3424 ms->set_beat (start);
3425 ms->set_pulse (m->pulse());
3427 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3431 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3432 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3433 ms->set_beat (start);
3434 ms->set_pulse (t->pulse());
3436 ms->set_frame (prev->frame());
3440 // metric will be at frames=0 bbt=1|1|0 by default
3441 // which is correct for our purpose
3444 // cerr << bbt << endl;
3446 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3450 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3452 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3453 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3454 bbt_time (m->frame(), bbt);
3456 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3462 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3463 /* round up to next beat */
3469 if (bbt.beats != 1) {
3470 /* round up to next bar */
3475 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3476 m->set_beat (start);
3477 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3479 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3481 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3482 abort(); /*NOTREACHED*/
3488 recompute_map (_metrics);
3492 PropertyChanged (PropertyChange ());
3495 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3499 std::list<MetricSection*> metric_kill_list;
3501 TempoSection* last_tempo = NULL;
3502 MeterSection* last_meter = NULL;
3503 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3504 bool meter_after = false; // is there a meter marker likewise?
3506 Glib::Threads::RWLock::WriterLock lm (lock);
3507 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3508 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3509 metric_kill_list.push_back(*i);
3510 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3513 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3517 else if ((*i)->frame() >= where) {
3518 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3519 (*i)->set_frame ((*i)->frame() - amount);
3520 if ((*i)->frame() == where) {
3521 // marker was immediately after end of range
3522 tempo_after = dynamic_cast<TempoSection*> (*i);
3523 meter_after = dynamic_cast<MeterSection*> (*i);
3529 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3530 if (last_tempo && !tempo_after) {
3531 metric_kill_list.remove(last_tempo);
3532 last_tempo->set_frame(where);
3535 if (last_meter && !meter_after) {
3536 metric_kill_list.remove(last_meter);
3537 last_meter->set_frame(where);
3541 //remove all the remaining metrics
3542 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3543 _metrics.remove(*i);
3548 recompute_map (_metrics);
3551 PropertyChanged (PropertyChange ());
3555 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3556 * pos can be -ve, if required.
3559 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3561 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3564 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3566 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3568 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3571 /** Add the BBT interval op to pos and return the result */
3573 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3575 Glib::Threads::RWLock::ReaderLock lm (lock);
3577 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3578 pos_bbt.ticks += op.ticks;
3579 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3581 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3583 pos_bbt.beats += op.beats;
3584 /* the meter in effect will start on the bar */
3585 double divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3586 while (pos_bbt.beats >= divisions_per_bar + 1) {
3588 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3589 pos_bbt.beats -= divisions_per_bar;
3591 pos_bbt.bars += op.bars;
3593 return frame_time_locked (_metrics, pos_bbt);
3596 /** Count the number of beats that are equivalent to distance when going forward,
3600 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3602 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3606 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3612 operator<< (std::ostream& o, const Meter& m) {
3613 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3617 operator<< (std::ostream& o, const Tempo& t) {
3618 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3622 operator<< (std::ostream& o, const MetricSection& section) {
3624 o << "MetricSection @ " << section.frame() << ' ';
3626 const TempoSection* ts;
3627 const MeterSection* ms;
3629 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3630 o << *((const Tempo*) ts);
3631 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3632 o << *((const Meter*) ms);