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())
81 const XMLProperty *prop;
87 if ((prop = node.property ("start")) != 0) {
88 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
92 /* legacy session - start used to be in bbt*/
98 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
104 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
109 if ((prop = node.property ("frame")) != 0) {
110 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
111 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
117 if ((prop = node.property ("beats-per-minute")) == 0) {
118 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
119 throw failed_constructor();
122 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
123 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
124 throw failed_constructor();
127 if ((prop = node.property ("note-type")) == 0) {
128 /* older session, make note type be quarter by default */
131 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
132 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
133 throw failed_constructor();
137 if ((prop = node.property ("movable")) == 0) {
138 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
139 throw failed_constructor();
142 set_movable (string_is_affirmative (prop->value()));
144 if ((prop = node.property ("active")) == 0) {
145 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
148 set_active (string_is_affirmative (prop->value()));
151 if ((prop = node.property ("tempo-type")) == 0) {
154 _type = Type (string_2_enum (prop->value(), _type));
157 if ((prop = node.property ("lock-style")) == 0) {
158 set_position_lock_style (MusicTime);
160 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
165 TempoSection::get_state() const
167 XMLNode *root = new XMLNode (xml_state_node_name);
171 snprintf (buf, sizeof (buf), "%f", pulse());
172 root->add_property ("pulse", buf);
173 snprintf (buf, sizeof (buf), "%li", frame());
174 root->add_property ("frame", buf);
175 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
176 root->add_property ("beats-per-minute", buf);
177 snprintf (buf, sizeof (buf), "%f", _note_type);
178 root->add_property ("note-type", buf);
179 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
180 root->add_property ("movable", buf);
181 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
182 root->add_property ("active", buf);
183 root->add_property ("tempo-type", enum_2_string (_type));
184 root->add_property ("lock-style", enum_2_string (position_lock_style()));
190 TempoSection::set_type (Type type)
195 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
198 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
201 if (_type == Constant) {
202 return pulses_per_minute();
205 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
208 /** returns the zero-based frame (relative to session)
209 where the tempo in whole pulses per minute occurs in this section.
210 beat b is only used for constant tempos.
211 note that the tempo map may have multiple such values.
214 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
216 if (_type == Constant) {
217 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
220 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
222 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
225 TempoSection::tempo_at_pulse (const double& p) const
228 if (_type == Constant) {
229 return pulses_per_minute();
231 double const ppm = pulse_tempo_at_pulse (p - pulse());
235 /** returns the zero-based beat (relative to session)
236 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
237 note that the session tempo map may have multiple beats at a given tempo.
240 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
242 if (_type == Constant) {
243 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
246 return pulse_at_pulse_tempo (ppm) + pulse();
249 /** returns the zero-based pulse (relative to session origin)
250 where the zero-based frame (relative to session)
254 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
256 if (_type == Constant) {
257 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
260 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
263 /** returns the zero-based frame (relative to session start frame)
264 where the zero-based pulse (relative to session start)
269 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
271 if (_type == Constant) {
272 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
275 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
283 Tt----|-----------------*|
284 Ta----|--------------|* |
290 _______________|___|____
291 time a t (next tempo)
294 Duration in beats at time a is the integral of some Tempo function.
295 In our case, the Tempo function (Tempo at time t) is
298 with function constant
303 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
304 b(t) = T0(e^(ct) - 1) / c
306 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:
307 t(b) = log((cb / T0) + 1) / c
309 The time t at which Tempo T occurs is a as above:
310 t(T) = log(T / T0) / c
312 The beat at which a Tempo T occurs is:
315 The Tempo at which beat b occurs is:
318 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
319 Our problem is that we usually don't know t.
320 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.
321 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
322 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
324 By substituting our expanded t as a in the c function above, our problem is reduced to:
325 c = T0 (e^(log (Ta / T0)) - 1) / b
327 We can now store c for future time calculations.
328 If the following tempo section (the one that defines c in conjunction with this one)
329 is changed or moved, c is no longer valid.
331 The public methods are session-relative.
333 Most of this stuff is taken from this paper:
336 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
339 Zurich University of Arts
340 Institute for Computer Music and Sound Technology
342 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
347 compute this ramp's function constant using the end tempo (in whole pulses per minute)
348 and duration (pulses into global start) of some later tempo section.
351 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
353 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
354 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
357 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
359 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
361 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
365 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
367 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
371 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
373 return (frame / (double) frame_rate) / 60.0;
376 /* position function */
378 TempoSection::a_func (double end_bpm, double c_func) const
380 return log (end_bpm / pulses_per_minute()) / c_func;
383 /*function constant*/
385 TempoSection::c_func (double end_bpm, double end_time) const
387 return log (end_bpm / pulses_per_minute()) / end_time;
390 /* tempo in ppm at time in minutes */
392 TempoSection::pulse_tempo_at_time (const double& time) const
394 return exp (_c_func * time) * pulses_per_minute();
397 /* time in minutes at tempo in ppm */
399 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
401 return log (pulse_tempo / pulses_per_minute()) / _c_func;
404 /* tick at tempo in ppm */
406 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
408 return (pulse_tempo - pulses_per_minute()) / _c_func;
411 /* tempo in ppm at tick */
413 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
415 return (pulse * _c_func) + pulses_per_minute();
418 /* pulse at time in minutes */
420 TempoSection::pulse_at_time (const double& time) const
422 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
425 /* time in minutes at pulse */
427 TempoSection::time_at_pulse (const double& pulse) const
429 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
432 /***********************************************************************/
434 const string MeterSection::xml_state_node_name = "Meter";
436 MeterSection::MeterSection (const XMLNode& node)
437 : MetricSection (0.0), Meter (TempoMap::default_meter())
439 XMLProperty const * prop;
442 const XMLProperty *prop;
446 framepos_t frame = 0;
447 pair<double, BBT_Time> start;
449 if ((prop = node.property ("start")) != 0) {
450 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
454 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
456 /* legacy session - start used to be in bbt*/
460 error << _("MeterSection XML node has no \"start\" property") << endmsg;
463 if ((prop = node.property ("pulse")) != 0) {
464 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
465 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
470 if ((prop = node.property ("beat")) != 0) {
471 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
472 error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
478 if ((prop = node.property ("bbt")) == 0) {
479 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
480 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
484 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
485 //throw failed_constructor();
491 if ((prop = node.property ("frame")) != 0) {
492 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
493 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
499 /* beats-per-bar is old; divisions-per-bar is new */
501 if ((prop = node.property ("divisions-per-bar")) == 0) {
502 if ((prop = node.property ("beats-per-bar")) == 0) {
503 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
504 throw failed_constructor();
507 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
508 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
509 throw failed_constructor();
512 if ((prop = node.property ("note-type")) == 0) {
513 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
514 throw failed_constructor();
516 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
517 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
518 throw failed_constructor();
521 if ((prop = node.property ("lock-style")) == 0) {
522 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
523 set_position_lock_style (MusicTime);
525 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
528 if ((prop = node.property ("movable")) == 0) {
529 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
530 throw failed_constructor();
533 set_movable (string_is_affirmative (prop->value()));
537 MeterSection::get_state() const
539 XMLNode *root = new XMLNode (xml_state_node_name);
543 snprintf (buf, sizeof (buf), "%lf", pulse());
544 root->add_property ("pulse", buf);
545 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
549 root->add_property ("bbt", buf);
550 snprintf (buf, sizeof (buf), "%lf", beat());
551 root->add_property ("beat", buf);
552 snprintf (buf, sizeof (buf), "%f", _note_type);
553 root->add_property ("note-type", buf);
554 snprintf (buf, sizeof (buf), "%li", frame());
555 root->add_property ("frame", buf);
556 root->add_property ("lock-style", enum_2_string (position_lock_style()));
557 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
558 root->add_property ("divisions-per-bar", buf);
559 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
560 root->add_property ("movable", buf);
565 /***********************************************************************/
569 Tempo can be thought of as a source of the musical pulse.
570 Meters divide that pulse into measures and beats.
571 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
572 at any particular time.
573 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
574 It should rather be thought of as tempo note divisions per minute.
576 TempoSections, which are nice to think of in whole pulses per minute,
577 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
578 and beats (via note_divisor) are used to form a tempo map.
579 TempoSections and MeterSections may be locked to either audio or music (position lock style).
580 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
581 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
583 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
585 The first tempo and first meter are special. they must move together, and must be locked to audio.
586 Audio locked tempos which lie before the first meter are made inactive.
587 They will be re-activated if the first meter is again placed before them.
589 Both tempos and meters have a pulse position and a frame position.
590 Meters also have a beat position, which is always 0.0 for the first meter.
592 A tempo locked to music is locked to musical pulses.
593 A meter locked to music is locked to beats.
595 Recomputing the tempo map is the process where the 'missing' position
596 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
598 It is important to keep the _metrics in an order that makes sense.
599 Because ramped MusicTime and AudioTime tempos can interact with each other,
600 reordering is frequent. Care must be taken to keep _metrics in a solved state.
601 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
603 struct MetricSectionSorter {
604 bool operator() (const MetricSection* a, const MetricSection* b) {
605 return a->pulse() < b->pulse();
609 struct MetricSectionFrameSorter {
610 bool operator() (const MetricSection* a, const MetricSection* b) {
611 return a->frame() < b->frame();
615 TempoMap::TempoMap (framecnt_t fr)
618 BBT_Time start (1, 1, 0);
620 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
621 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
623 t->set_movable (false);
624 m->set_movable (false);
626 /* note: frame time is correct (zero) for both of these */
628 _metrics.push_back (t);
629 _metrics.push_back (m);
633 TempoMap::~TempoMap ()
638 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
640 bool removed = false;
643 Glib::Threads::RWLock::WriterLock lm (lock);
644 if ((removed = remove_tempo_locked (tempo))) {
645 if (complete_operation) {
646 recompute_map (_metrics);
651 if (removed && complete_operation) {
652 PropertyChanged (PropertyChange ());
657 TempoMap::remove_tempo_locked (const TempoSection& tempo)
661 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
662 if (dynamic_cast<TempoSection*> (*i) != 0) {
663 if (tempo.frame() == (*i)->frame()) {
664 if ((*i)->movable()) {
676 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
678 bool removed = false;
681 Glib::Threads::RWLock::WriterLock lm (lock);
682 if ((removed = remove_meter_locked (tempo))) {
683 if (complete_operation) {
684 recompute_map (_metrics);
689 if (removed && complete_operation) {
690 PropertyChanged (PropertyChange ());
695 TempoMap::remove_meter_locked (const MeterSection& tempo)
699 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
700 if (dynamic_cast<MeterSection*> (*i) != 0) {
701 if (tempo.frame() == (*i)->frame()) {
702 if ((*i)->movable()) {
714 TempoMap::do_insert (MetricSection* section)
716 bool need_add = true;
717 /* we only allow new meters to be inserted on beat 1 of an existing
721 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
722 //assert (m->bbt().ticks == 0);
724 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
726 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
727 corrected.second.beats = 1;
728 corrected.second.ticks = 0;
729 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
730 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
731 m->bbt(), corrected.second) << endmsg;
732 //m->set_pulse (corrected);
736 /* Look for any existing MetricSection that is of the same type and
737 in the same bar as the new one, and remove it before adding
738 the new one. Note that this means that if we find a matching,
739 existing section, we can break out of the loop since we're
740 guaranteed that there is only one such match.
743 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
745 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
746 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
747 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
748 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
750 if (tempo && insert_tempo) {
753 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
754 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
756 if (!tempo->movable()) {
758 /* can't (re)move this section, so overwrite
759 * its data content (but not its properties as
763 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
764 (*i)->set_position_lock_style (AudioTime);
766 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
767 t->set_type (insert_tempo->type());
776 } else if (meter && insert_meter) {
780 bool const ipm = insert_meter->position_lock_style() == MusicTime;
782 if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
784 if (!meter->movable()) {
786 /* can't (re)move this section, so overwrite
787 * its data content (but not its properties as
791 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
792 (*i)->set_position_lock_style (insert_meter->position_lock_style());
802 /* non-matching types, so we don't care */
806 /* Add the given MetricSection, if we didn't just reset an existing
811 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
812 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
815 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
816 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
819 bool const ipm = insert_meter->position_lock_style() == MusicTime;
820 if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
825 } else if (insert_tempo) {
826 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
827 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
830 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
831 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
838 _metrics.insert (i, section);
839 //dump (_metrics, std::cerr);
844 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
847 Glib::Threads::RWLock::WriterLock lm (lock);
848 TempoSection& first (first_tempo());
849 if (ts.pulse() != first.pulse()) {
850 remove_tempo_locked (ts);
851 add_tempo_locked (tempo, pulse, true, type);
853 first.set_type (type);
855 /* cannot move the first tempo section */
856 *static_cast<Tempo*>(&first) = tempo;
857 recompute_map (_metrics);
862 PropertyChanged (PropertyChange ());
866 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
869 Glib::Threads::RWLock::WriterLock lm (lock);
870 TempoSection& first (first_tempo());
871 if (ts.frame() != first.frame()) {
872 remove_tempo_locked (ts);
873 add_tempo_locked (tempo, frame, true, type);
875 first.set_type (type);
876 first.set_pulse (0.0);
877 first.set_position_lock_style (AudioTime);
879 /* cannot move the first tempo section */
880 *static_cast<Tempo*>(&first) = tempo;
881 recompute_map (_metrics);
886 PropertyChanged (PropertyChange ());
890 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
893 Glib::Threads::RWLock::WriterLock lm (lock);
894 add_tempo_locked (tempo, pulse, true, type);
897 PropertyChanged (PropertyChange ());
901 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
904 Glib::Threads::RWLock::WriterLock lm (lock);
905 add_tempo_locked (tempo, frame, true, type);
909 PropertyChanged (PropertyChange ());
913 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
915 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
920 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
925 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
927 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
932 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
937 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
940 Glib::Threads::RWLock::WriterLock lm (lock);
943 remove_meter_locked (ms);
944 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
946 MeterSection& first (first_meter());
947 const PositionLockStyle pl = ms.position_lock_style();
948 /* cannot move the first meter section */
949 *static_cast<Meter*>(&first) = meter;
950 first.set_position_lock_style (pl);
951 recompute_map (_metrics);
955 PropertyChanged (PropertyChange ());
959 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
962 Glib::Threads::RWLock::WriterLock lm (lock);
964 const double beat = ms.beat();
965 const BBT_Time bbt = ms.bbt();
968 remove_meter_locked (ms);
969 add_meter_locked (meter, frame, beat, bbt, true);
971 MeterSection& first (first_meter());
972 TempoSection& first_t (first_tempo());
973 /* cannot move the first meter section */
974 *static_cast<Meter*>(&first) = meter;
975 first.set_position_lock_style (AudioTime);
976 first.set_pulse (0.0);
977 first.set_frame (frame);
978 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
979 first.set_beat (beat);
980 first_t.set_frame (first.frame());
981 first_t.set_pulse (0.0);
982 first_t.set_position_lock_style (AudioTime);
984 recompute_map (_metrics);
987 PropertyChanged (PropertyChange ());
992 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
995 Glib::Threads::RWLock::WriterLock lm (lock);
996 add_meter_locked (meter, beat, where, true);
1001 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1002 dump (_metrics, std::cerr);
1006 PropertyChanged (PropertyChange ());
1010 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1013 Glib::Threads::RWLock::WriterLock lm (lock);
1014 add_meter_locked (meter, frame, beat, where, true);
1019 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1020 dump (_metrics, std::cerr);
1024 PropertyChanged (PropertyChange ());
1028 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1030 /* a new meter always starts a new bar on the first beat. so
1031 round the start time appropriately. remember that
1032 `where' is based on the existing tempo map, not
1033 the result after we insert the new meter.
1037 if (where.beats != 1) {
1041 /* new meters *always* start on a beat. */
1043 double pulse = pulse_at_beat_locked (_metrics, beat);
1045 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1046 do_insert (new_meter);
1049 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1055 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1058 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1060 double pulse = pulse_at_frame_locked (_metrics, frame);
1061 new_meter->set_pulse (pulse);
1063 do_insert (new_meter);
1066 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1072 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1074 Tempo newtempo (beats_per_minute, note_type);
1077 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1078 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1083 Glib::Threads::RWLock::WriterLock lm (lock);
1084 *((Tempo*) t) = newtempo;
1085 recompute_map (_metrics);
1087 PropertyChanged (PropertyChange ());
1094 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1096 Tempo newtempo (beats_per_minute, note_type);
1099 TempoSection* first;
1100 Metrics::iterator i;
1102 /* find the TempoSection immediately preceding "where"
1105 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1107 if ((*i)->frame() > where) {
1113 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1126 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1136 Glib::Threads::RWLock::WriterLock lm (lock);
1137 /* cannot move the first tempo section */
1138 *((Tempo*)prev) = newtempo;
1139 recompute_map (_metrics);
1142 PropertyChanged (PropertyChange ());
1146 TempoMap::first_meter () const
1148 const MeterSection *m = 0;
1150 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1151 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1156 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1157 abort(); /*NOTREACHED*/
1162 TempoMap::first_meter ()
1164 MeterSection *m = 0;
1166 /* CALLER MUST HOLD LOCK */
1168 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1169 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1174 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1175 abort(); /*NOTREACHED*/
1180 TempoMap::first_tempo () const
1182 const TempoSection *t = 0;
1184 /* CALLER MUST HOLD LOCK */
1186 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1187 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1191 if (!t->movable()) {
1197 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1198 abort(); /*NOTREACHED*/
1203 TempoMap::first_tempo ()
1205 TempoSection *t = 0;
1207 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1208 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1212 if (!t->movable()) {
1218 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1219 abort(); /*NOTREACHED*/
1223 TempoMap::recompute_tempos (Metrics& metrics)
1225 TempoSection* prev_ts = 0;
1227 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1230 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1235 if (t->position_lock_style() == AudioTime) {
1236 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1237 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1240 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1241 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1248 prev_ts->set_c_func (0.0);
1251 /* tempos must be positioned correctly */
1253 TempoMap::recompute_meters (Metrics& metrics)
1255 MeterSection* meter = 0;
1256 MeterSection* prev_m = 0;
1258 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1259 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1260 if (meter->position_lock_style() == AudioTime) {
1262 pair<double, BBT_Time> b_bbt;
1263 if (meter->movable()) {
1264 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1265 const double ceil_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1266 b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1267 const double true_pulse = prev_m->pulse() + (ceil_beats - prev_m->beat()) / prev_m->note_divisor();
1268 const double pulse_off = true_pulse - ((beats - prev_m->beat()) / prev_m->note_divisor());
1269 pulse = true_pulse - pulse_off;
1271 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1273 meter->set_beat (b_bbt);
1274 meter->set_pulse (pulse);
1278 pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1280 pulse = pulse_at_beat_locked (metrics, meter->beat());
1282 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1283 meter->set_pulse (pulse);
1292 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1294 /* CALLER MUST HOLD WRITE LOCK */
1298 /* we will actually stop once we hit
1305 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1308 /* silly call from Session::process() during startup
1313 recompute_tempos (metrics);
1314 recompute_meters (metrics);
1318 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1320 Glib::Threads::RWLock::ReaderLock lm (lock);
1321 TempoMetric m (first_meter(), first_tempo());
1323 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1324 at something, because we insert the default tempo and meter during
1325 TempoMap construction.
1327 now see if we can find better candidates.
1330 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1332 if ((*i)->frame() > frame) {
1346 /* XX meters only */
1348 TempoMap::metric_at (BBT_Time bbt) const
1350 Glib::Threads::RWLock::ReaderLock lm (lock);
1351 TempoMetric m (first_meter(), first_tempo());
1353 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1354 at something, because we insert the default tempo and meter during
1355 TempoMap construction.
1357 now see if we can find better candidates.
1360 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1362 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1363 BBT_Time section_start (mw->bbt());
1365 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1377 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1379 /* HOLD (at least) THE READER LOCK */
1380 TempoSection* prev_ts = 0;
1381 double accumulated_pulses = 0.0;
1383 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1385 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1389 if (prev_ts && t->frame() > frame) {
1390 /*the previous ts is the one containing the frame */
1391 const double ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1394 accumulated_pulses = t->pulse();
1399 /* treated as constant for this ts */
1400 const double pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1402 return pulses_in_section + accumulated_pulses;
1406 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1408 MeterSection* prev_ms = 0;
1409 double accumulated_beats = 0.0;
1411 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1413 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1414 if (prev_ms && m->beat() > beat) {
1417 accumulated_beats = m->beat();
1422 double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1427 TempoMap::pulse_at_beat (const double& beat) const
1429 Glib::Threads::RWLock::ReaderLock lm (lock);
1430 return pulse_at_beat_locked (_metrics, beat);
1434 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1436 MeterSection* prev_ms = 0;
1437 double accumulated_beats = 0.0;
1439 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1441 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1442 if (prev_ms && m->pulse() > pulse) {
1445 accumulated_beats = m->beat();
1450 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1452 return beats_in_section + accumulated_beats;
1456 TempoMap::beat_at_pulse (const double& pulse) const
1458 Glib::Threads::RWLock::ReaderLock lm (lock);
1459 return beat_at_pulse_locked (_metrics, pulse);
1463 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1465 /* HOLD THE READER LOCK */
1467 const TempoSection* prev_ts = 0;
1468 double accumulated_pulses = 0.0;
1470 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1473 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1477 if (prev_ts && t->pulse() > pulse) {
1478 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1481 accumulated_pulses = t->pulse();
1485 /* must be treated as constant, irrespective of _type */
1486 double const pulses_in_section = pulse - accumulated_pulses;
1487 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1489 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1495 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1497 const double pulse = pulse_at_frame_locked (metrics, frame);
1498 return beat_at_pulse_locked (metrics, pulse);
1502 TempoMap::beat_at_frame (const framecnt_t& frame) const
1504 Glib::Threads::RWLock::ReaderLock lm (lock);
1505 return beat_at_frame_locked (_metrics, frame);
1509 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1511 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1516 TempoMap::frame_at_beat (const double& beat) const
1518 Glib::Threads::RWLock::ReaderLock lm (lock);
1519 return frame_at_beat_locked (_metrics, beat);
1523 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1525 /* CALLER HOLDS READ LOCK */
1527 double accumulated_beats = 0.0;
1528 double accumulated_bars = 0.0;
1529 MeterSection* prev_ms = 0;
1531 /* because audio-locked meters have 'fake' integral beats,
1532 there is no pulse offset here.
1534 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1536 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1538 const double bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1539 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1542 accumulated_beats = m->beat();
1543 accumulated_bars += bars_to_m;
1549 const double remaining_bars = (bbt.bars - 1) - accumulated_bars;
1550 const double remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1551 const double ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1557 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1559 Glib::Threads::RWLock::ReaderLock lm (lock);
1560 return bbt_to_beats_locked (_metrics, bbt);
1564 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1566 /* CALLER HOLDS READ LOCK */
1567 MeterSection* prev_ms = 0;
1568 const double beats = (b < 0.0) ? 0.0 : b;
1569 uint32_t accumulated_bars = 0;
1570 double accumulated_beats = 0.0;
1572 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1573 MeterSection* m = 0;
1575 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1577 if (m->beat() > beats) {
1578 /* this is the meter after the one our beat is on*/
1581 const double beats_to_m = m->beat() - prev_ms->beat();
1582 /* we need a whole number of bars. */
1583 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1584 accumulated_beats += beats_to_m;
1591 const double beats_in_ms = beats - accumulated_beats;
1592 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1593 const uint32_t total_bars = bars_in_ms + accumulated_bars;
1594 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1595 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1599 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1600 ret.beats = (uint32_t) floor (remaining_beats);
1601 ret.bars = total_bars;
1603 /* 0 0 0 to 1 1 0 - based mapping*/
1607 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1609 ret.ticks -= BBT_Time::ticks_per_beat;
1612 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1621 TempoMap::beats_to_bbt (const double& beats)
1623 Glib::Threads::RWLock::ReaderLock lm (lock);
1624 return beats_to_bbt_locked (_metrics, beats);
1628 TempoMap::pulse_to_bbt (const double& pulse)
1630 Glib::Threads::RWLock::ReaderLock lm (lock);
1631 MeterSection* prev_ms = 0;
1632 uint32_t accumulated_bars = 0;
1633 double accumulated_pulses = 0.0;
1635 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1636 MeterSection* m = 0;
1638 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1641 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1642 if (accumulated_pulses + pulses_to_m > pulse) {
1643 /* this is the meter after the one our beat is on*/
1647 /* we need a whole number of bars. */
1648 accumulated_pulses += pulses_to_m;
1649 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1655 const double beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1656 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1657 const uint32_t total_bars = bars_in_ms + accumulated_bars;
1658 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1659 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1663 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1664 ret.beats = (uint32_t) floor (remaining_beats);
1665 ret.bars = total_bars;
1667 /* 0 0 0 to 1 1 0 mapping*/
1671 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1673 ret.ticks -= BBT_Time::ticks_per_beat;
1676 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1685 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1692 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1695 Glib::Threads::RWLock::ReaderLock lm (lock);
1696 const double beat = beat_at_frame_locked (_metrics, frame);
1698 bbt = beats_to_bbt_locked (_metrics, beat);
1702 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1704 /* HOLD THE READER LOCK */
1706 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1711 TempoMap::frame_time (const BBT_Time& bbt)
1714 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1718 if (bbt.beats < 1) {
1719 throw std::logic_error ("beats are counted from one");
1721 Glib::Threads::RWLock::ReaderLock lm (lock);
1722 const double beat = bbt_to_beats_locked (_metrics, bbt);
1723 const framecnt_t frame = frame_at_beat_locked (_metrics, beat);
1729 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1731 TempoSection* prev_ts = 0;
1733 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1735 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1740 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
1744 if (t->frame() == prev_ts->frame()) {
1748 /* precision check ensures pulses and frames align independent of lock style.*/
1749 if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
1761 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1763 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1765 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1766 if (!t->movable()) {
1767 t->set_active (true);
1770 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1771 t->set_active (false);
1773 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1774 t->set_active (true);
1775 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1784 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1786 TempoSection* prev_ts = 0;
1787 TempoSection* section_prev = 0;
1788 framepos_t first_m_frame = 0;
1790 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1792 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1793 if (!m->movable()) {
1794 first_m_frame = m->frame();
1799 if (section->movable() && frame <= first_m_frame) {
1802 section->set_active (true);
1804 section->set_frame (frame);
1806 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1808 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1815 section_prev = prev_ts;
1818 if (t->position_lock_style() == MusicTime) {
1819 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1820 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1822 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1823 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1831 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1832 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1835 if (section->position_lock_style() == MusicTime) {
1836 /* we're setting the frame */
1837 section->set_position_lock_style (AudioTime);
1838 recompute_tempos (imaginary);
1839 section->set_position_lock_style (MusicTime);
1841 recompute_tempos (imaginary);
1844 if (check_solved (imaginary, true)) {
1845 recompute_meters (imaginary);
1849 MetricSectionFrameSorter fcmp;
1850 imaginary.sort (fcmp);
1851 if (section->position_lock_style() == MusicTime) {
1852 /* we're setting the frame */
1853 section->set_position_lock_style (AudioTime);
1854 recompute_tempos (imaginary);
1855 section->set_position_lock_style (MusicTime);
1857 recompute_tempos (imaginary);
1859 if (check_solved (imaginary, true)) {
1860 recompute_meters (imaginary);
1864 MetricSectionSorter cmp;
1865 imaginary.sort (cmp);
1866 if (section->position_lock_style() == MusicTime) {
1867 /* we're setting the frame */
1868 section->set_position_lock_style (AudioTime);
1869 recompute_tempos (imaginary);
1870 section->set_position_lock_style (MusicTime);
1872 recompute_tempos (imaginary);
1874 if (check_solved (imaginary, true)) {
1875 recompute_meters (imaginary);
1878 //dump (imaginary, std::cerr);
1884 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
1886 TempoSection* prev_ts = 0;
1887 TempoSection* section_prev = 0;
1889 section->set_pulse (pulse);
1891 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1893 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1899 section_prev = prev_ts;
1902 if (t->position_lock_style() == MusicTime) {
1903 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1904 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1906 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1907 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1914 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1915 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1918 if (section->position_lock_style() == AudioTime) {
1919 /* we're setting the pulse */
1920 section->set_position_lock_style (MusicTime);
1921 recompute_tempos (imaginary);
1922 section->set_position_lock_style (AudioTime);
1924 recompute_tempos (imaginary);
1926 if (check_solved (imaginary, false)) {
1927 recompute_meters (imaginary);
1931 MetricSectionSorter cmp;
1932 imaginary.sort (cmp);
1933 if (section->position_lock_style() == AudioTime) {
1934 /* we're setting the pulse */
1935 section->set_position_lock_style (MusicTime);
1936 recompute_tempos (imaginary);
1937 section->set_position_lock_style (AudioTime);
1939 recompute_tempos (imaginary);
1942 if (check_solved (imaginary, false)) {
1943 recompute_meters (imaginary);
1947 MetricSectionFrameSorter fcmp;
1948 imaginary.sort (fcmp);
1949 if (section->position_lock_style() == AudioTime) {
1950 /* we're setting the pulse */
1951 section->set_position_lock_style (MusicTime);
1952 recompute_tempos (imaginary);
1953 section->set_position_lock_style (AudioTime);
1955 recompute_tempos (imaginary);
1958 if (check_solved (imaginary, false)) {
1959 recompute_meters (imaginary);
1963 //dump (imaginary, std::cerr);
1969 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
1971 MeterSection* prev_ms = 0;
1973 if (!section->movable()) {
1974 /* lock the first tempo to our first meter */
1975 if (!set_active_tempos (imaginary, frame)) {
1978 TempoSection* first_t = &first_tempo();
1980 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1982 new_section->set_frame (frame);
1983 new_section->set_pulse (0.0);
1984 new_section->set_active (true);
1986 if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
1987 first_t->set_frame (frame);
1988 first_t->set_pulse (0.0);
1989 first_t->set_active (true);
1990 solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
1996 section->set_frame (frame);
1998 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2000 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2003 here we set the beat for this frame.
2004 we set it 'incorrectly' to the next bar's beat
2005 and use this difference to find the meter's pulse.
2008 pair<double, BBT_Time> b_bbt;
2010 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2011 const double ceil_beats = beats - fmod (beats, prev_ms->divisions_per_bar());
2012 b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 1, 0));
2013 const double true_pulse = prev_ms->pulse() + ((ceil_beats - prev_ms->beat()) / prev_ms->note_divisor());
2014 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2015 pulse = true_pulse - pulse_off;
2017 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2019 m->set_beat (b_bbt);
2020 m->set_pulse (pulse);
2025 if (m->position_lock_style() == MusicTime) {
2026 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2027 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2028 m->set_pulse (pulse);
2031 pair<double, BBT_Time> b_bbt;
2033 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2034 const double ceil_beats = beats - fmod (beats , prev_ms->divisions_per_bar());
2035 b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 1, 0));
2036 const double true_pulse = prev_ms->pulse() + (ceil_beats - prev_ms->beat()) / prev_ms->note_divisor();
2037 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2038 pulse = true_pulse - pulse_off;
2040 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2043 m->set_beat (b_bbt);
2044 m->set_pulse (pulse);
2051 if (section->position_lock_style() == MusicTime) {
2052 /* we're setting the frame */
2053 section->set_position_lock_style (AudioTime);
2054 recompute_meters (imaginary);
2055 section->set_position_lock_style (MusicTime);
2057 recompute_meters (imaginary);
2059 //dump (imaginary, std::cerr);
2063 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2065 MeterSection* prev_ms = 0;
2067 section->set_pulse (pulse);
2069 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2071 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2073 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2074 const double beats = ((pulse - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2075 const int32_t bars = (beats + 1) / prev_ms->divisions_per_bar();
2076 pair<double, BBT_Time> b_bbt = make_pair (beats, BBT_Time (bars + 1, 1, 0));
2077 section->set_beat (b_bbt);
2082 if (m->position_lock_style() == MusicTime) {
2083 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2084 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2085 m->set_pulse (pulse);
2088 pair<double, BBT_Time> b_bbt;
2090 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2091 const double ceil_beats = beats - fmod (beats, prev_ms->divisions_per_bar());
2092 b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 1, 0));
2093 const double true_pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2094 const double pulse_off = true_pulse - ((ceil_beats - prev_ms->beat()) / prev_ms->note_divisor());
2095 pulse = true_pulse - pulse_off;
2097 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2099 m->set_beat (b_bbt);
2100 m->set_pulse (pulse);
2107 if (section->position_lock_style() == AudioTime) {
2108 /* we're setting the pulse */
2109 section->set_position_lock_style (MusicTime);
2110 recompute_meters (imaginary);
2111 section->set_position_lock_style (AudioTime);
2113 recompute_meters (imaginary);
2117 /** places a copy of _metrics into copy and returns a pointer
2118 * to section's equivalent.
2121 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2124 TempoSection* ret = 0;
2127 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2128 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2130 if (t->position_lock_style() == MusicTime) {
2131 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2133 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2135 ret->set_active (t->active());
2136 ret->set_movable (t->movable());
2137 copy.push_back (ret);
2140 TempoSection* cp = 0;
2141 if (t->position_lock_style() == MusicTime) {
2142 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2144 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2146 cp->set_active (t->active());
2147 cp->set_movable (t->movable());
2148 copy.push_back (cp);
2150 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2151 MeterSection* cp = 0;
2152 if (m->position_lock_style() == MusicTime) {
2153 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2154 cp->set_frame (m->frame());
2156 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2157 cp->set_pulse (m->pulse());
2159 cp->set_movable (m->movable());
2160 copy.push_back (cp);
2163 //recompute_map (copy);
2168 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
2171 TempoSection* new_section = 0;
2174 Glib::Threads::RWLock::ReaderLock lm (lock);
2175 new_section = copy_metrics_and_point (copy, ts);
2178 double const beat = bbt_to_beats_locked (copy, bbt);
2179 bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
2181 Metrics::const_iterator d = copy.begin();
2182 while (d != copy.end()) {
2191 * 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,
2192 * taking any possible reordering as a consequence of this into account.
2193 * @param section - the section to be altered
2194 * @param bpm - the new Tempo
2195 * @param bbt - the bbt where the altered tempo will fall
2196 * @return returns - the position in frames where the new tempo section will lie.
2199 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
2201 Glib::Threads::RWLock::ReaderLock lm (lock);
2204 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2206 const double beat = bbt_to_beats_locked (future_map, bbt);
2207 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2208 ret = new_section->frame();
2210 ret = frame_at_beat_locked (_metrics, beat);
2213 Metrics::const_iterator d = future_map.begin();
2214 while (d != future_map.end()) {
2222 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2224 Glib::Threads::RWLock::ReaderLock lm (lock);
2227 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2229 if (solve_map (future_map, new_section, bpm, frame)) {
2230 ret = new_section->pulse();
2232 ret = pulse_at_frame_locked (_metrics, frame);
2235 Metrics::const_iterator d = future_map.begin();
2236 while (d != future_map.end()) {
2244 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
2248 Glib::Threads::RWLock::WriterLock lm (lock);
2249 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2250 if (solve_map (future_map, new_section, bpm, frame)) {
2251 solve_map (_metrics, ts, bpm, frame);
2255 Metrics::const_iterator d = future_map.begin();
2256 while (d != future_map.end()) {
2261 MetricPositionChanged (); // Emit Signal
2265 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
2269 Glib::Threads::RWLock::WriterLock lm (lock);
2270 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2271 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2272 solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
2276 Metrics::const_iterator d = future_map.begin();
2277 while (d != future_map.end()) {
2282 MetricPositionChanged (); // Emit Signal
2286 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
2289 Glib::Threads::RWLock::WriterLock lm (lock);
2290 solve_map (_metrics, ms, mt, frame);
2293 MetricPositionChanged (); // Emit Signal
2297 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
2300 Glib::Threads::RWLock::WriterLock lm (lock);
2301 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
2304 MetricPositionChanged (); // Emit Signal
2308 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2311 bool can_solve = false;
2313 Glib::Threads::RWLock::WriterLock lm (lock);
2314 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2315 new_section->set_beats_per_minute (bpm.beats_per_minute());
2316 recompute_tempos (future_map);
2318 if (check_solved (future_map, true)) {
2319 ts->set_beats_per_minute (bpm.beats_per_minute());
2320 recompute_map (_metrics);
2325 Metrics::const_iterator d = future_map.begin();
2326 while (d != future_map.end()) {
2331 MetricPositionChanged (); // Emit Signal
2337 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2339 Glib::Threads::RWLock::ReaderLock lm (lock);
2341 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2342 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2343 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2344 const framecnt_t time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2345 const framecnt_t ret = time_at_bbt;
2351 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2353 return round_to_type (fr, dir, Bar);
2357 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2359 return round_to_type (fr, dir, Beat);
2363 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2365 Glib::Threads::RWLock::ReaderLock lm (lock);
2366 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2367 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2368 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2370 ticks -= beats * BBT_Time::ticks_per_beat;
2373 /* round to next (or same iff dir == RoundUpMaybe) */
2375 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2377 if (mod == 0 && dir == RoundUpMaybe) {
2378 /* right on the subdivision, which is fine, so do nothing */
2380 } else if (mod == 0) {
2381 /* right on the subdivision, so the difference is just the subdivision ticks */
2382 ticks += ticks_one_subdivisions_worth;
2385 /* not on subdivision, compute distance to next subdivision */
2387 ticks += ticks_one_subdivisions_worth - mod;
2390 if (ticks >= BBT_Time::ticks_per_beat) {
2391 ticks -= BBT_Time::ticks_per_beat;
2393 } else if (dir < 0) {
2395 /* round to previous (or same iff dir == RoundDownMaybe) */
2397 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2399 if (difference == 0 && dir == RoundDownAlways) {
2400 /* right on the subdivision, but force-rounding down,
2401 so the difference is just the subdivision ticks */
2402 difference = ticks_one_subdivisions_worth;
2405 if (ticks < difference) {
2406 ticks = BBT_Time::ticks_per_beat - ticks;
2408 ticks -= difference;
2412 /* round to nearest */
2415 /* compute the distance to the previous and next subdivision */
2417 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2419 /* closer to the next subdivision, so shift forward */
2421 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2423 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2425 if (ticks > BBT_Time::ticks_per_beat) {
2427 ticks -= BBT_Time::ticks_per_beat;
2428 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2431 } else if (rem > 0) {
2433 /* closer to previous subdivision, so shift backward */
2437 /* can't go backwards past zero, so ... */
2440 /* step back to previous beat */
2442 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2443 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2445 ticks = lrint (ticks - rem);
2446 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2449 /* on the subdivision, do nothing */
2453 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2459 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2461 if (sub_num == -1) {
2462 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2463 if ((double) when.beats > bpb / 2.0) {
2469 } else if (sub_num == 0) {
2470 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2471 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2473 while ((double) when.beats > bpb) {
2475 when.beats -= (uint32_t) floor (bpb);
2481 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2483 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2484 /* closer to the next subdivision, so shift forward */
2486 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2488 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2490 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2493 } else if (rem > 0) {
2494 /* closer to previous subdivision, so shift backward */
2496 if (rem > when.ticks) {
2497 if (when.beats == 0) {
2498 /* can't go backwards past zero, so ... */
2500 /* step back to previous beat */
2502 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2504 when.ticks = when.ticks - rem;
2510 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2512 Glib::Threads::RWLock::ReaderLock lm (lock);
2514 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2515 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2520 /* find bar previous to 'frame' */
2523 return frame_time (bbt);
2525 } else if (dir > 0) {
2526 /* find bar following 'frame' */
2530 return frame_time (bbt);
2532 /* true rounding: find nearest bar */
2533 framepos_t raw_ft = frame_time (bbt);
2536 framepos_t prev_ft = frame_time (bbt);
2538 framepos_t next_ft = frame_time (bbt);
2540 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2551 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2552 } else if (dir > 0) {
2553 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2555 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2564 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2565 framepos_t lower, framepos_t upper)
2567 Glib::Threads::RWLock::ReaderLock lm (lock);
2568 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2569 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2570 /* although the map handles negative beats, bbt doesn't. */
2574 while (cnt <= upper_beat) {
2575 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2576 const TempoSection tempo = tempo_section_at_locked (pos);
2577 const MeterSection meter = meter_section_at_locked (pos);
2578 const BBT_Time bbt = beats_to_bbt (cnt);
2579 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
2585 TempoMap::tempo_section_at (framepos_t frame) const
2587 Glib::Threads::RWLock::ReaderLock lm (lock);
2588 return tempo_section_at_locked (frame);
2592 TempoMap::tempo_section_at_locked (framepos_t frame) const
2594 Metrics::const_iterator i;
2595 TempoSection* prev = 0;
2597 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2600 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2604 if (t->frame() > frame) {
2614 abort(); /*NOTREACHED*/
2621 /* don't use this to calculate length (the tempo is only correct for this frame).
2622 do that stuff based on the beat_at_frame and frame_at_beat api
2625 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2627 Glib::Threads::RWLock::ReaderLock lm (lock);
2629 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2630 const TempoSection* ts_after = 0;
2631 Metrics::const_iterator i;
2633 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2636 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2640 if ((*i)->frame() > frame) {
2648 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2650 /* must be treated as constant tempo */
2651 return ts_at->frames_per_beat (_frame_rate);
2655 TempoMap::tempo_at (const framepos_t& frame) const
2657 Glib::Threads::RWLock::ReaderLock lm (lock);
2658 return tempo_at_locked (frame);
2662 TempoMap::tempo_at_locked (const framepos_t& frame) const
2664 TempoSection* prev_ts = 0;
2666 Metrics::const_iterator i;
2668 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2670 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2674 if ((prev_ts) && t->frame() > frame) {
2675 /* t is the section past frame */
2676 const double ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2677 const Tempo ret_tempo (ret, prev_ts->note_type());
2684 const double ret = prev_ts->beats_per_minute();
2685 const Tempo ret_tempo (ret, prev_ts->note_type ());
2691 TempoMap::meter_section_at (framepos_t frame) const
2693 Glib::Threads::RWLock::ReaderLock lm (lock);
2694 return meter_section_at_locked (frame);
2698 TempoMap::meter_section_at_locked (framepos_t frame) const
2700 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2701 Metrics::const_iterator i;
2702 MeterSection* prev = 0;
2704 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2707 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2709 if (prev && (*i)->frame() > frame) {
2719 abort(); /*NOTREACHED*/
2726 TempoMap::meter_at (framepos_t frame) const
2728 TempoMetric m (metric_at (frame));
2733 TempoMap::meter_section_at (const double& beat) const
2735 MeterSection* prev_ms = 0;
2736 Glib::Threads::RWLock::ReaderLock lm (lock);
2738 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2740 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2741 if (prev_ms && m->beat() > beat) {
2752 TempoMap::get_state ()
2754 Metrics::const_iterator i;
2755 XMLNode *root = new XMLNode ("TempoMap");
2758 Glib::Threads::RWLock::ReaderLock lm (lock);
2759 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2760 root->add_child_nocopy ((*i)->get_state());
2768 TempoMap::set_state (const XMLNode& node, int /*version*/)
2771 Glib::Threads::RWLock::WriterLock lm (lock);
2774 XMLNodeConstIterator niter;
2775 Metrics old_metrics (_metrics);
2778 nlist = node.children();
2780 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2781 XMLNode* child = *niter;
2783 if (child->name() == TempoSection::xml_state_node_name) {
2786 TempoSection* ts = new TempoSection (*child);
2787 _metrics.push_back (ts);
2790 catch (failed_constructor& err){
2791 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2792 _metrics = old_metrics;
2796 } else if (child->name() == MeterSection::xml_state_node_name) {
2799 MeterSection* ms = new MeterSection (*child);
2800 _metrics.push_back (ms);
2803 catch (failed_constructor& err) {
2804 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2805 _metrics = old_metrics;
2811 if (niter == nlist.end()) {
2812 MetricSectionSorter cmp;
2813 _metrics.sort (cmp);
2815 /* check for legacy sessions where bbt was the base musical unit for tempo */
2816 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2819 MeterSection* prev_ms = 0;
2820 TempoSection* prev_ts = 0;
2821 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2822 if (prev_ms && prev_ms->pulse() < 0.0) {
2823 /*XX we cannot possibly make this work??. */
2824 pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
2825 prev_ms->set_beat (start);
2826 const double start_pulse = ((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat);
2827 prev_ms->set_pulse (start_pulse);
2830 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2834 if (prev_ts && prev_ts->pulse() < 0.0) {
2835 double const start = ((prev_ts->legacy_bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2836 prev_ts->set_pulse (start);
2841 /* check for multiple tempo/meters at the same location, which
2842 ardour2 somehow allowed.
2845 Metrics::iterator prev = _metrics.end();
2846 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2847 if (prev != _metrics.end()) {
2849 MeterSection* prev_ms;
2851 TempoSection* prev_ts;
2852 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2853 if (prev_ms->pulse() == ms->pulse()) {
2854 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2855 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2858 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2859 if (prev_ts->pulse() == ts->pulse()) {
2860 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2861 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2869 recompute_map (_metrics);
2872 PropertyChanged (PropertyChange ());
2878 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2880 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2881 const MeterSection* m;
2882 const TempoSection* t;
2883 const TempoSection* prev_ts = 0;
2885 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2887 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2888 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2889 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2890 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2892 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2893 o << "calculated : " << prev_ts->tempo_at_pulse (t->pulse()) * prev_ts->note_type() << " | " << prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << " | " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2896 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2897 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2898 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2901 o << "------" << std::endl;
2905 TempoMap::n_tempos() const
2907 Glib::Threads::RWLock::ReaderLock lm (lock);
2910 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2911 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2920 TempoMap::n_meters() const
2922 Glib::Threads::RWLock::ReaderLock lm (lock);
2925 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2926 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2935 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2938 Glib::Threads::RWLock::WriterLock lm (lock);
2939 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2940 if ((*i)->frame() >= where && (*i)->movable ()) {
2941 (*i)->set_frame ((*i)->frame() + amount);
2945 /* now reset the BBT time of all metrics, based on their new
2946 * audio time. This is the only place where we do this reverse
2950 Metrics::iterator i;
2951 const MeterSection* meter;
2952 const TempoSection* tempo;
2956 meter = &first_meter ();
2957 tempo = &first_tempo ();
2962 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2965 MetricSection* prev = 0;
2967 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2970 //TempoMetric metric (*meter, *tempo);
2971 MeterSection* ms = const_cast<MeterSection*>(meter);
2972 TempoSection* ts = const_cast<TempoSection*>(tempo);
2975 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2979 ts->set_pulse (t->pulse());
2981 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2982 ts->set_pulse (m->pulse());
2984 ts->set_frame (prev->frame());
2988 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2989 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2990 ms->set_beat (start);
2991 ms->set_pulse (m->pulse());
2993 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2997 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
2998 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
2999 ms->set_beat (start);
3000 ms->set_pulse (t->pulse());
3002 ms->set_frame (prev->frame());
3006 // metric will be at frames=0 bbt=1|1|0 by default
3007 // which is correct for our purpose
3010 // cerr << bbt << endl;
3012 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3016 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3018 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3019 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3020 bbt_time (m->frame(), bbt);
3022 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3028 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3029 /* round up to next beat */
3035 if (bbt.beats != 1) {
3036 /* round up to next bar */
3041 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3042 m->set_beat (start);
3043 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3045 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3047 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3048 abort(); /*NOTREACHED*/
3054 recompute_map (_metrics);
3058 PropertyChanged (PropertyChange ());
3061 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3065 std::list<MetricSection*> metric_kill_list;
3067 TempoSection* last_tempo = NULL;
3068 MeterSection* last_meter = NULL;
3069 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3070 bool meter_after = false; // is there a meter marker likewise?
3072 Glib::Threads::RWLock::WriterLock lm (lock);
3073 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3074 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3075 metric_kill_list.push_back(*i);
3076 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3079 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3083 else if ((*i)->frame() >= where) {
3084 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3085 (*i)->set_frame ((*i)->frame() - amount);
3086 if ((*i)->frame() == where) {
3087 // marker was immediately after end of range
3088 tempo_after = dynamic_cast<TempoSection*> (*i);
3089 meter_after = dynamic_cast<MeterSection*> (*i);
3095 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3096 if (last_tempo && !tempo_after) {
3097 metric_kill_list.remove(last_tempo);
3098 last_tempo->set_frame(where);
3101 if (last_meter && !meter_after) {
3102 metric_kill_list.remove(last_meter);
3103 last_meter->set_frame(where);
3107 //remove all the remaining metrics
3108 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3109 _metrics.remove(*i);
3114 recompute_map (_metrics);
3117 PropertyChanged (PropertyChange ());
3121 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3122 * pos can be -ve, if required.
3125 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3127 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3130 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3132 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3134 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3137 /** Add the BBT interval op to pos and return the result */
3139 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3141 Glib::Threads::RWLock::ReaderLock lm (lock);
3143 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3144 pos_bbt.ticks += op.ticks;
3145 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3147 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3149 pos_bbt.beats += op.beats;
3150 /* the meter in effect will start on the bar */
3151 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3152 while (pos_bbt.beats >= divisions_per_bar + 1) {
3154 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3155 pos_bbt.beats -= divisions_per_bar;
3157 pos_bbt.bars += op.bars;
3159 return frame_time_locked (_metrics, pos_bbt);
3162 /** Count the number of beats that are equivalent to distance when going forward,
3166 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3168 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3172 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3178 operator<< (std::ostream& o, const Meter& m) {
3179 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3183 operator<< (std::ostream& o, const Tempo& t) {
3184 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3188 operator<< (std::ostream& o, const MetricSection& section) {
3190 o << "MetricSection @ " << section.frame() << ' ';
3192 const TempoSection* ts;
3193 const MeterSection* ms;
3195 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3196 o << *((const Tempo*) ts);
3197 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3198 o << *((const Meter*) ms);