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, where);
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 meter_locked_tempo = t;
2101 MeterSection* prev_m = 0;
2103 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2105 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2107 if (prev_m && section->movable()) {
2108 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2109 if (beats + prev_m->beat() < section->beat()) {
2110 /* disallow position change if it will alter our beat
2111 we allow tempo changes to do this in recompute_meters().
2112 blocking this is an option, but i'm not convinced that
2113 this is what the user would actually want.
2114 here we set the frame/pulse corresponding to its musical position.
2117 if (meter_locked_tempo) {
2119 bool solved = false;
2120 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2121 const double new_pulse = ((section->beat() - prev_m->beat())
2122 / prev_m->note_divisor()) + prev_m->pulse();
2123 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2124 if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
2125 meter_locked_tempo->set_pulse (new_pulse);
2126 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2127 section->set_frame (smallest_frame);
2128 section->set_pulse (new_pulse);
2133 Metrics::const_iterator d = future_map.begin();
2134 while (d != future_map.end()) {
2145 if (meter_locked_tempo) {
2147 bool solved = false;
2149 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2150 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2151 meter_copy->set_frame (frame);
2153 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2154 section->set_frame (frame);
2155 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2156 / prev_m->note_divisor()) + prev_m->pulse());
2157 solve_map (imaginary, meter_locked_tempo, frame);
2162 Metrics::const_iterator d = future_map.begin();
2163 while (d != future_map.end()) {
2174 /* not movable (first meter atm) */
2175 if (meter_locked_tempo) {
2177 bool solved = false;
2178 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2180 tempo_copy->set_frame (frame);
2181 tempo_copy->set_pulse (0.0);
2183 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2184 section->set_frame (frame);
2185 meter_locked_tempo->set_frame (frame);
2186 meter_locked_tempo->set_pulse (0.0);
2187 solve_map (imaginary, meter_locked_tempo, frame);
2192 Metrics::const_iterator d = future_map.begin();
2193 while (d != future_map.end()) {
2205 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2206 section->set_beat (b_bbt);
2207 section->set_pulse (0.0);
2217 MetricSectionFrameSorter fcmp;
2218 imaginary.sort (fcmp);
2219 if (section->position_lock_style() == MusicTime) {
2220 /* we're setting the frame */
2221 section->set_position_lock_style (AudioTime);
2222 recompute_meters (imaginary);
2223 section->set_position_lock_style (MusicTime);
2225 recompute_meters (imaginary);
2227 //dump (imaginary, std::cerr);
2232 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2234 MeterSection* prev_m = 0;
2236 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2238 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2239 pair<double, BBT_Time> b_bbt;
2240 double new_pulse = 0.0;
2242 if (prev_m && m == section){
2243 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2244 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2246 b_bbt = make_pair (beats + prev_m->beat(), when);
2248 section->set_beat (b_bbt);
2249 section->set_pulse (pulse);
2250 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2255 } else if (m->bbt().bars == when.bars) {
2259 if (m->position_lock_style() == AudioTime) {
2261 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2263 if (beats + prev_m->beat() != m->beat()) {
2264 /* tempo/ meter change caused a change in beat (bar). */
2265 b_bbt = make_pair (beats + prev_m->beat()
2266 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2267 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2269 b_bbt = make_pair (m->beat(), m->bbt());
2270 new_pulse = pulse_at_frame_locked (imaginary, m->frame());
2273 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2276 m->set_beat (b_bbt);
2277 m->set_pulse (new_pulse);
2280 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2282 b_bbt = make_pair (beats + prev_m->beat()
2283 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2284 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar()
2285 / prev_m->note_divisor());
2287 m->set_beat (b_bbt);
2288 m->set_pulse (new_pulse);
2289 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2296 MetricSectionSorter cmp;
2297 imaginary.sort (cmp);
2298 if (section->position_lock_style() == AudioTime) {
2299 /* we're setting the pulse */
2300 section->set_position_lock_style (MusicTime);
2301 recompute_meters (imaginary);
2302 section->set_position_lock_style (AudioTime);
2304 recompute_meters (imaginary);
2309 /** places a copy of _metrics into copy and returns a pointer
2310 * to section's equivalent in copy.
2313 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2315 TempoSection* ret = 0;
2317 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2320 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2322 ret = new TempoSection (*t);
2323 copy.push_back (ret);
2327 TempoSection* cp = new TempoSection (*t);
2328 copy.push_back (cp);
2330 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2331 MeterSection* cp = new MeterSection (*m);
2332 copy.push_back (cp);
2340 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2342 MeterSection* ret = 0;
2344 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2347 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2348 TempoSection* cp = new TempoSection (*t);
2349 copy.push_back (cp);
2352 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2354 ret = new MeterSection (*m);
2355 copy.push_back (ret);
2358 MeterSection* cp = new MeterSection (*m);
2359 copy.push_back (cp);
2367 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2370 TempoSection* tempo_copy = 0;
2373 Glib::Threads::RWLock::ReaderLock lm (lock);
2374 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2380 const double beat = bbt_to_beats_locked (copy, bbt);
2381 const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2383 Metrics::const_iterator d = copy.begin();
2384 while (d != copy.end()) {
2393 * 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,
2394 * taking any possible reordering as a consequence of this into account.
2395 * @param section - the section to be altered
2396 * @param bpm - the new Tempo
2397 * @param bbt - the bbt where the altered tempo will fall
2398 * @return returns - the position in frames where the new tempo section will lie.
2401 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2403 Glib::Threads::RWLock::ReaderLock lm (lock);
2406 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2410 const double beat = bbt_to_beats_locked (future_map, bbt);
2412 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2413 ret = tempo_copy->frame();
2415 ret = section->frame();
2418 Metrics::const_iterator d = future_map.begin();
2419 while (d != future_map.end()) {
2427 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2429 Glib::Threads::RWLock::ReaderLock lm (lock);
2432 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2434 if (solve_map (future_map, tempo_copy, frame)) {
2435 ret = tempo_copy->pulse();
2437 ret = section->pulse();
2440 Metrics::const_iterator d = future_map.begin();
2441 while (d != future_map.end()) {
2449 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2453 Glib::Threads::RWLock::WriterLock lm (lock);
2454 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2455 if (solve_map (future_map, tempo_copy, frame)) {
2456 solve_map (_metrics, ts, frame);
2457 recompute_meters (_metrics);
2461 Metrics::const_iterator d = future_map.begin();
2462 while (d != future_map.end()) {
2467 MetricPositionChanged (); // Emit Signal
2471 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2475 Glib::Threads::RWLock::WriterLock lm (lock);
2476 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2477 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2478 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2479 recompute_meters (_metrics);
2483 Metrics::const_iterator d = future_map.begin();
2484 while (d != future_map.end()) {
2489 MetricPositionChanged (); // Emit Signal
2493 TempoMap::gui_move_tempo_pulse (TempoSection* ts, const double& pulse)
2497 Glib::Threads::RWLock::WriterLock lm (lock);
2498 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2499 if (solve_map (future_map, tempo_copy, pulse)) {
2500 solve_map (_metrics, ts, pulse);
2501 recompute_meters (_metrics);
2505 Metrics::const_iterator d = future_map.begin();
2506 while (d != future_map.end()) {
2511 MetricPositionChanged (); // Emit Signal
2515 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2519 Glib::Threads::RWLock::WriterLock lm (lock);
2520 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2521 if (solve_map (future_map, copy, frame)) {
2522 solve_map (_metrics, ms, frame);
2526 Metrics::const_iterator d = future_map.begin();
2527 while (d != future_map.end()) {
2532 MetricPositionChanged (); // Emit Signal
2536 TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
2540 Glib::Threads::RWLock::WriterLock lm (lock);
2541 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2542 if (solve_map (future_map, copy, bbt)) {
2543 solve_map (_metrics, ms, bbt);
2547 Metrics::const_iterator d = future_map.begin();
2548 while (d != future_map.end()) {
2553 MetricPositionChanged (); // Emit Signal
2557 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2560 bool can_solve = false;
2562 Glib::Threads::RWLock::WriterLock lm (lock);
2563 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2564 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2565 recompute_tempos (future_map);
2567 if (check_solved (future_map, true)) {
2568 ts->set_beats_per_minute (bpm.beats_per_minute());
2569 recompute_map (_metrics);
2574 Metrics::const_iterator d = future_map.begin();
2575 while (d != future_map.end()) {
2580 MetricPositionChanged (); // Emit Signal
2586 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2589 TempoSection* ts = 0;
2591 if (ms->position_lock_style() == AudioTime) {
2592 /* disabled for now due to faked tempo locked to meter pulse */
2596 Glib::Threads::RWLock::WriterLock lm (lock);
2597 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2601 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2602 TempoSection* prev_to_prev_t = 0;
2603 const frameoffset_t fr_off = frame - ms->frame();
2604 double new_bpm = 0.0;
2607 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2610 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2611 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2613 double contribution = 0.0;
2614 frameoffset_t frame_contribution = 0.0;
2615 frameoffset_t prev_t_frame_contribution = 0.0;
2617 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2618 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2619 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2620 frame_contribution = contribution * (double) fr_off;
2621 prev_t_frame_contribution = fr_off - frame_contribution;
2624 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2626 if (prev_t->position_lock_style() == MusicTime) {
2627 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2628 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2629 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2632 /* prev to prev is irrelevant */
2633 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2634 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2636 if (frame_pulse != prev_t->pulse()) {
2637 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2639 new_bpm = prev_t->beats_per_minute();
2644 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2645 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2646 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2648 /* prev_to_prev_t is irrelevant */
2650 if (frame != prev_t->frame()) {
2651 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2653 new_bpm = prev_t->beats_per_minute();
2657 } else if (prev_t->c_func() < 0.0) {
2658 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2659 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2661 /* prev_to_prev_t is irrelevant */
2662 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2665 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2666 if (diff > -0.1 && diff < 0.1) {
2667 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2668 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2671 } else if (prev_t->c_func() > 0.0) {
2672 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2673 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2675 /* prev_to_prev_t is irrelevant */
2676 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2679 /* limits - a bit clunky, but meh */
2680 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2681 if (diff > -0.1 && diff < 0.1) {
2682 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2683 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2687 prev_t->set_beats_per_minute (new_bpm);
2688 recompute_tempos (future_map);
2689 recompute_meters (future_map);
2691 if (check_solved (future_map, true)) {
2693 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2694 prev_t->set_beats_per_minute (new_bpm);
2695 recompute_tempos (_metrics);
2697 if (ms->position_lock_style() == AudioTime) {
2698 ms->set_frame (frame);
2701 recompute_meters (_metrics);
2705 Metrics::const_iterator d = future_map.begin();
2706 while (d != future_map.end()) {
2711 MetricPositionChanged (); // Emit Signal
2716 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2718 Glib::Threads::RWLock::ReaderLock lm (lock);
2720 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2721 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2722 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2724 return frame_at_beat_locked (_metrics, total_beats);
2728 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2730 return round_to_type (fr, dir, Bar);
2734 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2736 return round_to_type (fr, dir, Beat);
2740 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2742 Glib::Threads::RWLock::ReaderLock lm (lock);
2743 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2744 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2745 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2747 ticks -= beats * BBT_Time::ticks_per_beat;
2750 /* round to next (or same iff dir == RoundUpMaybe) */
2752 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2754 if (mod == 0 && dir == RoundUpMaybe) {
2755 /* right on the subdivision, which is fine, so do nothing */
2757 } else if (mod == 0) {
2758 /* right on the subdivision, so the difference is just the subdivision ticks */
2759 ticks += ticks_one_subdivisions_worth;
2762 /* not on subdivision, compute distance to next subdivision */
2764 ticks += ticks_one_subdivisions_worth - mod;
2767 if (ticks >= BBT_Time::ticks_per_beat) {
2768 ticks -= BBT_Time::ticks_per_beat;
2770 } else if (dir < 0) {
2772 /* round to previous (or same iff dir == RoundDownMaybe) */
2774 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2776 if (difference == 0 && dir == RoundDownAlways) {
2777 /* right on the subdivision, but force-rounding down,
2778 so the difference is just the subdivision ticks */
2779 difference = ticks_one_subdivisions_worth;
2782 if (ticks < difference) {
2783 ticks = BBT_Time::ticks_per_beat - ticks;
2785 ticks -= difference;
2789 /* round to nearest */
2792 /* compute the distance to the previous and next subdivision */
2794 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2796 /* closer to the next subdivision, so shift forward */
2798 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2800 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2802 if (ticks > BBT_Time::ticks_per_beat) {
2804 ticks -= BBT_Time::ticks_per_beat;
2805 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2808 } else if (rem > 0) {
2810 /* closer to previous subdivision, so shift backward */
2814 /* can't go backwards past zero, so ... */
2817 /* step back to previous beat */
2819 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2820 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2822 ticks = lrint (ticks - rem);
2823 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2826 /* on the subdivision, do nothing */
2830 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2836 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
2838 if (sub_num == -1) {
2843 } else if (dir < 0) {
2847 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2848 if ((double) when.beats > bpb / 2.0) {
2857 } else if (sub_num == 0) {
2858 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2859 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2861 while ((double) when.beats > bpb) {
2863 when.beats -= (uint32_t) floor (bpb);
2871 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2874 /* round to next (or same iff dir == RoundUpMaybe) */
2876 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
2878 if (mod == 0 && dir == RoundUpMaybe) {
2879 /* right on the subdivision, which is fine, so do nothing */
2881 } else if (mod == 0) {
2882 /* right on the subdivision, so the difference is just the subdivision ticks */
2883 when.ticks += ticks_one_subdivisions_worth;
2886 /* not on subdivision, compute distance to next subdivision */
2888 when.ticks += ticks_one_subdivisions_worth - mod;
2891 if (when.ticks >= BBT_Time::ticks_per_beat) {
2892 when.ticks -= BBT_Time::ticks_per_beat;
2895 } else if (dir < 0) {
2896 /* round to previous (or same iff dir == RoundDownMaybe) */
2898 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
2900 if (difference == 0 && dir == RoundDownAlways) {
2901 /* right on the subdivision, but force-rounding down,
2902 so the difference is just the subdivision ticks */
2903 difference = ticks_one_subdivisions_worth;
2906 if (when.ticks < difference) {
2907 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
2909 when.ticks -= difference;
2913 /* round to nearest */ double rem;
2914 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2915 /* closer to the next subdivision, so shift forward */
2917 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2919 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2921 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2924 } else if (rem > 0) {
2925 /* closer to previous subdivision, so shift backward */
2927 if (rem > when.ticks) {
2928 if (when.beats == 0) {
2929 /* can't go backwards past zero, so ... */
2931 /* step back to previous beat */
2933 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2935 when.ticks = when.ticks - rem;
2942 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2944 Glib::Threads::RWLock::ReaderLock lm (lock);
2946 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2947 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2952 /* find bar previous to 'frame' */
2955 return frame_time_locked (_metrics, bbt);
2957 } else if (dir > 0) {
2958 /* find bar following 'frame' */
2962 return frame_time_locked (_metrics, bbt);
2964 /* true rounding: find nearest bar */
2965 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2968 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2970 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2972 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2983 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2984 } else if (dir > 0) {
2985 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2987 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2996 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2997 framepos_t lower, framepos_t upper)
2999 Glib::Threads::RWLock::ReaderLock lm (lock);
3000 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3002 /* although the map handles negative beats, bbt doesn't. */
3006 while (pos < upper) {
3007 pos = frame_at_beat_locked (_metrics, cnt);
3008 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3009 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3010 const BBT_Time bbt = beats_to_bbt (cnt);
3011 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3017 TempoMap::tempo_section_at (framepos_t frame) const
3019 Glib::Threads::RWLock::ReaderLock lm (lock);
3020 return tempo_section_at_locked (_metrics, frame);
3024 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3026 Metrics::const_iterator i;
3027 TempoSection* prev = 0;
3029 for (i = metrics.begin(); i != metrics.end(); ++i) {
3032 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3036 if (prev && t->frame() > frame) {
3046 abort(); /*NOTREACHED*/
3053 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3055 TempoSection* prev_t = 0;
3056 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3058 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3060 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3061 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3072 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3074 TempoSection* prev_t = 0;
3076 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3078 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3079 if (prev_t && t->pulse() > pulse) {
3089 /* don't use this to calculate length (the tempo is only correct for this frame).
3090 do that stuff based on the beat_at_frame and frame_at_beat api
3093 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3095 Glib::Threads::RWLock::ReaderLock lm (lock);
3097 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3098 const TempoSection* ts_after = 0;
3099 Metrics::const_iterator i;
3101 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3104 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3108 if ((*i)->frame() > frame) {
3116 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3118 /* must be treated as constant tempo */
3119 return ts_at->frames_per_beat (_frame_rate);
3123 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3125 TempoSection* prev_t = 0;
3127 Metrics::const_iterator i;
3129 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3131 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3135 if ((prev_t) && t->frame() > frame) {
3136 /* t is the section past frame */
3137 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3138 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3145 const double ret = prev_t->beats_per_minute();
3146 const Tempo ret_tempo (ret, prev_t->note_type ());
3152 TempoMap::tempo_at (const framepos_t& frame) const
3154 Glib::Threads::RWLock::ReaderLock lm (lock);
3155 return tempo_at_locked (_metrics, frame);
3159 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3161 Metrics::const_iterator i;
3162 MeterSection* prev = 0;
3164 for (i = metrics.begin(); i != metrics.end(); ++i) {
3167 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3169 if (prev && (*i)->frame() > frame) {
3179 abort(); /*NOTREACHED*/
3187 TempoMap::meter_section_at (framepos_t frame) const
3189 Glib::Threads::RWLock::ReaderLock lm (lock);
3190 return meter_section_at_locked (_metrics, frame);
3194 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3196 MeterSection* prev_m = 0;
3198 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3200 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3201 if (prev_m && m->beat() > beat) {
3212 TempoMap::meter_section_at_beat (double beat) const
3214 Glib::Threads::RWLock::ReaderLock lm (lock);
3215 return meter_section_at_beat_locked (_metrics, beat);
3219 TempoMap::meter_at (framepos_t frame) const
3221 TempoMetric m (metric_at (frame));
3226 TempoMap::fix_legacy_session ()
3228 MeterSection* prev_m = 0;
3229 TempoSection* prev_t = 0;
3231 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3235 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3236 if (!m->movable()) {
3237 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3241 m->set_position_lock_style (AudioTime);
3246 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3247 + (m->bbt().beats - 1)
3248 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3250 m->set_beat (start);
3251 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3252 + (m->bbt().beats - 1)
3253 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3254 m->set_pulse (start_beat / prev_m->note_divisor());
3257 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3263 if (!t->movable()) {
3266 t->set_position_lock_style (AudioTime);
3272 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3273 + (t->legacy_bbt().beats - 1)
3274 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3276 t->set_pulse (beat / prev_m->note_divisor());
3278 /* really shouldn't happen but.. */
3279 t->set_pulse (beat / 4.0);
3288 TempoMap::get_state ()
3290 Metrics::const_iterator i;
3291 XMLNode *root = new XMLNode ("TempoMap");
3294 Glib::Threads::RWLock::ReaderLock lm (lock);
3295 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3296 root->add_child_nocopy ((*i)->get_state());
3304 TempoMap::set_state (const XMLNode& node, int /*version*/)
3307 Glib::Threads::RWLock::WriterLock lm (lock);
3310 XMLNodeConstIterator niter;
3311 Metrics old_metrics (_metrics);
3314 nlist = node.children();
3316 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3317 XMLNode* child = *niter;
3319 if (child->name() == TempoSection::xml_state_node_name) {
3322 TempoSection* ts = new TempoSection (*child);
3323 _metrics.push_back (ts);
3326 catch (failed_constructor& err){
3327 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3328 _metrics = old_metrics;
3332 } else if (child->name() == MeterSection::xml_state_node_name) {
3335 MeterSection* ms = new MeterSection (*child);
3336 _metrics.push_back (ms);
3339 catch (failed_constructor& err) {
3340 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3341 _metrics = old_metrics;
3347 if (niter == nlist.end()) {
3348 MetricSectionSorter cmp;
3349 _metrics.sort (cmp);
3352 /* check for legacy sessions where bbt was the base musical unit for tempo */
3353 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3355 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3356 if (t->legacy_bbt().bars != 0) {
3357 fix_legacy_session();
3364 /* check for multiple tempo/meters at the same location, which
3365 ardour2 somehow allowed.
3368 Metrics::iterator prev = _metrics.end();
3369 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3370 if (prev != _metrics.end()) {
3372 MeterSection* prev_m;
3374 TempoSection* prev_t;
3375 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3376 if (prev_m->pulse() == ms->pulse()) {
3377 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3378 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3381 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3382 if (prev_t->pulse() == ts->pulse()) {
3383 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3384 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3392 recompute_map (_metrics);
3395 PropertyChanged (PropertyChange ());
3401 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3403 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3404 const MeterSection* m;
3405 const TempoSection* t;
3406 const TempoSection* prev_t = 0;
3408 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3410 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3411 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3412 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3413 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3415 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3416 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;
3419 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3420 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3421 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3424 o << "------" << std::endl;
3428 TempoMap::n_tempos() const
3430 Glib::Threads::RWLock::ReaderLock lm (lock);
3433 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3434 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3443 TempoMap::n_meters() const
3445 Glib::Threads::RWLock::ReaderLock lm (lock);
3448 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3449 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3458 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3461 Glib::Threads::RWLock::WriterLock lm (lock);
3462 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3463 if ((*i)->frame() >= where && (*i)->movable ()) {
3464 (*i)->set_frame ((*i)->frame() + amount);
3468 /* now reset the BBT time of all metrics, based on their new
3469 * audio time. This is the only place where we do this reverse
3473 Metrics::iterator i;
3474 const MeterSection* meter;
3475 const TempoSection* tempo;
3479 meter = &first_meter ();
3480 tempo = &first_tempo ();
3485 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3488 MetricSection* prev = 0;
3490 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3493 //TempoMetric metric (*meter, *tempo);
3494 MeterSection* ms = const_cast<MeterSection*>(meter);
3495 TempoSection* ts = const_cast<TempoSection*>(tempo);
3498 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3502 ts->set_pulse (t->pulse());
3504 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3505 ts->set_pulse (m->pulse());
3507 ts->set_frame (prev->frame());
3511 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3512 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3513 ms->set_beat (start);
3514 ms->set_pulse (m->pulse());
3516 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3520 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3521 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3522 ms->set_beat (start);
3523 ms->set_pulse (t->pulse());
3525 ms->set_frame (prev->frame());
3529 // metric will be at frames=0 bbt=1|1|0 by default
3530 // which is correct for our purpose
3533 // cerr << bbt << endl;
3535 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3539 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3541 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3542 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3543 bbt_time (m->frame(), bbt);
3545 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3551 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3552 /* round up to next beat */
3558 if (bbt.beats != 1) {
3559 /* round up to next bar */
3564 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3565 m->set_beat (start);
3566 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3568 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3570 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3571 abort(); /*NOTREACHED*/
3577 recompute_map (_metrics);
3581 PropertyChanged (PropertyChange ());
3584 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3588 std::list<MetricSection*> metric_kill_list;
3590 TempoSection* last_tempo = NULL;
3591 MeterSection* last_meter = NULL;
3592 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3593 bool meter_after = false; // is there a meter marker likewise?
3595 Glib::Threads::RWLock::WriterLock lm (lock);
3596 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3597 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3598 metric_kill_list.push_back(*i);
3599 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3602 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3606 else if ((*i)->frame() >= where) {
3607 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3608 (*i)->set_frame ((*i)->frame() - amount);
3609 if ((*i)->frame() == where) {
3610 // marker was immediately after end of range
3611 tempo_after = dynamic_cast<TempoSection*> (*i);
3612 meter_after = dynamic_cast<MeterSection*> (*i);
3618 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3619 if (last_tempo && !tempo_after) {
3620 metric_kill_list.remove(last_tempo);
3621 last_tempo->set_frame(where);
3624 if (last_meter && !meter_after) {
3625 metric_kill_list.remove(last_meter);
3626 last_meter->set_frame(where);
3630 //remove all the remaining metrics
3631 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3632 _metrics.remove(*i);
3637 recompute_map (_metrics);
3640 PropertyChanged (PropertyChange ());
3644 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3645 * pos can be -ve, if required.
3648 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3650 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3653 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3655 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3657 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3660 /** Add the BBT interval op to pos and return the result */
3662 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3664 Glib::Threads::RWLock::ReaderLock lm (lock);
3666 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3667 pos_bbt.ticks += op.ticks;
3668 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3670 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3672 pos_bbt.beats += op.beats;
3673 /* the meter in effect will start on the bar */
3674 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();
3675 while (pos_bbt.beats >= divisions_per_bar + 1) {
3677 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3678 pos_bbt.beats -= divisions_per_bar;
3680 pos_bbt.bars += op.bars;
3682 return frame_time_locked (_metrics, pos_bbt);
3685 /** Count the number of beats that are equivalent to distance when going forward,
3689 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3691 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3695 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3701 operator<< (std::ostream& o, const Meter& m) {
3702 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3706 operator<< (std::ostream& o, const Tempo& t) {
3707 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3711 operator<< (std::ostream& o, const MetricSection& section) {
3713 o << "MetricSection @ " << section.frame() << ' ';
3715 const TempoSection* ts;
3716 const MeterSection* ms;
3718 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3719 o << *((const Tempo*) ts);
3720 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3721 o << *((const Meter*) ms);