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;
1255 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1256 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1257 if (meter->position_lock_style() == AudioTime) {
1259 pair<double, BBT_Time> b_bbt;
1260 if (meter->movable()) {
1261 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1262 const double ceil_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1263 b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1264 const double true_pulse = prev_m->pulse() + (ceil_beats - prev_m->beat()) / prev_m->note_divisor();
1265 const double pulse_off = true_pulse - ((beats - prev_m->beat()) / prev_m->note_divisor());
1266 pulse = true_pulse - pulse_off;
1268 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1270 meter->set_beat (b_bbt);
1271 meter->set_pulse (pulse);
1275 pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1277 pulse = pulse_at_beat_locked (metrics, meter->beat());
1279 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1280 meter->set_pulse (pulse);
1289 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1291 /* CALLER MUST HOLD WRITE LOCK */
1295 /* we will actually stop once we hit
1302 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1305 /* silly call from Session::process() during startup
1310 recompute_tempos (metrics);
1311 recompute_meters (metrics);
1315 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1317 Glib::Threads::RWLock::ReaderLock lm (lock);
1318 TempoMetric m (first_meter(), first_tempo());
1320 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1321 at something, because we insert the default tempo and meter during
1322 TempoMap construction.
1324 now see if we can find better candidates.
1327 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1329 if ((*i)->frame() > frame) {
1343 /* XX meters only */
1345 TempoMap::metric_at (BBT_Time bbt) const
1347 Glib::Threads::RWLock::ReaderLock lm (lock);
1348 TempoMetric m (first_meter(), first_tempo());
1350 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1351 at something, because we insert the default tempo and meter during
1352 TempoMap construction.
1354 now see if we can find better candidates.
1357 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1359 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1360 BBT_Time section_start (mw->bbt());
1362 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1374 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1376 /* HOLD (at least) THE READER LOCK */
1377 TempoSection* prev_ts = 0;
1378 double accumulated_pulses = 0.0;
1380 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1382 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1386 if (prev_ts && t->frame() > frame) {
1387 /*the previous ts is the one containing the frame */
1388 const double ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1391 accumulated_pulses = t->pulse();
1396 /* treated as constant for this ts */
1397 const double pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1399 return pulses_in_section + accumulated_pulses;
1403 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1405 MeterSection* prev_ms = 0;
1406 double accumulated_beats = 0.0;
1408 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1410 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1411 if (prev_ms && m->beat() > beat) {
1414 accumulated_beats = m->beat();
1419 double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1424 TempoMap::pulse_at_beat (const double& beat) const
1426 Glib::Threads::RWLock::ReaderLock lm (lock);
1427 return pulse_at_beat_locked (_metrics, beat);
1431 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1433 MeterSection* prev_ms = 0;
1434 double accumulated_beats = 0.0;
1436 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1438 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1439 if (prev_ms && m->pulse() > pulse) {
1442 accumulated_beats = m->beat();
1447 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1449 return beats_in_section + accumulated_beats;
1453 TempoMap::beat_at_pulse (const double& pulse) const
1455 Glib::Threads::RWLock::ReaderLock lm (lock);
1456 return beat_at_pulse_locked (_metrics, pulse);
1460 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1462 /* HOLD THE READER LOCK */
1464 const TempoSection* prev_ts = 0;
1465 double accumulated_pulses = 0.0;
1467 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1470 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1474 if (prev_ts && t->pulse() > pulse) {
1475 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1478 accumulated_pulses = t->pulse();
1482 /* must be treated as constant, irrespective of _type */
1483 double const pulses_in_section = pulse - accumulated_pulses;
1484 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1486 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1492 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1494 const double pulse = pulse_at_frame_locked (metrics, frame);
1495 return beat_at_pulse_locked (metrics, pulse);
1499 TempoMap::beat_at_frame (const framecnt_t& frame) const
1501 Glib::Threads::RWLock::ReaderLock lm (lock);
1502 return beat_at_frame_locked (_metrics, frame);
1506 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1508 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1513 TempoMap::frame_at_beat (const double& beat) const
1515 Glib::Threads::RWLock::ReaderLock lm (lock);
1516 return frame_at_beat_locked (_metrics, beat);
1520 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1522 /* CALLER HOLDS READ LOCK */
1524 double accumulated_beats = 0.0;
1525 double accumulated_bars = 0.0;
1526 MeterSection* prev_ms = 0;
1528 /* because audio-locked meters have 'fake' integral beats,
1529 there is no pulse offset here.
1531 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1533 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1535 const double bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1536 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1539 accumulated_beats = m->beat();
1540 accumulated_bars += bars_to_m;
1546 const double remaining_bars = (bbt.bars - 1) - accumulated_bars;
1547 const double remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1548 const double ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1554 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1556 Glib::Threads::RWLock::ReaderLock lm (lock);
1557 return bbt_to_beats_locked (_metrics, bbt);
1561 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1563 /* CALLER HOLDS READ LOCK */
1564 MeterSection* prev_ms = 0;
1565 const double beats = (b < 0.0) ? 0.0 : b;
1566 uint32_t accumulated_bars = 0;
1567 double accumulated_beats = 0.0;
1569 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1570 MeterSection* m = 0;
1572 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1574 if (m->beat() > beats) {
1575 /* this is the meter after the one our beat is on*/
1578 const double beats_to_m = m->beat() - prev_ms->beat();
1579 /* we need a whole number of bars. */
1580 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1581 accumulated_beats += beats_to_m;
1588 const double beats_in_ms = beats - accumulated_beats;
1589 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1590 const uint32_t total_bars = bars_in_ms + accumulated_bars;
1591 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1592 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1596 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1597 ret.beats = (uint32_t) floor (remaining_beats);
1598 ret.bars = total_bars;
1600 /* 0 0 0 to 1 1 0 - based mapping*/
1604 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1606 ret.ticks -= BBT_Time::ticks_per_beat;
1609 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1618 TempoMap::beats_to_bbt (const double& beats)
1620 Glib::Threads::RWLock::ReaderLock lm (lock);
1621 return beats_to_bbt_locked (_metrics, beats);
1625 TempoMap::pulse_to_bbt (const double& pulse)
1627 Glib::Threads::RWLock::ReaderLock lm (lock);
1628 MeterSection* prev_ms = 0;
1629 uint32_t accumulated_bars = 0;
1630 double accumulated_pulses = 0.0;
1632 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1633 MeterSection* m = 0;
1635 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1638 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1639 if (accumulated_pulses + pulses_to_m > pulse) {
1640 /* this is the meter after the one our beat is on*/
1644 /* we need a whole number of bars. */
1645 accumulated_pulses += pulses_to_m;
1646 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1652 const double beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1653 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1654 const uint32_t total_bars = bars_in_ms + accumulated_bars;
1655 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1656 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1660 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1661 ret.beats = (uint32_t) floor (remaining_beats);
1662 ret.bars = total_bars;
1664 /* 0 0 0 to 1 1 0 mapping*/
1668 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1670 ret.ticks -= BBT_Time::ticks_per_beat;
1673 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1682 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1689 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1692 Glib::Threads::RWLock::ReaderLock lm (lock);
1693 const double beat = beat_at_frame_locked (_metrics, frame);
1695 bbt = beats_to_bbt_locked (_metrics, beat);
1699 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1701 /* HOLD THE READER LOCK */
1703 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1708 TempoMap::frame_time (const BBT_Time& bbt)
1711 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1715 if (bbt.beats < 1) {
1716 throw std::logic_error ("beats are counted from one");
1718 Glib::Threads::RWLock::ReaderLock lm (lock);
1719 const double beat = bbt_to_beats_locked (_metrics, bbt);
1720 const framecnt_t frame = frame_at_beat_locked (_metrics, beat);
1726 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1728 TempoSection* prev_ts = 0;
1730 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1732 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1737 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
1741 if (t->frame() == prev_ts->frame()) {
1745 /* precision check ensures pulses and frames align independent of lock style.*/
1746 if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
1758 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1760 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1762 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1763 if (!t->movable()) {
1764 t->set_active (true);
1767 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1768 t->set_active (false);
1770 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1771 t->set_active (true);
1772 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1781 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1783 TempoSection* prev_ts = 0;
1784 TempoSection* section_prev = 0;
1785 framepos_t first_m_frame = 0;
1787 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1789 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1790 if (!m->movable()) {
1791 first_m_frame = m->frame();
1796 if (section->movable() && frame <= first_m_frame) {
1799 section->set_active (true);
1801 section->set_frame (frame);
1803 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1805 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1812 section_prev = prev_ts;
1815 if (t->position_lock_style() == MusicTime) {
1816 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1817 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1819 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1820 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1828 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1829 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1832 if (section->position_lock_style() == MusicTime) {
1833 /* we're setting the frame */
1834 section->set_position_lock_style (AudioTime);
1835 recompute_tempos (imaginary);
1836 section->set_position_lock_style (MusicTime);
1838 recompute_tempos (imaginary);
1841 if (check_solved (imaginary, true)) {
1842 recompute_meters (imaginary);
1846 MetricSectionFrameSorter fcmp;
1847 imaginary.sort (fcmp);
1848 if (section->position_lock_style() == MusicTime) {
1849 /* we're setting the frame */
1850 section->set_position_lock_style (AudioTime);
1851 recompute_tempos (imaginary);
1852 section->set_position_lock_style (MusicTime);
1854 recompute_tempos (imaginary);
1856 if (check_solved (imaginary, true)) {
1857 recompute_meters (imaginary);
1861 MetricSectionSorter cmp;
1862 imaginary.sort (cmp);
1863 if (section->position_lock_style() == MusicTime) {
1864 /* we're setting the frame */
1865 section->set_position_lock_style (AudioTime);
1866 recompute_tempos (imaginary);
1867 section->set_position_lock_style (MusicTime);
1869 recompute_tempos (imaginary);
1871 if (check_solved (imaginary, true)) {
1872 recompute_meters (imaginary);
1875 //dump (imaginary, std::cerr);
1881 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
1883 TempoSection* prev_ts = 0;
1884 TempoSection* section_prev = 0;
1886 section->set_pulse (pulse);
1888 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1890 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1896 section_prev = prev_ts;
1899 if (t->position_lock_style() == MusicTime) {
1900 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1901 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1903 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1904 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1911 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1912 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1915 if (section->position_lock_style() == AudioTime) {
1916 /* we're setting the pulse */
1917 section->set_position_lock_style (MusicTime);
1918 recompute_tempos (imaginary);
1919 section->set_position_lock_style (AudioTime);
1921 recompute_tempos (imaginary);
1923 if (check_solved (imaginary, false)) {
1924 recompute_meters (imaginary);
1928 MetricSectionSorter cmp;
1929 imaginary.sort (cmp);
1930 if (section->position_lock_style() == AudioTime) {
1931 /* we're setting the pulse */
1932 section->set_position_lock_style (MusicTime);
1933 recompute_tempos (imaginary);
1934 section->set_position_lock_style (AudioTime);
1936 recompute_tempos (imaginary);
1939 if (check_solved (imaginary, false)) {
1940 recompute_meters (imaginary);
1944 MetricSectionFrameSorter fcmp;
1945 imaginary.sort (fcmp);
1946 if (section->position_lock_style() == AudioTime) {
1947 /* we're setting the pulse */
1948 section->set_position_lock_style (MusicTime);
1949 recompute_tempos (imaginary);
1950 section->set_position_lock_style (AudioTime);
1952 recompute_tempos (imaginary);
1955 if (check_solved (imaginary, false)) {
1956 recompute_meters (imaginary);
1960 //dump (imaginary, std::cerr);
1966 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
1968 MeterSection* prev_ms = 0;
1970 if (!section->movable()) {
1971 /* lock the first tempo to our first meter */
1972 if (!set_active_tempos (imaginary, frame)) {
1975 TempoSection* first_t = &first_tempo();
1977 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1979 new_section->set_frame (frame);
1980 new_section->set_pulse (0.0);
1981 new_section->set_active (true);
1983 if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
1984 first_t->set_frame (frame);
1985 first_t->set_pulse (0.0);
1986 first_t->set_active (true);
1987 solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
1993 section->set_frame (frame);
1995 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1997 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2000 here we set the beat for this frame.
2001 we set it 'incorrectly' to the next bar's beat
2002 and use this difference to find the meter's pulse.
2005 pair<double, BBT_Time> b_bbt;
2007 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2008 const double ceil_beats = beats - fmod (beats, prev_ms->divisions_per_bar());
2009 b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 1, 0));
2010 const double true_pulse = prev_ms->pulse() + ((ceil_beats - prev_ms->beat()) / prev_ms->note_divisor());
2011 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2012 pulse = true_pulse - pulse_off;
2014 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2016 m->set_beat (b_bbt);
2017 m->set_pulse (pulse);
2022 if (m->position_lock_style() == MusicTime) {
2023 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2024 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2025 m->set_pulse (pulse);
2028 pair<double, BBT_Time> b_bbt;
2030 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2031 const double ceil_beats = beats - fmod (beats , prev_ms->divisions_per_bar());
2032 b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 1, 0));
2033 const double true_pulse = prev_ms->pulse() + (ceil_beats - prev_ms->beat()) / prev_ms->note_divisor();
2034 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2035 pulse = true_pulse - pulse_off;
2037 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2040 m->set_beat (b_bbt);
2041 m->set_pulse (pulse);
2048 if (section->position_lock_style() == MusicTime) {
2049 /* we're setting the frame */
2050 section->set_position_lock_style (AudioTime);
2051 recompute_meters (imaginary);
2052 section->set_position_lock_style (MusicTime);
2054 recompute_meters (imaginary);
2056 //dump (imaginary, std::cerr);
2060 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2062 MeterSection* prev_ms = 0;
2064 section->set_pulse (pulse);
2066 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2068 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2070 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2071 const double beats = ((pulse - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2072 const int32_t bars = (beats + 1) / prev_ms->divisions_per_bar();
2073 pair<double, BBT_Time> b_bbt = make_pair (beats, BBT_Time (bars + 1, 1, 0));
2074 section->set_beat (b_bbt);
2079 if (m->position_lock_style() == MusicTime) {
2080 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2081 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2082 m->set_pulse (pulse);
2085 pair<double, BBT_Time> b_bbt;
2087 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2088 const double ceil_beats = beats - fmod (beats, prev_ms->divisions_per_bar());
2089 b_bbt = make_pair (ceil_beats, BBT_Time ((ceil_beats / prev_ms->divisions_per_bar()) + prev_ms->bbt().bars, 1, 0));
2090 const double true_pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2091 const double pulse_off = true_pulse - ((ceil_beats - prev_ms->beat()) / prev_ms->note_divisor());
2092 pulse = true_pulse - pulse_off;
2094 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2096 m->set_beat (b_bbt);
2097 m->set_pulse (pulse);
2104 if (section->position_lock_style() == AudioTime) {
2105 /* we're setting the pulse */
2106 section->set_position_lock_style (MusicTime);
2107 recompute_meters (imaginary);
2108 section->set_position_lock_style (AudioTime);
2110 recompute_meters (imaginary);
2114 /** places a copy of _metrics into copy and returns a pointer
2115 * to section's equivalent.
2118 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2121 TempoSection* ret = 0;
2124 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2125 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2127 if (t->position_lock_style() == MusicTime) {
2128 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2130 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2132 ret->set_active (t->active());
2133 ret->set_movable (t->movable());
2134 copy.push_back (ret);
2137 TempoSection* cp = 0;
2138 if (t->position_lock_style() == MusicTime) {
2139 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2141 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2143 cp->set_active (t->active());
2144 cp->set_movable (t->movable());
2145 copy.push_back (cp);
2147 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2148 MeterSection* cp = 0;
2149 if (m->position_lock_style() == MusicTime) {
2150 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2151 cp->set_frame (m->frame());
2153 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2154 cp->set_pulse (m->pulse());
2156 cp->set_movable (m->movable());
2157 copy.push_back (cp);
2160 //recompute_map (copy);
2165 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
2168 TempoSection* new_section = 0;
2171 Glib::Threads::RWLock::ReaderLock lm (lock);
2172 new_section = copy_metrics_and_point (copy, ts);
2175 double const beat = bbt_to_beats_locked (copy, bbt);
2176 bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
2178 Metrics::const_iterator d = copy.begin();
2179 while (d != copy.end()) {
2188 * 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,
2189 * taking any possible reordering as a consequence of this into account.
2190 * @param section - the section to be altered
2191 * @param bpm - the new Tempo
2192 * @param bbt - the bbt where the altered tempo will fall
2193 * @return returns - the position in frames where the new tempo section will lie.
2196 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
2198 Glib::Threads::RWLock::ReaderLock lm (lock);
2201 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2203 const double beat = bbt_to_beats_locked (future_map, bbt);
2204 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2205 ret = new_section->frame();
2207 ret = frame_at_beat_locked (_metrics, beat);
2210 Metrics::const_iterator d = future_map.begin();
2211 while (d != future_map.end()) {
2219 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2221 Glib::Threads::RWLock::ReaderLock lm (lock);
2224 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2226 if (solve_map (future_map, new_section, bpm, frame)) {
2227 ret = new_section->pulse();
2229 ret = pulse_at_frame_locked (_metrics, frame);
2232 Metrics::const_iterator d = future_map.begin();
2233 while (d != future_map.end()) {
2241 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
2245 Glib::Threads::RWLock::WriterLock lm (lock);
2246 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2247 if (solve_map (future_map, new_section, bpm, frame)) {
2248 solve_map (_metrics, ts, bpm, frame);
2252 Metrics::const_iterator d = future_map.begin();
2253 while (d != future_map.end()) {
2258 MetricPositionChanged (); // Emit Signal
2262 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
2266 Glib::Threads::RWLock::WriterLock lm (lock);
2267 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2268 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2269 solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
2273 Metrics::const_iterator d = future_map.begin();
2274 while (d != future_map.end()) {
2279 MetricPositionChanged (); // Emit Signal
2283 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
2286 Glib::Threads::RWLock::WriterLock lm (lock);
2287 solve_map (_metrics, ms, mt, frame);
2290 MetricPositionChanged (); // Emit Signal
2294 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
2297 Glib::Threads::RWLock::WriterLock lm (lock);
2298 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
2301 MetricPositionChanged (); // Emit Signal
2305 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2308 bool can_solve = false;
2310 Glib::Threads::RWLock::WriterLock lm (lock);
2311 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2312 new_section->set_beats_per_minute (bpm.beats_per_minute());
2313 recompute_tempos (future_map);
2315 if (check_solved (future_map, true)) {
2316 ts->set_beats_per_minute (bpm.beats_per_minute());
2317 recompute_map (_metrics);
2322 Metrics::const_iterator d = future_map.begin();
2323 while (d != future_map.end()) {
2328 MetricPositionChanged (); // Emit Signal
2334 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2336 Glib::Threads::RWLock::ReaderLock lm (lock);
2338 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2339 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2340 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2341 const framecnt_t time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2342 const framecnt_t ret = time_at_bbt;
2348 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2350 return round_to_type (fr, dir, Bar);
2354 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2356 return round_to_type (fr, dir, Beat);
2360 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2362 Glib::Threads::RWLock::ReaderLock lm (lock);
2363 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2364 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2365 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2367 ticks -= beats * BBT_Time::ticks_per_beat;
2370 /* round to next (or same iff dir == RoundUpMaybe) */
2372 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2374 if (mod == 0 && dir == RoundUpMaybe) {
2375 /* right on the subdivision, which is fine, so do nothing */
2377 } else if (mod == 0) {
2378 /* right on the subdivision, so the difference is just the subdivision ticks */
2379 ticks += ticks_one_subdivisions_worth;
2382 /* not on subdivision, compute distance to next subdivision */
2384 ticks += ticks_one_subdivisions_worth - mod;
2387 if (ticks >= BBT_Time::ticks_per_beat) {
2388 ticks -= BBT_Time::ticks_per_beat;
2390 } else if (dir < 0) {
2392 /* round to previous (or same iff dir == RoundDownMaybe) */
2394 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2396 if (difference == 0 && dir == RoundDownAlways) {
2397 /* right on the subdivision, but force-rounding down,
2398 so the difference is just the subdivision ticks */
2399 difference = ticks_one_subdivisions_worth;
2402 if (ticks < difference) {
2403 ticks = BBT_Time::ticks_per_beat - ticks;
2405 ticks -= difference;
2409 /* round to nearest */
2412 /* compute the distance to the previous and next subdivision */
2414 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2416 /* closer to the next subdivision, so shift forward */
2418 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2420 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2422 if (ticks > BBT_Time::ticks_per_beat) {
2424 ticks -= BBT_Time::ticks_per_beat;
2425 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2428 } else if (rem > 0) {
2430 /* closer to previous subdivision, so shift backward */
2434 /* can't go backwards past zero, so ... */
2437 /* step back to previous beat */
2439 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2440 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2442 ticks = lrint (ticks - rem);
2443 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2446 /* on the subdivision, do nothing */
2450 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2456 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2458 if (sub_num == -1) {
2459 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2460 if ((double) when.beats > bpb / 2.0) {
2466 } else if (sub_num == 0) {
2467 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2468 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2470 while ((double) when.beats > bpb) {
2472 when.beats -= (uint32_t) floor (bpb);
2478 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2480 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2481 /* closer to the next subdivision, so shift forward */
2483 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2485 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2487 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2490 } else if (rem > 0) {
2491 /* closer to previous subdivision, so shift backward */
2493 if (rem > when.ticks) {
2494 if (when.beats == 0) {
2495 /* can't go backwards past zero, so ... */
2497 /* step back to previous beat */
2499 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2501 when.ticks = when.ticks - rem;
2507 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2509 Glib::Threads::RWLock::ReaderLock lm (lock);
2511 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2512 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2517 /* find bar previous to 'frame' */
2520 return frame_time (bbt);
2522 } else if (dir > 0) {
2523 /* find bar following 'frame' */
2527 return frame_time (bbt);
2529 /* true rounding: find nearest bar */
2530 framepos_t raw_ft = frame_time (bbt);
2533 framepos_t prev_ft = frame_time (bbt);
2535 framepos_t next_ft = frame_time (bbt);
2537 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2548 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2549 } else if (dir > 0) {
2550 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2552 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2561 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2562 framepos_t lower, framepos_t upper)
2564 Glib::Threads::RWLock::ReaderLock lm (lock);
2565 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2566 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2567 /* although the map handles negative beats, bbt doesn't. */
2571 while (cnt <= upper_beat) {
2572 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2573 const TempoSection tempo = tempo_section_at_locked (pos);
2574 const MeterSection meter = meter_section_at_locked (pos);
2575 const BBT_Time bbt = beats_to_bbt (cnt);
2576 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
2582 TempoMap::tempo_section_at (framepos_t frame) const
2584 Glib::Threads::RWLock::ReaderLock lm (lock);
2585 return tempo_section_at_locked (frame);
2589 TempoMap::tempo_section_at_locked (framepos_t frame) const
2591 Metrics::const_iterator i;
2592 TempoSection* prev = 0;
2594 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2597 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2601 if (t->frame() > frame) {
2611 abort(); /*NOTREACHED*/
2618 /* don't use this to calculate length (the tempo is only correct for this frame).
2619 do that stuff based on the beat_at_frame and frame_at_beat api
2622 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2624 Glib::Threads::RWLock::ReaderLock lm (lock);
2626 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2627 const TempoSection* ts_after = 0;
2628 Metrics::const_iterator i;
2630 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2633 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2637 if ((*i)->frame() > frame) {
2645 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2647 /* must be treated as constant tempo */
2648 return ts_at->frames_per_beat (_frame_rate);
2652 TempoMap::tempo_at (const framepos_t& frame) const
2654 Glib::Threads::RWLock::ReaderLock lm (lock);
2655 return tempo_at_locked (frame);
2659 TempoMap::tempo_at_locked (const framepos_t& frame) const
2661 TempoSection* prev_ts = 0;
2663 Metrics::const_iterator i;
2665 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2667 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2671 if ((prev_ts) && t->frame() > frame) {
2672 /* t is the section past frame */
2673 const double ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2674 const Tempo ret_tempo (ret, prev_ts->note_type());
2681 const double ret = prev_ts->beats_per_minute();
2682 const Tempo ret_tempo (ret, prev_ts->note_type ());
2688 TempoMap::meter_section_at (framepos_t frame) const
2690 Glib::Threads::RWLock::ReaderLock lm (lock);
2691 return meter_section_at_locked (frame);
2695 TempoMap::meter_section_at_locked (framepos_t frame) const
2697 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2698 Metrics::const_iterator i;
2699 MeterSection* prev = 0;
2701 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2704 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2706 if (prev && (*i)->frame() > frame) {
2716 abort(); /*NOTREACHED*/
2723 TempoMap::meter_at (framepos_t frame) const
2725 TempoMetric m (metric_at (frame));
2730 TempoMap::meter_section_at (const double& beat) const
2732 MeterSection* prev_ms = 0;
2733 Glib::Threads::RWLock::ReaderLock lm (lock);
2735 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2737 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2738 if (prev_ms && m->beat() > beat) {
2749 TempoMap::get_state ()
2751 Metrics::const_iterator i;
2752 XMLNode *root = new XMLNode ("TempoMap");
2755 Glib::Threads::RWLock::ReaderLock lm (lock);
2756 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2757 root->add_child_nocopy ((*i)->get_state());
2765 TempoMap::set_state (const XMLNode& node, int /*version*/)
2768 Glib::Threads::RWLock::WriterLock lm (lock);
2771 XMLNodeConstIterator niter;
2772 Metrics old_metrics (_metrics);
2775 nlist = node.children();
2777 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2778 XMLNode* child = *niter;
2780 if (child->name() == TempoSection::xml_state_node_name) {
2783 TempoSection* ts = new TempoSection (*child);
2784 _metrics.push_back (ts);
2787 catch (failed_constructor& err){
2788 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2789 _metrics = old_metrics;
2793 } else if (child->name() == MeterSection::xml_state_node_name) {
2796 MeterSection* ms = new MeterSection (*child);
2797 _metrics.push_back (ms);
2800 catch (failed_constructor& err) {
2801 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2802 _metrics = old_metrics;
2808 if (niter == nlist.end()) {
2809 MetricSectionSorter cmp;
2810 _metrics.sort (cmp);
2812 /* check for legacy sessions where bbt was the base musical unit for tempo */
2813 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2816 MeterSection* prev_ms = 0;
2817 TempoSection* prev_ts = 0;
2818 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2819 if (prev_ms && prev_ms->pulse() < 0.0) {
2820 /*XX we cannot possibly make this work??. */
2821 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());
2822 prev_ms->set_beat (start);
2823 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);
2824 prev_ms->set_pulse (start_pulse);
2827 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2831 if (prev_ts && prev_ts->pulse() < 0.0) {
2832 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);
2833 prev_ts->set_pulse (start);
2838 /* check for multiple tempo/meters at the same location, which
2839 ardour2 somehow allowed.
2842 Metrics::iterator prev = _metrics.end();
2843 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2844 if (prev != _metrics.end()) {
2846 MeterSection* prev_ms;
2848 TempoSection* prev_ts;
2849 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2850 if (prev_ms->pulse() == ms->pulse()) {
2851 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2852 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2855 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2856 if (prev_ts->pulse() == ts->pulse()) {
2857 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2858 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2866 recompute_map (_metrics);
2869 PropertyChanged (PropertyChange ());
2875 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2877 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2878 const MeterSection* m;
2879 const TempoSection* t;
2880 const TempoSection* prev_ts = 0;
2882 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2884 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2885 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2886 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2887 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2889 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2890 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;
2893 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2894 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2895 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2898 o << "------" << std::endl;
2902 TempoMap::n_tempos() const
2904 Glib::Threads::RWLock::ReaderLock lm (lock);
2907 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2908 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2917 TempoMap::n_meters() const
2919 Glib::Threads::RWLock::ReaderLock lm (lock);
2922 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2923 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2932 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2935 Glib::Threads::RWLock::WriterLock lm (lock);
2936 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2937 if ((*i)->frame() >= where && (*i)->movable ()) {
2938 (*i)->set_frame ((*i)->frame() + amount);
2942 /* now reset the BBT time of all metrics, based on their new
2943 * audio time. This is the only place where we do this reverse
2947 Metrics::iterator i;
2948 const MeterSection* meter;
2949 const TempoSection* tempo;
2953 meter = &first_meter ();
2954 tempo = &first_tempo ();
2959 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2962 MetricSection* prev = 0;
2964 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2967 //TempoMetric metric (*meter, *tempo);
2968 MeterSection* ms = const_cast<MeterSection*>(meter);
2969 TempoSection* ts = const_cast<TempoSection*>(tempo);
2972 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2976 ts->set_pulse (t->pulse());
2978 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2979 ts->set_pulse (m->pulse());
2981 ts->set_frame (prev->frame());
2985 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2986 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2987 ms->set_beat (start);
2988 ms->set_pulse (m->pulse());
2990 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2994 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
2995 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
2996 ms->set_beat (start);
2997 ms->set_pulse (t->pulse());
2999 ms->set_frame (prev->frame());
3003 // metric will be at frames=0 bbt=1|1|0 by default
3004 // which is correct for our purpose
3007 // cerr << bbt << endl;
3009 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3013 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3015 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3016 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3017 bbt_time (m->frame(), bbt);
3019 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3025 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3026 /* round up to next beat */
3032 if (bbt.beats != 1) {
3033 /* round up to next bar */
3038 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3039 m->set_beat (start);
3040 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3042 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3044 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3045 abort(); /*NOTREACHED*/
3051 recompute_map (_metrics);
3055 PropertyChanged (PropertyChange ());
3058 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3062 std::list<MetricSection*> metric_kill_list;
3064 TempoSection* last_tempo = NULL;
3065 MeterSection* last_meter = NULL;
3066 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3067 bool meter_after = false; // is there a meter marker likewise?
3069 Glib::Threads::RWLock::WriterLock lm (lock);
3070 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3071 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3072 metric_kill_list.push_back(*i);
3073 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3076 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3080 else if ((*i)->frame() >= where) {
3081 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3082 (*i)->set_frame ((*i)->frame() - amount);
3083 if ((*i)->frame() == where) {
3084 // marker was immediately after end of range
3085 tempo_after = dynamic_cast<TempoSection*> (*i);
3086 meter_after = dynamic_cast<MeterSection*> (*i);
3092 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3093 if (last_tempo && !tempo_after) {
3094 metric_kill_list.remove(last_tempo);
3095 last_tempo->set_frame(where);
3098 if (last_meter && !meter_after) {
3099 metric_kill_list.remove(last_meter);
3100 last_meter->set_frame(where);
3104 //remove all the remaining metrics
3105 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3106 _metrics.remove(*i);
3111 recompute_map (_metrics);
3114 PropertyChanged (PropertyChange ());
3118 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3119 * pos can be -ve, if required.
3122 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3124 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3127 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3129 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3131 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3134 /** Add the BBT interval op to pos and return the result */
3136 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3138 Glib::Threads::RWLock::ReaderLock lm (lock);
3140 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3141 pos_bbt.ticks += op.ticks;
3142 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3144 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3146 pos_bbt.beats += op.beats;
3147 /* the meter in effect will start on the bar */
3148 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3149 while (pos_bbt.beats >= divisions_per_bar + 1) {
3151 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3152 pos_bbt.beats -= divisions_per_bar;
3154 pos_bbt.bars += op.bars;
3156 return frame_time_locked (_metrics, pos_bbt);
3159 /** Count the number of beats that are equivalent to distance when going forward,
3163 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3165 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3169 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3175 operator<< (std::ostream& o, const Meter& m) {
3176 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3180 operator<< (std::ostream& o, const Tempo& t) {
3181 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3185 operator<< (std::ostream& o, const MetricSection& section) {
3187 o << "MetricSection @ " << section.frame() << ' ';
3189 const TempoSection* ts;
3190 const MeterSection* ms;
3192 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3193 o << *((const Tempo*) ts);
3194 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3195 o << *((const Meter*) ms);