2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
77 , Tempo (TempoMap::default_tempo())
81 const XMLProperty *prop;
87 if ((prop = node.property ("start")) != 0) {
88 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
92 /* legacy session - start used to be in bbt*/
98 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
104 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
109 if ((prop = node.property ("frame")) != 0) {
110 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
111 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
117 if ((prop = node.property ("beats-per-minute")) == 0) {
118 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
119 throw failed_constructor();
122 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
123 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
124 throw failed_constructor();
127 if ((prop = node.property ("note-type")) == 0) {
128 /* older session, make note type be quarter by default */
131 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
132 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
133 throw failed_constructor();
137 if ((prop = node.property ("movable")) == 0) {
138 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
139 throw failed_constructor();
142 set_movable (string_is_affirmative (prop->value()));
144 if ((prop = node.property ("active")) == 0) {
145 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
148 set_active (string_is_affirmative (prop->value()));
151 if ((prop = node.property ("tempo-type")) == 0) {
154 _type = Type (string_2_enum (prop->value(), _type));
157 if ((prop = node.property ("lock-style")) == 0) {
158 set_position_lock_style (MusicTime);
160 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
165 TempoSection::get_state() const
167 XMLNode *root = new XMLNode (xml_state_node_name);
171 snprintf (buf, sizeof (buf), "%f", pulse());
172 root->add_property ("pulse", buf);
173 snprintf (buf, sizeof (buf), "%li", frame());
174 root->add_property ("frame", buf);
175 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
176 root->add_property ("beats-per-minute", buf);
177 snprintf (buf, sizeof (buf), "%f", _note_type);
178 root->add_property ("note-type", buf);
179 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
180 root->add_property ("movable", buf);
181 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
182 root->add_property ("active", buf);
183 root->add_property ("tempo-type", enum_2_string (_type));
184 root->add_property ("lock-style", enum_2_string (position_lock_style()));
190 TempoSection::set_type (Type type)
195 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
198 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
201 if (_type == Constant) {
202 return pulses_per_minute();
205 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
208 /** returns the zero-based frame (relative to session)
209 where the tempo in whole pulses per minute occurs in this section.
210 beat b is only used for constant tempos.
211 note that the tempo map may have multiple such values.
214 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
216 if (_type == Constant) {
217 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
220 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
222 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
225 TempoSection::tempo_at_pulse (const double& p) const
228 if (_type == Constant) {
229 return pulses_per_minute();
231 double const ppm = pulse_tempo_at_pulse (p - pulse());
235 /** returns the zero-based beat (relative to session)
236 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
237 note that the session tempo map may have multiple beats at a given tempo.
240 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
242 if (_type == Constant) {
243 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
246 return pulse_at_pulse_tempo (ppm) + pulse();
249 /** returns the zero-based pulse (relative to session origin)
250 where the zero-based frame (relative to session)
254 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
256 if (_type == Constant) {
257 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
260 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
263 /** returns the zero-based frame (relative to session start frame)
264 where the zero-based pulse (relative to session start)
269 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
271 if (_type == Constant) {
272 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
275 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
283 Tt----|-----------------*|
284 Ta----|--------------|* |
290 _______________|___|____
291 time a t (next tempo)
294 Duration in beats at time a is the integral of some Tempo function.
295 In our case, the Tempo function (Tempo at time t) is
298 with function constant
303 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
304 b(t) = T0(e^(ct) - 1) / c
306 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
307 t(b) = log((cb / T0) + 1) / c
309 The time t at which Tempo T occurs is a as above:
310 t(T) = log(T / T0) / c
312 The beat at which a Tempo T occurs is:
315 The Tempo at which beat b occurs is:
318 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
319 Our problem is that we usually don't know t.
320 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
321 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
322 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
324 By substituting our expanded t as a in the c function above, our problem is reduced to:
325 c = T0 (e^(log (Ta / T0)) - 1) / b
327 We can now store c for future time calculations.
328 If the following tempo section (the one that defines c in conjunction with this one)
329 is changed or moved, c is no longer valid.
331 The public methods are session-relative.
333 Most of this stuff is taken from this paper:
336 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
339 Zurich University of Arts
340 Institute for Computer Music and Sound Technology
342 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
347 compute this ramp's function constant using the end tempo (in whole pulses per minute)
348 and duration (pulses into global start) of some later tempo section.
351 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
353 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
354 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
357 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
359 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
361 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
365 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
367 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
371 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
373 return (frame / (double) frame_rate) / 60.0;
376 /* position function */
378 TempoSection::a_func (double end_bpm, double c_func) const
380 return log (end_bpm / pulses_per_minute()) / c_func;
383 /*function constant*/
385 TempoSection::c_func (double end_bpm, double end_time) const
387 return log (end_bpm / pulses_per_minute()) / end_time;
390 /* tempo in ppm at time in minutes */
392 TempoSection::pulse_tempo_at_time (const double& time) const
394 return exp (_c_func * time) * pulses_per_minute();
397 /* time in minutes at tempo in ppm */
399 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
401 return log (pulse_tempo / pulses_per_minute()) / _c_func;
404 /* tick at tempo in ppm */
406 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
408 return (pulse_tempo - pulses_per_minute()) / _c_func;
411 /* tempo in ppm at tick */
413 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
415 return (pulse * _c_func) + pulses_per_minute();
418 /* pulse at time in minutes */
420 TempoSection::pulse_at_time (const double& time) const
422 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
425 /* time in minutes at pulse */
427 TempoSection::time_at_pulse (const double& pulse) const
429 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
432 /***********************************************************************/
434 const string MeterSection::xml_state_node_name = "Meter";
436 MeterSection::MeterSection (const XMLNode& node)
437 : MetricSection (0.0), Meter (TempoMap::default_meter())
439 XMLProperty const * prop;
442 const XMLProperty *prop;
446 framepos_t frame = 0;
447 pair<double, BBT_Time> start;
449 if ((prop = node.property ("start")) != 0) {
450 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
454 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
456 /* legacy session - start used to be in bbt*/
460 error << _("MeterSection XML node has no \"start\" property") << endmsg;
463 if ((prop = node.property ("pulse")) != 0) {
464 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
465 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
470 if ((prop = node.property ("beat")) != 0) {
471 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
472 error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
478 if ((prop = node.property ("bbt")) == 0) {
479 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
480 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
484 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
485 //throw failed_constructor();
491 if ((prop = node.property ("frame")) != 0) {
492 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
493 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
499 /* beats-per-bar is old; divisions-per-bar is new */
501 if ((prop = node.property ("divisions-per-bar")) == 0) {
502 if ((prop = node.property ("beats-per-bar")) == 0) {
503 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
504 throw failed_constructor();
507 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
508 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
509 throw failed_constructor();
512 if ((prop = node.property ("note-type")) == 0) {
513 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
514 throw failed_constructor();
516 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
517 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
518 throw failed_constructor();
521 if ((prop = node.property ("lock-style")) == 0) {
522 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
523 set_position_lock_style (MusicTime);
525 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
528 if ((prop = node.property ("movable")) == 0) {
529 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
530 throw failed_constructor();
533 set_movable (string_is_affirmative (prop->value()));
537 MeterSection::get_state() const
539 XMLNode *root = new XMLNode (xml_state_node_name);
543 snprintf (buf, sizeof (buf), "%lf", pulse());
544 root->add_property ("pulse", buf);
545 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
549 root->add_property ("bbt", buf);
550 snprintf (buf, sizeof (buf), "%lf", beat());
551 root->add_property ("beat", buf);
552 snprintf (buf, sizeof (buf), "%f", _note_type);
553 root->add_property ("note-type", buf);
554 snprintf (buf, sizeof (buf), "%li", frame());
555 root->add_property ("frame", buf);
556 root->add_property ("lock-style", enum_2_string (position_lock_style()));
557 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
558 root->add_property ("divisions-per-bar", buf);
559 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
560 root->add_property ("movable", buf);
565 /***********************************************************************/
569 Tempo can be thought of as a source of the musical pulse.
570 Meters divide that pulse into measures and beats.
571 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
572 at any particular time.
573 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
574 It should rather be thought of as tempo note divisions per minute.
576 TempoSections, which are nice to think of in whole pulses per minute,
577 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
578 and beats (via note_divisor) are used to form a tempo map.
579 TempoSections and MeterSections may be locked to either audio or music (position lock style).
580 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
581 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
583 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
585 The first tempo and first meter are special. they must move together, and must be locked to audio.
586 Audio locked tempos which lie before the first meter are made inactive.
587 They will be re-activated if the first meter is again placed before them.
589 Both tempos and meters have a pulse position and a frame position.
590 Meters also have a beat position, which is always 0.0 for the first meter.
592 A tempo locked to music is locked to musical pulses.
593 A meter locked to music is locked to beats.
595 Recomputing the tempo map is the process where the 'missing' position
596 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
598 It is important to keep the _metrics in an order that makes sense.
599 Because ramped MusicTime and AudioTime tempos can interact with each other,
600 reordering is frequent. Care must be taken to keep _metrics in a solved state.
601 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
603 struct MetricSectionSorter {
604 bool operator() (const MetricSection* a, const MetricSection* b) {
605 return a->pulse() < b->pulse();
609 struct MetricSectionFrameSorter {
610 bool operator() (const MetricSection* a, const MetricSection* b) {
611 return a->frame() < b->frame();
615 TempoMap::TempoMap (framecnt_t fr)
618 BBT_Time start (1, 1, 0);
620 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
621 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
623 t->set_movable (false);
624 m->set_movable (false);
626 /* note: frame time is correct (zero) for both of these */
628 _metrics.push_back (t);
629 _metrics.push_back (m);
633 TempoMap::~TempoMap ()
638 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
640 bool removed = false;
643 Glib::Threads::RWLock::WriterLock lm (lock);
644 if ((removed = remove_tempo_locked (tempo))) {
645 if (complete_operation) {
646 recompute_map (_metrics);
651 if (removed && complete_operation) {
652 PropertyChanged (PropertyChange ());
657 TempoMap::remove_tempo_locked (const TempoSection& tempo)
661 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
662 if (dynamic_cast<TempoSection*> (*i) != 0) {
663 if (tempo.frame() == (*i)->frame()) {
664 if ((*i)->movable()) {
676 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
678 bool removed = false;
681 Glib::Threads::RWLock::WriterLock lm (lock);
682 if ((removed = remove_meter_locked (tempo))) {
683 if (complete_operation) {
684 recompute_map (_metrics);
689 if (removed && complete_operation) {
690 PropertyChanged (PropertyChange ());
695 TempoMap::remove_meter_locked (const MeterSection& tempo)
699 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
700 if (dynamic_cast<MeterSection*> (*i) != 0) {
701 if (tempo.frame() == (*i)->frame()) {
702 if ((*i)->movable()) {
714 TempoMap::do_insert (MetricSection* section)
716 bool need_add = true;
717 /* we only allow new meters to be inserted on beat 1 of an existing
721 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
722 //assert (m->bbt().ticks == 0);
724 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
726 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
727 corrected.second.beats = 1;
728 corrected.second.ticks = 0;
729 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
730 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
731 m->bbt(), corrected.second) << endmsg;
732 //m->set_pulse (corrected);
736 /* Look for any existing MetricSection that is of the same type and
737 in the same bar as the new one, and remove it before adding
738 the new one. Note that this means that if we find a matching,
739 existing section, we can break out of the loop since we're
740 guaranteed that there is only one such match.
743 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
745 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
746 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
747 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
748 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
750 if (tempo && insert_tempo) {
753 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
754 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
756 if (!tempo->movable()) {
758 /* can't (re)move this section, so overwrite
759 * its data content (but not its properties as
763 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
764 (*i)->set_position_lock_style (AudioTime);
766 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
767 t->set_type (insert_tempo->type());
776 } else if (meter && insert_meter) {
780 bool const ipm = insert_meter->position_lock_style() == MusicTime;
782 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
784 if (!meter->movable()) {
786 /* can't (re)move this section, so overwrite
787 * its data content (but not its properties as
791 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
792 (*i)->set_position_lock_style (insert_meter->position_lock_style());
801 /* non-matching types, so we don't care */
805 /* Add the given MetricSection, if we didn't just reset an existing
810 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
811 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
814 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
815 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
818 bool const ipm = insert_meter->position_lock_style() == MusicTime;
819 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
824 } else if (insert_tempo) {
825 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
826 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
829 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
830 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
837 _metrics.insert (i, section);
838 //dump (_metrics, std::cerr);
843 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
846 Glib::Threads::RWLock::WriterLock lm (lock);
847 TempoSection& first (first_tempo());
848 if (ts.pulse() != first.pulse()) {
849 remove_tempo_locked (ts);
850 add_tempo_locked (tempo, pulse, true, type);
852 first.set_type (type);
854 /* cannot move the first tempo section */
855 *static_cast<Tempo*>(&first) = tempo;
856 recompute_map (_metrics);
861 PropertyChanged (PropertyChange ());
865 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
868 Glib::Threads::RWLock::WriterLock lm (lock);
869 TempoSection& first (first_tempo());
870 if (ts.frame() != first.frame()) {
871 remove_tempo_locked (ts);
872 add_tempo_locked (tempo, frame, true, type);
874 first.set_type (type);
875 first.set_pulse (0.0);
876 first.set_position_lock_style (AudioTime);
878 /* cannot move the first tempo section */
879 *static_cast<Tempo*>(&first) = tempo;
880 recompute_map (_metrics);
885 PropertyChanged (PropertyChange ());
889 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
892 Glib::Threads::RWLock::WriterLock lm (lock);
893 add_tempo_locked (tempo, pulse, true, type);
896 PropertyChanged (PropertyChange ());
900 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
903 Glib::Threads::RWLock::WriterLock lm (lock);
904 add_tempo_locked (tempo, frame, true, type);
908 PropertyChanged (PropertyChange ());
912 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
914 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
919 solve_map (_metrics, ts, ts->pulse());
924 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
926 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
931 solve_map (_metrics, ts, ts->frame());
936 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
939 Glib::Threads::RWLock::WriterLock lm (lock);
942 remove_meter_locked (ms);
943 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
945 MeterSection& first (first_meter());
946 const PositionLockStyle pl = ms.position_lock_style();
947 /* cannot move the first meter section */
948 *static_cast<Meter*>(&first) = meter;
949 first.set_position_lock_style (pl);
951 recompute_map (_metrics);
954 PropertyChanged (PropertyChange ());
958 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
961 Glib::Threads::RWLock::WriterLock lm (lock);
963 const double beat = ms.beat();
964 const BBT_Time bbt = ms.bbt();
967 remove_meter_locked (ms);
968 add_meter_locked (meter, frame, beat, bbt, true);
970 MeterSection& first (first_meter());
971 TempoSection& first_t (first_tempo());
972 /* cannot move the first meter section */
973 *static_cast<Meter*>(&first) = meter;
974 first.set_position_lock_style (AudioTime);
975 first.set_pulse (0.0);
976 first.set_frame (frame);
977 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
978 first.set_beat (beat);
979 first_t.set_frame (first.frame());
980 first_t.set_pulse (0.0);
981 first_t.set_position_lock_style (AudioTime);
983 recompute_map (_metrics);
985 PropertyChanged (PropertyChange ());
990 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
994 Glib::Threads::RWLock::WriterLock lm (lock);
995 m = add_meter_locked (meter, beat, where, true);
1000 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1001 dump (_metrics, std::cerr);
1005 PropertyChanged (PropertyChange ());
1011 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1013 MeterSection* m = 0;
1015 Glib::Threads::RWLock::WriterLock lm (lock);
1016 m = add_meter_locked (meter, frame, beat, where, true);
1021 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1022 dump (_metrics, std::cerr);
1026 PropertyChanged (PropertyChange ());
1032 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1034 /* a new meter always starts a new bar on the first beat. so
1035 round the start time appropriately. remember that
1036 `where' is based on the existing tempo map, not
1037 the result after we insert the new meter.
1041 if (where.beats != 1) {
1045 /* new meters *always* start on a beat. */
1047 const double pulse = pulse_at_beat_locked (_metrics, beat);
1048 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1049 do_insert (new_meter);
1052 solve_map (_metrics, new_meter, pulse);
1059 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1062 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1064 double pulse = pulse_at_frame_locked (_metrics, frame);
1065 new_meter->set_pulse (pulse);
1067 do_insert (new_meter);
1070 solve_map (_metrics, new_meter, frame);
1077 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1079 Tempo newtempo (beats_per_minute, note_type);
1082 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1083 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1088 Glib::Threads::RWLock::WriterLock lm (lock);
1089 *((Tempo*) t) = newtempo;
1090 recompute_map (_metrics);
1092 PropertyChanged (PropertyChange ());
1099 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1101 Tempo newtempo (beats_per_minute, note_type);
1104 TempoSection* first;
1105 Metrics::iterator i;
1107 /* find the TempoSection immediately preceding "where"
1110 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1112 if ((*i)->frame() > where) {
1118 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1131 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1141 Glib::Threads::RWLock::WriterLock lm (lock);
1142 /* cannot move the first tempo section */
1143 *((Tempo*)prev) = newtempo;
1144 recompute_map (_metrics);
1147 PropertyChanged (PropertyChange ());
1151 TempoMap::first_meter () const
1153 const MeterSection *m = 0;
1155 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1156 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1161 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1162 abort(); /*NOTREACHED*/
1167 TempoMap::first_meter ()
1169 MeterSection *m = 0;
1171 /* CALLER MUST HOLD LOCK */
1173 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1174 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1179 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1180 abort(); /*NOTREACHED*/
1185 TempoMap::first_tempo () const
1187 const TempoSection *t = 0;
1189 /* CALLER MUST HOLD LOCK */
1191 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1192 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1196 if (!t->movable()) {
1202 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1203 abort(); /*NOTREACHED*/
1208 TempoMap::first_tempo ()
1210 TempoSection *t = 0;
1212 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1213 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1217 if (!t->movable()) {
1223 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1224 abort(); /*NOTREACHED*/
1228 TempoMap::recompute_tempos (Metrics& metrics)
1230 TempoSection* prev_t = 0;
1232 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1235 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1240 if (t->position_lock_style() == AudioTime) {
1241 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1242 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1245 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1246 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1253 prev_t->set_c_func (0.0);
1256 /* tempos must be positioned correctly */
1258 TempoMap::recompute_meters (Metrics& metrics)
1260 MeterSection* meter = 0;
1261 MeterSection* prev_m = 0;
1263 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1264 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1265 if (meter->position_lock_style() == AudioTime) {
1267 pair<double, BBT_Time> b_bbt;
1268 if (meter->movable()) {
1269 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor());
1270 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1271 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1272 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1273 const double pulse_off = true_pulse - (beats / prev_m->note_divisor());
1274 pulse = true_pulse - pulse_off;
1276 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1278 meter->set_beat (b_bbt);
1279 meter->set_pulse (pulse);
1283 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1285 /* shouldn't happen - the first is audio-locked */
1286 pulse = pulse_at_beat_locked (metrics, meter->beat());
1288 pair<double, BBT_Time> new_beat (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1289 meter->set_beat (new_beat);
1290 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1291 meter->set_pulse (pulse);
1297 //dump (_metrics, std::cerr;
1301 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1303 /* CALLER MUST HOLD WRITE LOCK */
1307 /* we will actually stop once we hit
1314 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1317 /* silly call from Session::process() during startup
1322 recompute_tempos (metrics);
1323 recompute_meters (metrics);
1327 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1329 Glib::Threads::RWLock::ReaderLock lm (lock);
1330 TempoMetric m (first_meter(), first_tempo());
1332 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1333 at something, because we insert the default tempo and meter during
1334 TempoMap construction.
1336 now see if we can find better candidates.
1339 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1341 if ((*i)->frame() > frame) {
1355 /* XX meters only */
1357 TempoMap::metric_at (BBT_Time bbt) const
1359 Glib::Threads::RWLock::ReaderLock lm (lock);
1360 TempoMetric m (first_meter(), first_tempo());
1362 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1363 at something, because we insert the default tempo and meter during
1364 TempoMap construction.
1366 now see if we can find better candidates.
1369 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1371 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1372 BBT_Time section_start (mw->bbt());
1374 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1386 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1388 /* HOLD (at least) THE READER LOCK */
1389 TempoSection* prev_t = 0;
1391 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1393 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1397 if (prev_t && t->frame() > frame) {
1398 /*the previous ts is the one containing the frame */
1399 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1406 /* treated as constant for this ts */
1407 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1409 return pulses_in_section + prev_t->pulse();
1413 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1415 MeterSection* prev_m = 0;
1417 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1419 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1420 if (prev_m && m->beat() > beat) {
1427 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1432 TempoMap::pulse_at_beat (const double& beat) const
1434 Glib::Threads::RWLock::ReaderLock lm (lock);
1435 return pulse_at_beat_locked (_metrics, beat);
1439 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1441 MeterSection* prev_m = 0;
1443 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1445 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1446 if (prev_m && m->pulse() > pulse) {
1453 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1455 return beats_in_section + prev_m->beat();
1459 TempoMap::beat_at_pulse (const double& pulse) const
1461 Glib::Threads::RWLock::ReaderLock lm (lock);
1462 return beat_at_pulse_locked (_metrics, pulse);
1466 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1468 /* HOLD THE READER LOCK */
1470 const TempoSection* prev_t = 0;
1472 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1475 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1479 if (prev_t && t->pulse() > pulse) {
1480 return prev_t->frame_at_pulse (pulse, _frame_rate);
1486 /* must be treated as constant, irrespective of _type */
1487 double const pulses_in_section = pulse - prev_t->pulse();
1488 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1490 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1496 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1498 const double pulse = pulse_at_frame_locked (metrics, frame);
1499 return beat_at_pulse_locked (metrics, pulse);
1503 TempoMap::beat_at_frame (const framecnt_t& frame) const
1505 Glib::Threads::RWLock::ReaderLock lm (lock);
1506 return beat_at_frame_locked (_metrics, frame);
1510 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1512 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1517 TempoMap::frame_at_beat (const double& beat) const
1519 Glib::Threads::RWLock::ReaderLock lm (lock);
1520 return frame_at_beat_locked (_metrics, beat);
1524 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1526 /* CALLER HOLDS READ LOCK */
1528 MeterSection* prev_m = 0;
1530 /* because audio-locked meters have 'fake' integral beats,
1531 there is no pulse offset here.
1533 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1535 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1537 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1538 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1546 const double remaining_bars = (bbt.bars - 1) - (prev_m->bbt().bars - 1);
1547 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1548 const double ret = remaining_bars_in_beats + prev_m->beat() + (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_m = 0;
1565 const double beats = (b < 0.0) ? 0.0 : b;
1567 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1568 MeterSection* m = 0;
1570 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1572 if (m->beat() > beats) {
1573 /* this is the meter after the one our beat is on*/
1582 const double beats_in_ms = beats - prev_m->beat();
1583 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1584 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1585 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1586 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1590 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1591 ret.beats = (uint32_t) floor (remaining_beats);
1592 ret.bars = total_bars;
1594 /* 0 0 0 to 1 1 0 - based mapping*/
1598 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1600 ret.ticks -= BBT_Time::ticks_per_beat;
1603 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1612 TempoMap::beats_to_bbt (const double& beats)
1614 Glib::Threads::RWLock::ReaderLock lm (lock);
1615 return beats_to_bbt_locked (_metrics, beats);
1619 TempoMap::pulse_to_bbt (const double& pulse)
1621 Glib::Threads::RWLock::ReaderLock lm (lock);
1622 MeterSection* prev_m = 0;
1624 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1625 MeterSection* m = 0;
1627 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1630 double const pulses_to_m = m->pulse() - prev_m->pulse();
1631 if (prev_m->pulse() + pulses_to_m > pulse) {
1632 /* this is the meter after the one our beat is on*/
1641 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1642 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1643 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1644 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1645 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1649 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1650 ret.beats = (uint32_t) floor (remaining_beats);
1651 ret.bars = total_bars;
1653 /* 0 0 0 to 1 1 0 mapping*/
1657 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1659 ret.ticks -= BBT_Time::ticks_per_beat;
1662 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1671 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1678 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1681 Glib::Threads::RWLock::ReaderLock lm (lock);
1682 const double beat = beat_at_frame_locked (_metrics, frame);
1684 bbt = beats_to_bbt_locked (_metrics, beat);
1688 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1690 /* HOLD THE READER LOCK */
1692 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1697 TempoMap::frame_time (const BBT_Time& bbt)
1700 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1704 if (bbt.beats < 1) {
1705 throw std::logic_error ("beats are counted from one");
1707 Glib::Threads::RWLock::ReaderLock lm (lock);
1709 return frame_time_locked (_metrics, bbt);
1713 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1715 TempoSection* prev_t = 0;
1717 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1719 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1724 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1728 if (t->frame() == prev_t->frame()) {
1732 /* precision check ensures pulses and frames align independent of lock style.*/
1733 if (by_frame && t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1745 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1747 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1749 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1750 if (!t->movable()) {
1751 t->set_active (true);
1754 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1755 t->set_active (false);
1757 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1758 t->set_active (true);
1759 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1768 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1770 TempoSection* prev_t = 0;
1771 TempoSection* section_prev = 0;
1772 framepos_t first_m_frame = 0;
1774 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1776 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1777 if (!m->movable()) {
1778 first_m_frame = m->frame();
1783 if (section->movable() && frame <= first_m_frame) {
1786 section->set_active (true);
1788 section->set_frame (frame);
1790 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1792 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1799 section_prev = prev_t;
1802 if (t->position_lock_style() == MusicTime) {
1803 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1804 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1806 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1807 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1815 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1816 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1819 if (section->position_lock_style() == MusicTime) {
1820 /* we're setting the frame */
1821 section->set_position_lock_style (AudioTime);
1822 recompute_tempos (imaginary);
1823 section->set_position_lock_style (MusicTime);
1825 recompute_tempos (imaginary);
1828 if (check_solved (imaginary, true)) {
1829 recompute_meters (imaginary);
1833 MetricSectionFrameSorter fcmp;
1834 imaginary.sort (fcmp);
1835 if (section->position_lock_style() == MusicTime) {
1836 /* we're setting the frame */
1837 section->set_position_lock_style (AudioTime);
1838 recompute_tempos (imaginary);
1839 section->set_position_lock_style (MusicTime);
1841 recompute_tempos (imaginary);
1843 if (check_solved (imaginary, true)) {
1844 recompute_meters (imaginary);
1848 MetricSectionSorter cmp;
1849 imaginary.sort (cmp);
1850 if (section->position_lock_style() == MusicTime) {
1851 /* we're setting the frame */
1852 section->set_position_lock_style (AudioTime);
1853 recompute_tempos (imaginary);
1854 section->set_position_lock_style (MusicTime);
1856 recompute_tempos (imaginary);
1858 if (check_solved (imaginary, true)) {
1859 recompute_meters (imaginary);
1862 //dump (imaginary, std::cerr);
1868 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1870 TempoSection* prev_t = 0;
1871 TempoSection* section_prev = 0;
1873 section->set_pulse (pulse);
1875 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1877 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1883 section_prev = prev_t;
1886 if (t->position_lock_style() == MusicTime) {
1887 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1888 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1890 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1891 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1898 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1899 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1902 if (section->position_lock_style() == AudioTime) {
1903 /* we're setting the pulse */
1904 section->set_position_lock_style (MusicTime);
1905 recompute_tempos (imaginary);
1906 section->set_position_lock_style (AudioTime);
1908 recompute_tempos (imaginary);
1910 if (check_solved (imaginary, false)) {
1911 recompute_meters (imaginary);
1915 MetricSectionSorter cmp;
1916 imaginary.sort (cmp);
1917 if (section->position_lock_style() == AudioTime) {
1918 /* we're setting the pulse */
1919 section->set_position_lock_style (MusicTime);
1920 recompute_tempos (imaginary);
1921 section->set_position_lock_style (AudioTime);
1923 recompute_tempos (imaginary);
1926 if (check_solved (imaginary, false)) {
1927 recompute_meters (imaginary);
1931 MetricSectionFrameSorter fcmp;
1932 imaginary.sort (fcmp);
1933 if (section->position_lock_style() == AudioTime) {
1934 /* we're setting the pulse */
1935 section->set_position_lock_style (MusicTime);
1936 recompute_tempos (imaginary);
1937 section->set_position_lock_style (AudioTime);
1939 recompute_tempos (imaginary);
1942 if (check_solved (imaginary, false)) {
1943 recompute_meters (imaginary);
1947 //dump (imaginary, std::cerr);
1953 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
1955 MeterSection* prev_m = 0;
1956 MeterSection* section_prev = 0; // section previous to section
1958 if (!section->movable()) {
1959 /* lock the first tempo to our first meter */
1960 if (!set_active_tempos (imaginary, frame)) {
1963 TempoSection* first_t = &first_tempo();
1965 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1967 new_section->set_frame (frame);
1968 new_section->set_pulse (0.0);
1969 new_section->set_active (true);
1971 if (solve_map (future_map, new_section, frame)) {
1972 first_t->set_frame (frame);
1973 first_t->set_pulse (0.0);
1974 first_t->set_active (true);
1975 solve_map (imaginary, first_t, frame);
1981 section->set_frame (frame);
1983 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1985 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1987 section_prev = prev_m;
1992 if (m->position_lock_style() == MusicTime) {
1993 pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1994 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
1995 if (pulse > section->pulse()) {
1996 /* moving 'section' will affect later meters' beat (but not bbt).*/
1997 pair<double, BBT_Time> new_beat (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
1998 m->set_beat (new_beat);
2001 pair<double, BBT_Time> b_bbt;
2003 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2004 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2005 b_bbt = make_pair (floor_beats + prev_m->beat()
2006 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2007 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2008 const double pulse_off = true_pulse - (beats / prev_m->note_divisor());
2009 pulse = true_pulse - pulse_off;
2011 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2014 m->set_beat (b_bbt);
2016 m->set_pulse (pulse);
2022 if (section_prev || !section->movable()) {
2024 here we set the beat for this frame.
2025 we set it 'incorrectly' to the next bar's first beat
2026 and use the delat to find the meter's pulse.
2029 pair<double, BBT_Time> b_bbt;
2030 if (section->movable()) {
2031 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2032 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2033 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2034 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2035 const double pulse_off = true_pulse - (beats / prev_m->note_divisor());
2036 pulse = true_pulse - pulse_off;
2038 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2040 section->set_beat (b_bbt);
2041 section->set_pulse (pulse);
2044 if (section->position_lock_style() == MusicTime) {
2045 /* we're setting the frame */
2046 section->set_position_lock_style (AudioTime);
2047 recompute_meters (imaginary);
2048 section->set_position_lock_style (MusicTime);
2050 recompute_meters (imaginary);
2052 //dump (imaginary, std::cerr);
2056 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2058 MeterSection* prev_m = 0;
2059 MeterSection* section_prev = 0;
2061 section->set_pulse (pulse);
2063 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2065 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2067 section_prev = prev_m;
2072 if (m->position_lock_style() == MusicTime) {
2073 pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2074 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2076 if (pulse > section->pulse()) {
2077 /* moving 'section' will affect later meters' beat (but not bbt).*/
2078 pair<double, BBT_Time> new_beat (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2079 m->set_beat (new_beat);
2082 pair<double, BBT_Time> b_bbt;
2084 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2085 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2086 b_bbt = make_pair (floor_beats + prev_m->beat()
2087 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2088 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2089 const double pulse_off = true_pulse - (beats / prev_m->note_divisor());
2090 pulse = true_pulse - pulse_off;
2092 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2094 m->set_beat (b_bbt);
2096 m->set_pulse (pulse);
2103 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2104 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2105 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2106 section->set_beat (b_bbt);
2107 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2110 MetricSectionSorter cmp;
2111 imaginary.sort (cmp);
2112 if (section->position_lock_style() == AudioTime) {
2113 /* we're setting the pulse */
2114 section->set_position_lock_style (MusicTime);
2115 recompute_meters (imaginary);
2116 section->set_position_lock_style (AudioTime);
2118 recompute_meters (imaginary);
2122 /** places a copy of _metrics into copy and returns a pointer
2123 * to section's equivalent.
2126 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2129 TempoSection* ret = 0;
2132 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2133 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2135 if (t->position_lock_style() == MusicTime) {
2136 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2138 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2140 ret->set_active (t->active());
2141 ret->set_movable (t->movable());
2142 copy.push_back (ret);
2145 TempoSection* cp = 0;
2146 if (t->position_lock_style() == MusicTime) {
2147 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2149 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2151 cp->set_active (t->active());
2152 cp->set_movable (t->movable());
2153 copy.push_back (cp);
2155 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2156 MeterSection* cp = 0;
2157 if (m->position_lock_style() == MusicTime) {
2158 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2159 cp->set_frame (m->frame());
2161 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2162 cp->set_pulse (m->pulse());
2164 cp->set_movable (m->movable());
2165 copy.push_back (cp);
2168 //recompute_map (copy);
2173 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2176 TempoSection* new_section = 0;
2179 Glib::Threads::RWLock::ReaderLock lm (lock);
2180 new_section = copy_metrics_and_point (copy, ts);
2183 double const beat = bbt_to_beats_locked (copy, bbt);
2184 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2186 Metrics::const_iterator d = copy.begin();
2187 while (d != copy.end()) {
2196 * 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,
2197 * taking any possible reordering as a consequence of this into account.
2198 * @param section - the section to be altered
2199 * @param bpm - the new Tempo
2200 * @param bbt - the bbt where the altered tempo will fall
2201 * @return returns - the position in frames where the new tempo section will lie.
2204 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2206 Glib::Threads::RWLock::ReaderLock lm (lock);
2209 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2211 const double beat = bbt_to_beats_locked (future_map, bbt);
2212 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2213 ret = new_section->frame();
2215 ret = frame_at_beat_locked (_metrics, beat);
2218 Metrics::const_iterator d = future_map.begin();
2219 while (d != future_map.end()) {
2227 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2229 Glib::Threads::RWLock::ReaderLock lm (lock);
2232 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2234 if (solve_map (future_map, new_section, frame)) {
2235 ret = new_section->pulse();
2237 ret = pulse_at_frame_locked (_metrics, frame);
2240 Metrics::const_iterator d = future_map.begin();
2241 while (d != future_map.end()) {
2249 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2253 Glib::Threads::RWLock::WriterLock lm (lock);
2254 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2255 if (solve_map (future_map, new_section, frame)) {
2256 solve_map (_metrics, ts, frame);
2260 Metrics::const_iterator d = future_map.begin();
2261 while (d != future_map.end()) {
2266 MetricPositionChanged (); // Emit Signal
2270 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2274 Glib::Threads::RWLock::WriterLock lm (lock);
2275 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2276 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2277 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2281 Metrics::const_iterator d = future_map.begin();
2282 while (d != future_map.end()) {
2287 MetricPositionChanged (); // Emit Signal
2291 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2294 Glib::Threads::RWLock::WriterLock lm (lock);
2295 solve_map (_metrics, ms, frame);
2298 MetricPositionChanged (); // Emit Signal
2302 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2305 Glib::Threads::RWLock::WriterLock lm (lock);
2306 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2309 MetricPositionChanged (); // Emit Signal
2313 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2316 bool can_solve = false;
2318 Glib::Threads::RWLock::WriterLock lm (lock);
2319 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2320 new_section->set_beats_per_minute (bpm.beats_per_minute());
2321 recompute_tempos (future_map);
2323 if (check_solved (future_map, true)) {
2324 ts->set_beats_per_minute (bpm.beats_per_minute());
2325 recompute_map (_metrics);
2330 Metrics::const_iterator d = future_map.begin();
2331 while (d != future_map.end()) {
2336 MetricPositionChanged (); // Emit Signal
2342 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2344 Glib::Threads::RWLock::ReaderLock lm (lock);
2346 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2347 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2348 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2349 const framecnt_t time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2350 const framecnt_t ret = time_at_bbt;
2356 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2358 return round_to_type (fr, dir, Bar);
2362 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2364 return round_to_type (fr, dir, Beat);
2368 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2370 Glib::Threads::RWLock::ReaderLock lm (lock);
2371 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2372 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2373 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2375 ticks -= beats * BBT_Time::ticks_per_beat;
2378 /* round to next (or same iff dir == RoundUpMaybe) */
2380 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2382 if (mod == 0 && dir == RoundUpMaybe) {
2383 /* right on the subdivision, which is fine, so do nothing */
2385 } else if (mod == 0) {
2386 /* right on the subdivision, so the difference is just the subdivision ticks */
2387 ticks += ticks_one_subdivisions_worth;
2390 /* not on subdivision, compute distance to next subdivision */
2392 ticks += ticks_one_subdivisions_worth - mod;
2395 if (ticks >= BBT_Time::ticks_per_beat) {
2396 ticks -= BBT_Time::ticks_per_beat;
2398 } else if (dir < 0) {
2400 /* round to previous (or same iff dir == RoundDownMaybe) */
2402 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2404 if (difference == 0 && dir == RoundDownAlways) {
2405 /* right on the subdivision, but force-rounding down,
2406 so the difference is just the subdivision ticks */
2407 difference = ticks_one_subdivisions_worth;
2410 if (ticks < difference) {
2411 ticks = BBT_Time::ticks_per_beat - ticks;
2413 ticks -= difference;
2417 /* round to nearest */
2420 /* compute the distance to the previous and next subdivision */
2422 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2424 /* closer to the next subdivision, so shift forward */
2426 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2428 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2430 if (ticks > BBT_Time::ticks_per_beat) {
2432 ticks -= BBT_Time::ticks_per_beat;
2433 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2436 } else if (rem > 0) {
2438 /* closer to previous subdivision, so shift backward */
2442 /* can't go backwards past zero, so ... */
2445 /* step back to previous beat */
2447 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2448 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2450 ticks = lrint (ticks - rem);
2451 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2454 /* on the subdivision, do nothing */
2458 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2464 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2466 if (sub_num == -1) {
2467 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2468 if ((double) when.beats > bpb / 2.0) {
2474 } else if (sub_num == 0) {
2475 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2476 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2478 while ((double) when.beats > bpb) {
2480 when.beats -= (uint32_t) floor (bpb);
2486 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2488 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2489 /* closer to the next subdivision, so shift forward */
2491 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2493 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2495 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2498 } else if (rem > 0) {
2499 /* closer to previous subdivision, so shift backward */
2501 if (rem > when.ticks) {
2502 if (when.beats == 0) {
2503 /* can't go backwards past zero, so ... */
2505 /* step back to previous beat */
2507 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2509 when.ticks = when.ticks - rem;
2515 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2517 Glib::Threads::RWLock::ReaderLock lm (lock);
2519 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2520 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2525 /* find bar previous to 'frame' */
2528 return frame_time_locked (_metrics, bbt);
2530 } else if (dir > 0) {
2531 /* find bar following 'frame' */
2535 return frame_time_locked (_metrics, bbt);
2537 /* true rounding: find nearest bar */
2538 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2541 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2543 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2545 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2556 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2557 } else if (dir > 0) {
2558 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2560 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2569 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2570 framepos_t lower, framepos_t upper)
2572 Glib::Threads::RWLock::ReaderLock lm (lock);
2573 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2574 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2575 /* although the map handles negative beats, bbt doesn't. */
2579 while (cnt <= upper_beat) {
2580 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2581 const TempoSection tempo = tempo_section_at_locked (pos);
2582 const MeterSection meter = meter_section_at_locked (pos);
2583 const BBT_Time bbt = beats_to_bbt (cnt);
2584 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
2590 TempoMap::tempo_section_at (framepos_t frame) const
2592 Glib::Threads::RWLock::ReaderLock lm (lock);
2593 return tempo_section_at_locked (frame);
2597 TempoMap::tempo_section_at_locked (framepos_t frame) const
2599 Metrics::const_iterator i;
2600 TempoSection* prev = 0;
2602 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2605 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2609 if (t->frame() > frame) {
2619 abort(); /*NOTREACHED*/
2626 /* don't use this to calculate length (the tempo is only correct for this frame).
2627 do that stuff based on the beat_at_frame and frame_at_beat api
2630 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2632 Glib::Threads::RWLock::ReaderLock lm (lock);
2634 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2635 const TempoSection* ts_after = 0;
2636 Metrics::const_iterator i;
2638 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2641 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2645 if ((*i)->frame() > frame) {
2653 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2655 /* must be treated as constant tempo */
2656 return ts_at->frames_per_beat (_frame_rate);
2660 TempoMap::tempo_at (const framepos_t& frame) const
2662 Glib::Threads::RWLock::ReaderLock lm (lock);
2663 return tempo_at_locked (frame);
2667 TempoMap::tempo_at_locked (const framepos_t& frame) const
2669 TempoSection* prev_t = 0;
2671 Metrics::const_iterator i;
2673 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2675 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2679 if ((prev_t) && t->frame() > frame) {
2680 /* t is the section past frame */
2681 const double ret = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2682 const Tempo ret_tempo (ret, prev_t->note_type());
2689 const double ret = prev_t->beats_per_minute();
2690 const Tempo ret_tempo (ret, prev_t->note_type ());
2696 TempoMap::meter_section_at (framepos_t frame) const
2698 Glib::Threads::RWLock::ReaderLock lm (lock);
2699 return meter_section_at_locked (frame);
2703 TempoMap::meter_section_at_locked (framepos_t frame) const
2705 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2706 Metrics::const_iterator i;
2707 MeterSection* prev = 0;
2709 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2712 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2714 if (prev && (*i)->frame() > frame) {
2724 abort(); /*NOTREACHED*/
2731 TempoMap::meter_at (framepos_t frame) const
2733 TempoMetric m (metric_at (frame));
2738 TempoMap::meter_section_at (const double& beat) const
2740 MeterSection* prev_m = 0;
2741 Glib::Threads::RWLock::ReaderLock lm (lock);
2743 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2745 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2746 if (prev_m && m->beat() > beat) {
2757 TempoMap::get_state ()
2759 Metrics::const_iterator i;
2760 XMLNode *root = new XMLNode ("TempoMap");
2763 Glib::Threads::RWLock::ReaderLock lm (lock);
2764 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2765 root->add_child_nocopy ((*i)->get_state());
2773 TempoMap::set_state (const XMLNode& node, int /*version*/)
2776 Glib::Threads::RWLock::WriterLock lm (lock);
2779 XMLNodeConstIterator niter;
2780 Metrics old_metrics (_metrics);
2783 nlist = node.children();
2785 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2786 XMLNode* child = *niter;
2788 if (child->name() == TempoSection::xml_state_node_name) {
2791 TempoSection* ts = new TempoSection (*child);
2792 _metrics.push_back (ts);
2795 catch (failed_constructor& err){
2796 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2797 _metrics = old_metrics;
2801 } else if (child->name() == MeterSection::xml_state_node_name) {
2804 MeterSection* ms = new MeterSection (*child);
2805 _metrics.push_back (ms);
2808 catch (failed_constructor& err) {
2809 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2810 _metrics = old_metrics;
2816 if (niter == nlist.end()) {
2817 MetricSectionSorter cmp;
2818 _metrics.sort (cmp);
2820 /* check for legacy sessions where bbt was the base musical unit for tempo */
2821 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2824 MeterSection* prev_m = 0;
2825 TempoSection* prev_t = 0;
2826 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2827 if (prev_m && prev_m->pulse() < 0.0) {
2828 /*XX we cannot possibly make this work??. */
2829 pair<double, BBT_Time> start = make_pair (((prev_m->bbt().bars - 1) * prev_m->note_divisor()) + (prev_m->bbt().beats - 1) + (prev_m->bbt().ticks / BBT_Time::ticks_per_beat), prev_m->bbt());
2830 prev_m->set_beat (start);
2831 const double start_pulse = ((prev_m->bbt().bars - 1) * prev_m->note_divisor()) + (prev_m->bbt().beats - 1) + (prev_m->bbt().ticks / BBT_Time::ticks_per_beat);
2832 prev_m->set_pulse (start_pulse);
2835 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2839 if (prev_t && prev_t->pulse() < 0.0) {
2840 double const start = ((prev_t->legacy_bbt().bars - 1) * prev_m->note_divisor()) + (prev_t->legacy_bbt().beats - 1) + (prev_t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2841 prev_t->set_pulse (start);
2846 /* check for multiple tempo/meters at the same location, which
2847 ardour2 somehow allowed.
2850 Metrics::iterator prev = _metrics.end();
2851 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2852 if (prev != _metrics.end()) {
2854 MeterSection* prev_m;
2856 TempoSection* prev_t;
2857 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2858 if (prev_m->pulse() == ms->pulse()) {
2859 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2860 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2863 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2864 if (prev_t->pulse() == ts->pulse()) {
2865 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2866 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2874 recompute_map (_metrics);
2877 PropertyChanged (PropertyChange ());
2883 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2885 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2886 const MeterSection* m;
2887 const TempoSection* t;
2888 const TempoSection* prev_t = 0;
2890 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2892 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2893 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2894 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2895 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2897 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
2898 o << "calculated : " << prev_t->tempo_at_pulse (t->pulse()) * prev_t->note_type() << " | " << prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << " | " << prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2901 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2902 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2903 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2906 o << "------" << std::endl;
2910 TempoMap::n_tempos() const
2912 Glib::Threads::RWLock::ReaderLock lm (lock);
2915 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2916 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2925 TempoMap::n_meters() const
2927 Glib::Threads::RWLock::ReaderLock lm (lock);
2930 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2931 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2940 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2943 Glib::Threads::RWLock::WriterLock lm (lock);
2944 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2945 if ((*i)->frame() >= where && (*i)->movable ()) {
2946 (*i)->set_frame ((*i)->frame() + amount);
2950 /* now reset the BBT time of all metrics, based on their new
2951 * audio time. This is the only place where we do this reverse
2955 Metrics::iterator i;
2956 const MeterSection* meter;
2957 const TempoSection* tempo;
2961 meter = &first_meter ();
2962 tempo = &first_tempo ();
2967 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2970 MetricSection* prev = 0;
2972 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2975 //TempoMetric metric (*meter, *tempo);
2976 MeterSection* ms = const_cast<MeterSection*>(meter);
2977 TempoSection* ts = const_cast<TempoSection*>(tempo);
2980 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2984 ts->set_pulse (t->pulse());
2986 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2987 ts->set_pulse (m->pulse());
2989 ts->set_frame (prev->frame());
2993 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2994 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2995 ms->set_beat (start);
2996 ms->set_pulse (m->pulse());
2998 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3002 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3003 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3004 ms->set_beat (start);
3005 ms->set_pulse (t->pulse());
3007 ms->set_frame (prev->frame());
3011 // metric will be at frames=0 bbt=1|1|0 by default
3012 // which is correct for our purpose
3015 // cerr << bbt << endl;
3017 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3021 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3023 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3024 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3025 bbt_time (m->frame(), bbt);
3027 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3033 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3034 /* round up to next beat */
3040 if (bbt.beats != 1) {
3041 /* round up to next bar */
3046 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3047 m->set_beat (start);
3048 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3050 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3052 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3053 abort(); /*NOTREACHED*/
3059 recompute_map (_metrics);
3063 PropertyChanged (PropertyChange ());
3066 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3070 std::list<MetricSection*> metric_kill_list;
3072 TempoSection* last_tempo = NULL;
3073 MeterSection* last_meter = NULL;
3074 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3075 bool meter_after = false; // is there a meter marker likewise?
3077 Glib::Threads::RWLock::WriterLock lm (lock);
3078 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3079 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3080 metric_kill_list.push_back(*i);
3081 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3084 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3088 else if ((*i)->frame() >= where) {
3089 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3090 (*i)->set_frame ((*i)->frame() - amount);
3091 if ((*i)->frame() == where) {
3092 // marker was immediately after end of range
3093 tempo_after = dynamic_cast<TempoSection*> (*i);
3094 meter_after = dynamic_cast<MeterSection*> (*i);
3100 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3101 if (last_tempo && !tempo_after) {
3102 metric_kill_list.remove(last_tempo);
3103 last_tempo->set_frame(where);
3106 if (last_meter && !meter_after) {
3107 metric_kill_list.remove(last_meter);
3108 last_meter->set_frame(where);
3112 //remove all the remaining metrics
3113 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3114 _metrics.remove(*i);
3119 recompute_map (_metrics);
3122 PropertyChanged (PropertyChange ());
3126 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3127 * pos can be -ve, if required.
3130 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3132 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3135 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3137 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3139 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3142 /** Add the BBT interval op to pos and return the result */
3144 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3146 Glib::Threads::RWLock::ReaderLock lm (lock);
3148 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3149 pos_bbt.ticks += op.ticks;
3150 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3152 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3154 pos_bbt.beats += op.beats;
3155 /* the meter in effect will start on the bar */
3156 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3157 while (pos_bbt.beats >= divisions_per_bar + 1) {
3159 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3160 pos_bbt.beats -= divisions_per_bar;
3162 pos_bbt.bars += op.bars;
3164 return frame_time_locked (_metrics, pos_bbt);
3167 /** Count the number of beats that are equivalent to distance when going forward,
3171 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3173 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3177 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3183 operator<< (std::ostream& o, const Meter& m) {
3184 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3188 operator<< (std::ostream& o, const Tempo& t) {
3189 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3193 operator<< (std::ostream& o, const MetricSection& section) {
3195 o << "MetricSection @ " << section.frame() << ' ';
3197 const TempoSection* ts;
3198 const MeterSection* ms;
3200 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3201 o << *((const Tempo*) ts);
3202 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3203 o << *((const Meter*) ms);