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 Tempo can be thought of as a source of the musical pulse.
571 Meters divide that pulse into measures and beats.
572 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
573 at any particular time.
574 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
575 It should rather be thought of as tempo note divisions per minute.
577 TempoSections, which are nice to think of in whole pulses per minute,
578 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
579 and beats (via note_divisor) are used to form a tempo map.
580 TempoSections and MeterSections may be locked to either audio or music (position lock style).
581 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
582 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
584 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
586 The first tempo and first meter are special. they must move together, and must be locked to audio.
587 Audio locked tempos which lie before the first meter are made inactive.
588 They will be re-activated if the first meter is again placed before them.
590 Both tempos and meters have a pulse position and a frame position.
591 Meters also have a beat position, which is always 0.0 for the first meter.
593 A tempo locked to music is locked to musical pulses.
594 A meter locked to music is locked to beats.
596 Recomputing the tempo map is the process where the 'missing' position
597 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
599 It is important to keep the _metrics in an order that makes sense.
600 Because ramped MusicTime and AudioTime tempos can interact with each other,
601 reordering is frequent. Care must be taken to keep _metrics in a solved state.
602 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
604 struct MetricSectionSorter {
605 bool operator() (const MetricSection* a, const MetricSection* b) {
606 return a->pulse() < b->pulse();
610 struct MetricSectionFrameSorter {
611 bool operator() (const MetricSection* a, const MetricSection* b) {
612 return a->frame() < b->frame();
616 TempoMap::TempoMap (framecnt_t fr)
619 BBT_Time start (1, 1, 0);
621 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
622 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
624 t->set_movable (false);
625 m->set_movable (false);
627 /* note: frame time is correct (zero) for both of these */
629 _metrics.push_back (t);
630 _metrics.push_back (m);
634 TempoMap::~TempoMap ()
639 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
641 bool removed = false;
644 Glib::Threads::RWLock::WriterLock lm (lock);
645 if ((removed = remove_tempo_locked (tempo))) {
646 if (complete_operation) {
647 recompute_map (_metrics);
652 if (removed && complete_operation) {
653 PropertyChanged (PropertyChange ());
658 TempoMap::remove_tempo_locked (const TempoSection& tempo)
662 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
663 if (dynamic_cast<TempoSection*> (*i) != 0) {
664 if (tempo.frame() == (*i)->frame()) {
665 if ((*i)->movable()) {
677 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
679 bool removed = false;
682 Glib::Threads::RWLock::WriterLock lm (lock);
683 if ((removed = remove_meter_locked (tempo))) {
684 if (complete_operation) {
685 recompute_map (_metrics);
690 if (removed && complete_operation) {
691 PropertyChanged (PropertyChange ());
696 TempoMap::remove_meter_locked (const MeterSection& tempo)
700 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
701 if (dynamic_cast<MeterSection*> (*i) != 0) {
702 if (tempo.frame() == (*i)->frame()) {
703 if ((*i)->movable()) {
715 TempoMap::do_insert (MetricSection* section)
717 bool need_add = true;
718 /* we only allow new meters to be inserted on beat 1 of an existing
722 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
723 //assert (m->bbt().ticks == 0);
725 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
727 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
728 corrected.second.beats = 1;
729 corrected.second.ticks = 0;
730 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
731 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
732 m->bbt(), corrected.second) << endmsg;
733 //m->set_pulse (corrected);
737 /* Look for any existing MetricSection that is of the same type and
738 in the same bar as the new one, and remove it before adding
739 the new one. Note that this means that if we find a matching,
740 existing section, we can break out of the loop since we're
741 guaranteed that there is only one such match.
744 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
746 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
747 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
748 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
749 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
751 if (tempo && insert_tempo) {
754 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
755 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
757 if (!tempo->movable()) {
759 /* can't (re)move this section, so overwrite
760 * its data content (but not its properties as
764 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
765 (*i)->set_position_lock_style (AudioTime);
773 } else if (meter && insert_meter) {
777 bool const ipm = insert_meter->position_lock_style() == MusicTime;
779 if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
781 if (!meter->movable()) {
783 /* can't (re)move this section, so overwrite
784 * its data content (but not its properties as
788 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
789 (*i)->set_position_lock_style (insert_meter->position_lock_style());
799 /* non-matching types, so we don't care */
803 /* Add the given MetricSection, if we didn't just reset an existing
808 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
809 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
812 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
813 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
816 bool const ipm = insert_meter->position_lock_style() == MusicTime;
817 if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
822 } else if (insert_tempo) {
823 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
824 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
827 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
828 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
835 _metrics.insert (i, section);
836 //dump (_metrics, std::cerr);
841 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
844 Glib::Threads::RWLock::WriterLock lm (lock);
845 TempoSection& first (first_tempo());
846 if (ts.pulse() != first.pulse()) {
847 remove_tempo_locked (ts);
848 add_tempo_locked (tempo, pulse, true, type);
850 first.set_type (type);
852 /* cannot move the first tempo section */
853 *static_cast<Tempo*>(&first) = tempo;
854 recompute_map (_metrics);
859 PropertyChanged (PropertyChange ());
863 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
866 Glib::Threads::RWLock::WriterLock lm (lock);
867 TempoSection& first (first_tempo());
868 if (ts.frame() != first.frame()) {
869 remove_tempo_locked (ts);
870 add_tempo_locked (tempo, frame, true, type);
872 first.set_type (type);
873 first.set_pulse (0.0);
874 first.set_position_lock_style (AudioTime);
876 /* cannot move the first tempo section */
877 *static_cast<Tempo*>(&first) = tempo;
878 recompute_map (_metrics);
883 PropertyChanged (PropertyChange ());
887 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
890 Glib::Threads::RWLock::WriterLock lm (lock);
891 add_tempo_locked (tempo, pulse, true, type);
894 PropertyChanged (PropertyChange ());
898 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
901 Glib::Threads::RWLock::WriterLock lm (lock);
902 add_tempo_locked (tempo, frame, true, type);
906 PropertyChanged (PropertyChange ());
910 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
912 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
917 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
922 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
924 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
929 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
934 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
937 Glib::Threads::RWLock::WriterLock lm (lock);
938 MeterSection& first (first_meter());
941 remove_meter_locked (ms);
942 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
944 const PositionLockStyle pl = ms.position_lock_style();
945 /* cannot move the first meter section */
946 *static_cast<Meter*>(&first) = meter;
947 first.set_position_lock_style (pl);
948 recompute_map (_metrics);
952 PropertyChanged (PropertyChange ());
956 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
959 Glib::Threads::RWLock::WriterLock lm (lock);
961 const double beat = ms.beat();
962 const BBT_Time bbt = ms.bbt();
964 remove_meter_locked (ms);
965 add_meter_locked (meter, frame, beat, bbt, true);
967 MeterSection& first (first_meter());
968 TempoSection& first_t (first_tempo());
969 /* cannot move the first meter section */
970 *static_cast<Meter*>(&first) = meter;
971 first.set_position_lock_style (AudioTime);
972 first.set_pulse (0.0);
973 first.set_frame (frame);
974 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
975 first.set_beat (beat);
976 first_t.set_frame (first.frame());
977 first_t.set_pulse (0.0);
978 first_t.set_position_lock_style (AudioTime);
980 recompute_map (_metrics);
983 PropertyChanged (PropertyChange ());
988 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
991 Glib::Threads::RWLock::WriterLock lm (lock);
992 add_meter_locked (meter, beat, where, true);
997 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
998 dump (_metrics, std::cerr);
1002 PropertyChanged (PropertyChange ());
1006 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1009 Glib::Threads::RWLock::WriterLock lm (lock);
1010 add_meter_locked (meter, frame, beat, where, true);
1015 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1016 dump (_metrics, std::cerr);
1020 PropertyChanged (PropertyChange ());
1024 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1026 /* a new meter always starts a new bar on the first beat. so
1027 round the start time appropriately. remember that
1028 `where' is based on the existing tempo map, not
1029 the result after we insert the new meter.
1033 if (where.beats != 1) {
1037 /* new meters *always* start on a beat. */
1039 double pulse = pulse_at_beat_locked (_metrics, beat);
1041 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1042 do_insert (new_meter);
1045 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1051 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1054 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1056 double pulse = pulse_at_frame_locked (_metrics, frame);
1057 new_meter->set_pulse (pulse);
1059 do_insert (new_meter);
1062 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1068 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1070 Tempo newtempo (beats_per_minute, note_type);
1073 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1074 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1079 Glib::Threads::RWLock::WriterLock lm (lock);
1080 *((Tempo*) t) = newtempo;
1081 recompute_map (_metrics);
1083 PropertyChanged (PropertyChange ());
1090 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1092 Tempo newtempo (beats_per_minute, note_type);
1095 TempoSection* first;
1096 Metrics::iterator i;
1098 /* find the TempoSection immediately preceding "where"
1101 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1103 if ((*i)->frame() > where) {
1109 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1122 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1132 Glib::Threads::RWLock::WriterLock lm (lock);
1133 /* cannot move the first tempo section */
1134 *((Tempo*)prev) = newtempo;
1135 recompute_map (_metrics);
1138 PropertyChanged (PropertyChange ());
1142 TempoMap::first_meter () const
1144 const MeterSection *m = 0;
1146 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1147 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1152 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1153 abort(); /*NOTREACHED*/
1158 TempoMap::first_meter ()
1160 MeterSection *m = 0;
1162 /* CALLER MUST HOLD LOCK */
1164 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1165 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1170 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1171 abort(); /*NOTREACHED*/
1176 TempoMap::first_tempo () const
1178 const TempoSection *t = 0;
1180 /* CALLER MUST HOLD LOCK */
1182 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1183 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1187 if (!t->movable()) {
1193 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1194 abort(); /*NOTREACHED*/
1199 TempoMap::first_tempo ()
1201 TempoSection *t = 0;
1203 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1204 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1208 if (!t->movable()) {
1214 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1215 abort(); /*NOTREACHED*/
1219 TempoMap::recompute_tempos (Metrics& metrics)
1221 TempoSection* prev_ts = 0;
1223 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1226 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1231 if (t->position_lock_style() == AudioTime) {
1232 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1233 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1236 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1237 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1244 prev_ts->set_c_func (0.0);
1247 /* tempos must be positioned correctly */
1249 TempoMap::recompute_meters (Metrics& metrics)
1251 MeterSection* meter = 0;
1252 MeterSection* prev_m = 0;
1253 uint32_t accumulated_bars = 0;
1255 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1256 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1258 const double beats_in_m = (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
1259 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1261 if (meter->position_lock_style() == AudioTime) {
1263 pair<double, BBT_Time> b_bbt;
1264 if (meter->movable()) {
1265 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1266 b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 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::pulse_at_beat (const double& beat) const
1320 Glib::Threads::RWLock::ReaderLock lm (lock);
1321 return pulse_at_beat_locked (_metrics, beat);
1325 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1327 MeterSection* prev_ms = 0;
1328 double accumulated_beats = 0.0;
1330 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1332 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1333 if (prev_ms && m->beat() > beat) {
1336 accumulated_beats = m->beat();
1341 double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1346 TempoMap::beat_at_pulse (const double& pulse) const
1348 Glib::Threads::RWLock::ReaderLock lm (lock);
1349 return beat_at_pulse_locked (_metrics, pulse);
1353 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1355 MeterSection* prev_ms = 0;
1356 double accumulated_beats = 0.0;
1358 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1360 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1361 if (prev_ms && m->pulse() > pulse) {
1364 accumulated_beats = m->beat();
1369 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1371 return beats_in_section + accumulated_beats;
1375 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1377 Glib::Threads::RWLock::ReaderLock lm (lock);
1378 TempoMetric m (first_meter(), first_tempo());
1380 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1381 at something, because we insert the default tempo and meter during
1382 TempoMap construction.
1384 now see if we can find better candidates.
1387 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1389 if ((*i)->frame() > frame) {
1403 /* XX meters only */
1405 TempoMap::metric_at (BBT_Time bbt) const
1407 Glib::Threads::RWLock::ReaderLock lm (lock);
1408 TempoMetric m (first_meter(), first_tempo());
1410 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1411 at something, because we insert the default tempo and meter during
1412 TempoMap construction.
1414 now see if we can find better candidates.
1417 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1419 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1420 BBT_Time section_start (mw->bbt());
1422 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1434 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1441 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1444 Glib::Threads::RWLock::ReaderLock lm (lock);
1445 double const beat = beat_at_frame_locked (_metrics, frame);
1447 bbt = beats_to_bbt_locked (_metrics, beat);
1451 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1453 Glib::Threads::RWLock::ReaderLock lm (lock);
1455 return bbt_to_beats_locked (_metrics, bbt);
1459 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1461 /* CALLER HOLDS READ LOCK */
1463 double accumulated_beats = 0.0;
1464 double accumulated_bars = 0.0;
1465 MeterSection* prev_ms = 0;
1466 /* because audio-locked meters have 'fake' integral beats,
1467 there is no pulse offset here.
1469 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1471 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1472 double bars_to_m = 0.0;
1474 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1475 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1478 accumulated_beats = m->beat();
1479 accumulated_bars += bars_to_m;
1485 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1486 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1487 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1493 TempoMap::beats_to_bbt (const double& beats)
1495 Glib::Threads::RWLock::ReaderLock lm (lock);
1497 return beats_to_bbt_locked (_metrics, beats);
1501 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1503 /* CALLER HOLDS READ LOCK */
1504 MeterSection* prev_ms = 0;
1505 const double beats = (b < 0.0) ? 0.0 : b;
1506 uint32_t accumulated_bars = 0;
1507 double accumulated_beats = 0.0;
1509 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1510 MeterSection* m = 0;
1512 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1515 double const beats_to_m = m->beat() - prev_ms->beat();
1516 if (accumulated_beats + beats_to_m > beats) {
1517 /* this is the meter after the one our beat is on*/
1521 /* we need a whole number of bars. */
1522 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1523 accumulated_beats += beats_to_m;
1530 double const beats_in_ms = beats - accumulated_beats;
1531 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1532 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1533 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1534 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1538 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1539 ret.beats = (uint32_t) floor (remaining_beats);
1540 ret.bars = total_bars;
1542 /* 0 0 0 to 1 1 0 - based mapping*/
1546 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1548 ret.ticks -= BBT_Time::ticks_per_beat;
1551 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1560 TempoMap::pulse_to_bbt (const double& pulse)
1562 Glib::Threads::RWLock::ReaderLock lm (lock);
1563 MeterSection* prev_ms = 0;
1564 uint32_t accumulated_bars = 0;
1565 double accumulated_pulses = 0.0;
1567 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1568 MeterSection* m = 0;
1570 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1573 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1574 if (accumulated_pulses + pulses_to_m > pulse) {
1575 /* this is the meter after the one our beat is on*/
1579 /* we need a whole number of bars. */
1580 accumulated_pulses += pulses_to_m;
1581 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1587 double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1588 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1589 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1590 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1591 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1595 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1596 ret.beats = (uint32_t) floor (remaining_beats);
1597 ret.bars = total_bars;
1599 /* 0 0 0 to 1 1 0 mapping*/
1603 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1605 ret.ticks -= BBT_Time::ticks_per_beat;
1608 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1617 TempoMap::beat_at_frame (const framecnt_t& frame) const
1619 Glib::Threads::RWLock::ReaderLock lm (lock);
1620 return beat_at_frame_locked (_metrics, frame);
1624 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1626 //framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
1627 double const pulse = pulse_at_frame_locked (metrics, frame);
1629 return beat_at_pulse_locked (metrics, pulse);
1633 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1635 /* HOLD (at least) THE READER LOCK */
1636 TempoSection* prev_ts = 0;
1637 double accumulated_pulses = 0.0;
1639 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1641 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1645 if (prev_ts && t->frame() > frame) {
1646 /*the previous ts is the one containing the frame */
1647 double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1650 accumulated_pulses = t->pulse();
1655 /* treated as constant for this ts */
1656 double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1658 return pulses_in_section + accumulated_pulses;
1662 TempoMap::frame_at_beat (const double& beat) const
1664 Glib::Threads::RWLock::ReaderLock lm (lock);
1665 return frame_at_beat_locked (_metrics, beat);
1669 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1671 framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1672 //frameoffset_t const frame_off = frame_offset_at (metrics, frame);
1677 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1679 /* HOLD THE READER LOCK */
1681 const TempoSection* prev_ts = 0;
1682 double accumulated_pulses = 0.0;
1684 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1687 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1691 if (prev_ts && t->pulse() > pulse) {
1692 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1695 accumulated_pulses = t->pulse();
1699 /* must be treated as constant, irrespective of _type */
1700 double const pulses_in_section = pulse - accumulated_pulses;
1701 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1703 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1709 TempoMap::frame_time (const BBT_Time& bbt)
1712 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1716 if (bbt.beats < 1) {
1717 throw std::logic_error ("beats are counted from one");
1719 Glib::Threads::RWLock::ReaderLock lm (lock);
1720 double const beat = bbt_to_beats_locked (_metrics, bbt);
1721 framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1726 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1728 /* HOLD THE READER LOCK */
1730 framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt)));
1736 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1738 TempoSection* prev_ts = 0;
1740 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1742 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1747 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
1751 if (t->frame() == prev_ts->frame()) {
1755 /* precision check ensures pulses and frames align independent of lock style.*/
1756 if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
1768 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1770 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1772 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1773 if (!t->movable()) {
1774 t->set_active (true);
1777 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1778 t->set_active (false);
1780 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1781 t->set_active (true);
1782 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1791 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1793 TempoSection* prev_ts = 0;
1794 TempoSection* section_prev = 0;
1795 framepos_t first_m_frame = 0;
1797 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1799 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1800 if (!m->movable()) {
1801 first_m_frame = m->frame();
1806 if (section->movable() && frame <= first_m_frame) {
1809 section->set_active (true);
1811 section->set_frame (frame);
1813 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1815 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1822 section_prev = prev_ts;
1825 if (t->position_lock_style() == MusicTime) {
1826 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1827 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1829 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1830 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1838 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1839 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1842 if (section->position_lock_style() == MusicTime) {
1843 /* we're setting the frame */
1844 section->set_position_lock_style (AudioTime);
1845 recompute_tempos (imaginary);
1846 section->set_position_lock_style (MusicTime);
1848 recompute_tempos (imaginary);
1851 if (check_solved (imaginary, true)) {
1852 recompute_meters (imaginary);
1856 MetricSectionFrameSorter fcmp;
1857 imaginary.sort (fcmp);
1858 if (section->position_lock_style() == MusicTime) {
1859 /* we're setting the frame */
1860 section->set_position_lock_style (AudioTime);
1861 recompute_tempos (imaginary);
1862 section->set_position_lock_style (MusicTime);
1864 recompute_tempos (imaginary);
1866 if (check_solved (imaginary, true)) {
1867 recompute_meters (imaginary);
1871 MetricSectionSorter cmp;
1872 imaginary.sort (cmp);
1873 if (section->position_lock_style() == MusicTime) {
1874 /* we're setting the frame */
1875 section->set_position_lock_style (AudioTime);
1876 recompute_tempos (imaginary);
1877 section->set_position_lock_style (MusicTime);
1879 recompute_tempos (imaginary);
1881 if (check_solved (imaginary, true)) {
1882 recompute_meters (imaginary);
1885 //dump (imaginary, std::cerr);
1891 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
1893 TempoSection* prev_ts = 0;
1894 TempoSection* section_prev = 0;
1896 section->set_pulse (pulse);
1898 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1900 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1906 section_prev = prev_ts;
1909 if (t->position_lock_style() == MusicTime) {
1910 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1911 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1913 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1914 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1921 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1922 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1925 if (section->position_lock_style() == AudioTime) {
1926 /* we're setting the pulse */
1927 section->set_position_lock_style (MusicTime);
1928 recompute_tempos (imaginary);
1929 section->set_position_lock_style (AudioTime);
1931 recompute_tempos (imaginary);
1933 if (check_solved (imaginary, false)) {
1934 recompute_meters (imaginary);
1938 MetricSectionSorter cmp;
1939 imaginary.sort (cmp);
1940 if (section->position_lock_style() == AudioTime) {
1941 /* we're setting the pulse */
1942 section->set_position_lock_style (MusicTime);
1943 recompute_tempos (imaginary);
1944 section->set_position_lock_style (AudioTime);
1946 recompute_tempos (imaginary);
1949 if (check_solved (imaginary, false)) {
1950 recompute_meters (imaginary);
1954 MetricSectionFrameSorter fcmp;
1955 imaginary.sort (fcmp);
1956 if (section->position_lock_style() == AudioTime) {
1957 /* we're setting the pulse */
1958 section->set_position_lock_style (MusicTime);
1959 recompute_tempos (imaginary);
1960 section->set_position_lock_style (AudioTime);
1962 recompute_tempos (imaginary);
1965 if (check_solved (imaginary, false)) {
1966 recompute_meters (imaginary);
1970 //dump (imaginary, std::cerr);
1976 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
1978 MeterSection* prev_ms = 0;
1980 if (!section->movable()) {
1981 /* lock the first tempo to our first meter */
1982 if (!set_active_tempos (imaginary, frame)) {
1985 TempoSection* first_t = &first_tempo();
1987 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1989 new_section->set_frame (frame);
1990 new_section->set_pulse (0.0);
1991 new_section->set_active (true);
1993 if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
1994 first_t->set_frame (frame);
1995 first_t->set_pulse (0.0);
1996 first_t->set_active (true);
1997 solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
2003 uint32_t accumulated_bars = 0;
2005 section->set_frame (frame);
2007 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2009 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2011 const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2012 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2016 here we set the beat for this frame.
2017 we're going to set it 'incorrectly' to the next integer and use this difference
2018 to find the meter's pulse.
2019 (meters should fall on integral beats to keep us sane)
2020 XXX do it to the bar, you fool, not the beat! - and don't forget to recompute_meters that way too.
2023 pair<double, BBT_Time> b_bbt;
2025 double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2026 b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
2027 const double true_pulse = prev_ms->pulse() + ((ceil (beats) - prev_ms->beat()) / prev_ms->note_divisor());
2028 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2029 pulse = true_pulse - pulse_off;
2031 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2033 m->set_beat (b_bbt);
2034 m->set_pulse (pulse);
2039 if (m->position_lock_style() == MusicTime) {
2040 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2041 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2042 m->set_pulse (pulse);
2045 pair<double, BBT_Time> b_bbt;
2047 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2048 const double true_pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2049 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2050 b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
2051 pulse = true_pulse - pulse_off;
2053 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2056 m->set_beat (b_bbt);
2057 m->set_pulse (pulse);
2064 if (section->position_lock_style() == MusicTime) {
2065 /* we're setting the frame */
2066 section->set_position_lock_style (AudioTime);
2067 recompute_meters (imaginary);
2068 section->set_position_lock_style (MusicTime);
2070 recompute_meters (imaginary);
2072 //dump (imaginary, std::cerr);
2076 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2078 MeterSection* prev_ms = 0;
2079 double accumulated_beats = 0.0;
2080 uint32_t accumulated_bars = 0;
2082 section->set_pulse (pulse);
2084 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2086 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2088 double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2089 accumulated_beats += beats_in_m;
2090 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2093 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2094 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2095 section->set_beat (b_bbt);
2100 if (m->position_lock_style() == MusicTime) {
2101 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2102 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2103 m->set_pulse (pulse);
2105 if (!m->movable()) {
2106 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2107 m->set_beat (b_bbt);
2109 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2110 m->set_pulse (pulse);
2117 if (section->position_lock_style() == AudioTime) {
2118 /* we're setting the pulse */
2119 section->set_position_lock_style (MusicTime);
2120 recompute_meters (imaginary);
2121 section->set_position_lock_style (AudioTime);
2123 recompute_meters (imaginary);
2127 /** places a copy of _metrics into copy and returns a pointer
2128 * to section's equivalent.
2131 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2134 TempoSection* ret = 0;
2137 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2138 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2140 if (t->position_lock_style() == MusicTime) {
2141 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2143 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2145 ret->set_active (t->active());
2146 ret->set_movable (t->movable());
2147 copy.push_back (ret);
2150 TempoSection* cp = 0;
2151 if (t->position_lock_style() == MusicTime) {
2152 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2154 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2156 cp->set_active (t->active());
2157 cp->set_movable (t->movable());
2158 copy.push_back (cp);
2160 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2161 MeterSection* cp = 0;
2162 if (m->position_lock_style() == MusicTime) {
2163 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2164 cp->set_frame (m->frame());
2166 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2167 cp->set_pulse (m->pulse());
2169 cp->set_movable (m->movable());
2170 copy.push_back (cp);
2173 //recompute_map (copy);
2178 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
2181 TempoSection* new_section = 0;
2184 Glib::Threads::RWLock::ReaderLock lm (lock);
2185 new_section = copy_metrics_and_point (copy, ts);
2188 double const beat = bbt_to_beats_locked (copy, bbt);
2189 bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
2191 Metrics::const_iterator d = copy.begin();
2192 while (d != copy.end()) {
2201 * 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,
2202 * taking any possible reordering as a consequence of this into account.
2203 * @param section - the section to be altered
2204 * @param bpm - the new Tempo
2205 * @param bbt - the bbt where the altered tempo will fall
2206 * @return returns - the position in frames where the new tempo section will lie.
2209 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
2211 Glib::Threads::RWLock::ReaderLock lm (lock);
2214 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2216 double const beat = bbt_to_beats_locked (future_map, bbt);
2217 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2218 ret = new_section->frame();
2220 ret = frame_at_beat_locked (_metrics, beat);
2223 Metrics::const_iterator d = future_map.begin();
2224 while (d != future_map.end()) {
2232 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2234 Glib::Threads::RWLock::ReaderLock lm (lock);
2237 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2239 if (solve_map (future_map, new_section, bpm, frame)) {
2240 ret = new_section->pulse();
2242 ret = pulse_at_frame_locked (_metrics, frame);
2245 Metrics::const_iterator d = future_map.begin();
2246 while (d != future_map.end()) {
2254 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
2258 Glib::Threads::RWLock::WriterLock lm (lock);
2259 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2260 if (solve_map (future_map, new_section, bpm, frame)) {
2261 solve_map (_metrics, ts, bpm, frame);
2265 Metrics::const_iterator d = future_map.begin();
2266 while (d != future_map.end()) {
2271 MetricPositionChanged (); // Emit Signal
2275 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
2279 Glib::Threads::RWLock::WriterLock lm (lock);
2280 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2281 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2282 solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
2286 Metrics::const_iterator d = future_map.begin();
2287 while (d != future_map.end()) {
2292 MetricPositionChanged (); // Emit Signal
2296 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
2299 Glib::Threads::RWLock::WriterLock lm (lock);
2300 solve_map (_metrics, ms, mt, frame);
2303 MetricPositionChanged (); // Emit Signal
2307 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
2310 Glib::Threads::RWLock::WriterLock lm (lock);
2311 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
2314 MetricPositionChanged (); // Emit Signal
2318 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2321 bool can_solve = false;
2323 Glib::Threads::RWLock::WriterLock lm (lock);
2324 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2325 new_section->set_beats_per_minute (bpm.beats_per_minute());
2326 recompute_tempos (future_map);
2328 if (check_solved (future_map, true)) {
2329 ts->set_beats_per_minute (bpm.beats_per_minute());
2330 recompute_map (_metrics);
2335 Metrics::const_iterator d = future_map.begin();
2336 while (d != future_map.end()) {
2341 MetricPositionChanged (); // Emit Signal
2347 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2349 Glib::Threads::RWLock::ReaderLock lm (lock);
2351 double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2352 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2353 double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2354 framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2355 framecnt_t const ret = time_at_bbt;
2361 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2363 return round_to_type (fr, dir, Bar);
2367 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2369 return round_to_type (fr, dir, Beat);
2373 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2375 Glib::Threads::RWLock::ReaderLock lm (lock);
2376 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2377 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2378 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2380 ticks -= beats * BBT_Time::ticks_per_beat;
2383 /* round to next (or same iff dir == RoundUpMaybe) */
2385 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2387 if (mod == 0 && dir == RoundUpMaybe) {
2388 /* right on the subdivision, which is fine, so do nothing */
2390 } else if (mod == 0) {
2391 /* right on the subdivision, so the difference is just the subdivision ticks */
2392 ticks += ticks_one_subdivisions_worth;
2395 /* not on subdivision, compute distance to next subdivision */
2397 ticks += ticks_one_subdivisions_worth - mod;
2400 if (ticks >= BBT_Time::ticks_per_beat) {
2401 ticks -= BBT_Time::ticks_per_beat;
2403 } else if (dir < 0) {
2405 /* round to previous (or same iff dir == RoundDownMaybe) */
2407 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2409 if (difference == 0 && dir == RoundDownAlways) {
2410 /* right on the subdivision, but force-rounding down,
2411 so the difference is just the subdivision ticks */
2412 difference = ticks_one_subdivisions_worth;
2415 if (ticks < difference) {
2416 ticks = BBT_Time::ticks_per_beat - ticks;
2418 ticks -= difference;
2422 /* round to nearest */
2425 /* compute the distance to the previous and next subdivision */
2427 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2429 /* closer to the next subdivision, so shift forward */
2431 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2433 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2435 if (ticks > BBT_Time::ticks_per_beat) {
2437 ticks -= BBT_Time::ticks_per_beat;
2438 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2441 } else if (rem > 0) {
2443 /* closer to previous subdivision, so shift backward */
2447 /* can't go backwards past zero, so ... */
2450 /* step back to previous beat */
2452 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2453 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2455 ticks = lrint (ticks - rem);
2456 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2459 /* on the subdivision, do nothing */
2463 framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2469 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2471 if (sub_num == -1) {
2472 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2473 if ((double) when.beats > bpb / 2.0) {
2479 } else if (sub_num == 0) {
2480 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2481 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2483 while ((double) when.beats > bpb) {
2485 when.beats -= (uint32_t) floor (bpb);
2491 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2493 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2494 /* closer to the next subdivision, so shift forward */
2496 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2498 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2500 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2503 } else if (rem > 0) {
2504 /* closer to previous subdivision, so shift backward */
2506 if (rem > when.ticks) {
2507 if (when.beats == 0) {
2508 /* can't go backwards past zero, so ... */
2510 /* step back to previous beat */
2512 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2514 when.ticks = when.ticks - rem;
2520 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2522 Glib::Threads::RWLock::ReaderLock lm (lock);
2524 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2525 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2530 /* find bar previous to 'frame' */
2533 return frame_time (bbt);
2535 } else if (dir > 0) {
2536 /* find bar following 'frame' */
2540 return frame_time (bbt);
2542 /* true rounding: find nearest bar */
2543 framepos_t raw_ft = frame_time (bbt);
2546 framepos_t prev_ft = frame_time (bbt);
2548 framepos_t next_ft = frame_time (bbt);
2550 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2561 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2562 } else if (dir > 0) {
2563 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2565 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2574 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2575 framepos_t lower, framepos_t upper)
2577 Glib::Threads::RWLock::ReaderLock lm (lock);
2578 int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2579 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2580 /* although the map handles negative beats, bbt doesn't. */
2584 while (cnt <= upper_beat) {
2585 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2586 TempoSection const tempo = tempo_section_at_locked (pos);
2587 MeterSection const meter = meter_section_at_locked (pos);
2588 BBT_Time const bbt = beats_to_bbt (cnt);
2589 BBTPoint point = BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func());
2590 points.push_back (point);
2596 TempoMap::tempo_section_at (framepos_t frame) const
2598 Glib::Threads::RWLock::ReaderLock lm (lock);
2599 return tempo_section_at_locked (frame);
2603 TempoMap::tempo_section_at_locked (framepos_t frame) const
2605 Metrics::const_iterator i;
2606 TempoSection* prev = 0;
2608 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2611 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2615 if (t->frame() > frame) {
2625 abort(); /*NOTREACHED*/
2632 /* don't use this to calculate length (the tempo is only correct for this frame).
2633 do that stuff based on the beat_at_frame and frame_at_beat api
2636 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2638 Glib::Threads::RWLock::ReaderLock lm (lock);
2640 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2641 const TempoSection* ts_after = 0;
2642 Metrics::const_iterator i;
2644 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2647 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2651 if ((*i)->frame() > frame) {
2659 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2661 /* must be treated as constant tempo */
2662 return ts_at->frames_per_beat (_frame_rate);
2666 TempoMap::tempo_at (const framepos_t& frame) const
2668 Glib::Threads::RWLock::ReaderLock lm (lock);
2669 return tempo_at_locked (frame);
2673 TempoMap::tempo_at_locked (const framepos_t& frame) const
2675 TempoSection* prev_ts = 0;
2677 Metrics::const_iterator i;
2679 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2681 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2685 if ((prev_ts) && t->frame() > frame) {
2686 /* t is the section past frame */
2687 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2688 Tempo const ret_tempo (ret, prev_ts->note_type());
2695 double const ret = prev_ts->beats_per_minute();
2696 Tempo const ret_tempo (ret, prev_ts->note_type ());
2702 TempoMap::meter_section_at (framepos_t frame) const
2704 Glib::Threads::RWLock::ReaderLock lm (lock);
2705 return meter_section_at_locked (frame);
2709 TempoMap::meter_section_at_locked (framepos_t frame) const
2711 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2712 Metrics::const_iterator i;
2713 MeterSection* prev = 0;
2715 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2718 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2720 if (prev && (*i)->frame() > frame) {
2730 abort(); /*NOTREACHED*/
2737 TempoMap::meter_at (framepos_t frame) const
2739 TempoMetric m (metric_at (frame));
2744 TempoMap::meter_section_at (const double& beat) const
2746 MeterSection* prev_ms = 0;
2747 Glib::Threads::RWLock::ReaderLock lm (lock);
2749 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2751 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2752 if (prev_ms && m->beat() > beat) {
2763 TempoMap::get_state ()
2765 Metrics::const_iterator i;
2766 XMLNode *root = new XMLNode ("TempoMap");
2769 Glib::Threads::RWLock::ReaderLock lm (lock);
2770 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2771 root->add_child_nocopy ((*i)->get_state());
2779 TempoMap::set_state (const XMLNode& node, int /*version*/)
2782 Glib::Threads::RWLock::WriterLock lm (lock);
2785 XMLNodeConstIterator niter;
2786 Metrics old_metrics (_metrics);
2789 nlist = node.children();
2791 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2792 XMLNode* child = *niter;
2794 if (child->name() == TempoSection::xml_state_node_name) {
2797 TempoSection* ts = new TempoSection (*child);
2798 _metrics.push_back (ts);
2801 catch (failed_constructor& err){
2802 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2803 _metrics = old_metrics;
2807 } else if (child->name() == MeterSection::xml_state_node_name) {
2810 MeterSection* ms = new MeterSection (*child);
2811 _metrics.push_back (ms);
2814 catch (failed_constructor& err) {
2815 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2816 _metrics = old_metrics;
2822 if (niter == nlist.end()) {
2823 MetricSectionSorter cmp;
2824 _metrics.sort (cmp);
2826 /* check for legacy sessions where bbt was the base musical unit for tempo */
2827 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2830 MeterSection* prev_ms = 0;
2831 TempoSection* prev_ts = 0;
2832 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2833 if (prev_ms && prev_ms->pulse() < 0.0) {
2834 /*XX we cannot possibly make this work??. */
2835 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());
2836 prev_ms->set_beat (start);
2837 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);
2838 prev_ms->set_pulse (start_pulse);
2841 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2845 if (prev_ts && prev_ts->pulse() < 0.0) {
2846 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);
2847 prev_ts->set_pulse (start);
2852 /* check for multiple tempo/meters at the same location, which
2853 ardour2 somehow allowed.
2856 Metrics::iterator prev = _metrics.end();
2857 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2858 if (prev != _metrics.end()) {
2860 MeterSection* prev_ms;
2862 TempoSection* prev_ts;
2863 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2864 if (prev_ms->pulse() == ms->pulse()) {
2865 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2866 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2869 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2870 if (prev_ts->pulse() == ts->pulse()) {
2871 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2872 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2880 recompute_map (_metrics);
2883 PropertyChanged (PropertyChange ());
2889 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2891 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2892 const MeterSection* m;
2893 const TempoSection* t;
2894 const TempoSection* prev_ts = 0;
2896 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2898 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2899 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2900 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2901 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2903 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2904 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;
2907 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2908 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2909 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2912 o << "------" << std::endl;
2916 TempoMap::n_tempos() const
2918 Glib::Threads::RWLock::ReaderLock lm (lock);
2921 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2922 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2931 TempoMap::n_meters() const
2933 Glib::Threads::RWLock::ReaderLock lm (lock);
2936 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2937 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2946 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2949 Glib::Threads::RWLock::WriterLock lm (lock);
2950 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2951 if ((*i)->frame() >= where && (*i)->movable ()) {
2952 (*i)->set_frame ((*i)->frame() + amount);
2956 /* now reset the BBT time of all metrics, based on their new
2957 * audio time. This is the only place where we do this reverse
2961 Metrics::iterator i;
2962 const MeterSection* meter;
2963 const TempoSection* tempo;
2967 meter = &first_meter ();
2968 tempo = &first_tempo ();
2973 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2976 MetricSection* prev = 0;
2978 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2981 //TempoMetric metric (*meter, *tempo);
2982 MeterSection* ms = const_cast<MeterSection*>(meter);
2983 TempoSection* ts = const_cast<TempoSection*>(tempo);
2986 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2990 ts->set_pulse (t->pulse());
2992 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2993 ts->set_pulse (m->pulse());
2995 ts->set_frame (prev->frame());
2999 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3000 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3001 ms->set_beat (start);
3002 ms->set_pulse (m->pulse());
3004 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3008 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3009 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3010 ms->set_beat (start);
3011 ms->set_pulse (t->pulse());
3013 ms->set_frame (prev->frame());
3017 // metric will be at frames=0 bbt=1|1|0 by default
3018 // which is correct for our purpose
3021 // cerr << bbt << endl;
3023 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3027 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3029 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3030 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3031 bbt_time (m->frame(), bbt);
3033 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3039 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3040 /* round up to next beat */
3046 if (bbt.beats != 1) {
3047 /* round up to next bar */
3052 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3053 m->set_beat (start);
3054 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3056 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3058 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3059 abort(); /*NOTREACHED*/
3065 recompute_map (_metrics);
3069 PropertyChanged (PropertyChange ());
3072 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3076 std::list<MetricSection*> metric_kill_list;
3078 TempoSection* last_tempo = NULL;
3079 MeterSection* last_meter = NULL;
3080 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3081 bool meter_after = false; // is there a meter marker likewise?
3083 Glib::Threads::RWLock::WriterLock lm (lock);
3084 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3085 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3086 metric_kill_list.push_back(*i);
3087 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3090 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3094 else if ((*i)->frame() >= where) {
3095 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3096 (*i)->set_frame ((*i)->frame() - amount);
3097 if ((*i)->frame() == where) {
3098 // marker was immediately after end of range
3099 tempo_after = dynamic_cast<TempoSection*> (*i);
3100 meter_after = dynamic_cast<MeterSection*> (*i);
3106 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3107 if (last_tempo && !tempo_after) {
3108 metric_kill_list.remove(last_tempo);
3109 last_tempo->set_frame(where);
3112 if (last_meter && !meter_after) {
3113 metric_kill_list.remove(last_meter);
3114 last_meter->set_frame(where);
3118 //remove all the remaining metrics
3119 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3120 _metrics.remove(*i);
3125 recompute_map (_metrics);
3128 PropertyChanged (PropertyChange ());
3132 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3133 * pos can be -ve, if required.
3136 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3138 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3141 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3143 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3145 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3148 /** Add the BBT interval op to pos and return the result */
3150 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3152 Glib::Threads::RWLock::ReaderLock lm (lock);
3154 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3155 pos_bbt.ticks += op.ticks;
3156 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3158 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3160 pos_bbt.beats += op.beats;
3161 /* the meter in effect will start on the bar */
3162 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3163 while (pos_bbt.beats >= divisions_per_bar + 1) {
3165 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3166 pos_bbt.beats -= divisions_per_bar;
3168 pos_bbt.bars += op.bars;
3170 return frame_time_locked (_metrics, pos_bbt);
3173 /** Count the number of beats that are equivalent to distance when going forward,
3177 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3179 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3183 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3189 operator<< (std::ostream& o, const Meter& m) {
3190 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3194 operator<< (std::ostream& o, const Tempo& t) {
3195 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3199 operator<< (std::ostream& o, const MetricSection& section) {
3201 o << "MetricSection @ " << section.frame() << ' ';
3203 const TempoSection* ts;
3204 const MeterSection* ms;
3206 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3207 o << *((const Tempo*) ts);
3208 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3209 o << *((const Meter*) ms);