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 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2122 const double new_pulse = ((section->beat() - prev_m->beat())
2123 / prev_m->note_divisor()) + prev_m->pulse();
2124 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2125 if (solve_map (future_map, tempo_copy, smallest_frame)) {
2126 meter_locked_tempo->set_pulse (new_pulse);
2127 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2128 section->set_frame (smallest_frame);
2129 section->set_pulse (new_pulse);
2136 if (meter_locked_tempo) {
2139 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2140 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2141 meter_copy->set_frame (frame);
2143 if (solve_map (future_map, tempo_copy, frame)) {
2144 section->set_frame (frame);
2145 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2146 / prev_m->note_divisor()) + prev_m->pulse());
2147 solve_map (imaginary, meter_locked_tempo, frame);
2154 /* not movable (first meter atm) */
2155 if (meter_locked_tempo) {
2157 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2159 tempo_copy->set_frame (frame);
2160 tempo_copy->set_pulse (0.0);
2162 if (solve_map (future_map, tempo_copy, frame)) {
2163 section->set_frame (frame);
2164 meter_locked_tempo->set_frame (frame);
2165 meter_locked_tempo->set_pulse (0.0);
2166 solve_map (imaginary, meter_locked_tempo, frame);
2173 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2174 section->set_beat (b_bbt);
2175 section->set_pulse (0.0);
2178 //section->set_frame (frame);
2186 MetricSectionFrameSorter fcmp;
2187 imaginary.sort (fcmp);
2188 if (section->position_lock_style() == MusicTime) {
2189 /* we're setting the frame */
2190 section->set_position_lock_style (AudioTime);
2191 recompute_meters (imaginary);
2192 section->set_position_lock_style (MusicTime);
2194 recompute_meters (imaginary);
2196 //dump (imaginary, std::cerr);
2201 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2203 MeterSection* prev_m = 0;
2205 section->set_pulse (pulse);
2207 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2209 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2210 double new_pulse = 0.0;
2211 pair<double, BBT_Time> b_bbt;
2213 if (prev_m && m == section){
2214 /* the first meter is always audio-locked, so prev_m should exist.
2215 should we allow setting audio locked meters by pulse?
2217 const double beats = floor (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + 0.5);
2218 const int32_t bars = (beats) / prev_m->divisions_per_bar();
2219 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), BBT_Time (bars + prev_m->bbt().bars, 1, 0));
2220 section->set_beat (b_bbt);
2221 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2225 if (m->position_lock_style() == AudioTime) {
2227 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2228 if (beats + prev_m->beat() != m->beat()) {
2229 /* tempo/ meter change caused a change in beat (bar). */
2230 b_bbt = make_pair (beats + prev_m->beat()
2231 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2232 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2234 b_bbt = make_pair (m->beat(), m->bbt());
2235 new_pulse = pulse_at_frame_locked (imaginary, m->frame());
2238 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2241 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2242 b_bbt = make_pair (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2244 m->set_beat (b_bbt);
2245 m->set_pulse (new_pulse);
2250 MetricSectionSorter cmp;
2251 imaginary.sort (cmp);
2252 if (section->position_lock_style() == AudioTime) {
2253 /* we're setting the pulse */
2254 section->set_position_lock_style (MusicTime);
2255 recompute_meters (imaginary);
2256 section->set_position_lock_style (AudioTime);
2258 recompute_meters (imaginary);
2263 /** places a copy of _metrics into copy and returns a pointer
2264 * to section's equivalent in copy.
2267 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2269 TempoSection* ret = 0;
2271 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2274 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2276 ret = new TempoSection (*t);
2277 copy.push_back (ret);
2281 TempoSection* cp = new TempoSection (*t);
2282 copy.push_back (cp);
2284 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2285 MeterSection* cp = new MeterSection (*m);
2286 copy.push_back (cp);
2294 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2296 MeterSection* ret = 0;
2298 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2301 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2302 TempoSection* cp = new TempoSection (*t);
2303 copy.push_back (cp);
2306 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2308 ret = new MeterSection (*m);
2309 copy.push_back (ret);
2312 MeterSection* cp = new MeterSection (*m);
2313 copy.push_back (cp);
2321 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2324 TempoSection* tempo_copy = 0;
2327 Glib::Threads::RWLock::ReaderLock lm (lock);
2328 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2334 const double beat = bbt_to_beats_locked (copy, bbt);
2335 const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2337 Metrics::const_iterator d = copy.begin();
2338 while (d != copy.end()) {
2347 * 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,
2348 * taking any possible reordering as a consequence of this into account.
2349 * @param section - the section to be altered
2350 * @param bpm - the new Tempo
2351 * @param bbt - the bbt where the altered tempo will fall
2352 * @return returns - the position in frames where the new tempo section will lie.
2355 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2357 Glib::Threads::RWLock::ReaderLock lm (lock);
2360 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2364 const double beat = bbt_to_beats_locked (future_map, bbt);
2366 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2367 ret = tempo_copy->frame();
2369 ret = frame_at_beat_locked (_metrics, beat);
2372 Metrics::const_iterator d = future_map.begin();
2373 while (d != future_map.end()) {
2381 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2383 Glib::Threads::RWLock::ReaderLock lm (lock);
2386 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2388 if (solve_map (future_map, tempo_copy, frame)) {
2389 ret = tempo_copy->pulse();
2391 ret = pulse_at_frame_locked (_metrics, frame);
2394 Metrics::const_iterator d = future_map.begin();
2395 while (d != future_map.end()) {
2403 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2407 Glib::Threads::RWLock::WriterLock lm (lock);
2408 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2409 if (solve_map (future_map, tempo_copy, frame)) {
2410 solve_map (_metrics, ts, frame);
2411 recompute_meters (_metrics);
2415 Metrics::const_iterator d = future_map.begin();
2416 while (d != future_map.end()) {
2421 MetricPositionChanged (); // Emit Signal
2425 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2429 Glib::Threads::RWLock::WriterLock lm (lock);
2430 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2431 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2432 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2433 recompute_meters (_metrics);
2437 Metrics::const_iterator d = future_map.begin();
2438 while (d != future_map.end()) {
2443 MetricPositionChanged (); // Emit Signal
2447 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2451 Glib::Threads::RWLock::WriterLock lm (lock);
2452 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2453 if (solve_map (future_map, copy, frame)) {
2454 solve_map (_metrics, ms, frame);
2458 Metrics::const_iterator d = future_map.begin();
2459 while (d != future_map.end()) {
2464 MetricPositionChanged (); // Emit Signal
2468 TempoMap::gui_move_meter (MeterSection* ms, const double& pulse)
2472 Glib::Threads::RWLock::WriterLock lm (lock);
2473 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2474 if (solve_map (future_map, copy, pulse)) {
2475 solve_map (_metrics, ms, pulse);
2479 Metrics::const_iterator d = future_map.begin();
2480 while (d != future_map.end()) {
2485 MetricPositionChanged (); // Emit Signal
2489 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2492 bool can_solve = false;
2494 Glib::Threads::RWLock::WriterLock lm (lock);
2495 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2496 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2497 recompute_tempos (future_map);
2499 if (check_solved (future_map, true)) {
2500 ts->set_beats_per_minute (bpm.beats_per_minute());
2501 recompute_map (_metrics);
2506 Metrics::const_iterator d = future_map.begin();
2507 while (d != future_map.end()) {
2512 MetricPositionChanged (); // Emit Signal
2518 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2521 TempoSection* ts = 0;
2523 if (ms->position_lock_style() == AudioTime) {
2524 /* disabled for now due to faked tempo locked to meter pulse */
2528 Glib::Threads::RWLock::WriterLock lm (lock);
2529 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2533 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2534 TempoSection* prev_to_prev_t = 0;
2535 const frameoffset_t fr_off = frame - ms->frame();
2536 double new_bpm = 0.0;
2539 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
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 frameoffset_t frame_contribution = 0.0;
2547 frameoffset_t prev_t_frame_contribution = 0.0;
2549 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2550 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2551 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2552 frame_contribution = contribution * (double) fr_off;
2553 prev_t_frame_contribution = fr_off - frame_contribution;
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) {
2560 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2561 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2564 /* prev to prev is irrelevant */
2565 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2566 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2568 if (frame_pulse != prev_t->pulse()) {
2569 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2571 new_bpm = prev_t->beats_per_minute();
2576 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2577 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2578 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2580 /* prev_to_prev_t is irrelevant */
2582 if (frame != prev_t->frame()) {
2583 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2585 new_bpm = prev_t->beats_per_minute();
2589 } else if (prev_t->c_func() < 0.0) {
2590 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2591 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2593 /* prev_to_prev_t is irrelevant */
2594 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2597 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2598 if (diff > -0.1 && diff < 0.1) {
2599 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2600 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2603 } else if (prev_t->c_func() > 0.0) {
2604 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2605 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2607 /* prev_to_prev_t is irrelevant */
2608 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2611 /* limits - a bit clunky, but meh */
2612 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2613 if (diff > -0.1 && diff < 0.1) {
2614 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2615 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2619 prev_t->set_beats_per_minute (new_bpm);
2620 recompute_tempos (future_map);
2621 recompute_meters (future_map);
2623 if (check_solved (future_map, true)) {
2625 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2626 prev_t->set_beats_per_minute (new_bpm);
2627 recompute_tempos (_metrics);
2629 if (ms->position_lock_style() == AudioTime) {
2630 ms->set_frame (frame);
2633 recompute_meters (_metrics);
2637 Metrics::const_iterator d = future_map.begin();
2638 while (d != future_map.end()) {
2643 MetricPositionChanged (); // Emit Signal
2648 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2650 Glib::Threads::RWLock::ReaderLock lm (lock);
2652 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2653 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2654 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2656 return frame_at_beat_locked (_metrics, total_beats);
2660 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2662 return round_to_type (fr, dir, Bar);
2666 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2668 return round_to_type (fr, dir, Beat);
2672 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2674 Glib::Threads::RWLock::ReaderLock lm (lock);
2675 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2676 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2677 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2679 ticks -= beats * BBT_Time::ticks_per_beat;
2682 /* round to next (or same iff dir == RoundUpMaybe) */
2684 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2686 if (mod == 0 && dir == RoundUpMaybe) {
2687 /* right on the subdivision, which is fine, so do nothing */
2689 } else if (mod == 0) {
2690 /* right on the subdivision, so the difference is just the subdivision ticks */
2691 ticks += ticks_one_subdivisions_worth;
2694 /* not on subdivision, compute distance to next subdivision */
2696 ticks += ticks_one_subdivisions_worth - mod;
2699 if (ticks >= BBT_Time::ticks_per_beat) {
2700 ticks -= BBT_Time::ticks_per_beat;
2702 } else if (dir < 0) {
2704 /* round to previous (or same iff dir == RoundDownMaybe) */
2706 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2708 if (difference == 0 && dir == RoundDownAlways) {
2709 /* right on the subdivision, but force-rounding down,
2710 so the difference is just the subdivision ticks */
2711 difference = ticks_one_subdivisions_worth;
2714 if (ticks < difference) {
2715 ticks = BBT_Time::ticks_per_beat - ticks;
2717 ticks -= difference;
2721 /* round to nearest */
2724 /* compute the distance to the previous and next subdivision */
2726 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2728 /* closer to the next subdivision, so shift forward */
2730 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2732 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2734 if (ticks > BBT_Time::ticks_per_beat) {
2736 ticks -= BBT_Time::ticks_per_beat;
2737 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2740 } else if (rem > 0) {
2742 /* closer to previous subdivision, so shift backward */
2746 /* can't go backwards past zero, so ... */
2749 /* step back to previous beat */
2751 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2752 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2754 ticks = lrint (ticks - rem);
2755 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2758 /* on the subdivision, do nothing */
2762 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2768 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2770 if (sub_num == -1) {
2771 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2772 if ((double) when.beats > bpb / 2.0) {
2778 } else if (sub_num == 0) {
2779 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2780 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2782 while ((double) when.beats > bpb) {
2784 when.beats -= (uint32_t) floor (bpb);
2790 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2792 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2793 /* closer to the next subdivision, so shift forward */
2795 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2797 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2799 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2802 } else if (rem > 0) {
2803 /* closer to previous subdivision, so shift backward */
2805 if (rem > when.ticks) {
2806 if (when.beats == 0) {
2807 /* can't go backwards past zero, so ... */
2809 /* step back to previous beat */
2811 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2813 when.ticks = when.ticks - rem;
2819 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2821 Glib::Threads::RWLock::ReaderLock lm (lock);
2823 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2824 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2829 /* find bar previous to 'frame' */
2832 return frame_time_locked (_metrics, bbt);
2834 } else if (dir > 0) {
2835 /* find bar following 'frame' */
2839 return frame_time_locked (_metrics, bbt);
2841 /* true rounding: find nearest bar */
2842 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2845 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2847 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2849 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2860 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2861 } else if (dir > 0) {
2862 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2864 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2873 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2874 framepos_t lower, framepos_t upper)
2876 Glib::Threads::RWLock::ReaderLock lm (lock);
2877 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2879 /* although the map handles negative beats, bbt doesn't. */
2883 while (pos < upper) {
2884 pos = frame_at_beat_locked (_metrics, cnt);
2885 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2886 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2887 const BBT_Time bbt = beats_to_bbt (cnt);
2888 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2894 TempoMap::tempo_section_at (framepos_t frame) const
2896 Glib::Threads::RWLock::ReaderLock lm (lock);
2897 return tempo_section_at_locked (_metrics, frame);
2901 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2903 Metrics::const_iterator i;
2904 TempoSection* prev = 0;
2906 for (i = metrics.begin(); i != metrics.end(); ++i) {
2909 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2913 if (prev && t->frame() > frame) {
2923 abort(); /*NOTREACHED*/
2930 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2932 TempoSection* prev_t = 0;
2933 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2935 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2937 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2938 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2949 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2951 TempoSection* prev_t = 0;
2953 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2955 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2956 if (prev_t && t->pulse() > pulse) {
2966 /* don't use this to calculate length (the tempo is only correct for this frame).
2967 do that stuff based on the beat_at_frame and frame_at_beat api
2970 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2972 Glib::Threads::RWLock::ReaderLock lm (lock);
2974 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
2975 const TempoSection* ts_after = 0;
2976 Metrics::const_iterator i;
2978 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2981 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2985 if ((*i)->frame() > frame) {
2993 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2995 /* must be treated as constant tempo */
2996 return ts_at->frames_per_beat (_frame_rate);
3000 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3002 TempoSection* prev_t = 0;
3004 Metrics::const_iterator i;
3006 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3008 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3012 if ((prev_t) && t->frame() > frame) {
3013 /* t is the section past frame */
3014 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3015 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3022 const double ret = prev_t->beats_per_minute();
3023 const Tempo ret_tempo (ret, prev_t->note_type ());
3029 TempoMap::tempo_at (const framepos_t& frame) const
3031 Glib::Threads::RWLock::ReaderLock lm (lock);
3032 return tempo_at_locked (_metrics, frame);
3036 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3038 Metrics::const_iterator i;
3039 MeterSection* prev = 0;
3041 for (i = metrics.begin(); i != metrics.end(); ++i) {
3044 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3046 if (prev && (*i)->frame() > frame) {
3056 abort(); /*NOTREACHED*/
3064 TempoMap::meter_section_at (framepos_t frame) const
3066 Glib::Threads::RWLock::ReaderLock lm (lock);
3067 return meter_section_at_locked (_metrics, frame);
3071 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3073 MeterSection* prev_m = 0;
3075 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3077 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3078 if (prev_m && m->beat() > beat) {
3089 TempoMap::meter_section_at_beat (double beat) const
3091 Glib::Threads::RWLock::ReaderLock lm (lock);
3092 return meter_section_at_beat_locked (_metrics, beat);
3096 TempoMap::meter_at (framepos_t frame) const
3098 TempoMetric m (metric_at (frame));
3103 TempoMap::fix_legacy_session ()
3105 MeterSection* prev_m = 0;
3106 TempoSection* prev_t = 0;
3108 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3112 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3113 if (!m->movable()) {
3114 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3118 m->set_position_lock_style (AudioTime);
3123 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3124 + (m->bbt().beats - 1)
3125 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3127 m->set_beat (start);
3128 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3129 + (m->bbt().beats - 1)
3130 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3131 m->set_pulse (start_beat / prev_m->note_divisor());
3134 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3140 if (!t->movable()) {
3143 t->set_position_lock_style (AudioTime);
3149 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3150 + (t->legacy_bbt().beats - 1)
3151 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3153 t->set_pulse (beat / prev_m->note_divisor());
3155 /* really shouldn't happen but.. */
3156 t->set_pulse (beat / 4.0);
3165 TempoMap::get_state ()
3167 Metrics::const_iterator i;
3168 XMLNode *root = new XMLNode ("TempoMap");
3171 Glib::Threads::RWLock::ReaderLock lm (lock);
3172 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3173 root->add_child_nocopy ((*i)->get_state());
3181 TempoMap::set_state (const XMLNode& node, int /*version*/)
3184 Glib::Threads::RWLock::WriterLock lm (lock);
3187 XMLNodeConstIterator niter;
3188 Metrics old_metrics (_metrics);
3191 nlist = node.children();
3193 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3194 XMLNode* child = *niter;
3196 if (child->name() == TempoSection::xml_state_node_name) {
3199 TempoSection* ts = new TempoSection (*child);
3200 _metrics.push_back (ts);
3203 catch (failed_constructor& err){
3204 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3205 _metrics = old_metrics;
3209 } else if (child->name() == MeterSection::xml_state_node_name) {
3212 MeterSection* ms = new MeterSection (*child);
3213 _metrics.push_back (ms);
3216 catch (failed_constructor& err) {
3217 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3218 _metrics = old_metrics;
3224 if (niter == nlist.end()) {
3225 MetricSectionSorter cmp;
3226 _metrics.sort (cmp);
3229 /* check for legacy sessions where bbt was the base musical unit for tempo */
3230 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3232 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3233 if (t->legacy_bbt().bars != 0) {
3234 fix_legacy_session();
3241 /* check for multiple tempo/meters at the same location, which
3242 ardour2 somehow allowed.
3245 Metrics::iterator prev = _metrics.end();
3246 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3247 if (prev != _metrics.end()) {
3249 MeterSection* prev_m;
3251 TempoSection* prev_t;
3252 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3253 if (prev_m->pulse() == ms->pulse()) {
3254 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3255 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3258 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3259 if (prev_t->pulse() == ts->pulse()) {
3260 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3261 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3269 recompute_map (_metrics);
3272 PropertyChanged (PropertyChange ());
3278 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3280 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3281 const MeterSection* m;
3282 const TempoSection* t;
3283 const TempoSection* prev_t = 0;
3285 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3287 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3288 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3289 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3290 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3292 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3293 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;
3296 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3297 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3298 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3301 o << "------" << std::endl;
3305 TempoMap::n_tempos() const
3307 Glib::Threads::RWLock::ReaderLock lm (lock);
3310 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3311 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3320 TempoMap::n_meters() const
3322 Glib::Threads::RWLock::ReaderLock lm (lock);
3325 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3326 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3335 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3338 Glib::Threads::RWLock::WriterLock lm (lock);
3339 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3340 if ((*i)->frame() >= where && (*i)->movable ()) {
3341 (*i)->set_frame ((*i)->frame() + amount);
3345 /* now reset the BBT time of all metrics, based on their new
3346 * audio time. This is the only place where we do this reverse
3350 Metrics::iterator i;
3351 const MeterSection* meter;
3352 const TempoSection* tempo;
3356 meter = &first_meter ();
3357 tempo = &first_tempo ();
3362 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3365 MetricSection* prev = 0;
3367 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3370 //TempoMetric metric (*meter, *tempo);
3371 MeterSection* ms = const_cast<MeterSection*>(meter);
3372 TempoSection* ts = const_cast<TempoSection*>(tempo);
3375 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3379 ts->set_pulse (t->pulse());
3381 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3382 ts->set_pulse (m->pulse());
3384 ts->set_frame (prev->frame());
3388 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3389 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3390 ms->set_beat (start);
3391 ms->set_pulse (m->pulse());
3393 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3397 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3398 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3399 ms->set_beat (start);
3400 ms->set_pulse (t->pulse());
3402 ms->set_frame (prev->frame());
3406 // metric will be at frames=0 bbt=1|1|0 by default
3407 // which is correct for our purpose
3410 // cerr << bbt << endl;
3412 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3416 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3418 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3419 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3420 bbt_time (m->frame(), bbt);
3422 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3428 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3429 /* round up to next beat */
3435 if (bbt.beats != 1) {
3436 /* round up to next bar */
3441 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3442 m->set_beat (start);
3443 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3445 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3447 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3448 abort(); /*NOTREACHED*/
3454 recompute_map (_metrics);
3458 PropertyChanged (PropertyChange ());
3461 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3465 std::list<MetricSection*> metric_kill_list;
3467 TempoSection* last_tempo = NULL;
3468 MeterSection* last_meter = NULL;
3469 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3470 bool meter_after = false; // is there a meter marker likewise?
3472 Glib::Threads::RWLock::WriterLock lm (lock);
3473 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3474 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3475 metric_kill_list.push_back(*i);
3476 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3479 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3483 else if ((*i)->frame() >= where) {
3484 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3485 (*i)->set_frame ((*i)->frame() - amount);
3486 if ((*i)->frame() == where) {
3487 // marker was immediately after end of range
3488 tempo_after = dynamic_cast<TempoSection*> (*i);
3489 meter_after = dynamic_cast<MeterSection*> (*i);
3495 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3496 if (last_tempo && !tempo_after) {
3497 metric_kill_list.remove(last_tempo);
3498 last_tempo->set_frame(where);
3501 if (last_meter && !meter_after) {
3502 metric_kill_list.remove(last_meter);
3503 last_meter->set_frame(where);
3507 //remove all the remaining metrics
3508 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3509 _metrics.remove(*i);
3514 recompute_map (_metrics);
3517 PropertyChanged (PropertyChange ());
3521 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3522 * pos can be -ve, if required.
3525 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3527 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3530 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3532 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3534 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3537 /** Add the BBT interval op to pos and return the result */
3539 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3541 Glib::Threads::RWLock::ReaderLock lm (lock);
3543 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3544 pos_bbt.ticks += op.ticks;
3545 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3547 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3549 pos_bbt.beats += op.beats;
3550 /* the meter in effect will start on the bar */
3551 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();
3552 while (pos_bbt.beats >= divisions_per_bar + 1) {
3554 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3555 pos_bbt.beats -= divisions_per_bar;
3557 pos_bbt.bars += op.bars;
3559 return frame_time_locked (_metrics, pos_bbt);
3562 /** Count the number of beats that are equivalent to distance when going forward,
3566 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3568 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3572 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3578 operator<< (std::ostream& o, const Meter& m) {
3579 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3583 operator<< (std::ostream& o, const Tempo& t) {
3584 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3588 operator<< (std::ostream& o, const MetricSection& section) {
3590 o << "MetricSection @ " << section.frame() << ' ';
3592 const TempoSection* ts;
3593 const MeterSection* ms;
3595 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3596 o << *((const Tempo*) ts);
3597 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3598 o << *((const Meter*) ms);