2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 const XMLProperty *prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const frameoffset_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - (frameoffset_t) frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses per minute occurs in this section.
222 beat b is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_pulse_tempo (ppm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (frameoffset_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (frameoffset_t) floor ((time * 60.0 * (frameoffset_t) frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const frameoffset_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
461 const XMLProperty *prop;
465 framepos_t frame = 0;
466 pair<double, BBT_Time> start;
468 if ((prop = node.property ("start")) != 0) {
469 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
473 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
475 /* legacy session - start used to be in bbt*/
476 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
481 if ((prop = node.property ("pulse")) != 0) {
482 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
483 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
488 if ((prop = node.property ("beat")) != 0) {
489 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
490 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
496 if ((prop = node.property ("bbt")) == 0) {
497 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
498 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
502 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
503 throw failed_constructor();
509 if ((prop = node.property ("frame")) != 0) {
510 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
511 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
517 /* beats-per-bar is old; divisions-per-bar is new */
519 if ((prop = node.property ("divisions-per-bar")) == 0) {
520 if ((prop = node.property ("beats-per-bar")) == 0) {
521 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
522 throw failed_constructor();
525 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
526 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
527 throw failed_constructor();
530 if ((prop = node.property ("note-type")) == 0) {
531 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
532 throw failed_constructor();
534 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
535 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
536 throw failed_constructor();
539 if ((prop = node.property ("movable")) == 0) {
540 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
541 throw failed_constructor();
544 set_movable (string_is_affirmative (prop->value()));
546 if ((prop = node.property ("lock-style")) == 0) {
547 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
549 set_position_lock_style (MusicTime);
551 set_position_lock_style (AudioTime);
554 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
559 MeterSection::get_state() const
561 XMLNode *root = new XMLNode (xml_state_node_name);
565 snprintf (buf, sizeof (buf), "%lf", pulse());
566 root->add_property ("pulse", buf);
567 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
571 root->add_property ("bbt", buf);
572 snprintf (buf, sizeof (buf), "%lf", beat());
573 root->add_property ("beat", buf);
574 snprintf (buf, sizeof (buf), "%f", _note_type);
575 root->add_property ("note-type", buf);
576 snprintf (buf, sizeof (buf), "%li", frame());
577 root->add_property ("frame", buf);
578 root->add_property ("lock-style", enum_2_string (position_lock_style()));
579 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
580 root->add_property ("divisions-per-bar", buf);
581 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
582 root->add_property ("movable", buf);
587 /***********************************************************************/
591 Tempo can be thought of as a source of the musical pulse.
592 Meters divide that pulse into measures and beats.
593 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
594 at any particular time.
595 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
596 It should rather be thought of as tempo note divisions per minute.
598 TempoSections, which are nice to think of in whole pulses per minute,
599 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
600 and beats (via note_divisor) are used to form a tempo map.
601 TempoSections and MeterSections may be locked to either audio or music (position lock style).
602 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
603 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
605 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 Both tempos and meters have a pulse position and a frame position.
612 Meters also have a beat position, which is always 0.0 for the first meter.
614 A tempo locked to music is locked to musical pulses.
615 A meter locked to music is locked to beats.
617 Recomputing the tempo map is the process where the 'missing' position
618 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
620 It is important to keep the _metrics in an order that makes sense.
621 Because ramped MusicTime and AudioTime tempos can interact with each other,
622 reordering is frequent. Care must be taken to keep _metrics in a solved state.
623 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
625 struct MetricSectionSorter {
626 bool operator() (const MetricSection* a, const MetricSection* b) {
627 return a->pulse() < b->pulse();
631 struct MetricSectionFrameSorter {
632 bool operator() (const MetricSection* a, const MetricSection* b) {
633 return a->frame() < b->frame();
637 TempoMap::TempoMap (framecnt_t fr)
640 BBT_Time start (1, 1, 0);
642 TempoSection *t = new TempoSection ((framepos_t) 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
643 MeterSection *m = new MeterSection ((framepos_t) 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
645 t->set_movable (false);
646 m->set_movable (false);
648 /* note: frame time is correct (zero) for both of these */
650 _metrics.push_back (t);
651 _metrics.push_back (m);
655 TempoMap::~TempoMap ()
657 Metrics::const_iterator d = _metrics.begin();
658 while (d != _metrics.end()) {
666 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
668 bool removed = false;
671 Glib::Threads::RWLock::WriterLock lm (lock);
672 if ((removed = remove_tempo_locked (tempo))) {
673 if (complete_operation) {
674 recompute_map (_metrics);
679 if (removed && complete_operation) {
680 PropertyChanged (PropertyChange ());
685 TempoMap::remove_tempo_locked (const TempoSection& tempo)
689 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
690 if (dynamic_cast<TempoSection*> (*i) != 0) {
691 if (tempo.frame() == (*i)->frame()) {
692 if ((*i)->movable()) {
705 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
707 bool removed = false;
710 Glib::Threads::RWLock::WriterLock lm (lock);
711 if ((removed = remove_meter_locked (tempo))) {
712 if (complete_operation) {
713 recompute_map (_metrics);
718 if (removed && complete_operation) {
719 PropertyChanged (PropertyChange ());
724 TempoMap::remove_meter_locked (const MeterSection& meter)
728 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
730 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
731 if (meter.frame() == (*i)->frame()) {
732 if (t->locked_to_meter()) {
741 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
742 if (dynamic_cast<MeterSection*> (*i) != 0) {
743 if (meter.frame() == (*i)->frame()) {
744 if ((*i)->movable()) {
757 TempoMap::do_insert (MetricSection* section)
759 bool need_add = true;
760 /* we only allow new meters to be inserted on beat 1 of an existing
764 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
765 //assert (m->bbt().ticks == 0);
767 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
769 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
770 corrected.second.beats = 1;
771 corrected.second.ticks = 0;
772 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
773 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
774 m->bbt(), corrected.second) << endmsg;
775 //m->set_pulse (corrected);
779 /* Look for any existing MetricSection that is of the same type and
780 in the same bar as the new one, and remove it before adding
781 the new one. Note that this means that if we find a matching,
782 existing section, we can break out of the loop since we're
783 guaranteed that there is only one such match.
786 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
788 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
789 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
790 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
791 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
793 if (tempo && insert_tempo) {
796 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
797 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
799 if (!tempo->movable()) {
801 /* can't (re)move this section, so overwrite
802 * its data content (but not its properties as
806 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
807 (*i)->set_position_lock_style (AudioTime);
809 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
810 t->set_type (insert_tempo->type());
820 } else if (meter && insert_meter) {
824 bool const ipm = insert_meter->position_lock_style() == MusicTime;
826 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
828 if (!meter->movable()) {
830 /* can't (re)move this section, so overwrite
831 * its data content (but not its properties as
835 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
836 (*i)->set_position_lock_style (AudioTime);
846 /* non-matching types, so we don't care */
850 /* Add the given MetricSection, if we didn't just reset an existing
855 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
856 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
859 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
860 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
863 bool const ipm = insert_meter->position_lock_style() == MusicTime;
864 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
869 } else if (insert_tempo) {
870 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
871 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
874 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
875 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
882 _metrics.insert (i, section);
883 //dump (_metrics, std::cerr);
888 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
891 Glib::Threads::RWLock::WriterLock lm (lock);
892 TempoSection& first (first_tempo());
893 if (ts.pulse() != first.pulse()) {
894 remove_tempo_locked (ts);
895 add_tempo_locked (tempo, pulse, true, type);
897 first.set_type (type);
899 /* cannot move the first tempo section */
900 *static_cast<Tempo*>(&first) = tempo;
901 recompute_map (_metrics);
906 PropertyChanged (PropertyChange ());
910 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
913 Glib::Threads::RWLock::WriterLock lm (lock);
914 TempoSection& first (first_tempo());
915 if (ts.frame() != first.frame()) {
916 remove_tempo_locked (ts);
917 add_tempo_locked (tempo, frame, true, type);
919 first.set_type (type);
920 first.set_pulse (0.0);
921 first.set_position_lock_style (AudioTime);
923 /* cannot move the first tempo section */
924 *static_cast<Tempo*>(&first) = tempo;
925 recompute_map (_metrics);
930 PropertyChanged (PropertyChange ());
934 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
936 TempoSection* ts = 0;
938 Glib::Threads::RWLock::WriterLock lm (lock);
939 ts = add_tempo_locked (tempo, pulse, true, type);
942 PropertyChanged (PropertyChange ());
948 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
950 TempoSection* ts = 0;
952 Glib::Threads::RWLock::WriterLock lm (lock);
953 ts = add_tempo_locked (tempo, frame, true, type);
957 PropertyChanged (PropertyChange ());
963 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
965 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
970 solve_map (_metrics, t, t->pulse());
971 recompute_meters (_metrics);
978 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
980 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
985 solve_map (_metrics, t, t->frame());
986 recompute_meters (_metrics);
993 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
996 Glib::Threads::RWLock::WriterLock lm (lock);
999 remove_meter_locked (ms);
1000 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
1002 MeterSection& first (first_meter());
1003 /* cannot move the first meter section */
1004 *static_cast<Meter*>(&first) = meter;
1005 first.set_position_lock_style (AudioTime);
1007 recompute_map (_metrics);
1010 PropertyChanged (PropertyChange ());
1014 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1017 Glib::Threads::RWLock::WriterLock lm (lock);
1019 const double beat = ms.beat();
1020 const BBT_Time bbt = ms.bbt();
1023 remove_meter_locked (ms);
1024 add_meter_locked (meter, frame, beat, bbt, true);
1026 MeterSection& first (first_meter());
1027 TempoSection& first_t (first_tempo());
1028 /* cannot move the first meter section */
1029 *static_cast<Meter*>(&first) = meter;
1030 first.set_position_lock_style (AudioTime);
1031 first.set_pulse (0.0);
1032 first.set_frame (frame);
1033 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1034 first.set_beat (beat);
1035 first_t.set_frame (first.frame());
1036 first_t.set_pulse (0.0);
1037 first_t.set_position_lock_style (AudioTime);
1039 recompute_map (_metrics);
1041 PropertyChanged (PropertyChange ());
1046 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1048 MeterSection* m = 0;
1050 Glib::Threads::RWLock::WriterLock lm (lock);
1051 m = add_meter_locked (meter, beat, where, true);
1056 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1057 dump (_metrics, std::cerr);
1061 PropertyChanged (PropertyChange ());
1067 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1069 MeterSection* m = 0;
1071 Glib::Threads::RWLock::WriterLock lm (lock);
1072 m = add_meter_locked (meter, frame, beat, where, true);
1077 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1078 dump (_metrics, std::cerr);
1082 PropertyChanged (PropertyChange ());
1088 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1090 /* a new meter always starts a new bar on the first beat. so
1091 round the start time appropriately. remember that
1092 `where' is based on the existing tempo map, not
1093 the result after we insert the new meter.
1097 if (where.beats != 1) {
1101 /* new meters *always* start on a beat. */
1103 const double pulse = pulse_at_beat_locked (_metrics, beat);
1104 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1105 do_insert (new_meter);
1108 solve_map (_metrics, new_meter, pulse);
1115 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1117 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1118 TempoSection* t = 0;
1119 double pulse = pulse_at_frame_locked (_metrics, frame);
1120 new_meter->set_pulse (pulse);
1122 do_insert (new_meter);
1124 /* add meter-locked tempo */
1125 t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1126 t->set_locked_to_meter (true);
1129 solve_map (_metrics, new_meter, frame);
1136 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1138 Tempo newtempo (beats_per_minute, note_type);
1141 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1142 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1147 Glib::Threads::RWLock::WriterLock lm (lock);
1148 *((Tempo*) t) = newtempo;
1149 recompute_map (_metrics);
1151 PropertyChanged (PropertyChange ());
1158 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1160 Tempo newtempo (beats_per_minute, note_type);
1163 TempoSection* first;
1164 Metrics::iterator i;
1166 /* find the TempoSection immediately preceding "where"
1169 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1171 if ((*i)->frame() > where) {
1177 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1190 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1200 Glib::Threads::RWLock::WriterLock lm (lock);
1201 /* cannot move the first tempo section */
1202 *((Tempo*)prev) = newtempo;
1203 recompute_map (_metrics);
1206 PropertyChanged (PropertyChange ());
1210 TempoMap::first_meter () const
1212 const MeterSection *m = 0;
1214 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1215 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1220 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1221 abort(); /*NOTREACHED*/
1226 TempoMap::first_meter ()
1228 MeterSection *m = 0;
1230 /* CALLER MUST HOLD LOCK */
1232 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1233 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1238 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1239 abort(); /*NOTREACHED*/
1244 TempoMap::first_tempo () const
1246 const TempoSection *t = 0;
1248 /* CALLER MUST HOLD LOCK */
1250 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1251 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1255 if (!t->movable()) {
1261 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1262 abort(); /*NOTREACHED*/
1267 TempoMap::first_tempo ()
1269 TempoSection *t = 0;
1271 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1272 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1276 if (!t->movable()) {
1282 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1283 abort(); /*NOTREACHED*/
1287 TempoMap::recompute_tempos (Metrics& metrics)
1289 TempoSection* prev_t = 0;
1291 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1294 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1298 if (!t->movable()) {
1306 if (t->position_lock_style() == AudioTime) {
1307 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1308 if (!t->locked_to_meter()) {
1309 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1313 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1314 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1321 prev_t->set_c_func (0.0);
1324 /* tempos must be positioned correctly */
1326 TempoMap::recompute_meters (Metrics& metrics)
1328 MeterSection* meter = 0;
1329 MeterSection* prev_m = 0;
1331 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1332 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1333 if (meter->position_lock_style() == AudioTime) {
1335 pair<double, BBT_Time> b_bbt;
1336 if (meter->movable()) {
1337 b_bbt = make_pair (meter->beat(), meter->bbt());
1338 pulse = pulse_at_frame_locked (metrics, meter->frame());
1340 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1342 meter->set_beat (b_bbt);
1343 meter->set_pulse (pulse);
1346 pair<double, BBT_Time> new_beat;
1348 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1349 //new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1351 /* shouldn't happen - the first is audio-locked */
1352 pulse = pulse_at_beat_locked (metrics, meter->beat());
1353 new_beat = make_pair (meter->beat(), meter->bbt());
1356 //meter->set_beat (new_beat);
1357 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1358 meter->set_pulse (pulse);
1364 //dump (_metrics, std::cerr;
1368 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1370 /* CALLER MUST HOLD WRITE LOCK */
1374 /* we will actually stop once we hit
1381 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1384 /* silly call from Session::process() during startup
1389 recompute_tempos (metrics);
1390 recompute_meters (metrics);
1394 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1396 Glib::Threads::RWLock::ReaderLock lm (lock);
1397 TempoMetric m (first_meter(), first_tempo());
1399 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1400 at something, because we insert the default tempo and meter during
1401 TempoMap construction.
1403 now see if we can find better candidates.
1406 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1408 if ((*i)->frame() > frame) {
1422 /* XX meters only */
1424 TempoMap::metric_at (BBT_Time bbt) const
1426 Glib::Threads::RWLock::ReaderLock lm (lock);
1427 TempoMetric m (first_meter(), first_tempo());
1429 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1430 at something, because we insert the default tempo and meter during
1431 TempoMap construction.
1433 now see if we can find better candidates.
1436 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1438 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1439 BBT_Time section_start (mw->bbt());
1441 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1453 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1455 MeterSection* prev_m = 0;
1457 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1459 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1460 if (prev_m && m->beat() > beat) {
1467 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1472 TempoMap::pulse_at_beat (const double& beat) const
1474 Glib::Threads::RWLock::ReaderLock lm (lock);
1475 return pulse_at_beat_locked (_metrics, beat);
1479 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1481 MeterSection* prev_m = 0;
1483 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1485 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1486 if (prev_m && m->pulse() > pulse) {
1487 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1495 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1500 TempoMap::beat_at_pulse (const double& pulse) const
1502 Glib::Threads::RWLock::ReaderLock lm (lock);
1503 return beat_at_pulse_locked (_metrics, pulse);
1506 /* tempo section based */
1508 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1510 /* HOLD (at least) THE READER LOCK */
1511 TempoSection* prev_t = 0;
1513 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1515 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1519 if (prev_t && t->frame() > frame) {
1520 /*the previous ts is the one containing the frame */
1521 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1528 /* treated as constant for this ts */
1529 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1531 return pulses_in_section + prev_t->pulse();
1535 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1537 Glib::Threads::RWLock::ReaderLock lm (lock);
1538 return pulse_at_frame_locked (_metrics, frame);
1541 /* tempo section based */
1543 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1545 /* HOLD THE READER LOCK */
1547 const TempoSection* prev_t = 0;
1549 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1552 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1556 if (prev_t && t->pulse() > pulse) {
1557 return prev_t->frame_at_pulse (pulse, _frame_rate);
1563 /* must be treated as constant, irrespective of _type */
1564 double const pulses_in_section = pulse - prev_t->pulse();
1565 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1567 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1573 TempoMap::frame_at_pulse (const double& pulse) const
1575 Glib::Threads::RWLock::ReaderLock lm (lock);
1576 return frame_at_pulse_locked (_metrics, pulse);
1579 /* meter section based */
1581 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1583 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1584 MeterSection* prev_m = 0;
1585 MeterSection* next_m = 0;
1587 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1589 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1590 if (prev_m && m->frame() > frame) {
1597 if (frame < prev_m->frame()) {
1600 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1602 if (next_m && next_m->beat() < beat) {
1603 return next_m->beat();
1610 TempoMap::beat_at_frame (const framecnt_t& frame) const
1612 Glib::Threads::RWLock::ReaderLock lm (lock);
1613 return beat_at_frame_locked (_metrics, frame);
1616 /* meter section based */
1618 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1620 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1621 MeterSection* prev_m = 0;
1623 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1625 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1626 if (prev_m && m->beat() > beat) {
1633 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1637 TempoMap::frame_at_beat (const double& beat) const
1639 Glib::Threads::RWLock::ReaderLock lm (lock);
1640 return frame_at_beat_locked (_metrics, beat);
1644 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1646 /* CALLER HOLDS READ LOCK */
1648 MeterSection* prev_m = 0;
1650 /* because audio-locked meters have 'fake' integral beats,
1651 there is no pulse offset here.
1653 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1655 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1657 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1658 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1666 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1667 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1668 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1674 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1676 Glib::Threads::RWLock::ReaderLock lm (lock);
1677 return bbt_to_beats_locked (_metrics, bbt);
1681 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1683 /* CALLER HOLDS READ LOCK */
1684 MeterSection* prev_m = 0;
1685 const double beats = (b < 0.0) ? 0.0 : b;
1687 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1688 MeterSection* m = 0;
1690 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1692 if (m->beat() > beats) {
1693 /* this is the meter after the one our beat is on*/
1702 const double beats_in_ms = beats - prev_m->beat();
1703 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1704 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1705 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1706 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1710 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1711 ret.beats = (uint32_t) floor (remaining_beats);
1712 ret.bars = total_bars;
1714 /* 0 0 0 to 1 1 0 - based mapping*/
1718 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1720 ret.ticks -= BBT_Time::ticks_per_beat;
1723 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1732 TempoMap::beats_to_bbt (const double& beats)
1734 Glib::Threads::RWLock::ReaderLock lm (lock);
1735 return beats_to_bbt_locked (_metrics, beats);
1739 TempoMap::pulse_to_bbt (const double& pulse)
1741 Glib::Threads::RWLock::ReaderLock lm (lock);
1742 MeterSection* prev_m = 0;
1744 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1745 MeterSection* m = 0;
1747 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1750 double const pulses_to_m = m->pulse() - prev_m->pulse();
1751 if (prev_m->pulse() + pulses_to_m > pulse) {
1752 /* this is the meter after the one our beat is on*/
1761 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1762 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1763 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1764 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1765 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1769 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1770 ret.beats = (uint32_t) floor (remaining_beats);
1771 ret.bars = total_bars;
1773 /* 0 0 0 to 1 1 0 mapping*/
1777 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1779 ret.ticks -= BBT_Time::ticks_per_beat;
1782 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1791 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1798 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1801 Glib::Threads::RWLock::ReaderLock lm (lock);
1802 const double beat = beat_at_frame_locked (_metrics, frame);
1804 bbt = beats_to_bbt_locked (_metrics, beat);
1807 /* meter section based */
1809 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1811 /* HOLD THE READER LOCK */
1813 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1818 TempoMap::frame_time (const BBT_Time& bbt)
1821 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1825 if (bbt.beats < 1) {
1826 throw std::logic_error ("beats are counted from one");
1828 Glib::Threads::RWLock::ReaderLock lm (lock);
1830 return frame_time_locked (_metrics, bbt);
1834 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1836 TempoSection* prev_t = 0;
1837 MeterSection* prev_m = 0;
1839 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1842 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1847 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1851 if (t->frame() == prev_t->frame()) {
1855 /* precision check ensures pulses and frames align.*/
1856 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1857 if (!t->locked_to_meter()) {
1865 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1866 if (prev_m && m->position_lock_style() == AudioTime) {
1867 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1868 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1869 const frameoffset_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1870 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1884 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1886 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1888 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1889 if (!t->movable()) {
1890 t->set_active (true);
1893 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1894 t->set_active (false);
1896 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1897 t->set_active (true);
1898 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1907 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1909 TempoSection* prev_t = 0;
1910 TempoSection* section_prev = 0;
1911 framepos_t first_m_frame = 0;
1913 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1915 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1916 if (!m->movable()) {
1917 first_m_frame = m->frame();
1922 if (section->movable() && frame <= first_m_frame) {
1926 section->set_active (true);
1927 section->set_frame (frame);
1929 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1931 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1938 section_prev = prev_t;
1941 if (t->position_lock_style() == MusicTime) {
1942 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1943 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1945 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1946 if (!t->locked_to_meter()) {
1947 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1956 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1957 if (!section->locked_to_meter()) {
1958 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1962 if (section->position_lock_style() == MusicTime) {
1963 /* we're setting the frame */
1964 section->set_position_lock_style (AudioTime);
1965 recompute_tempos (imaginary);
1966 section->set_position_lock_style (MusicTime);
1968 recompute_tempos (imaginary);
1971 recompute_meters (imaginary);
1972 if (check_solved (imaginary, true)) {
1976 MetricSectionFrameSorter fcmp;
1977 imaginary.sort (fcmp);
1978 if (section->position_lock_style() == MusicTime) {
1979 /* we're setting the frame */
1980 section->set_position_lock_style (AudioTime);
1981 recompute_tempos (imaginary);
1982 section->set_position_lock_style (MusicTime);
1984 recompute_tempos (imaginary);
1987 recompute_meters (imaginary);
1988 if (check_solved (imaginary, true)) {
1992 //dump (imaginary, std::cerr);
1998 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
2000 TempoSection* prev_t = 0;
2001 TempoSection* section_prev = 0;
2003 section->set_pulse (pulse);
2005 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2007 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2011 if (!t->movable()) {
2018 section_prev = prev_t;
2021 if (t->position_lock_style() == MusicTime) {
2022 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2023 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2025 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2026 if (!t->locked_to_meter()) {
2027 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2035 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2036 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2039 if (section->position_lock_style() == AudioTime) {
2040 /* we're setting the pulse */
2041 section->set_position_lock_style (MusicTime);
2042 recompute_tempos (imaginary);
2043 section->set_position_lock_style (AudioTime);
2045 recompute_tempos (imaginary);
2048 recompute_meters (imaginary);
2049 if (check_solved (imaginary, false)) {
2053 MetricSectionSorter cmp;
2054 imaginary.sort (cmp);
2055 if (section->position_lock_style() == AudioTime) {
2056 /* we're setting the pulse */
2057 section->set_position_lock_style (MusicTime);
2058 recompute_tempos (imaginary);
2059 section->set_position_lock_style (AudioTime);
2061 recompute_tempos (imaginary);
2064 recompute_meters (imaginary);
2065 if (check_solved (imaginary, false)) {
2069 //dump (imaginary, std::cerr);
2075 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2077 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2078 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2079 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2083 if (!section->movable()) {
2084 /* lock the first tempo to our first meter */
2085 if (!set_active_tempos (imaginary, frame)) {
2090 TempoSection* meter_locked_tempo = 0;
2091 for (Metrics::const_iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2093 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2094 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2095 if (t->frame() == section->frame()) {
2096 meter_locked_tempo = t;
2103 MeterSection* prev_m = 0;
2105 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2107 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2109 if (prev_m && section->movable()) {
2110 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2111 if (beats + prev_m->beat() < section->beat()) {
2112 /* disallow position change if it will alter our beat
2113 we allow tempo changes to do this in recompute_meters().
2114 blocking this is an option, but i'm not convinced that
2115 this is what the user would actually want.
2116 here we set the frame/pulse corresponding to its musical position.
2119 if (meter_locked_tempo) {
2121 bool solved = false;
2122 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2123 const double new_pulse = ((section->beat() - prev_m->beat())
2124 / prev_m->note_divisor()) + prev_m->pulse();
2125 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2126 if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
2127 meter_locked_tempo->set_pulse (new_pulse);
2128 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2129 section->set_frame (smallest_frame);
2130 section->set_pulse (new_pulse);
2135 Metrics::const_iterator d = future_map.begin();
2136 while (d != future_map.end()) {
2147 if (meter_locked_tempo) {
2149 bool solved = false;
2151 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2152 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2153 meter_copy->set_frame (frame);
2155 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2156 section->set_frame (frame);
2157 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2158 / prev_m->note_divisor()) + prev_m->pulse());
2159 solve_map (imaginary, meter_locked_tempo, frame);
2164 Metrics::const_iterator d = future_map.begin();
2165 while (d != future_map.end()) {
2176 /* not movable (first meter atm) */
2177 if (meter_locked_tempo) {
2179 bool solved = false;
2180 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2182 tempo_copy->set_frame (frame);
2183 tempo_copy->set_pulse (0.0);
2185 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2186 section->set_frame (frame);
2187 meter_locked_tempo->set_frame (frame);
2188 meter_locked_tempo->set_pulse (0.0);
2189 solve_map (imaginary, meter_locked_tempo, frame);
2194 Metrics::const_iterator d = future_map.begin();
2195 while (d != future_map.end()) {
2207 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2208 section->set_beat (b_bbt);
2209 section->set_pulse (0.0);
2212 //section->set_frame (frame);
2220 MetricSectionFrameSorter fcmp;
2221 imaginary.sort (fcmp);
2222 if (section->position_lock_style() == MusicTime) {
2223 /* we're setting the frame */
2224 section->set_position_lock_style (AudioTime);
2225 recompute_meters (imaginary);
2226 section->set_position_lock_style (MusicTime);
2228 recompute_meters (imaginary);
2230 //dump (imaginary, std::cerr);
2235 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2237 MeterSection* prev_m = 0;
2239 section->set_pulse (pulse);
2241 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2243 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2244 double new_pulse = 0.0;
2245 pair<double, BBT_Time> b_bbt;
2247 if (prev_m && m == section){
2248 /* the first meter is always audio-locked, so prev_m should exist.
2249 should we allow setting audio locked meters by pulse?
2251 const double beats = floor (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + 0.5);
2252 const int32_t bars = (beats) / prev_m->divisions_per_bar();
2253 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), BBT_Time (bars + prev_m->bbt().bars, 1, 0));
2254 section->set_beat (b_bbt);
2255 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2259 if (m->position_lock_style() == AudioTime) {
2261 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2262 if (beats + prev_m->beat() != m->beat()) {
2263 /* tempo/ meter change caused a change in beat (bar). */
2264 b_bbt = make_pair (beats + prev_m->beat()
2265 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2266 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2268 b_bbt = make_pair (m->beat(), m->bbt());
2269 new_pulse = pulse_at_frame_locked (imaginary, m->frame());
2272 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2275 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2276 b_bbt = make_pair (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2278 m->set_beat (b_bbt);
2279 m->set_pulse (new_pulse);
2284 MetricSectionSorter cmp;
2285 imaginary.sort (cmp);
2286 if (section->position_lock_style() == AudioTime) {
2287 /* we're setting the pulse */
2288 section->set_position_lock_style (MusicTime);
2289 recompute_meters (imaginary);
2290 section->set_position_lock_style (AudioTime);
2292 recompute_meters (imaginary);
2297 /** places a copy of _metrics into copy and returns a pointer
2298 * to section's equivalent in copy.
2301 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2303 TempoSection* ret = 0;
2305 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2308 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2310 ret = new TempoSection (*t);
2311 copy.push_back (ret);
2315 TempoSection* cp = new TempoSection (*t);
2316 copy.push_back (cp);
2318 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2319 MeterSection* cp = new MeterSection (*m);
2320 copy.push_back (cp);
2328 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2330 MeterSection* ret = 0;
2332 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2335 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2336 TempoSection* cp = new TempoSection (*t);
2337 copy.push_back (cp);
2340 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2342 ret = new MeterSection (*m);
2343 copy.push_back (ret);
2346 MeterSection* cp = new MeterSection (*m);
2347 copy.push_back (cp);
2355 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2358 TempoSection* tempo_copy = 0;
2361 Glib::Threads::RWLock::ReaderLock lm (lock);
2362 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2368 const double beat = bbt_to_beats_locked (copy, bbt);
2369 const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2371 Metrics::const_iterator d = copy.begin();
2372 while (d != copy.end()) {
2381 * This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
2382 * taking any possible reordering as a consequence of this into account.
2383 * @param section - the section to be altered
2384 * @param bpm - the new Tempo
2385 * @param bbt - the bbt where the altered tempo will fall
2386 * @return returns - the position in frames where the new tempo section will lie.
2389 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2391 Glib::Threads::RWLock::ReaderLock lm (lock);
2394 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2398 const double beat = bbt_to_beats_locked (future_map, bbt);
2400 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2401 ret = tempo_copy->frame();
2403 ret = section->frame();
2406 Metrics::const_iterator d = future_map.begin();
2407 while (d != future_map.end()) {
2415 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2417 Glib::Threads::RWLock::ReaderLock lm (lock);
2420 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2422 if (solve_map (future_map, tempo_copy, frame)) {
2423 ret = tempo_copy->pulse();
2425 ret = section->pulse();
2428 Metrics::const_iterator d = future_map.begin();
2429 while (d != future_map.end()) {
2437 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2441 Glib::Threads::RWLock::WriterLock lm (lock);
2442 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2443 if (solve_map (future_map, tempo_copy, frame)) {
2444 solve_map (_metrics, ts, frame);
2445 recompute_meters (_metrics);
2449 Metrics::const_iterator d = future_map.begin();
2450 while (d != future_map.end()) {
2455 MetricPositionChanged (); // Emit Signal
2459 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2463 Glib::Threads::RWLock::WriterLock lm (lock);
2464 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2465 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2466 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2467 recompute_meters (_metrics);
2471 Metrics::const_iterator d = future_map.begin();
2472 while (d != future_map.end()) {
2477 MetricPositionChanged (); // Emit Signal
2481 TempoMap::gui_move_tempo_pulse (TempoSection* ts, const double& pulse)
2485 Glib::Threads::RWLock::WriterLock lm (lock);
2486 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2487 if (solve_map (future_map, tempo_copy, pulse)) {
2488 solve_map (_metrics, ts, pulse);
2489 recompute_meters (_metrics);
2493 Metrics::const_iterator d = future_map.begin();
2494 while (d != future_map.end()) {
2499 MetricPositionChanged (); // Emit Signal
2503 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2507 Glib::Threads::RWLock::WriterLock lm (lock);
2508 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2509 if (solve_map (future_map, copy, frame)) {
2510 solve_map (_metrics, ms, frame);
2514 Metrics::const_iterator d = future_map.begin();
2515 while (d != future_map.end()) {
2520 MetricPositionChanged (); // Emit Signal
2524 TempoMap::gui_move_meter (MeterSection* ms, const double& pulse)
2528 Glib::Threads::RWLock::WriterLock lm (lock);
2529 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2530 if (solve_map (future_map, copy, pulse)) {
2531 solve_map (_metrics, ms, pulse);
2535 Metrics::const_iterator d = future_map.begin();
2536 while (d != future_map.end()) {
2541 MetricPositionChanged (); // Emit Signal
2545 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2548 bool can_solve = false;
2550 Glib::Threads::RWLock::WriterLock lm (lock);
2551 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2552 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2553 recompute_tempos (future_map);
2555 if (check_solved (future_map, true)) {
2556 ts->set_beats_per_minute (bpm.beats_per_minute());
2557 recompute_map (_metrics);
2562 Metrics::const_iterator d = future_map.begin();
2563 while (d != future_map.end()) {
2568 MetricPositionChanged (); // Emit Signal
2574 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2577 TempoSection* ts = 0;
2579 if (ms->position_lock_style() == AudioTime) {
2580 /* disabled for now due to faked tempo locked to meter pulse */
2584 Glib::Threads::RWLock::WriterLock lm (lock);
2585 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2589 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2590 TempoSection* prev_to_prev_t = 0;
2591 const frameoffset_t fr_off = frame - ms->frame();
2592 double new_bpm = 0.0;
2595 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2598 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2599 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2601 double contribution = 0.0;
2602 frameoffset_t frame_contribution = 0.0;
2603 frameoffset_t prev_t_frame_contribution = 0.0;
2605 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2606 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2607 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2608 frame_contribution = contribution * (double) fr_off;
2609 prev_t_frame_contribution = fr_off - frame_contribution;
2612 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2614 if (prev_t->position_lock_style() == MusicTime) {
2615 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2616 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2617 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2620 /* prev to prev is irrelevant */
2621 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2622 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2624 if (frame_pulse != prev_t->pulse()) {
2625 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2627 new_bpm = prev_t->beats_per_minute();
2632 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2633 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2634 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2636 /* prev_to_prev_t is irrelevant */
2638 if (frame != prev_t->frame()) {
2639 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2641 new_bpm = prev_t->beats_per_minute();
2645 } else if (prev_t->c_func() < 0.0) {
2646 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2647 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2649 /* prev_to_prev_t is irrelevant */
2650 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2653 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2654 if (diff > -0.1 && diff < 0.1) {
2655 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2656 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2659 } else if (prev_t->c_func() > 0.0) {
2660 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2661 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2663 /* prev_to_prev_t is irrelevant */
2664 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2667 /* limits - a bit clunky, but meh */
2668 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2669 if (diff > -0.1 && diff < 0.1) {
2670 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2671 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2675 prev_t->set_beats_per_minute (new_bpm);
2676 recompute_tempos (future_map);
2677 recompute_meters (future_map);
2679 if (check_solved (future_map, true)) {
2681 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2682 prev_t->set_beats_per_minute (new_bpm);
2683 recompute_tempos (_metrics);
2685 if (ms->position_lock_style() == AudioTime) {
2686 ms->set_frame (frame);
2689 recompute_meters (_metrics);
2693 Metrics::const_iterator d = future_map.begin();
2694 while (d != future_map.end()) {
2699 MetricPositionChanged (); // Emit Signal
2704 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2706 Glib::Threads::RWLock::ReaderLock lm (lock);
2708 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2709 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2710 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2712 return frame_at_beat_locked (_metrics, total_beats);
2716 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2718 return round_to_type (fr, dir, Bar);
2722 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2724 return round_to_type (fr, dir, Beat);
2728 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2730 Glib::Threads::RWLock::ReaderLock lm (lock);
2731 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2732 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2733 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2735 ticks -= beats * BBT_Time::ticks_per_beat;
2738 /* round to next (or same iff dir == RoundUpMaybe) */
2740 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2742 if (mod == 0 && dir == RoundUpMaybe) {
2743 /* right on the subdivision, which is fine, so do nothing */
2745 } else if (mod == 0) {
2746 /* right on the subdivision, so the difference is just the subdivision ticks */
2747 ticks += ticks_one_subdivisions_worth;
2750 /* not on subdivision, compute distance to next subdivision */
2752 ticks += ticks_one_subdivisions_worth - mod;
2755 if (ticks >= BBT_Time::ticks_per_beat) {
2756 ticks -= BBT_Time::ticks_per_beat;
2758 } else if (dir < 0) {
2760 /* round to previous (or same iff dir == RoundDownMaybe) */
2762 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2764 if (difference == 0 && dir == RoundDownAlways) {
2765 /* right on the subdivision, but force-rounding down,
2766 so the difference is just the subdivision ticks */
2767 difference = ticks_one_subdivisions_worth;
2770 if (ticks < difference) {
2771 ticks = BBT_Time::ticks_per_beat - ticks;
2773 ticks -= difference;
2777 /* round to nearest */
2780 /* compute the distance to the previous and next subdivision */
2782 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2784 /* closer to the next subdivision, so shift forward */
2786 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2788 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2790 if (ticks > BBT_Time::ticks_per_beat) {
2792 ticks -= BBT_Time::ticks_per_beat;
2793 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2796 } else if (rem > 0) {
2798 /* closer to previous subdivision, so shift backward */
2802 /* can't go backwards past zero, so ... */
2805 /* step back to previous beat */
2807 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2808 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2810 ticks = lrint (ticks - rem);
2811 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2814 /* on the subdivision, do nothing */
2818 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2824 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2826 if (sub_num == -1) {
2827 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2828 if ((double) when.beats > bpb / 2.0) {
2834 } else if (sub_num == 0) {
2835 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2836 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2838 while ((double) when.beats > bpb) {
2840 when.beats -= (uint32_t) floor (bpb);
2846 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2848 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2849 /* closer to the next subdivision, so shift forward */
2851 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2853 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2855 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2858 } else if (rem > 0) {
2859 /* closer to previous subdivision, so shift backward */
2861 if (rem > when.ticks) {
2862 if (when.beats == 0) {
2863 /* can't go backwards past zero, so ... */
2865 /* step back to previous beat */
2867 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2869 when.ticks = when.ticks - rem;
2875 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2877 Glib::Threads::RWLock::ReaderLock lm (lock);
2879 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2880 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2885 /* find bar previous to 'frame' */
2888 return frame_time_locked (_metrics, bbt);
2890 } else if (dir > 0) {
2891 /* find bar following 'frame' */
2895 return frame_time_locked (_metrics, bbt);
2897 /* true rounding: find nearest bar */
2898 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2901 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2903 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2905 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2916 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2917 } else if (dir > 0) {
2918 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2920 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2929 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2930 framepos_t lower, framepos_t upper)
2932 Glib::Threads::RWLock::ReaderLock lm (lock);
2933 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2935 /* although the map handles negative beats, bbt doesn't. */
2939 while (pos < upper) {
2940 pos = frame_at_beat_locked (_metrics, cnt);
2941 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2942 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2943 const BBT_Time bbt = beats_to_bbt (cnt);
2944 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2950 TempoMap::tempo_section_at (framepos_t frame) const
2952 Glib::Threads::RWLock::ReaderLock lm (lock);
2953 return tempo_section_at_locked (_metrics, frame);
2957 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2959 Metrics::const_iterator i;
2960 TempoSection* prev = 0;
2962 for (i = metrics.begin(); i != metrics.end(); ++i) {
2965 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2969 if (prev && t->frame() > frame) {
2979 abort(); /*NOTREACHED*/
2986 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2988 TempoSection* prev_t = 0;
2989 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2991 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2993 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2994 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3005 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3007 TempoSection* prev_t = 0;
3009 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3011 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3012 if (prev_t && t->pulse() > pulse) {
3022 /* don't use this to calculate length (the tempo is only correct for this frame).
3023 do that stuff based on the beat_at_frame and frame_at_beat api
3026 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3028 Glib::Threads::RWLock::ReaderLock lm (lock);
3030 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3031 const TempoSection* ts_after = 0;
3032 Metrics::const_iterator i;
3034 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3037 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3041 if ((*i)->frame() > frame) {
3049 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3051 /* must be treated as constant tempo */
3052 return ts_at->frames_per_beat (_frame_rate);
3056 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3058 TempoSection* prev_t = 0;
3060 Metrics::const_iterator i;
3062 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3064 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3068 if ((prev_t) && t->frame() > frame) {
3069 /* t is the section past frame */
3070 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3071 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3078 const double ret = prev_t->beats_per_minute();
3079 const Tempo ret_tempo (ret, prev_t->note_type ());
3085 TempoMap::tempo_at (const framepos_t& frame) const
3087 Glib::Threads::RWLock::ReaderLock lm (lock);
3088 return tempo_at_locked (_metrics, frame);
3092 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3094 Metrics::const_iterator i;
3095 MeterSection* prev = 0;
3097 for (i = metrics.begin(); i != metrics.end(); ++i) {
3100 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3102 if (prev && (*i)->frame() > frame) {
3112 abort(); /*NOTREACHED*/
3120 TempoMap::meter_section_at (framepos_t frame) const
3122 Glib::Threads::RWLock::ReaderLock lm (lock);
3123 return meter_section_at_locked (_metrics, frame);
3127 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3129 MeterSection* prev_m = 0;
3131 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3133 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3134 if (prev_m && m->beat() > beat) {
3145 TempoMap::meter_section_at_beat (double beat) const
3147 Glib::Threads::RWLock::ReaderLock lm (lock);
3148 return meter_section_at_beat_locked (_metrics, beat);
3152 TempoMap::meter_at (framepos_t frame) const
3154 TempoMetric m (metric_at (frame));
3159 TempoMap::fix_legacy_session ()
3161 MeterSection* prev_m = 0;
3162 TempoSection* prev_t = 0;
3164 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3168 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3169 if (!m->movable()) {
3170 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3174 m->set_position_lock_style (AudioTime);
3179 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3180 + (m->bbt().beats - 1)
3181 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3183 m->set_beat (start);
3184 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3185 + (m->bbt().beats - 1)
3186 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3187 m->set_pulse (start_beat / prev_m->note_divisor());
3190 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3196 if (!t->movable()) {
3199 t->set_position_lock_style (AudioTime);
3205 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3206 + (t->legacy_bbt().beats - 1)
3207 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3209 t->set_pulse (beat / prev_m->note_divisor());
3211 /* really shouldn't happen but.. */
3212 t->set_pulse (beat / 4.0);
3221 TempoMap::get_state ()
3223 Metrics::const_iterator i;
3224 XMLNode *root = new XMLNode ("TempoMap");
3227 Glib::Threads::RWLock::ReaderLock lm (lock);
3228 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3229 root->add_child_nocopy ((*i)->get_state());
3237 TempoMap::set_state (const XMLNode& node, int /*version*/)
3240 Glib::Threads::RWLock::WriterLock lm (lock);
3243 XMLNodeConstIterator niter;
3244 Metrics old_metrics (_metrics);
3247 nlist = node.children();
3249 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3250 XMLNode* child = *niter;
3252 if (child->name() == TempoSection::xml_state_node_name) {
3255 TempoSection* ts = new TempoSection (*child);
3256 _metrics.push_back (ts);
3259 catch (failed_constructor& err){
3260 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3261 _metrics = old_metrics;
3265 } else if (child->name() == MeterSection::xml_state_node_name) {
3268 MeterSection* ms = new MeterSection (*child);
3269 _metrics.push_back (ms);
3272 catch (failed_constructor& err) {
3273 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3274 _metrics = old_metrics;
3280 if (niter == nlist.end()) {
3281 MetricSectionSorter cmp;
3282 _metrics.sort (cmp);
3285 /* check for legacy sessions where bbt was the base musical unit for tempo */
3286 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3288 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3289 if (t->legacy_bbt().bars != 0) {
3290 fix_legacy_session();
3297 /* check for multiple tempo/meters at the same location, which
3298 ardour2 somehow allowed.
3301 Metrics::iterator prev = _metrics.end();
3302 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3303 if (prev != _metrics.end()) {
3305 MeterSection* prev_m;
3307 TempoSection* prev_t;
3308 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3309 if (prev_m->pulse() == ms->pulse()) {
3310 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3311 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3314 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3315 if (prev_t->pulse() == ts->pulse()) {
3316 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3317 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3325 recompute_map (_metrics);
3328 PropertyChanged (PropertyChange ());
3334 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3336 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3337 const MeterSection* m;
3338 const TempoSection* t;
3339 const TempoSection* prev_t = 0;
3341 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3343 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3344 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3345 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3346 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3348 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3349 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;
3352 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3353 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3354 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3357 o << "------" << std::endl;
3361 TempoMap::n_tempos() const
3363 Glib::Threads::RWLock::ReaderLock lm (lock);
3366 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3367 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3376 TempoMap::n_meters() const
3378 Glib::Threads::RWLock::ReaderLock lm (lock);
3381 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3382 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3391 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3394 Glib::Threads::RWLock::WriterLock lm (lock);
3395 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3396 if ((*i)->frame() >= where && (*i)->movable ()) {
3397 (*i)->set_frame ((*i)->frame() + amount);
3401 /* now reset the BBT time of all metrics, based on their new
3402 * audio time. This is the only place where we do this reverse
3406 Metrics::iterator i;
3407 const MeterSection* meter;
3408 const TempoSection* tempo;
3412 meter = &first_meter ();
3413 tempo = &first_tempo ();
3418 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3421 MetricSection* prev = 0;
3423 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3426 //TempoMetric metric (*meter, *tempo);
3427 MeterSection* ms = const_cast<MeterSection*>(meter);
3428 TempoSection* ts = const_cast<TempoSection*>(tempo);
3431 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3435 ts->set_pulse (t->pulse());
3437 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3438 ts->set_pulse (m->pulse());
3440 ts->set_frame (prev->frame());
3444 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3445 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3446 ms->set_beat (start);
3447 ms->set_pulse (m->pulse());
3449 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3453 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3454 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3455 ms->set_beat (start);
3456 ms->set_pulse (t->pulse());
3458 ms->set_frame (prev->frame());
3462 // metric will be at frames=0 bbt=1|1|0 by default
3463 // which is correct for our purpose
3466 // cerr << bbt << endl;
3468 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3472 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3474 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3475 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3476 bbt_time (m->frame(), bbt);
3478 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3484 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3485 /* round up to next beat */
3491 if (bbt.beats != 1) {
3492 /* round up to next bar */
3497 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3498 m->set_beat (start);
3499 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3501 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3503 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3504 abort(); /*NOTREACHED*/
3510 recompute_map (_metrics);
3514 PropertyChanged (PropertyChange ());
3517 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3521 std::list<MetricSection*> metric_kill_list;
3523 TempoSection* last_tempo = NULL;
3524 MeterSection* last_meter = NULL;
3525 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3526 bool meter_after = false; // is there a meter marker likewise?
3528 Glib::Threads::RWLock::WriterLock lm (lock);
3529 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3530 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3531 metric_kill_list.push_back(*i);
3532 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3535 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3539 else if ((*i)->frame() >= where) {
3540 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3541 (*i)->set_frame ((*i)->frame() - amount);
3542 if ((*i)->frame() == where) {
3543 // marker was immediately after end of range
3544 tempo_after = dynamic_cast<TempoSection*> (*i);
3545 meter_after = dynamic_cast<MeterSection*> (*i);
3551 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3552 if (last_tempo && !tempo_after) {
3553 metric_kill_list.remove(last_tempo);
3554 last_tempo->set_frame(where);
3557 if (last_meter && !meter_after) {
3558 metric_kill_list.remove(last_meter);
3559 last_meter->set_frame(where);
3563 //remove all the remaining metrics
3564 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3565 _metrics.remove(*i);
3570 recompute_map (_metrics);
3573 PropertyChanged (PropertyChange ());
3577 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3578 * pos can be -ve, if required.
3581 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3583 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3586 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3588 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3590 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3593 /** Add the BBT interval op to pos and return the result */
3595 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3597 Glib::Threads::RWLock::ReaderLock lm (lock);
3599 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3600 pos_bbt.ticks += op.ticks;
3601 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3603 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3605 pos_bbt.beats += op.beats;
3606 /* the meter in effect will start on the bar */
3607 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();
3608 while (pos_bbt.beats >= divisions_per_bar + 1) {
3610 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3611 pos_bbt.beats -= divisions_per_bar;
3613 pos_bbt.bars += op.bars;
3615 return frame_time_locked (_metrics, pos_bbt);
3618 /** Count the number of beats that are equivalent to distance when going forward,
3622 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3624 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3628 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3634 operator<< (std::ostream& o, const Meter& m) {
3635 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3639 operator<< (std::ostream& o, const Tempo& t) {
3640 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3644 operator<< (std::ostream& o, const MetricSection& section) {
3646 o << "MetricSection @ " << section.frame() << ' ';
3648 const TempoSection* ts;
3649 const MeterSection* ms;
3651 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3652 o << *((const Tempo*) ts);
3653 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3654 o << *((const Meter*) ms);