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);
940 remove_meter_locked (ms);
941 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
943 MeterSection& first (first_meter());
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();
965 remove_meter_locked (ms);
966 add_meter_locked (meter, frame, beat, bbt, true);
968 MeterSection& first (first_meter());
969 TempoSection& first_t (first_tempo());
970 /* cannot move the first meter section */
971 *static_cast<Meter*>(&first) = meter;
972 first.set_position_lock_style (AudioTime);
973 first.set_pulse (0.0);
974 first.set_frame (frame);
975 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
976 first.set_beat (beat);
977 first_t.set_frame (first.frame());
978 first_t.set_pulse (0.0);
979 first_t.set_position_lock_style (AudioTime);
981 recompute_map (_metrics);
984 PropertyChanged (PropertyChange ());
989 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
992 Glib::Threads::RWLock::WriterLock lm (lock);
993 add_meter_locked (meter, beat, where, true);
998 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
999 dump (_metrics, std::cerr);
1003 PropertyChanged (PropertyChange ());
1007 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1010 Glib::Threads::RWLock::WriterLock lm (lock);
1011 add_meter_locked (meter, frame, beat, where, true);
1016 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1017 dump (_metrics, std::cerr);
1021 PropertyChanged (PropertyChange ());
1025 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1027 /* a new meter always starts a new bar on the first beat. so
1028 round the start time appropriately. remember that
1029 `where' is based on the existing tempo map, not
1030 the result after we insert the new meter.
1034 if (where.beats != 1) {
1038 /* new meters *always* start on a beat. */
1040 double pulse = pulse_at_beat_locked (_metrics, beat);
1042 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1043 do_insert (new_meter);
1046 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1052 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1055 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1057 double pulse = pulse_at_frame_locked (_metrics, frame);
1058 new_meter->set_pulse (pulse);
1060 do_insert (new_meter);
1063 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1069 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1071 Tempo newtempo (beats_per_minute, note_type);
1074 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1075 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1080 Glib::Threads::RWLock::WriterLock lm (lock);
1081 *((Tempo*) t) = newtempo;
1082 recompute_map (_metrics);
1084 PropertyChanged (PropertyChange ());
1091 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1093 Tempo newtempo (beats_per_minute, note_type);
1096 TempoSection* first;
1097 Metrics::iterator i;
1099 /* find the TempoSection immediately preceding "where"
1102 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1104 if ((*i)->frame() > where) {
1110 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1123 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1133 Glib::Threads::RWLock::WriterLock lm (lock);
1134 /* cannot move the first tempo section */
1135 *((Tempo*)prev) = newtempo;
1136 recompute_map (_metrics);
1139 PropertyChanged (PropertyChange ());
1143 TempoMap::first_meter () const
1145 const MeterSection *m = 0;
1147 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1148 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1153 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1154 abort(); /*NOTREACHED*/
1159 TempoMap::first_meter ()
1161 MeterSection *m = 0;
1163 /* CALLER MUST HOLD LOCK */
1165 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1166 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1171 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1172 abort(); /*NOTREACHED*/
1177 TempoMap::first_tempo () const
1179 const TempoSection *t = 0;
1181 /* CALLER MUST HOLD LOCK */
1183 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1184 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1188 if (!t->movable()) {
1194 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1195 abort(); /*NOTREACHED*/
1200 TempoMap::first_tempo ()
1202 TempoSection *t = 0;
1204 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1205 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1209 if (!t->movable()) {
1215 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1216 abort(); /*NOTREACHED*/
1220 TempoMap::recompute_tempos (Metrics& metrics)
1222 TempoSection* prev_ts = 0;
1224 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1227 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1232 if (t->position_lock_style() == AudioTime) {
1233 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1234 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1237 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1238 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1245 prev_ts->set_c_func (0.0);
1248 /* tempos must be positioned correctly */
1250 TempoMap::recompute_meters (Metrics& metrics)
1252 MeterSection* meter = 0;
1253 MeterSection* prev_m = 0;
1254 uint32_t accumulated_bars = 0;
1256 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1257 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1260 if (meter->position_lock_style() == MusicTime) {
1261 beats_in_m = meter->beat() - prev_m->beat();
1263 beats_in_m = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1265 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1267 if (meter->position_lock_style() == AudioTime) {
1269 pair<double, BBT_Time> b_bbt;
1270 if (meter->movable()) {
1271 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1272 const double ceil_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1273 const double true_pulse = prev_m->pulse() + (ceil_beats - prev_m->beat()) / prev_m->note_divisor();
1274 const double pulse_off = true_pulse - ((beats - prev_m->beat()) / prev_m->note_divisor());
1275 b_bbt = make_pair (ceil_beats, BBT_Time (accumulated_bars + 1, 1, 0));
1276 pulse = true_pulse - pulse_off;
1278 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1280 meter->set_beat (b_bbt);
1281 meter->set_pulse (pulse);
1285 pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1287 pulse = pulse_at_beat_locked (metrics, meter->beat());
1289 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1290 meter->set_pulse (pulse);
1299 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1301 /* CALLER MUST HOLD WRITE LOCK */
1305 /* we will actually stop once we hit
1312 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1315 /* silly call from Session::process() during startup
1320 recompute_tempos (metrics);
1321 recompute_meters (metrics);
1325 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1327 Glib::Threads::RWLock::ReaderLock lm (lock);
1328 TempoMetric m (first_meter(), first_tempo());
1330 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1331 at something, because we insert the default tempo and meter during
1332 TempoMap construction.
1334 now see if we can find better candidates.
1337 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1339 if ((*i)->frame() > frame) {
1353 /* XX meters only */
1355 TempoMap::metric_at (BBT_Time bbt) const
1357 Glib::Threads::RWLock::ReaderLock lm (lock);
1358 TempoMetric m (first_meter(), first_tempo());
1360 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1361 at something, because we insert the default tempo and meter during
1362 TempoMap construction.
1364 now see if we can find better candidates.
1367 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1369 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1370 BBT_Time section_start (mw->bbt());
1372 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1384 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1386 /* HOLD (at least) THE READER LOCK */
1387 TempoSection* prev_ts = 0;
1388 double accumulated_pulses = 0.0;
1390 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1392 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1396 if (prev_ts && t->frame() > frame) {
1397 /*the previous ts is the one containing the frame */
1398 const double ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1401 accumulated_pulses = t->pulse();
1406 /* treated as constant for this ts */
1407 const double pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1409 return pulses_in_section + accumulated_pulses;
1413 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1415 MeterSection* prev_ms = 0;
1416 double accumulated_beats = 0.0;
1418 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1420 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1421 if (prev_ms && m->beat() > beat) {
1424 accumulated_beats = m->beat();
1429 double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1434 TempoMap::pulse_at_beat (const double& beat) const
1436 Glib::Threads::RWLock::ReaderLock lm (lock);
1437 return pulse_at_beat_locked (_metrics, beat);
1441 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1443 MeterSection* prev_ms = 0;
1444 double accumulated_beats = 0.0;
1446 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1448 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1449 if (prev_ms && m->pulse() > pulse) {
1452 accumulated_beats = m->beat();
1457 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1459 return beats_in_section + accumulated_beats;
1463 TempoMap::beat_at_pulse (const double& pulse) const
1465 Glib::Threads::RWLock::ReaderLock lm (lock);
1466 return beat_at_pulse_locked (_metrics, pulse);
1470 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1472 /* HOLD THE READER LOCK */
1474 const TempoSection* prev_ts = 0;
1475 double accumulated_pulses = 0.0;
1477 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1480 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1484 if (prev_ts && t->pulse() > pulse) {
1485 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1488 accumulated_pulses = t->pulse();
1492 /* must be treated as constant, irrespective of _type */
1493 double const pulses_in_section = pulse - accumulated_pulses;
1494 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1496 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1502 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1504 const double pulse = pulse_at_frame_locked (metrics, frame);
1505 return beat_at_pulse_locked (metrics, pulse);
1509 TempoMap::beat_at_frame (const framecnt_t& frame) const
1511 Glib::Threads::RWLock::ReaderLock lm (lock);
1512 return beat_at_frame_locked (_metrics, frame);
1516 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1518 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1523 TempoMap::frame_at_beat (const double& beat) const
1525 Glib::Threads::RWLock::ReaderLock lm (lock);
1526 return frame_at_beat_locked (_metrics, beat);
1530 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1532 /* CALLER HOLDS READ LOCK */
1534 double accumulated_beats = 0.0;
1535 double accumulated_bars = 0.0;
1536 MeterSection* prev_ms = 0;
1538 /* because audio-locked meters have 'fake' integral beats,
1539 there is no pulse offset here.
1541 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1543 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1545 const double bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1546 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1549 accumulated_beats = m->beat();
1550 accumulated_bars += bars_to_m;
1556 const double remaining_bars = (bbt.bars - 1) - accumulated_bars;
1557 const double remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1558 const double ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1564 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1566 Glib::Threads::RWLock::ReaderLock lm (lock);
1567 return bbt_to_beats_locked (_metrics, bbt);
1571 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1573 /* CALLER HOLDS READ LOCK */
1574 MeterSection* prev_ms = 0;
1575 const double beats = (b < 0.0) ? 0.0 : b;
1576 uint32_t accumulated_bars = 0;
1577 double accumulated_beats = 0.0;
1579 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1580 MeterSection* m = 0;
1582 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1584 if (m->beat() > beats) {
1585 /* this is the meter after the one our beat is on*/
1588 const double beats_to_m = m->beat() - prev_ms->beat();
1589 /* we need a whole number of bars. */
1590 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1591 accumulated_beats += beats_to_m;
1598 const double beats_in_ms = beats - accumulated_beats;
1599 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1600 const uint32_t total_bars = bars_in_ms + accumulated_bars;
1601 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1602 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1606 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1607 ret.beats = (uint32_t) floor (remaining_beats);
1608 ret.bars = total_bars;
1610 /* 0 0 0 to 1 1 0 - based mapping*/
1614 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1616 ret.ticks -= BBT_Time::ticks_per_beat;
1619 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1628 TempoMap::beats_to_bbt (const double& beats)
1630 Glib::Threads::RWLock::ReaderLock lm (lock);
1631 return beats_to_bbt_locked (_metrics, beats);
1635 TempoMap::pulse_to_bbt (const double& pulse)
1637 Glib::Threads::RWLock::ReaderLock lm (lock);
1638 MeterSection* prev_ms = 0;
1639 uint32_t accumulated_bars = 0;
1640 double accumulated_pulses = 0.0;
1642 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1643 MeterSection* m = 0;
1645 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1648 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1649 if (accumulated_pulses + pulses_to_m > pulse) {
1650 /* this is the meter after the one our beat is on*/
1654 /* we need a whole number of bars. */
1655 accumulated_pulses += pulses_to_m;
1656 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1662 const double beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1663 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1664 const uint32_t total_bars = bars_in_ms + accumulated_bars;
1665 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1666 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1670 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1671 ret.beats = (uint32_t) floor (remaining_beats);
1672 ret.bars = total_bars;
1674 /* 0 0 0 to 1 1 0 mapping*/
1678 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1680 ret.ticks -= BBT_Time::ticks_per_beat;
1683 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1692 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1699 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1702 Glib::Threads::RWLock::ReaderLock lm (lock);
1703 const double beat = beat_at_frame_locked (_metrics, frame);
1705 bbt = beats_to_bbt_locked (_metrics, beat);
1709 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1711 /* HOLD THE READER LOCK */
1713 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1718 TempoMap::frame_time (const BBT_Time& bbt)
1721 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1725 if (bbt.beats < 1) {
1726 throw std::logic_error ("beats are counted from one");
1728 Glib::Threads::RWLock::ReaderLock lm (lock);
1729 const double beat = bbt_to_beats_locked (_metrics, bbt);
1730 const framecnt_t frame = frame_at_beat_locked (_metrics, beat);
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) {
2012 if (m->position_lock_style() == MusicTime) {
2013 beats_in_m = m->beat() - prev_ms->beat();
2015 beats_in_m = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2017 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2021 here we set the beat for this frame.
2022 we set it 'incorrectly' to the next bar's beat
2023 and use this difference to find the meter's pulse.
2026 pair<double, BBT_Time> b_bbt;
2028 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2029 const double ceil_beats = beats - fmod (beats, prev_ms->divisions_per_bar());
2030 b_bbt = make_pair (ceil_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2031 const double true_pulse = prev_ms->pulse() + ((ceil_beats - prev_ms->beat()) / prev_ms->note_divisor());
2032 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2033 pulse = true_pulse - pulse_off;
2035 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2037 m->set_beat (b_bbt);
2038 m->set_pulse (pulse);
2043 if (m->position_lock_style() == MusicTime) {
2044 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2045 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2046 m->set_pulse (pulse);
2049 pair<double, BBT_Time> b_bbt;
2051 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2052 const double ceil_beats = beats - fmod (beats , prev_ms->divisions_per_bar());
2053 const double true_pulse = prev_ms->pulse() + (ceil_beats - prev_ms->beat()) / prev_ms->note_divisor();
2054 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2055 b_bbt = make_pair (ceil_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2056 pulse = true_pulse - pulse_off;
2058 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2061 m->set_beat (b_bbt);
2062 m->set_pulse (pulse);
2069 if (section->position_lock_style() == MusicTime) {
2070 /* we're setting the frame */
2071 section->set_position_lock_style (AudioTime);
2072 recompute_meters (imaginary);
2073 section->set_position_lock_style (MusicTime);
2075 recompute_meters (imaginary);
2077 //dump (imaginary, std::cerr);
2081 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2083 MeterSection* prev_ms = 0;
2084 double accumulated_beats = 0.0;
2085 uint32_t accumulated_bars = 0;
2087 section->set_pulse (pulse);
2089 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2091 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2094 if (m->position_lock_style() == MusicTime) {
2095 beats_in_m = m->beat() - prev_ms->beat();
2097 beats_in_m = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2099 accumulated_beats += beats_in_m;
2100 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2103 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2104 const double beats = ((pulse - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2105 const int32_t bars = (beats + 1) / prev_ms->divisions_per_bar();
2106 pair<double, BBT_Time> b_bbt = make_pair (beats, BBT_Time (bars + 1, 1, 0));
2107 section->set_beat (b_bbt);
2112 if (m->position_lock_style() == MusicTime) {
2113 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2114 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2115 m->set_pulse (pulse);
2118 pair<double, BBT_Time> b_bbt;
2120 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2121 const double ceil_beats = beats - fmod (beats, prev_ms->divisions_per_bar());
2122 const double true_pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2123 const double pulse_off = true_pulse - ((ceil_beats - prev_ms->beat()) / prev_ms->note_divisor());
2124 b_bbt = make_pair (ceil_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2125 pulse = true_pulse - pulse_off;
2127 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2129 m->set_beat (b_bbt);
2130 m->set_pulse (pulse);
2137 if (section->position_lock_style() == AudioTime) {
2138 /* we're setting the pulse */
2139 section->set_position_lock_style (MusicTime);
2140 recompute_meters (imaginary);
2141 section->set_position_lock_style (AudioTime);
2143 recompute_meters (imaginary);
2147 /** places a copy of _metrics into copy and returns a pointer
2148 * to section's equivalent.
2151 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2154 TempoSection* ret = 0;
2157 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2158 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2160 if (t->position_lock_style() == MusicTime) {
2161 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2163 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2165 ret->set_active (t->active());
2166 ret->set_movable (t->movable());
2167 copy.push_back (ret);
2170 TempoSection* cp = 0;
2171 if (t->position_lock_style() == MusicTime) {
2172 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2174 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2176 cp->set_active (t->active());
2177 cp->set_movable (t->movable());
2178 copy.push_back (cp);
2180 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2181 MeterSection* cp = 0;
2182 if (m->position_lock_style() == MusicTime) {
2183 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2184 cp->set_frame (m->frame());
2186 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2187 cp->set_pulse (m->pulse());
2189 cp->set_movable (m->movable());
2190 copy.push_back (cp);
2193 //recompute_map (copy);
2198 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
2201 TempoSection* new_section = 0;
2204 Glib::Threads::RWLock::ReaderLock lm (lock);
2205 new_section = copy_metrics_and_point (copy, ts);
2208 double const beat = bbt_to_beats_locked (copy, bbt);
2209 bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
2211 Metrics::const_iterator d = copy.begin();
2212 while (d != copy.end()) {
2221 * 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,
2222 * taking any possible reordering as a consequence of this into account.
2223 * @param section - the section to be altered
2224 * @param bpm - the new Tempo
2225 * @param bbt - the bbt where the altered tempo will fall
2226 * @return returns - the position in frames where the new tempo section will lie.
2229 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
2231 Glib::Threads::RWLock::ReaderLock lm (lock);
2234 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2236 const double beat = bbt_to_beats_locked (future_map, bbt);
2237 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2238 ret = new_section->frame();
2240 ret = frame_at_beat_locked (_metrics, beat);
2243 Metrics::const_iterator d = future_map.begin();
2244 while (d != future_map.end()) {
2252 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2254 Glib::Threads::RWLock::ReaderLock lm (lock);
2257 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2259 if (solve_map (future_map, new_section, bpm, frame)) {
2260 ret = new_section->pulse();
2262 ret = pulse_at_frame_locked (_metrics, frame);
2265 Metrics::const_iterator d = future_map.begin();
2266 while (d != future_map.end()) {
2274 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
2278 Glib::Threads::RWLock::WriterLock lm (lock);
2279 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2280 if (solve_map (future_map, new_section, bpm, frame)) {
2281 solve_map (_metrics, ts, bpm, frame);
2285 Metrics::const_iterator d = future_map.begin();
2286 while (d != future_map.end()) {
2291 MetricPositionChanged (); // Emit Signal
2295 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
2299 Glib::Threads::RWLock::WriterLock lm (lock);
2300 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2301 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2302 solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
2306 Metrics::const_iterator d = future_map.begin();
2307 while (d != future_map.end()) {
2312 MetricPositionChanged (); // Emit Signal
2316 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
2319 Glib::Threads::RWLock::WriterLock lm (lock);
2320 solve_map (_metrics, ms, mt, frame);
2323 MetricPositionChanged (); // Emit Signal
2327 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
2330 Glib::Threads::RWLock::WriterLock lm (lock);
2331 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
2334 MetricPositionChanged (); // Emit Signal
2338 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2341 bool can_solve = false;
2343 Glib::Threads::RWLock::WriterLock lm (lock);
2344 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2345 new_section->set_beats_per_minute (bpm.beats_per_minute());
2346 recompute_tempos (future_map);
2348 if (check_solved (future_map, true)) {
2349 ts->set_beats_per_minute (bpm.beats_per_minute());
2350 recompute_map (_metrics);
2355 Metrics::const_iterator d = future_map.begin();
2356 while (d != future_map.end()) {
2361 MetricPositionChanged (); // Emit Signal
2367 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2369 Glib::Threads::RWLock::ReaderLock lm (lock);
2371 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2372 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2373 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2374 const framecnt_t time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2375 const framecnt_t ret = time_at_bbt;
2381 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2383 return round_to_type (fr, dir, Bar);
2387 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2389 return round_to_type (fr, dir, Beat);
2393 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2395 Glib::Threads::RWLock::ReaderLock lm (lock);
2396 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2397 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2398 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2400 ticks -= beats * BBT_Time::ticks_per_beat;
2403 /* round to next (or same iff dir == RoundUpMaybe) */
2405 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2407 if (mod == 0 && dir == RoundUpMaybe) {
2408 /* right on the subdivision, which is fine, so do nothing */
2410 } else if (mod == 0) {
2411 /* right on the subdivision, so the difference is just the subdivision ticks */
2412 ticks += ticks_one_subdivisions_worth;
2415 /* not on subdivision, compute distance to next subdivision */
2417 ticks += ticks_one_subdivisions_worth - mod;
2420 if (ticks >= BBT_Time::ticks_per_beat) {
2421 ticks -= BBT_Time::ticks_per_beat;
2423 } else if (dir < 0) {
2425 /* round to previous (or same iff dir == RoundDownMaybe) */
2427 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2429 if (difference == 0 && dir == RoundDownAlways) {
2430 /* right on the subdivision, but force-rounding down,
2431 so the difference is just the subdivision ticks */
2432 difference = ticks_one_subdivisions_worth;
2435 if (ticks < difference) {
2436 ticks = BBT_Time::ticks_per_beat - ticks;
2438 ticks -= difference;
2442 /* round to nearest */
2445 /* compute the distance to the previous and next subdivision */
2447 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2449 /* closer to the next subdivision, so shift forward */
2451 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2453 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2455 if (ticks > BBT_Time::ticks_per_beat) {
2457 ticks -= BBT_Time::ticks_per_beat;
2458 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2461 } else if (rem > 0) {
2463 /* closer to previous subdivision, so shift backward */
2467 /* can't go backwards past zero, so ... */
2470 /* step back to previous beat */
2472 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2473 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2475 ticks = lrint (ticks - rem);
2476 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2479 /* on the subdivision, do nothing */
2483 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2489 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2491 if (sub_num == -1) {
2492 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2493 if ((double) when.beats > bpb / 2.0) {
2499 } else if (sub_num == 0) {
2500 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2501 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2503 while ((double) when.beats > bpb) {
2505 when.beats -= (uint32_t) floor (bpb);
2511 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2513 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2514 /* closer to the next subdivision, so shift forward */
2516 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2518 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2520 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2523 } else if (rem > 0) {
2524 /* closer to previous subdivision, so shift backward */
2526 if (rem > when.ticks) {
2527 if (when.beats == 0) {
2528 /* can't go backwards past zero, so ... */
2530 /* step back to previous beat */
2532 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2534 when.ticks = when.ticks - rem;
2540 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2542 Glib::Threads::RWLock::ReaderLock lm (lock);
2544 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2545 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2550 /* find bar previous to 'frame' */
2553 return frame_time (bbt);
2555 } else if (dir > 0) {
2556 /* find bar following 'frame' */
2560 return frame_time (bbt);
2562 /* true rounding: find nearest bar */
2563 framepos_t raw_ft = frame_time (bbt);
2566 framepos_t prev_ft = frame_time (bbt);
2568 framepos_t next_ft = frame_time (bbt);
2570 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2581 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2582 } else if (dir > 0) {
2583 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2585 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2594 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2595 framepos_t lower, framepos_t upper)
2597 Glib::Threads::RWLock::ReaderLock lm (lock);
2598 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2599 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2600 /* although the map handles negative beats, bbt doesn't. */
2604 while (cnt <= upper_beat) {
2605 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2606 const TempoSection tempo = tempo_section_at_locked (pos);
2607 const MeterSection meter = meter_section_at_locked (pos);
2608 const BBT_Time bbt = beats_to_bbt (cnt);
2609 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
2615 TempoMap::tempo_section_at (framepos_t frame) const
2617 Glib::Threads::RWLock::ReaderLock lm (lock);
2618 return tempo_section_at_locked (frame);
2622 TempoMap::tempo_section_at_locked (framepos_t frame) const
2624 Metrics::const_iterator i;
2625 TempoSection* prev = 0;
2627 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2630 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2634 if (t->frame() > frame) {
2644 abort(); /*NOTREACHED*/
2651 /* don't use this to calculate length (the tempo is only correct for this frame).
2652 do that stuff based on the beat_at_frame and frame_at_beat api
2655 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2657 Glib::Threads::RWLock::ReaderLock lm (lock);
2659 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2660 const TempoSection* ts_after = 0;
2661 Metrics::const_iterator i;
2663 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2666 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2670 if ((*i)->frame() > frame) {
2678 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2680 /* must be treated as constant tempo */
2681 return ts_at->frames_per_beat (_frame_rate);
2685 TempoMap::tempo_at (const framepos_t& frame) const
2687 Glib::Threads::RWLock::ReaderLock lm (lock);
2688 return tempo_at_locked (frame);
2692 TempoMap::tempo_at_locked (const framepos_t& frame) const
2694 TempoSection* prev_ts = 0;
2696 Metrics::const_iterator i;
2698 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2700 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2704 if ((prev_ts) && t->frame() > frame) {
2705 /* t is the section past frame */
2706 const double ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2707 const Tempo ret_tempo (ret, prev_ts->note_type());
2714 const double ret = prev_ts->beats_per_minute();
2715 const Tempo ret_tempo (ret, prev_ts->note_type ());
2721 TempoMap::meter_section_at (framepos_t frame) const
2723 Glib::Threads::RWLock::ReaderLock lm (lock);
2724 return meter_section_at_locked (frame);
2728 TempoMap::meter_section_at_locked (framepos_t frame) const
2730 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2731 Metrics::const_iterator i;
2732 MeterSection* prev = 0;
2734 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2737 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2739 if (prev && (*i)->frame() > frame) {
2749 abort(); /*NOTREACHED*/
2756 TempoMap::meter_at (framepos_t frame) const
2758 TempoMetric m (metric_at (frame));
2763 TempoMap::meter_section_at (const double& beat) const
2765 MeterSection* prev_ms = 0;
2766 Glib::Threads::RWLock::ReaderLock lm (lock);
2768 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2770 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2771 if (prev_ms && m->beat() > beat) {
2782 TempoMap::get_state ()
2784 Metrics::const_iterator i;
2785 XMLNode *root = new XMLNode ("TempoMap");
2788 Glib::Threads::RWLock::ReaderLock lm (lock);
2789 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2790 root->add_child_nocopy ((*i)->get_state());
2798 TempoMap::set_state (const XMLNode& node, int /*version*/)
2801 Glib::Threads::RWLock::WriterLock lm (lock);
2804 XMLNodeConstIterator niter;
2805 Metrics old_metrics (_metrics);
2808 nlist = node.children();
2810 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2811 XMLNode* child = *niter;
2813 if (child->name() == TempoSection::xml_state_node_name) {
2816 TempoSection* ts = new TempoSection (*child);
2817 _metrics.push_back (ts);
2820 catch (failed_constructor& err){
2821 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2822 _metrics = old_metrics;
2826 } else if (child->name() == MeterSection::xml_state_node_name) {
2829 MeterSection* ms = new MeterSection (*child);
2830 _metrics.push_back (ms);
2833 catch (failed_constructor& err) {
2834 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2835 _metrics = old_metrics;
2841 if (niter == nlist.end()) {
2842 MetricSectionSorter cmp;
2843 _metrics.sort (cmp);
2845 /* check for legacy sessions where bbt was the base musical unit for tempo */
2846 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2849 MeterSection* prev_ms = 0;
2850 TempoSection* prev_ts = 0;
2851 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2852 if (prev_ms && prev_ms->pulse() < 0.0) {
2853 /*XX we cannot possibly make this work??. */
2854 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());
2855 prev_ms->set_beat (start);
2856 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);
2857 prev_ms->set_pulse (start_pulse);
2860 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2864 if (prev_ts && prev_ts->pulse() < 0.0) {
2865 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);
2866 prev_ts->set_pulse (start);
2871 /* check for multiple tempo/meters at the same location, which
2872 ardour2 somehow allowed.
2875 Metrics::iterator prev = _metrics.end();
2876 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2877 if (prev != _metrics.end()) {
2879 MeterSection* prev_ms;
2881 TempoSection* prev_ts;
2882 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2883 if (prev_ms->pulse() == ms->pulse()) {
2884 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2885 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2888 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2889 if (prev_ts->pulse() == ts->pulse()) {
2890 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2891 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2899 recompute_map (_metrics);
2902 PropertyChanged (PropertyChange ());
2908 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2910 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2911 const MeterSection* m;
2912 const TempoSection* t;
2913 const TempoSection* prev_ts = 0;
2915 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2917 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2918 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2919 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2920 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2922 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2923 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;
2926 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2927 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2928 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2931 o << "------" << std::endl;
2935 TempoMap::n_tempos() const
2937 Glib::Threads::RWLock::ReaderLock lm (lock);
2940 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2941 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2950 TempoMap::n_meters() const
2952 Glib::Threads::RWLock::ReaderLock lm (lock);
2955 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2956 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2965 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2968 Glib::Threads::RWLock::WriterLock lm (lock);
2969 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2970 if ((*i)->frame() >= where && (*i)->movable ()) {
2971 (*i)->set_frame ((*i)->frame() + amount);
2975 /* now reset the BBT time of all metrics, based on their new
2976 * audio time. This is the only place where we do this reverse
2980 Metrics::iterator i;
2981 const MeterSection* meter;
2982 const TempoSection* tempo;
2986 meter = &first_meter ();
2987 tempo = &first_tempo ();
2992 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2995 MetricSection* prev = 0;
2997 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3000 //TempoMetric metric (*meter, *tempo);
3001 MeterSection* ms = const_cast<MeterSection*>(meter);
3002 TempoSection* ts = const_cast<TempoSection*>(tempo);
3005 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3009 ts->set_pulse (t->pulse());
3011 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3012 ts->set_pulse (m->pulse());
3014 ts->set_frame (prev->frame());
3018 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3019 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3020 ms->set_beat (start);
3021 ms->set_pulse (m->pulse());
3023 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3027 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3028 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3029 ms->set_beat (start);
3030 ms->set_pulse (t->pulse());
3032 ms->set_frame (prev->frame());
3036 // metric will be at frames=0 bbt=1|1|0 by default
3037 // which is correct for our purpose
3040 // cerr << bbt << endl;
3042 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3046 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3048 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3049 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3050 bbt_time (m->frame(), bbt);
3052 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3058 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3059 /* round up to next beat */
3065 if (bbt.beats != 1) {
3066 /* round up to next bar */
3071 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3072 m->set_beat (start);
3073 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3075 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3077 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3078 abort(); /*NOTREACHED*/
3084 recompute_map (_metrics);
3088 PropertyChanged (PropertyChange ());
3091 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3095 std::list<MetricSection*> metric_kill_list;
3097 TempoSection* last_tempo = NULL;
3098 MeterSection* last_meter = NULL;
3099 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3100 bool meter_after = false; // is there a meter marker likewise?
3102 Glib::Threads::RWLock::WriterLock lm (lock);
3103 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3104 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3105 metric_kill_list.push_back(*i);
3106 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3109 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3113 else if ((*i)->frame() >= where) {
3114 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3115 (*i)->set_frame ((*i)->frame() - amount);
3116 if ((*i)->frame() == where) {
3117 // marker was immediately after end of range
3118 tempo_after = dynamic_cast<TempoSection*> (*i);
3119 meter_after = dynamic_cast<MeterSection*> (*i);
3125 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3126 if (last_tempo && !tempo_after) {
3127 metric_kill_list.remove(last_tempo);
3128 last_tempo->set_frame(where);
3131 if (last_meter && !meter_after) {
3132 metric_kill_list.remove(last_meter);
3133 last_meter->set_frame(where);
3137 //remove all the remaining metrics
3138 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3139 _metrics.remove(*i);
3144 recompute_map (_metrics);
3147 PropertyChanged (PropertyChange ());
3151 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3152 * pos can be -ve, if required.
3155 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3157 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3160 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3162 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3164 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3167 /** Add the BBT interval op to pos and return the result */
3169 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3171 Glib::Threads::RWLock::ReaderLock lm (lock);
3173 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3174 pos_bbt.ticks += op.ticks;
3175 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3177 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3179 pos_bbt.beats += op.beats;
3180 /* the meter in effect will start on the bar */
3181 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3182 while (pos_bbt.beats >= divisions_per_bar + 1) {
3184 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3185 pos_bbt.beats -= divisions_per_bar;
3187 pos_bbt.bars += op.bars;
3189 return frame_time_locked (_metrics, pos_bbt);
3192 /** Count the number of beats that are equivalent to distance when going forward,
3196 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3198 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3202 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3208 operator<< (std::ostream& o, const Meter& m) {
3209 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3213 operator<< (std::ostream& o, const Tempo& t) {
3214 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3218 operator<< (std::ostream& o, const MetricSection& section) {
3220 o << "MetricSection @ " << section.frame() << ' ';
3222 const TempoSection* ts;
3223 const MeterSection* ms;
3225 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3226 o << *((const Tempo*) ts);
3227 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3228 o << *((const Meter*) ms);