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 beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
247 return pulse_at_pulse_tempo (ppm) + pulse();
250 /** returns the zero-based pulse (relative to session origin)
251 where the zero-based frame (relative to session)
255 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
257 if (_type == Constant) {
258 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
261 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
264 /** returns the zero-based frame (relative to session start frame)
265 where the zero-based pulse (relative to session start)
270 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
272 if (_type == Constant) {
273 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
276 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
284 Tt----|-----------------*|
285 Ta----|--------------|* |
291 _______________|___|____
292 time a t (next tempo)
295 Duration in beats at time a is the integral of some Tempo function.
296 In our case, the Tempo function (Tempo at time t) is
299 with function constant
304 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
305 b(t) = T0(e^(ct) - 1) / c
307 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:
308 t(b) = log((cb / T0) + 1) / c
310 The time t at which Tempo T occurs is a as above:
311 t(T) = log(T / T0) / c
313 The beat at which a Tempo T occurs is:
316 The Tempo at which beat b occurs is:
319 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
320 Our problem is that we usually don't know t.
321 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.
322 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
323 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
325 By substituting our expanded t as a in the c function above, our problem is reduced to:
326 c = T0 (e^(log (Ta / T0)) - 1) / b
328 We can now store c for future time calculations.
329 If the following tempo section (the one that defines c in conjunction with this one)
330 is changed or moved, c is no longer valid.
332 The public methods are session-relative.
334 Most of this stuff is taken from this paper:
337 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
340 Zurich University of Arts
341 Institute for Computer Music and Sound Technology
343 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
348 compute this ramp's function constant using the end tempo (in whole pulses per minute)
349 and duration (pulses into global start) of some later tempo section.
352 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
354 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
355 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
358 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
360 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
362 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
366 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
368 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
372 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
374 return (frame / (double) frame_rate) / 60.0;
377 /* position function */
379 TempoSection::a_func (double end_bpm, double c_func) const
381 return log (end_bpm / pulses_per_minute()) / c_func;
384 /*function constant*/
386 TempoSection::c_func (double end_bpm, double end_time) const
388 return log (end_bpm / pulses_per_minute()) / end_time;
391 /* tempo in ppm at time in minutes */
393 TempoSection::pulse_tempo_at_time (const double& time) const
395 return exp (_c_func * time) * pulses_per_minute();
398 /* time in minutes at tempo in ppm */
400 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
402 return log (pulse_tempo / pulses_per_minute()) / _c_func;
405 /* tick at tempo in ppm */
407 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
409 return (pulse_tempo - pulses_per_minute()) / _c_func;
412 /* tempo in ppm at tick */
414 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
416 return (pulse * _c_func) + pulses_per_minute();
419 /* pulse at time in minutes */
421 TempoSection::pulse_at_time (const double& time) const
423 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
426 /* time in minutes at pulse */
428 TempoSection::time_at_pulse (const double& pulse) const
430 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
433 /***********************************************************************/
435 const string MeterSection::xml_state_node_name = "Meter";
437 MeterSection::MeterSection (const XMLNode& node)
438 : MetricSection (0.0), Meter (TempoMap::default_meter())
440 XMLProperty const * prop;
443 const XMLProperty *prop;
447 framepos_t frame = 0;
448 pair<double, BBT_Time> start;
450 if ((prop = node.property ("start")) != 0) {
451 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
455 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
457 /* legacy session - start used to be in bbt*/
461 error << _("MeterSection XML node has no \"start\" property") << endmsg;
464 if ((prop = node.property ("pulse")) != 0) {
465 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
466 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
471 if ((prop = node.property ("beat")) != 0) {
472 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
473 error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
479 if ((prop = node.property ("bbt")) == 0) {
480 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
481 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
485 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
486 //throw failed_constructor();
492 if ((prop = node.property ("frame")) != 0) {
493 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
494 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
500 /* beats-per-bar is old; divisions-per-bar is new */
502 if ((prop = node.property ("divisions-per-bar")) == 0) {
503 if ((prop = node.property ("beats-per-bar")) == 0) {
504 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
505 throw failed_constructor();
508 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
509 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
510 throw failed_constructor();
513 if ((prop = node.property ("note-type")) == 0) {
514 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
515 throw failed_constructor();
517 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
518 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
519 throw failed_constructor();
522 if ((prop = node.property ("lock-style")) == 0) {
523 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
524 set_position_lock_style (MusicTime);
526 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
529 if ((prop = node.property ("movable")) == 0) {
530 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
531 throw failed_constructor();
534 set_movable (string_is_affirmative (prop->value()));
538 MeterSection::get_state() const
540 XMLNode *root = new XMLNode (xml_state_node_name);
544 snprintf (buf, sizeof (buf), "%lf", pulse());
545 root->add_property ("pulse", buf);
546 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
550 root->add_property ("bbt", buf);
551 snprintf (buf, sizeof (buf), "%lf", beat());
552 root->add_property ("beat", buf);
553 snprintf (buf, sizeof (buf), "%f", _note_type);
554 root->add_property ("note-type", buf);
555 snprintf (buf, sizeof (buf), "%li", frame());
556 root->add_property ("frame", buf);
557 root->add_property ("lock-style", enum_2_string (position_lock_style()));
558 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
559 root->add_property ("divisions-per-bar", buf);
560 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
561 root->add_property ("movable", buf);
566 /***********************************************************************/
570 Tempos can be thought of as a source of the musical pulse.
572 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
573 It should rather be thought of as tempo note divisions per minute.
575 TempoSections, which are nice to think of in whole pulses per minute,
576 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
577 and beats (via note_divisor) are used to form a tempo map.
578 TempoSections and MeterSections may be locked to either audio or music (position lock style).
579 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
580 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
582 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
584 The first tempo and first meter are special. they must move together, and must be locked to audio.
585 Audio locked tempos which lie before the first meter are made inactive.
586 They will be re-activated if the first meter is again placed before them.
588 Both tempos and meters have a pulse position and a frame position.
589 Meters also have a beat position, which is always 0.0 for the first meter.
591 A tempo locked to music is locked to musical pulses.
592 A meter locked to music is locked to beats.
594 Recomputing the tempo map is the process where the 'missing' position
595 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
597 It is important to keep the _metrics in an order that makes sense.
598 Because ramped MusicTime and AudioTime tempos can interact with each other,
599 reordering is frequent. Care must be taken to keep _metrics in a solved state.
600 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
602 struct MetricSectionSorter {
603 bool operator() (const MetricSection* a, const MetricSection* b) {
604 return a->pulse() < b->pulse();
608 struct MetricSectionFrameSorter {
609 bool operator() (const MetricSection* a, const MetricSection* b) {
610 return a->frame() < b->frame();
614 TempoMap::TempoMap (framecnt_t fr)
617 BBT_Time start (1, 1, 0);
619 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
620 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
622 t->set_movable (false);
623 m->set_movable (false);
625 /* note: frame time is correct (zero) for both of these */
627 _metrics.push_back (t);
628 _metrics.push_back (m);
632 TempoMap::~TempoMap ()
637 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
639 bool removed = false;
642 Glib::Threads::RWLock::WriterLock lm (lock);
643 if ((removed = remove_tempo_locked (tempo))) {
644 if (complete_operation) {
645 recompute_map (_metrics);
650 if (removed && complete_operation) {
651 PropertyChanged (PropertyChange ());
656 TempoMap::remove_tempo_locked (const TempoSection& tempo)
660 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
661 if (dynamic_cast<TempoSection*> (*i) != 0) {
662 if (tempo.frame() == (*i)->frame()) {
663 if ((*i)->movable()) {
675 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
677 bool removed = false;
680 Glib::Threads::RWLock::WriterLock lm (lock);
681 if ((removed = remove_meter_locked (tempo))) {
682 if (complete_operation) {
683 recompute_map (_metrics);
688 if (removed && complete_operation) {
689 PropertyChanged (PropertyChange ());
694 TempoMap::remove_meter_locked (const MeterSection& tempo)
698 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
699 if (dynamic_cast<MeterSection*> (*i) != 0) {
700 if (tempo.frame() == (*i)->frame()) {
701 if ((*i)->movable()) {
713 TempoMap::do_insert (MetricSection* section)
715 bool need_add = true;
716 /* we only allow new meters to be inserted on beat 1 of an existing
720 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
721 //assert (m->bbt().ticks == 0);
723 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
725 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
726 corrected.second.beats = 1;
727 corrected.second.ticks = 0;
728 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
729 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
730 m->bbt(), corrected.second) << endmsg;
731 //m->set_pulse (corrected);
735 /* Look for any existing MetricSection that is of the same type and
736 in the same bar as the new one, and remove it before adding
737 the new one. Note that this means that if we find a matching,
738 existing section, we can break out of the loop since we're
739 guaranteed that there is only one such match.
742 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
744 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
745 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
746 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
747 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
749 if (tempo && insert_tempo) {
752 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
753 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
755 if (!tempo->movable()) {
757 /* can't (re)move this section, so overwrite
758 * its data content (but not its properties as
762 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
763 (*i)->set_position_lock_style (AudioTime);
771 } else if (meter && insert_meter) {
775 bool const ipm = insert_meter->position_lock_style() == MusicTime;
777 if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
779 if (!meter->movable()) {
781 /* can't (re)move this section, so overwrite
782 * its data content (but not its properties as
786 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
787 (*i)->set_position_lock_style (insert_meter->position_lock_style());
797 /* non-matching types, so we don't care */
801 /* Add the given MetricSection, if we didn't just reset an existing
806 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
807 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
810 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
811 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
814 bool const ipm = insert_meter->position_lock_style() == MusicTime;
815 if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
820 } else if (insert_tempo) {
821 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
822 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
825 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
826 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
833 _metrics.insert (i, section);
834 //dump (_metrics, std::cerr);
839 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
842 Glib::Threads::RWLock::WriterLock lm (lock);
843 TempoSection& first (first_tempo());
844 if (ts.pulse() != first.pulse()) {
845 remove_tempo_locked (ts);
846 add_tempo_locked (tempo, pulse, true, type);
848 first.set_type (type);
850 /* cannot move the first tempo section */
851 *static_cast<Tempo*>(&first) = tempo;
852 recompute_map (_metrics);
857 PropertyChanged (PropertyChange ());
861 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
864 Glib::Threads::RWLock::WriterLock lm (lock);
865 TempoSection& first (first_tempo());
866 if (ts.frame() != first.frame()) {
867 remove_tempo_locked (ts);
868 add_tempo_locked (tempo, frame, true, type);
870 first.set_type (type);
871 first.set_pulse (0.0);
872 first.set_position_lock_style (AudioTime);
874 /* cannot move the first tempo section */
875 *static_cast<Tempo*>(&first) = tempo;
876 recompute_map (_metrics);
881 PropertyChanged (PropertyChange ());
885 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
888 Glib::Threads::RWLock::WriterLock lm (lock);
889 add_tempo_locked (tempo, pulse, true, type);
892 PropertyChanged (PropertyChange ());
896 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
899 Glib::Threads::RWLock::WriterLock lm (lock);
900 add_tempo_locked (tempo, frame, true, type);
904 PropertyChanged (PropertyChange ());
908 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
910 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
915 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
920 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
922 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
927 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
932 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
935 Glib::Threads::RWLock::WriterLock lm (lock);
936 MeterSection& first (first_meter());
939 remove_meter_locked (ms);
940 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
942 const PositionLockStyle pl = ms.position_lock_style();
943 /* cannot move the first meter section */
944 *static_cast<Meter*>(&first) = meter;
945 first.set_position_lock_style (pl);
946 recompute_map (_metrics);
950 PropertyChanged (PropertyChange ());
954 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
957 Glib::Threads::RWLock::WriterLock lm (lock);
959 const double beat = ms.beat();
960 const BBT_Time bbt = ms.bbt();
962 remove_meter_locked (ms);
963 add_meter_locked (meter, frame, beat, bbt, true);
965 MeterSection& first (first_meter());
966 TempoSection& first_t (first_tempo());
967 /* cannot move the first meter section */
968 *static_cast<Meter*>(&first) = meter;
969 first.set_position_lock_style (AudioTime);
970 first.set_pulse (0.0);
971 first.set_frame (frame);
972 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
973 first.set_beat (beat);
974 first_t.set_frame (first.frame());
975 first_t.set_pulse (0.0);
976 first_t.set_position_lock_style (AudioTime);
978 recompute_map (_metrics);
981 PropertyChanged (PropertyChange ());
986 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
989 Glib::Threads::RWLock::WriterLock lm (lock);
990 add_meter_locked (meter, beat, where, true);
995 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
996 dump (_metrics, std::cerr);
1000 PropertyChanged (PropertyChange ());
1004 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1007 Glib::Threads::RWLock::WriterLock lm (lock);
1008 add_meter_locked (meter, frame, beat, where, true);
1013 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1014 dump (_metrics, std::cerr);
1018 PropertyChanged (PropertyChange ());
1022 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1024 /* a new meter always starts a new bar on the first beat. so
1025 round the start time appropriately. remember that
1026 `where' is based on the existing tempo map, not
1027 the result after we insert the new meter.
1031 if (where.beats != 1) {
1035 /* new meters *always* start on a beat. */
1037 double pulse = pulse_at_beat_locked (_metrics, beat);
1039 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1040 do_insert (new_meter);
1043 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1049 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1052 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1054 double pulse = pulse_at_frame_locked (_metrics, frame);
1055 new_meter->set_pulse (pulse);
1057 do_insert (new_meter);
1060 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1066 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1068 Tempo newtempo (beats_per_minute, note_type);
1071 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1072 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1077 Glib::Threads::RWLock::WriterLock lm (lock);
1078 *((Tempo*) t) = newtempo;
1079 recompute_map (_metrics);
1081 PropertyChanged (PropertyChange ());
1088 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1090 Tempo newtempo (beats_per_minute, note_type);
1093 TempoSection* first;
1094 Metrics::iterator i;
1096 /* find the TempoSection immediately preceding "where"
1099 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1101 if ((*i)->frame() > where) {
1107 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1120 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1130 Glib::Threads::RWLock::WriterLock lm (lock);
1131 /* cannot move the first tempo section */
1132 *((Tempo*)prev) = newtempo;
1133 recompute_map (_metrics);
1136 PropertyChanged (PropertyChange ());
1140 TempoMap::first_meter () const
1142 const MeterSection *m = 0;
1144 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1145 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1150 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1151 abort(); /*NOTREACHED*/
1156 TempoMap::first_meter ()
1158 MeterSection *m = 0;
1160 /* CALLER MUST HOLD LOCK */
1162 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1163 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1168 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1169 abort(); /*NOTREACHED*/
1174 TempoMap::first_tempo () const
1176 const TempoSection *t = 0;
1178 /* CALLER MUST HOLD LOCK */
1180 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1181 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1185 if (!t->movable()) {
1191 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1192 abort(); /*NOTREACHED*/
1197 TempoMap::first_tempo ()
1199 TempoSection *t = 0;
1201 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1202 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1206 if (!t->movable()) {
1212 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1213 abort(); /*NOTREACHED*/
1217 TempoMap::recompute_tempos (Metrics& metrics)
1219 TempoSection* prev_ts = 0;
1221 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1224 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1229 if (t->position_lock_style() == AudioTime) {
1230 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1231 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1234 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1235 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1242 prev_ts->set_c_func (0.0);
1245 /* tempos must be positioned correctly */
1247 TempoMap::recompute_meters (Metrics& metrics)
1249 MeterSection* meter = 0;
1250 MeterSection* prev_m = 0;
1251 uint32_t accumulated_bars = 0;
1253 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1254 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1256 const double beats_in_m = (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
1257 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1259 if (meter->position_lock_style() == AudioTime) {
1261 pair<double, BBT_Time> b_bbt;
1262 if (meter->movable()) {
1263 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1264 b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
1265 const double true_pulse = prev_m->pulse() + (ceil (beats) - prev_m->beat()) / prev_m->note_divisor();
1266 const double pulse_off = true_pulse - ((beats - prev_m->beat()) / prev_m->note_divisor());
1267 pulse = true_pulse - pulse_off;
1269 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1271 meter->set_beat (b_bbt);
1272 meter->set_pulse (pulse);
1276 pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1278 pulse = pulse_at_beat_locked (metrics, meter->beat());
1280 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1281 meter->set_pulse (pulse);
1290 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1292 /* CALLER MUST HOLD WRITE LOCK */
1296 /* we will actually stop once we hit
1303 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1306 /* silly call from Session::process() during startup
1311 recompute_tempos (metrics);
1312 recompute_meters (metrics);
1316 TempoMap::pulse_at_beat (const double& beat) const
1318 Glib::Threads::RWLock::ReaderLock lm (lock);
1319 return pulse_at_beat_locked (_metrics, beat);
1323 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1325 MeterSection* prev_ms = 0;
1326 double accumulated_beats = 0.0;
1328 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1330 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1331 if (prev_ms && m->beat() > beat) {
1334 accumulated_beats = m->beat();
1339 double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1344 TempoMap::beat_at_pulse (const double& pulse) const
1346 Glib::Threads::RWLock::ReaderLock lm (lock);
1347 return beat_at_pulse_locked (_metrics, pulse);
1351 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1353 MeterSection* prev_ms = 0;
1354 double accumulated_beats = 0.0;
1356 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1358 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1359 if (prev_ms && m->pulse() > pulse) {
1362 accumulated_beats = m->beat();
1367 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1369 return beats_in_section + accumulated_beats;
1373 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1375 Glib::Threads::RWLock::ReaderLock lm (lock);
1376 TempoMetric m (first_meter(), first_tempo());
1378 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1379 at something, because we insert the default tempo and meter during
1380 TempoMap construction.
1382 now see if we can find better candidates.
1385 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1387 if ((*i)->frame() > frame) {
1401 /* XX meters only */
1403 TempoMap::metric_at (BBT_Time bbt) const
1405 Glib::Threads::RWLock::ReaderLock lm (lock);
1406 TempoMetric m (first_meter(), first_tempo());
1408 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1409 at something, because we insert the default tempo and meter during
1410 TempoMap construction.
1412 now see if we can find better candidates.
1415 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1417 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1418 BBT_Time section_start (mw->bbt());
1420 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1432 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1439 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1442 Glib::Threads::RWLock::ReaderLock lm (lock);
1443 double const beat = beat_at_frame_locked (_metrics, frame);
1445 bbt = beats_to_bbt_locked (_metrics, beat);
1449 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1451 Glib::Threads::RWLock::ReaderLock lm (lock);
1453 return bbt_to_beats_locked (_metrics, bbt);
1457 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1459 /* CALLER HOLDS READ LOCK */
1461 double accumulated_beats = 0.0;
1462 double accumulated_bars = 0.0;
1463 MeterSection* prev_ms = 0;
1464 /* because audio-locked meters have 'fake' integral beats,
1465 there is no pulse offset here.
1467 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1469 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1470 double bars_to_m = 0.0;
1472 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1473 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1476 accumulated_beats = m->beat();
1477 accumulated_bars += bars_to_m;
1483 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1484 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1485 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1491 TempoMap::beats_to_bbt (const double& beats)
1493 Glib::Threads::RWLock::ReaderLock lm (lock);
1495 return beats_to_bbt_locked (_metrics, beats);
1499 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1501 /* CALLER HOLDS READ LOCK */
1502 MeterSection* prev_ms = 0;
1503 const double beats = (b < 0.0) ? 0.0 : b;
1504 uint32_t accumulated_bars = 0;
1505 double accumulated_beats = 0.0;
1507 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1508 MeterSection* m = 0;
1510 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1513 double const beats_to_m = m->beat() - prev_ms->beat();
1514 if (accumulated_beats + beats_to_m > beats) {
1515 /* this is the meter after the one our beat is on*/
1519 /* we need a whole number of bars. */
1520 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1521 accumulated_beats += beats_to_m;
1528 double const beats_in_ms = beats - accumulated_beats;
1529 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1530 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1531 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1532 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1536 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1537 ret.beats = (uint32_t) floor (remaining_beats);
1538 ret.bars = total_bars;
1540 /* 0 0 0 to 1 1 0 - based mapping*/
1544 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1546 ret.ticks -= BBT_Time::ticks_per_beat;
1549 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1558 TempoMap::pulse_to_bbt (const double& pulse)
1560 Glib::Threads::RWLock::ReaderLock lm (lock);
1561 MeterSection* prev_ms = 0;
1562 uint32_t accumulated_bars = 0;
1563 double accumulated_pulses = 0.0;
1565 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1566 MeterSection* m = 0;
1568 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1571 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1572 if (accumulated_pulses + pulses_to_m > pulse) {
1573 /* this is the meter after the one our beat is on*/
1577 /* we need a whole number of bars. */
1578 accumulated_pulses += pulses_to_m;
1579 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1585 double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1586 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1587 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1588 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1589 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1593 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1594 ret.beats = (uint32_t) floor (remaining_beats);
1595 ret.bars = total_bars;
1597 /* 0 0 0 to 1 1 0 mapping*/
1601 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1603 ret.ticks -= BBT_Time::ticks_per_beat;
1606 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1615 TempoMap::beat_at_frame (const framecnt_t& frame) const
1617 Glib::Threads::RWLock::ReaderLock lm (lock);
1618 return beat_at_frame_locked (_metrics, frame);
1622 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1624 //framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
1625 double const pulse = pulse_at_frame_locked (metrics, frame);
1627 return beat_at_pulse_locked (metrics, pulse);
1631 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1633 /* HOLD (at least) THE READER LOCK */
1634 TempoSection* prev_ts = 0;
1635 double accumulated_pulses = 0.0;
1637 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1639 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1643 if (prev_ts && t->frame() > frame) {
1644 /*the previous ts is the one containing the frame */
1645 double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1648 accumulated_pulses = t->pulse();
1653 /* treated as constant for this ts */
1654 double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1656 return pulses_in_section + accumulated_pulses;
1660 TempoMap::frame_at_beat (const double& beat) const
1662 Glib::Threads::RWLock::ReaderLock lm (lock);
1663 return frame_at_beat_locked (_metrics, beat);
1667 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1669 framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1670 //frameoffset_t const frame_off = frame_offset_at (metrics, frame);
1675 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1677 /* HOLD THE READER LOCK */
1679 const TempoSection* prev_ts = 0;
1680 double accumulated_pulses = 0.0;
1682 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1685 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1689 if (prev_ts && t->pulse() > pulse) {
1690 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1693 accumulated_pulses = t->pulse();
1697 /* must be treated as constant, irrespective of _type */
1698 double const pulses_in_section = pulse - accumulated_pulses;
1699 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1701 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1707 TempoMap::frame_time (const BBT_Time& bbt)
1710 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1714 if (bbt.beats < 1) {
1715 throw std::logic_error ("beats are counted from one");
1717 Glib::Threads::RWLock::ReaderLock lm (lock);
1718 double const beat = bbt_to_beats_locked (_metrics, bbt);
1719 framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1724 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1726 /* HOLD THE READER LOCK */
1728 framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt)));
1734 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1736 TempoSection* prev_ts = 0;
1738 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1740 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1745 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
1749 if (t->frame() == prev_ts->frame()) {
1753 /* precision check ensures pulses and frames align independent of lock style.*/
1754 if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
1766 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1768 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1770 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1771 if (!t->movable()) {
1772 t->set_active (true);
1775 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1776 t->set_active (false);
1778 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1779 t->set_active (true);
1780 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1789 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1791 TempoSection* prev_ts = 0;
1792 TempoSection* section_prev = 0;
1793 framepos_t first_m_frame = 0;
1795 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1797 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1798 if (!m->movable()) {
1799 first_m_frame = m->frame();
1804 if (section->movable() && frame <= first_m_frame) {
1807 section->set_active (true);
1809 section->set_frame (frame);
1811 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1813 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1820 section_prev = prev_ts;
1823 if (t->position_lock_style() == MusicTime) {
1824 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1825 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1827 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1828 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1836 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1837 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1840 if (section->position_lock_style() == MusicTime) {
1841 /* we're setting the frame */
1842 section->set_position_lock_style (AudioTime);
1843 recompute_tempos (imaginary);
1844 section->set_position_lock_style (MusicTime);
1846 recompute_tempos (imaginary);
1849 if (check_solved (imaginary, true)) {
1850 recompute_meters (imaginary);
1854 MetricSectionFrameSorter fcmp;
1855 imaginary.sort (fcmp);
1856 if (section->position_lock_style() == MusicTime) {
1857 /* we're setting the frame */
1858 section->set_position_lock_style (AudioTime);
1859 recompute_tempos (imaginary);
1860 section->set_position_lock_style (MusicTime);
1862 recompute_tempos (imaginary);
1864 if (check_solved (imaginary, true)) {
1865 recompute_meters (imaginary);
1869 MetricSectionSorter cmp;
1870 imaginary.sort (cmp);
1871 if (section->position_lock_style() == MusicTime) {
1872 /* we're setting the frame */
1873 section->set_position_lock_style (AudioTime);
1874 recompute_tempos (imaginary);
1875 section->set_position_lock_style (MusicTime);
1877 recompute_tempos (imaginary);
1879 if (check_solved (imaginary, true)) {
1880 recompute_meters (imaginary);
1883 //dump (imaginary, std::cerr);
1889 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
1891 TempoSection* prev_ts = 0;
1892 TempoSection* section_prev = 0;
1894 section->set_pulse (pulse);
1896 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1898 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1904 section_prev = prev_ts;
1907 if (t->position_lock_style() == MusicTime) {
1908 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1909 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1911 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1912 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1919 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1920 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1923 if (section->position_lock_style() == AudioTime) {
1924 /* we're setting the pulse */
1925 section->set_position_lock_style (MusicTime);
1926 recompute_tempos (imaginary);
1927 section->set_position_lock_style (AudioTime);
1929 recompute_tempos (imaginary);
1931 if (check_solved (imaginary, false)) {
1932 recompute_meters (imaginary);
1936 MetricSectionSorter cmp;
1937 imaginary.sort (cmp);
1938 if (section->position_lock_style() == AudioTime) {
1939 /* we're setting the pulse */
1940 section->set_position_lock_style (MusicTime);
1941 recompute_tempos (imaginary);
1942 section->set_position_lock_style (AudioTime);
1944 recompute_tempos (imaginary);
1947 if (check_solved (imaginary, false)) {
1948 recompute_meters (imaginary);
1952 MetricSectionFrameSorter fcmp;
1953 imaginary.sort (fcmp);
1954 if (section->position_lock_style() == AudioTime) {
1955 /* we're setting the pulse */
1956 section->set_position_lock_style (MusicTime);
1957 recompute_tempos (imaginary);
1958 section->set_position_lock_style (AudioTime);
1960 recompute_tempos (imaginary);
1963 if (check_solved (imaginary, false)) {
1964 recompute_meters (imaginary);
1968 //dump (imaginary, std::cerr);
1974 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
1976 MeterSection* prev_ms = 0;
1978 if (!section->movable()) {
1979 /* lock the first tempo to our first meter */
1980 if (!set_active_tempos (imaginary, frame)) {
1983 TempoSection* first_t = &first_tempo();
1985 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1987 new_section->set_frame (frame);
1988 new_section->set_pulse (0.0);
1989 new_section->set_active (true);
1991 if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
1992 first_t->set_frame (frame);
1993 first_t->set_pulse (0.0);
1994 first_t->set_active (true);
1995 solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
2001 uint32_t accumulated_bars = 0;
2003 section->set_frame (frame);
2005 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2007 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2009 const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2010 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2014 here we set the beat for this frame.
2015 we're going to set it 'incorrectly' to the next integer and use this difference
2016 to find the meter's pulse.
2017 (meters should fall on integral beats to keep us sane)
2020 pair<double, BBT_Time> b_bbt;
2022 double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2023 b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
2024 const double true_pulse = prev_ms->pulse() + ((ceil (beats) - prev_ms->beat()) / prev_ms->note_divisor());
2025 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2026 pulse = true_pulse - pulse_off;
2028 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2030 m->set_beat (b_bbt);
2031 m->set_pulse (pulse);
2036 if (m->position_lock_style() == MusicTime) {
2037 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2038 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2039 m->set_pulse (pulse);
2042 pair<double, BBT_Time> b_bbt;
2044 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2045 const double true_pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2046 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2047 b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
2048 pulse = true_pulse - pulse_off;
2050 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2053 m->set_beat (b_bbt);
2054 m->set_pulse (pulse);
2061 if (section->position_lock_style() == MusicTime) {
2062 /* we're setting the frame */
2063 section->set_position_lock_style (AudioTime);
2064 recompute_meters (imaginary);
2065 section->set_position_lock_style (MusicTime);
2067 recompute_meters (imaginary);
2069 //dump (imaginary, std::cerr);
2073 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2075 MeterSection* prev_ms = 0;
2076 double accumulated_beats = 0.0;
2077 uint32_t accumulated_bars = 0;
2079 section->set_pulse (pulse);
2081 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2083 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2085 double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2086 accumulated_beats += beats_in_m;
2087 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2090 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2091 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2092 section->set_beat (b_bbt);
2097 if (m->position_lock_style() == MusicTime) {
2098 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2099 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2100 m->set_pulse (pulse);
2102 if (!m->movable()) {
2103 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2104 m->set_beat (b_bbt);
2106 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2107 m->set_pulse (pulse);
2114 if (section->position_lock_style() == AudioTime) {
2115 /* we're setting the pulse */
2116 section->set_position_lock_style (MusicTime);
2117 recompute_meters (imaginary);
2118 section->set_position_lock_style (AudioTime);
2120 recompute_meters (imaginary);
2124 /** places a copy of _metrics into copy and returns a pointer
2125 * to section's equivalent.
2128 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2131 TempoSection* ret = 0;
2134 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2135 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2137 if (t->position_lock_style() == MusicTime) {
2138 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2140 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2142 ret->set_active (t->active());
2143 ret->set_movable (t->movable());
2144 copy.push_back (ret);
2147 TempoSection* cp = 0;
2148 if (t->position_lock_style() == MusicTime) {
2149 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2151 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2153 cp->set_active (t->active());
2154 cp->set_movable (t->movable());
2155 copy.push_back (cp);
2157 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2158 MeterSection* cp = 0;
2159 if (m->position_lock_style() == MusicTime) {
2160 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2161 cp->set_frame (m->frame());
2163 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2164 cp->set_pulse (m->pulse());
2166 cp->set_movable (m->movable());
2167 copy.push_back (cp);
2170 //recompute_map (copy);
2175 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
2178 TempoSection* new_section = 0;
2181 Glib::Threads::RWLock::ReaderLock lm (lock);
2182 new_section = copy_metrics_and_point (copy, ts);
2185 double const beat = bbt_to_beats_locked (copy, bbt);
2186 bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
2188 Metrics::const_iterator d = copy.begin();
2189 while (d != copy.end()) {
2198 * 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,
2199 * taking any possible reordering as a consequence of this into account.
2200 * @param section - the section to be altered
2201 * @param bpm - the new Tempo
2202 * @param bbt - the bbt where the altered tempo will fall
2203 * @return returns - the position in frames where the new tempo section will lie.
2206 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
2208 Glib::Threads::RWLock::ReaderLock lm (lock);
2211 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2213 double const beat = bbt_to_beats_locked (future_map, bbt);
2214 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2215 ret = new_section->frame();
2217 ret = frame_at_beat_locked (_metrics, beat);
2220 Metrics::const_iterator d = future_map.begin();
2221 while (d != future_map.end()) {
2229 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2231 Glib::Threads::RWLock::ReaderLock lm (lock);
2234 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2236 if (solve_map (future_map, new_section, bpm, frame)) {
2237 ret = new_section->pulse();
2239 ret = pulse_at_frame_locked (_metrics, frame);
2242 Metrics::const_iterator d = future_map.begin();
2243 while (d != future_map.end()) {
2251 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
2255 Glib::Threads::RWLock::WriterLock lm (lock);
2256 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2257 if (solve_map (future_map, new_section, bpm, frame)) {
2258 solve_map (_metrics, ts, bpm, frame);
2262 Metrics::const_iterator d = future_map.begin();
2263 while (d != future_map.end()) {
2268 MetricPositionChanged (); // Emit Signal
2272 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
2276 Glib::Threads::RWLock::WriterLock lm (lock);
2277 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2278 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2279 solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
2283 Metrics::const_iterator d = future_map.begin();
2284 while (d != future_map.end()) {
2289 MetricPositionChanged (); // Emit Signal
2293 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
2296 Glib::Threads::RWLock::WriterLock lm (lock);
2297 solve_map (_metrics, ms, mt, frame);
2300 MetricPositionChanged (); // Emit Signal
2304 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
2307 Glib::Threads::RWLock::WriterLock lm (lock);
2308 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
2311 MetricPositionChanged (); // Emit Signal
2315 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2318 bool can_solve = false;
2320 Glib::Threads::RWLock::WriterLock lm (lock);
2321 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2322 new_section->set_beats_per_minute (bpm.beats_per_minute());
2323 recompute_tempos (future_map);
2325 if (check_solved (future_map, true)) {
2326 ts->set_beats_per_minute (bpm.beats_per_minute());
2327 recompute_map (_metrics);
2332 Metrics::const_iterator d = future_map.begin();
2333 while (d != future_map.end()) {
2338 MetricPositionChanged (); // Emit Signal
2344 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2346 Glib::Threads::RWLock::ReaderLock lm (lock);
2348 double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2349 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2350 double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2351 framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2352 framecnt_t const ret = time_at_bbt;
2358 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2360 return round_to_type (fr, dir, Bar);
2364 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2366 return round_to_type (fr, dir, Beat);
2370 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2372 Glib::Threads::RWLock::ReaderLock lm (lock);
2373 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2374 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2375 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2377 ticks -= beats * BBT_Time::ticks_per_beat;
2380 /* round to next (or same iff dir == RoundUpMaybe) */
2382 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2384 if (mod == 0 && dir == RoundUpMaybe) {
2385 /* right on the subdivision, which is fine, so do nothing */
2387 } else if (mod == 0) {
2388 /* right on the subdivision, so the difference is just the subdivision ticks */
2389 ticks += ticks_one_subdivisions_worth;
2392 /* not on subdivision, compute distance to next subdivision */
2394 ticks += ticks_one_subdivisions_worth - mod;
2397 if (ticks >= BBT_Time::ticks_per_beat) {
2398 ticks -= BBT_Time::ticks_per_beat;
2400 } else if (dir < 0) {
2402 /* round to previous (or same iff dir == RoundDownMaybe) */
2404 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2406 if (difference == 0 && dir == RoundDownAlways) {
2407 /* right on the subdivision, but force-rounding down,
2408 so the difference is just the subdivision ticks */
2409 difference = ticks_one_subdivisions_worth;
2412 if (ticks < difference) {
2413 ticks = BBT_Time::ticks_per_beat - ticks;
2415 ticks -= difference;
2419 /* round to nearest */
2422 /* compute the distance to the previous and next subdivision */
2424 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2426 /* closer to the next subdivision, so shift forward */
2428 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2430 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2432 if (ticks > BBT_Time::ticks_per_beat) {
2434 ticks -= BBT_Time::ticks_per_beat;
2435 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2438 } else if (rem > 0) {
2440 /* closer to previous subdivision, so shift backward */
2444 /* can't go backwards past zero, so ... */
2447 /* step back to previous beat */
2449 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2450 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2452 ticks = lrint (ticks - rem);
2453 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2456 /* on the subdivision, do nothing */
2460 framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2466 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2468 if (sub_num == -1) {
2469 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2470 if ((double) when.beats > bpb / 2.0) {
2476 } else if (sub_num == 0) {
2477 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2478 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2480 while ((double) when.beats > bpb) {
2482 when.beats -= (uint32_t) floor (bpb);
2488 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2490 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2491 /* closer to the next subdivision, so shift forward */
2493 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2495 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2497 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2500 } else if (rem > 0) {
2501 /* closer to previous subdivision, so shift backward */
2503 if (rem > when.ticks) {
2504 if (when.beats == 0) {
2505 /* can't go backwards past zero, so ... */
2507 /* step back to previous beat */
2509 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2511 when.ticks = when.ticks - rem;
2517 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2519 Glib::Threads::RWLock::ReaderLock lm (lock);
2521 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2522 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2527 /* find bar previous to 'frame' */
2530 return frame_time (bbt);
2532 } else if (dir > 0) {
2533 /* find bar following 'frame' */
2537 return frame_time (bbt);
2539 /* true rounding: find nearest bar */
2540 framepos_t raw_ft = frame_time (bbt);
2543 framepos_t prev_ft = frame_time (bbt);
2545 framepos_t next_ft = frame_time (bbt);
2547 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2558 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2559 } else if (dir > 0) {
2560 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2562 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2571 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2572 framepos_t lower, framepos_t upper)
2574 Glib::Threads::RWLock::ReaderLock lm (lock);
2575 int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2576 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2577 /* although the map handles negative beats, bbt doesn't. */
2581 while (cnt <= upper_beat) {
2582 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2583 TempoSection const tempo = tempo_section_at_locked (pos);
2584 MeterSection const meter = meter_section_at_locked (pos);
2585 BBT_Time const bbt = beats_to_bbt (cnt);
2586 BBTPoint point = BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func());
2587 points.push_back (point);
2593 TempoMap::tempo_section_at (framepos_t frame) const
2595 Glib::Threads::RWLock::ReaderLock lm (lock);
2596 return tempo_section_at_locked (frame);
2600 TempoMap::tempo_section_at_locked (framepos_t frame) const
2602 Metrics::const_iterator i;
2603 TempoSection* prev = 0;
2605 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2608 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2612 if (t->frame() > frame) {
2622 abort(); /*NOTREACHED*/
2629 /* don't use this to calculate length (the tempo is only correct for this frame).
2630 do that stuff based on the beat_at_frame and frame_at_beat api
2633 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2635 Glib::Threads::RWLock::ReaderLock lm (lock);
2637 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2638 const TempoSection* ts_after = 0;
2639 Metrics::const_iterator i;
2641 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2644 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2648 if ((*i)->frame() > frame) {
2656 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2658 /* must be treated as constant tempo */
2659 return ts_at->frames_per_beat (_frame_rate);
2663 TempoMap::tempo_at (const framepos_t& frame) const
2665 Glib::Threads::RWLock::ReaderLock lm (lock);
2666 return tempo_at_locked (frame);
2670 TempoMap::tempo_at_locked (const framepos_t& frame) const
2672 TempoSection* prev_ts = 0;
2674 Metrics::const_iterator i;
2676 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2678 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2682 if ((prev_ts) && t->frame() > frame) {
2683 /* t is the section past frame */
2684 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2685 Tempo const ret_tempo (ret, prev_ts->note_type());
2692 double const ret = prev_ts->beats_per_minute();
2693 Tempo const ret_tempo (ret, prev_ts->note_type ());
2699 TempoMap::meter_section_at (framepos_t frame) const
2701 Glib::Threads::RWLock::ReaderLock lm (lock);
2702 return meter_section_at_locked (frame);
2706 TempoMap::meter_section_at_locked (framepos_t frame) const
2708 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2709 Metrics::const_iterator i;
2710 MeterSection* prev = 0;
2712 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2715 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2717 if (prev && (*i)->frame() > frame) {
2727 abort(); /*NOTREACHED*/
2734 TempoMap::meter_at (framepos_t frame) const
2736 TempoMetric m (metric_at (frame));
2741 TempoMap::meter_section_at (const double& beat) const
2743 MeterSection* prev_ms = 0;
2744 Glib::Threads::RWLock::ReaderLock lm (lock);
2746 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2748 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2749 if (prev_ms && m->beat() > beat) {
2760 TempoMap::get_state ()
2762 Metrics::const_iterator i;
2763 XMLNode *root = new XMLNode ("TempoMap");
2766 Glib::Threads::RWLock::ReaderLock lm (lock);
2767 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2768 root->add_child_nocopy ((*i)->get_state());
2776 TempoMap::set_state (const XMLNode& node, int /*version*/)
2779 Glib::Threads::RWLock::WriterLock lm (lock);
2782 XMLNodeConstIterator niter;
2783 Metrics old_metrics (_metrics);
2786 nlist = node.children();
2788 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2789 XMLNode* child = *niter;
2791 if (child->name() == TempoSection::xml_state_node_name) {
2794 TempoSection* ts = new TempoSection (*child);
2795 _metrics.push_back (ts);
2798 catch (failed_constructor& err){
2799 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2800 _metrics = old_metrics;
2804 } else if (child->name() == MeterSection::xml_state_node_name) {
2807 MeterSection* ms = new MeterSection (*child);
2808 _metrics.push_back (ms);
2811 catch (failed_constructor& err) {
2812 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2813 _metrics = old_metrics;
2819 if (niter == nlist.end()) {
2820 MetricSectionSorter cmp;
2821 _metrics.sort (cmp);
2823 /* check for legacy sessions where bbt was the base musical unit for tempo */
2824 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2827 MeterSection* prev_ms = 0;
2828 TempoSection* prev_ts = 0;
2829 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2830 if (prev_ms && prev_ms->pulse() < 0.0) {
2831 /*XX we cannot possibly make this work??. */
2832 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());
2833 prev_ms->set_beat (start);
2834 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);
2835 prev_ms->set_pulse (start_pulse);
2838 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2842 if (prev_ts && prev_ts->pulse() < 0.0) {
2843 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);
2844 prev_ts->set_pulse (start);
2849 /* check for multiple tempo/meters at the same location, which
2850 ardour2 somehow allowed.
2853 Metrics::iterator prev = _metrics.end();
2854 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2855 if (prev != _metrics.end()) {
2857 MeterSection* prev_ms;
2859 TempoSection* prev_ts;
2860 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2861 if (prev_ms->pulse() == ms->pulse()) {
2862 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2863 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2866 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2867 if (prev_ts->pulse() == ts->pulse()) {
2868 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2869 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2877 recompute_map (_metrics);
2880 PropertyChanged (PropertyChange ());
2886 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2888 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2889 const MeterSection* m;
2890 const TempoSection* t;
2891 const TempoSection* prev_ts = 0;
2893 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2895 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2896 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2897 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2898 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2900 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2901 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;
2904 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2905 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2906 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2909 o << "------" << std::endl;
2913 TempoMap::n_tempos() const
2915 Glib::Threads::RWLock::ReaderLock lm (lock);
2918 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2919 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2928 TempoMap::n_meters() const
2930 Glib::Threads::RWLock::ReaderLock lm (lock);
2933 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2934 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2943 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2946 Glib::Threads::RWLock::WriterLock lm (lock);
2947 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2948 if ((*i)->frame() >= where && (*i)->movable ()) {
2949 (*i)->set_frame ((*i)->frame() + amount);
2953 /* now reset the BBT time of all metrics, based on their new
2954 * audio time. This is the only place where we do this reverse
2958 Metrics::iterator i;
2959 const MeterSection* meter;
2960 const TempoSection* tempo;
2964 meter = &first_meter ();
2965 tempo = &first_tempo ();
2970 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2973 MetricSection* prev = 0;
2975 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2978 //TempoMetric metric (*meter, *tempo);
2979 MeterSection* ms = const_cast<MeterSection*>(meter);
2980 TempoSection* ts = const_cast<TempoSection*>(tempo);
2983 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2987 ts->set_pulse (t->pulse());
2989 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2990 ts->set_pulse (m->pulse());
2992 ts->set_frame (prev->frame());
2996 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2997 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2998 ms->set_beat (start);
2999 ms->set_pulse (m->pulse());
3001 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3005 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3006 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3007 ms->set_beat (start);
3008 ms->set_pulse (t->pulse());
3010 ms->set_frame (prev->frame());
3014 // metric will be at frames=0 bbt=1|1|0 by default
3015 // which is correct for our purpose
3018 // cerr << bbt << endl;
3020 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3024 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3026 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3027 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3028 bbt_time (m->frame(), bbt);
3030 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3036 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3037 /* round up to next beat */
3043 if (bbt.beats != 1) {
3044 /* round up to next bar */
3049 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3050 m->set_beat (start);
3051 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3053 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3055 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3056 abort(); /*NOTREACHED*/
3062 recompute_map (_metrics);
3066 PropertyChanged (PropertyChange ());
3069 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3073 std::list<MetricSection*> metric_kill_list;
3075 TempoSection* last_tempo = NULL;
3076 MeterSection* last_meter = NULL;
3077 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3078 bool meter_after = false; // is there a meter marker likewise?
3080 Glib::Threads::RWLock::WriterLock lm (lock);
3081 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3082 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3083 metric_kill_list.push_back(*i);
3084 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3087 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3091 else if ((*i)->frame() >= where) {
3092 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3093 (*i)->set_frame ((*i)->frame() - amount);
3094 if ((*i)->frame() == where) {
3095 // marker was immediately after end of range
3096 tempo_after = dynamic_cast<TempoSection*> (*i);
3097 meter_after = dynamic_cast<MeterSection*> (*i);
3103 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3104 if (last_tempo && !tempo_after) {
3105 metric_kill_list.remove(last_tempo);
3106 last_tempo->set_frame(where);
3109 if (last_meter && !meter_after) {
3110 metric_kill_list.remove(last_meter);
3111 last_meter->set_frame(where);
3115 //remove all the remaining metrics
3116 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3117 _metrics.remove(*i);
3122 recompute_map (_metrics);
3125 PropertyChanged (PropertyChange ());
3129 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3130 * pos can be -ve, if required.
3133 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3135 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3138 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3140 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3142 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3145 /** Add the BBT interval op to pos and return the result */
3147 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3149 Glib::Threads::RWLock::ReaderLock lm (lock);
3151 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3152 pos_bbt.ticks += op.ticks;
3153 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3155 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3157 pos_bbt.beats += op.beats;
3158 /* the meter in effect will start on the bar */
3159 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3160 while (pos_bbt.beats >= divisions_per_bar + 1) {
3162 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3163 pos_bbt.beats -= divisions_per_bar;
3165 pos_bbt.bars += op.bars;
3167 return frame_time_locked (_metrics, pos_bbt);
3170 /** Count the number of beats that are equivalent to distance when going forward,
3174 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3176 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3180 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3186 operator<< (std::ostream& o, const Meter& m) {
3187 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3191 operator<< (std::ostream& o, const Tempo& t) {
3192 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3196 operator<< (std::ostream& o, const MetricSection& section) {
3198 o << "MetricSection @ " << section.frame() << ' ';
3200 const TempoSection* ts;
3201 const MeterSection* ms;
3203 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3204 o << *((const Tempo*) ts);
3205 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3206 o << *((const Meter*) ms);