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 _legacy_bbt = BBT_Time (0, 0, 0);
89 if ((prop = node.property ("start")) != 0) {
90 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
94 /* legacy session - start used to be in bbt*/
97 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
101 if ((prop = node.property ("pulse")) != 0) {
102 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
103 error << _("TempoSection XML node has an illegal \"pulse\" 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) {
159 set_position_lock_style (MusicTime);
161 set_position_lock_style (AudioTime);
164 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
169 TempoSection::get_state() const
171 XMLNode *root = new XMLNode (xml_state_node_name);
175 snprintf (buf, sizeof (buf), "%f", pulse());
176 root->add_property ("pulse", buf);
177 snprintf (buf, sizeof (buf), "%li", frame());
178 root->add_property ("frame", buf);
179 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
180 root->add_property ("beats-per-minute", buf);
181 snprintf (buf, sizeof (buf), "%f", _note_type);
182 root->add_property ("note-type", buf);
183 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
184 root->add_property ("movable", buf);
185 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
186 root->add_property ("active", buf);
187 root->add_property ("tempo-type", enum_2_string (_type));
188 root->add_property ("lock-style", enum_2_string (position_lock_style()));
194 TempoSection::set_type (Type type)
199 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
202 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
205 if (_type == Constant || _c_func == 0.0) {
206 return pulses_per_minute();
209 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
212 /** returns the zero-based frame (relative to session)
213 where the tempo in whole pulses per minute occurs in this section.
214 beat b is only used for constant tempos.
215 note that the tempo map may have multiple such values.
218 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
220 if (_type == Constant || _c_func == 0.0) {
221 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
224 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
226 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
229 TempoSection::tempo_at_pulse (const double& p) const
232 if (_type == Constant || _c_func == 0.0) {
233 return pulses_per_minute();
235 double const ppm = pulse_tempo_at_pulse (p - pulse());
239 /** returns the zero-based beat (relative to session)
240 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
241 note that the session tempo map may have multiple beats at a given tempo.
244 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
246 if (_type == Constant || _c_func == 0.0) {
247 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
250 return pulse_at_pulse_tempo (ppm) + pulse();
253 /** returns the zero-based pulse (relative to session origin)
254 where the zero-based frame (relative to session)
258 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
260 if (_type == Constant || _c_func == 0.0) {
261 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
264 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
267 /** returns the zero-based frame (relative to session start frame)
268 where the zero-based pulse (relative to session start)
273 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
275 if (_type == Constant || _c_func == 0.0) {
276 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
279 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
287 Tt----|-----------------*|
288 Ta----|--------------|* |
294 _______________|___|____
295 time a t (next tempo)
298 Duration in beats at time a is the integral of some Tempo function.
299 In our case, the Tempo function (Tempo at time t) is
302 with function constant
307 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
308 b(t) = T0(e^(ct) - 1) / c
310 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:
311 t(b) = log((cb / T0) + 1) / c
313 The time t at which Tempo T occurs is a as above:
314 t(T) = log(T / T0) / c
316 The beat at which a Tempo T occurs is:
319 The Tempo at which beat b occurs is:
322 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
323 Our problem is that we usually don't know t.
324 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.
325 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
326 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
328 By substituting our expanded t as a in the c function above, our problem is reduced to:
329 c = T0 (e^(log (Ta / T0)) - 1) / b
331 Of course the word 'beat' has been left loosely defined above.
332 In music, a beat is defined by the musical pulse (which comes from the tempo)
333 and the meter in use at a particular time (how many pulse divisions there are in one bar).
334 It would be more accurate to substitute the work 'pulse' for 'beat' above.
338 We can now store c for future time calculations.
339 If the following tempo section (the one that defines c in conjunction with this one)
340 is changed or moved, c is no longer valid.
342 The public methods are session-relative.
344 Most of this stuff is taken from this paper:
347 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
350 Zurich University of Arts
351 Institute for Computer Music and Sound Technology
353 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
358 compute this ramp's function constant using the end tempo (in whole pulses per minute)
359 and duration (pulses into global start) of some later tempo section.
362 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
364 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
365 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
368 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
370 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
372 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
376 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
378 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
382 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
384 return (frame / (double) frame_rate) / 60.0;
387 /* position function */
389 TempoSection::a_func (double end_bpm, double c_func) const
391 return log (end_bpm / pulses_per_minute()) / c_func;
394 /*function constant*/
396 TempoSection::c_func (double end_bpm, double end_time) const
398 return log (end_bpm / pulses_per_minute()) / end_time;
401 /* tempo in ppm at time in minutes */
403 TempoSection::pulse_tempo_at_time (const double& time) const
405 return exp (_c_func * time) * pulses_per_minute();
408 /* time in minutes at tempo in ppm */
410 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
412 return log (pulse_tempo / pulses_per_minute()) / _c_func;
415 /* tick at tempo in ppm */
417 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
419 return (pulse_tempo - pulses_per_minute()) / _c_func;
422 /* tempo in ppm at tick */
424 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
426 return (pulse * _c_func) + pulses_per_minute();
429 /* pulse at time in minutes */
431 TempoSection::pulse_at_time (const double& time) const
433 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
436 /* time in minutes at pulse */
438 TempoSection::time_at_pulse (const double& pulse) const
440 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
443 /***********************************************************************/
445 const string MeterSection::xml_state_node_name = "Meter";
447 MeterSection::MeterSection (const XMLNode& node)
448 : MetricSection (0.0), Meter (TempoMap::default_meter())
450 XMLProperty const * prop;
453 const XMLProperty *prop;
457 framepos_t frame = 0;
458 pair<double, BBT_Time> start;
460 if ((prop = node.property ("start")) != 0) {
461 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
465 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
467 /* legacy session - start used to be in bbt*/
468 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
473 if ((prop = node.property ("pulse")) != 0) {
474 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
475 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
480 if ((prop = node.property ("beat")) != 0) {
481 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
482 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
488 if ((prop = node.property ("bbt")) == 0) {
489 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
490 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
494 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
495 throw failed_constructor();
501 if ((prop = node.property ("frame")) != 0) {
502 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
503 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
509 /* beats-per-bar is old; divisions-per-bar is new */
511 if ((prop = node.property ("divisions-per-bar")) == 0) {
512 if ((prop = node.property ("beats-per-bar")) == 0) {
513 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
514 throw failed_constructor();
517 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
518 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
519 throw failed_constructor();
522 if ((prop = node.property ("note-type")) == 0) {
523 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
524 throw failed_constructor();
526 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
527 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
528 throw failed_constructor();
531 if ((prop = node.property ("movable")) == 0) {
532 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
533 throw failed_constructor();
536 set_movable (string_is_affirmative (prop->value()));
538 if ((prop = node.property ("lock-style")) == 0) {
539 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
541 set_position_lock_style (MusicTime);
543 set_position_lock_style (AudioTime);
546 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
551 MeterSection::get_state() const
553 XMLNode *root = new XMLNode (xml_state_node_name);
557 snprintf (buf, sizeof (buf), "%lf", pulse());
558 root->add_property ("pulse", buf);
559 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
563 root->add_property ("bbt", buf);
564 snprintf (buf, sizeof (buf), "%lf", beat());
565 root->add_property ("beat", buf);
566 snprintf (buf, sizeof (buf), "%f", _note_type);
567 root->add_property ("note-type", buf);
568 snprintf (buf, sizeof (buf), "%li", frame());
569 root->add_property ("frame", buf);
570 root->add_property ("lock-style", enum_2_string (position_lock_style()));
571 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
572 root->add_property ("divisions-per-bar", buf);
573 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
574 root->add_property ("movable", buf);
579 /***********************************************************************/
583 Tempo can be thought of as a source of the musical pulse.
584 Meters divide that pulse into measures and beats.
585 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
586 at any particular time.
587 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
588 It should rather be thought of as tempo note divisions per minute.
590 TempoSections, which are nice to think of in whole pulses per minute,
591 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
592 and beats (via note_divisor) are used to form a tempo map.
593 TempoSections and MeterSections may be locked to either audio or music (position lock style).
594 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
595 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
597 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
599 The first tempo and first meter are special. they must move together, and must be locked to audio.
600 Audio locked tempos which lie before the first meter are made inactive.
601 They will be re-activated if the first meter is again placed before them.
603 Both tempos and meters have a pulse position and a frame position.
604 Meters also have a beat position, which is always 0.0 for the first meter.
606 A tempo locked to music is locked to musical pulses.
607 A meter locked to music is locked to beats.
609 Recomputing the tempo map is the process where the 'missing' position
610 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
612 It is important to keep the _metrics in an order that makes sense.
613 Because ramped MusicTime and AudioTime tempos can interact with each other,
614 reordering is frequent. Care must be taken to keep _metrics in a solved state.
615 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
617 struct MetricSectionSorter {
618 bool operator() (const MetricSection* a, const MetricSection* b) {
619 return a->pulse() < b->pulse();
623 struct MetricSectionFrameSorter {
624 bool operator() (const MetricSection* a, const MetricSection* b) {
625 return a->frame() < b->frame();
629 TempoMap::TempoMap (framecnt_t fr)
632 BBT_Time start (1, 1, 0);
634 TempoSection *t = new TempoSection ((framepos_t) 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
635 MeterSection *m = new MeterSection ((framepos_t) 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
637 t->set_movable (false);
638 m->set_movable (false);
640 /* note: frame time is correct (zero) for both of these */
642 _metrics.push_back (t);
643 _metrics.push_back (m);
647 TempoMap::~TempoMap ()
652 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
654 bool removed = false;
657 Glib::Threads::RWLock::WriterLock lm (lock);
658 if ((removed = remove_tempo_locked (tempo))) {
659 if (complete_operation) {
660 recompute_map (_metrics);
665 if (removed && complete_operation) {
666 PropertyChanged (PropertyChange ());
671 TempoMap::remove_tempo_locked (const TempoSection& tempo)
675 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
676 if (dynamic_cast<TempoSection*> (*i) != 0) {
677 if (tempo.frame() == (*i)->frame()) {
678 if ((*i)->movable()) {
690 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
692 bool removed = false;
695 Glib::Threads::RWLock::WriterLock lm (lock);
696 if ((removed = remove_meter_locked (tempo))) {
697 if (complete_operation) {
698 recompute_map (_metrics);
703 if (removed && complete_operation) {
704 PropertyChanged (PropertyChange ());
709 TempoMap::remove_meter_locked (const MeterSection& tempo)
713 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
714 if (dynamic_cast<MeterSection*> (*i) != 0) {
715 if (tempo.frame() == (*i)->frame()) {
716 if ((*i)->movable()) {
728 TempoMap::do_insert (MetricSection* section)
730 bool need_add = true;
731 /* we only allow new meters to be inserted on beat 1 of an existing
735 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
736 //assert (m->bbt().ticks == 0);
738 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
740 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
741 corrected.second.beats = 1;
742 corrected.second.ticks = 0;
743 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
744 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
745 m->bbt(), corrected.second) << endmsg;
746 //m->set_pulse (corrected);
750 /* Look for any existing MetricSection that is of the same type and
751 in the same bar as the new one, and remove it before adding
752 the new one. Note that this means that if we find a matching,
753 existing section, we can break out of the loop since we're
754 guaranteed that there is only one such match.
757 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
759 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
760 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
761 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
762 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
764 if (tempo && insert_tempo) {
767 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
768 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
770 if (!tempo->movable()) {
772 /* can't (re)move this section, so overwrite
773 * its data content (but not its properties as
777 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
778 (*i)->set_position_lock_style (AudioTime);
780 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
781 t->set_type (insert_tempo->type());
790 } else if (meter && insert_meter) {
794 bool const ipm = insert_meter->position_lock_style() == MusicTime;
796 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
798 if (!meter->movable()) {
800 /* can't (re)move this section, so overwrite
801 * its data content (but not its properties as
805 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
806 (*i)->set_position_lock_style (AudioTime);
815 /* non-matching types, so we don't care */
819 /* Add the given MetricSection, if we didn't just reset an existing
824 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
825 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
828 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
829 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
832 bool const ipm = insert_meter->position_lock_style() == MusicTime;
833 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
838 } else if (insert_tempo) {
839 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
840 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
843 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
844 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
851 _metrics.insert (i, section);
852 //dump (_metrics, std::cerr);
857 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
860 Glib::Threads::RWLock::WriterLock lm (lock);
861 TempoSection& first (first_tempo());
862 if (ts.pulse() != first.pulse()) {
863 remove_tempo_locked (ts);
864 add_tempo_locked (tempo, pulse, true, type);
866 first.set_type (type);
868 /* cannot move the first tempo section */
869 *static_cast<Tempo*>(&first) = tempo;
870 recompute_map (_metrics);
875 PropertyChanged (PropertyChange ());
879 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
882 Glib::Threads::RWLock::WriterLock lm (lock);
883 TempoSection& first (first_tempo());
884 if (ts.frame() != first.frame()) {
885 remove_tempo_locked (ts);
886 add_tempo_locked (tempo, frame, true, type);
888 first.set_type (type);
889 first.set_pulse (0.0);
890 first.set_position_lock_style (AudioTime);
892 /* cannot move the first tempo section */
893 *static_cast<Tempo*>(&first) = tempo;
894 recompute_map (_metrics);
899 PropertyChanged (PropertyChange ());
903 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
905 TempoSection* ts = 0;
907 Glib::Threads::RWLock::WriterLock lm (lock);
908 ts = add_tempo_locked (tempo, pulse, true, type);
911 PropertyChanged (PropertyChange ());
917 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
919 TempoSection* ts = 0;
921 Glib::Threads::RWLock::WriterLock lm (lock);
922 ts = add_tempo_locked (tempo, frame, true, type);
926 PropertyChanged (PropertyChange ());
932 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
934 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
939 solve_map (_metrics, t, t->pulse());
946 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
948 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
953 solve_map (_metrics, t, t->frame());
960 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
963 Glib::Threads::RWLock::WriterLock lm (lock);
966 remove_meter_locked (ms);
967 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
969 MeterSection& first (first_meter());
970 /* cannot move the first meter section */
971 *static_cast<Meter*>(&first) = meter;
972 first.set_position_lock_style (AudioTime);
974 recompute_map (_metrics);
977 PropertyChanged (PropertyChange ());
981 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
984 Glib::Threads::RWLock::WriterLock lm (lock);
986 const double beat = ms.beat();
987 const BBT_Time bbt = ms.bbt();
990 remove_meter_locked (ms);
991 add_meter_locked (meter, frame, beat, bbt, true);
993 MeterSection& first (first_meter());
994 TempoSection& first_t (first_tempo());
995 /* cannot move the first meter section */
996 *static_cast<Meter*>(&first) = meter;
997 first.set_position_lock_style (AudioTime);
998 first.set_pulse (0.0);
999 first.set_frame (frame);
1000 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1001 first.set_beat (beat);
1002 first_t.set_frame (first.frame());
1003 first_t.set_pulse (0.0);
1004 first_t.set_position_lock_style (AudioTime);
1006 recompute_map (_metrics);
1008 PropertyChanged (PropertyChange ());
1013 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1015 MeterSection* m = 0;
1017 Glib::Threads::RWLock::WriterLock lm (lock);
1018 m = add_meter_locked (meter, beat, where, true);
1023 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1024 dump (_metrics, std::cerr);
1028 PropertyChanged (PropertyChange ());
1034 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1036 MeterSection* m = 0;
1038 Glib::Threads::RWLock::WriterLock lm (lock);
1039 m = add_meter_locked (meter, frame, beat, where, true);
1044 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1045 dump (_metrics, std::cerr);
1049 PropertyChanged (PropertyChange ());
1055 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1057 /* a new meter always starts a new bar on the first beat. so
1058 round the start time appropriately. remember that
1059 `where' is based on the existing tempo map, not
1060 the result after we insert the new meter.
1064 if (where.beats != 1) {
1068 /* new meters *always* start on a beat. */
1070 const double pulse = pulse_at_beat_locked (_metrics, beat);
1071 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1072 do_insert (new_meter);
1075 solve_map (_metrics, new_meter, pulse);
1082 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1084 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1086 double pulse = pulse_at_frame_locked (_metrics, frame);
1087 new_meter->set_pulse (pulse);
1089 do_insert (new_meter);
1092 solve_map (_metrics, new_meter, frame);
1099 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1101 Tempo newtempo (beats_per_minute, note_type);
1104 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1105 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1110 Glib::Threads::RWLock::WriterLock lm (lock);
1111 *((Tempo*) t) = newtempo;
1112 recompute_map (_metrics);
1114 PropertyChanged (PropertyChange ());
1121 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1123 Tempo newtempo (beats_per_minute, note_type);
1126 TempoSection* first;
1127 Metrics::iterator i;
1129 /* find the TempoSection immediately preceding "where"
1132 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1134 if ((*i)->frame() > where) {
1140 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1153 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1163 Glib::Threads::RWLock::WriterLock lm (lock);
1164 /* cannot move the first tempo section */
1165 *((Tempo*)prev) = newtempo;
1166 recompute_map (_metrics);
1169 PropertyChanged (PropertyChange ());
1173 TempoMap::first_meter () const
1175 const MeterSection *m = 0;
1177 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1178 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1183 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1184 abort(); /*NOTREACHED*/
1189 TempoMap::first_meter ()
1191 MeterSection *m = 0;
1193 /* CALLER MUST HOLD LOCK */
1195 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1196 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1201 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1202 abort(); /*NOTREACHED*/
1207 TempoMap::first_tempo () const
1209 const TempoSection *t = 0;
1211 /* CALLER MUST HOLD LOCK */
1213 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1214 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1218 if (!t->movable()) {
1224 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1225 abort(); /*NOTREACHED*/
1230 TempoMap::first_tempo ()
1232 TempoSection *t = 0;
1234 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1235 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1239 if (!t->movable()) {
1245 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1246 abort(); /*NOTREACHED*/
1250 TempoMap::recompute_tempos (Metrics& metrics)
1252 TempoSection* prev_t = 0;
1254 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1257 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1261 if (!t->movable()) {
1267 if (t->position_lock_style() == AudioTime) {
1268 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1269 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1272 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1273 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1280 prev_t->set_c_func (0.0);
1283 /* tempos must be positioned correctly */
1285 TempoMap::recompute_meters (Metrics& metrics)
1287 MeterSection* meter = 0;
1288 MeterSection* prev_m = 0;
1290 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1291 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1292 if (meter->position_lock_style() == AudioTime) {
1294 pair<double, BBT_Time> b_bbt;
1295 if (meter->movable()) {
1296 const double beats = floor ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse())
1297 * prev_m->note_divisor() + 0.5);
1299 if (beats + prev_m->beat() < meter->beat()) {
1300 /* tempo change caused a change in beat (bar). */
1301 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1302 b_bbt = make_pair (floor_beats + prev_m->beat()
1303 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1304 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1305 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1306 pulse = true_pulse - pulse_off;
1308 b_bbt = make_pair (meter->beat(), meter->bbt());
1309 pulse = pulse_at_frame_locked (metrics, meter->frame());
1312 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1314 meter->set_beat (b_bbt);
1315 meter->set_pulse (pulse);
1318 pair<double, BBT_Time> new_beat;
1320 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1321 new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1323 /* shouldn't happen - the first is audio-locked */
1324 pulse = pulse_at_beat_locked (metrics, meter->beat());
1325 new_beat = make_pair (pulse, meter->bbt());
1328 meter->set_beat (new_beat);
1329 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1330 meter->set_pulse (pulse);
1336 //dump (_metrics, std::cerr;
1340 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1342 /* CALLER MUST HOLD WRITE LOCK */
1346 /* we will actually stop once we hit
1353 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1356 /* silly call from Session::process() during startup
1361 recompute_tempos (metrics);
1362 recompute_meters (metrics);
1366 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1368 Glib::Threads::RWLock::ReaderLock lm (lock);
1369 TempoMetric m (first_meter(), first_tempo());
1371 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1372 at something, because we insert the default tempo and meter during
1373 TempoMap construction.
1375 now see if we can find better candidates.
1378 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1380 if ((*i)->frame() > frame) {
1394 /* XX meters only */
1396 TempoMap::metric_at (BBT_Time bbt) const
1398 Glib::Threads::RWLock::ReaderLock lm (lock);
1399 TempoMetric m (first_meter(), first_tempo());
1401 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1402 at something, because we insert the default tempo and meter during
1403 TempoMap construction.
1405 now see if we can find better candidates.
1408 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1410 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1411 BBT_Time section_start (mw->bbt());
1413 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1425 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1427 MeterSection* prev_m = 0;
1429 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1431 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1432 if (prev_m && m->beat() > beat) {
1439 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1444 TempoMap::pulse_at_beat (const double& beat) const
1446 Glib::Threads::RWLock::ReaderLock lm (lock);
1447 return pulse_at_beat_locked (_metrics, beat);
1451 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1453 MeterSection* prev_m = 0;
1455 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1457 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1458 if (prev_m && m->pulse() > pulse) {
1459 if ((pulse - prev_m->pulse()) * prev_m->note_divisor() < m->beat()) {
1467 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1469 return beats_in_section + prev_m->beat();
1473 TempoMap::beat_at_pulse (const double& pulse) const
1475 Glib::Threads::RWLock::ReaderLock lm (lock);
1476 return beat_at_pulse_locked (_metrics, pulse);
1479 /* tempo section based */
1481 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1483 /* HOLD (at least) THE READER LOCK */
1484 TempoSection* prev_t = 0;
1486 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1488 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1492 if (prev_t && t->frame() > frame) {
1493 /*the previous ts is the one containing the frame */
1494 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1501 /* treated as constant for this ts */
1502 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1504 return pulses_in_section + prev_t->pulse();
1508 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1510 Glib::Threads::RWLock::ReaderLock lm (lock);
1511 return pulse_at_frame_locked (_metrics, frame);
1514 /* tempo section based */
1516 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1518 /* HOLD THE READER LOCK */
1520 const TempoSection* prev_t = 0;
1522 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1525 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1529 if (prev_t && t->pulse() > pulse) {
1530 return prev_t->frame_at_pulse (pulse, _frame_rate);
1536 /* must be treated as constant, irrespective of _type */
1537 double const pulses_in_section = pulse - prev_t->pulse();
1538 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1540 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1546 TempoMap::frame_at_pulse (const double& pulse) const
1548 Glib::Threads::RWLock::ReaderLock lm (lock);
1549 return frame_at_pulse_locked (_metrics, pulse);
1552 /* meter section based */
1554 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1556 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1557 MeterSection* prev_m = 0;
1558 MeterSection* next_m = 0;
1560 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1562 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1563 if (prev_m && m->frame() > frame) {
1570 if (frame < prev_m->frame()) {
1573 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1575 if (next_m && next_m->beat() < beat) {
1576 return next_m->beat();
1583 TempoMap::beat_at_frame (const framecnt_t& frame) const
1585 Glib::Threads::RWLock::ReaderLock lm (lock);
1586 return beat_at_frame_locked (_metrics, frame);
1589 /* meter section based */
1591 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1593 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1594 MeterSection* prev_m = 0;
1596 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1598 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1599 if (prev_m && m->beat() > beat) {
1606 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1610 TempoMap::frame_at_beat (const double& beat) const
1612 Glib::Threads::RWLock::ReaderLock lm (lock);
1613 return frame_at_beat_locked (_metrics, beat);
1617 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1619 /* CALLER HOLDS READ LOCK */
1621 MeterSection* prev_m = 0;
1623 /* because audio-locked meters have 'fake' integral beats,
1624 there is no pulse offset here.
1626 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1628 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1630 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1631 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1639 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1640 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1641 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1647 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1649 Glib::Threads::RWLock::ReaderLock lm (lock);
1650 return bbt_to_beats_locked (_metrics, bbt);
1654 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1656 /* CALLER HOLDS READ LOCK */
1657 MeterSection* prev_m = 0;
1658 const double beats = (b < 0.0) ? 0.0 : b;
1660 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1661 MeterSection* m = 0;
1663 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1665 if (m->beat() > beats) {
1666 /* this is the meter after the one our beat is on*/
1675 const double beats_in_ms = beats - prev_m->beat();
1676 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1677 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1678 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1679 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1683 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1684 ret.beats = (uint32_t) floor (remaining_beats);
1685 ret.bars = total_bars;
1687 /* 0 0 0 to 1 1 0 - based mapping*/
1691 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1693 ret.ticks -= BBT_Time::ticks_per_beat;
1696 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1705 TempoMap::beats_to_bbt (const double& beats)
1707 Glib::Threads::RWLock::ReaderLock lm (lock);
1708 return beats_to_bbt_locked (_metrics, beats);
1712 TempoMap::pulse_to_bbt (const double& pulse)
1714 Glib::Threads::RWLock::ReaderLock lm (lock);
1715 MeterSection* prev_m = 0;
1717 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1718 MeterSection* m = 0;
1720 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1723 double const pulses_to_m = m->pulse() - prev_m->pulse();
1724 if (prev_m->pulse() + pulses_to_m > pulse) {
1725 /* this is the meter after the one our beat is on*/
1734 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1735 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1736 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1737 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1738 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1742 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1743 ret.beats = (uint32_t) floor (remaining_beats);
1744 ret.bars = total_bars;
1746 /* 0 0 0 to 1 1 0 mapping*/
1750 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1752 ret.ticks -= BBT_Time::ticks_per_beat;
1755 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1764 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1771 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1774 Glib::Threads::RWLock::ReaderLock lm (lock);
1775 const double beat = beat_at_frame_locked (_metrics, frame);
1777 bbt = beats_to_bbt_locked (_metrics, beat);
1780 /* meter section based */
1782 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1784 /* HOLD THE READER LOCK */
1786 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1791 TempoMap::frame_time (const BBT_Time& bbt)
1794 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1798 if (bbt.beats < 1) {
1799 throw std::logic_error ("beats are counted from one");
1801 Glib::Threads::RWLock::ReaderLock lm (lock);
1803 return frame_time_locked (_metrics, bbt);
1807 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1809 TempoSection* prev_t = 0;
1811 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1813 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1818 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1822 if (t->frame() == prev_t->frame()) {
1826 /* precision check ensures pulses and frames align.*/
1827 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1839 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1841 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1843 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1844 if (!t->movable()) {
1845 t->set_active (true);
1848 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1849 t->set_active (false);
1851 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1852 t->set_active (true);
1853 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1862 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1864 TempoSection* prev_t = 0;
1865 TempoSection* section_prev = 0;
1866 framepos_t first_m_frame = 0;
1868 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1870 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1871 if (!m->movable()) {
1872 first_m_frame = m->frame();
1877 if (section->movable() && frame <= first_m_frame) {
1881 section->set_active (true);
1882 section->set_frame (frame);
1884 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1886 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1893 section_prev = prev_t;
1896 if (t->position_lock_style() == MusicTime) {
1897 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1898 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1900 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1901 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1909 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1910 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1913 if (section->position_lock_style() == MusicTime) {
1914 /* we're setting the frame */
1915 section->set_position_lock_style (AudioTime);
1916 recompute_tempos (imaginary);
1917 section->set_position_lock_style (MusicTime);
1919 recompute_tempos (imaginary);
1922 if (check_solved (imaginary, true)) {
1923 recompute_meters (imaginary);
1927 MetricSectionFrameSorter fcmp;
1928 imaginary.sort (fcmp);
1929 if (section->position_lock_style() == MusicTime) {
1930 /* we're setting the frame */
1931 section->set_position_lock_style (AudioTime);
1932 recompute_tempos (imaginary);
1933 section->set_position_lock_style (MusicTime);
1935 recompute_tempos (imaginary);
1938 if (check_solved (imaginary, true)) {
1939 recompute_meters (imaginary);
1943 //dump (imaginary, std::cerr);
1949 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1951 TempoSection* prev_t = 0;
1952 TempoSection* section_prev = 0;
1954 section->set_pulse (pulse);
1956 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1958 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1962 if (!t->movable()) {
1969 section_prev = prev_t;
1972 if (t->position_lock_style() == MusicTime) {
1973 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1974 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1976 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1977 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1984 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1985 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1988 if (section->position_lock_style() == AudioTime) {
1989 /* we're setting the pulse */
1990 section->set_position_lock_style (MusicTime);
1991 recompute_tempos (imaginary);
1992 section->set_position_lock_style (AudioTime);
1994 recompute_tempos (imaginary);
1997 if (check_solved (imaginary, false)) {
1998 recompute_meters (imaginary);
2002 MetricSectionSorter cmp;
2003 imaginary.sort (cmp);
2004 if (section->position_lock_style() == AudioTime) {
2005 /* we're setting the pulse */
2006 section->set_position_lock_style (MusicTime);
2007 recompute_tempos (imaginary);
2008 section->set_position_lock_style (AudioTime);
2010 recompute_tempos (imaginary);
2013 if (check_solved (imaginary, false)) {
2014 recompute_meters (imaginary);
2018 //dump (imaginary, std::cerr);
2024 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2026 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2027 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2028 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2031 MeterSection* prev_m = 0;
2033 if (!section->movable()) {
2034 /* lock the first tempo to our first meter */
2035 if (!set_active_tempos (imaginary, frame)) {
2038 TempoSection* first_t = &first_tempo();
2040 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2042 new_section->set_frame (frame);
2043 new_section->set_pulse (0.0);
2044 new_section->set_active (true);
2046 if (solve_map (future_map, new_section, frame)) {
2047 first_t->set_frame (frame);
2048 first_t->set_pulse (0.0);
2049 first_t->set_active (true);
2050 solve_map (imaginary, first_t, frame);
2056 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2058 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2060 double new_pulse = 0.0;
2061 if (prev_m && section->movable()) {
2062 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2063 if (beats + prev_m->beat() < section->beat()) {
2064 /* disallow position change if it will alter our beat
2065 we allow tempo changes to do this in recompute_meters().
2066 blocking this is an option, but i'm not convinced that
2067 this is what the user would actually want.
2068 here we set the frame/pulse corresponding to its musical position.
2070 new_pulse = ((section->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
2071 section->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2072 section->set_pulse (new_pulse);
2075 new_pulse = pulse_at_frame_locked (imaginary, frame);
2077 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2078 section->set_beat (b_bbt);
2080 section->set_frame (frame);
2081 section->set_pulse (new_pulse);
2089 MetricSectionFrameSorter fcmp;
2090 imaginary.sort (fcmp);
2091 if (section->position_lock_style() == MusicTime) {
2092 /* we're setting the frame */
2093 section->set_position_lock_style (AudioTime);
2094 recompute_meters (imaginary);
2095 section->set_position_lock_style (MusicTime);
2097 recompute_meters (imaginary);
2099 //dump (imaginary, std::cerr);
2103 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2105 MeterSection* prev_m = 0;
2107 section->set_pulse (pulse);
2109 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2111 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2112 if (prev_m && m == section){
2113 /* the first meter is always audio-locked, so prev_m should exist.
2114 should we allow setting audio locked meters by pulse?
2116 const double beats = ((pulse - prev_m->pulse()) * prev_m->note_divisor());
2117 const int32_t bars = (beats + 1) / prev_m->divisions_per_bar();
2118 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), BBT_Time (bars + prev_m->bbt().bars, 1, 0));
2119 section->set_beat (b_bbt);
2120 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2128 MetricSectionSorter cmp;
2129 imaginary.sort (cmp);
2130 if (section->position_lock_style() == AudioTime) {
2131 /* we're setting the pulse */
2132 section->set_position_lock_style (MusicTime);
2133 recompute_meters (imaginary);
2134 section->set_position_lock_style (AudioTime);
2136 recompute_meters (imaginary);
2140 /** places a copy of _metrics into copy and returns a pointer
2141 * to section's equivalent in copy.
2144 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2148 TempoSection* ret = 0;
2150 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2151 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2153 if (t->position_lock_style() == MusicTime) {
2154 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2155 ret->set_frame (t->frame());
2157 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2158 ret->set_pulse (t->pulse());
2160 ret->set_active (t->active());
2161 ret->set_movable (t->movable());
2162 copy.push_back (ret);
2165 TempoSection* cp = 0;
2166 if (t->position_lock_style() == MusicTime) {
2167 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2168 cp->set_frame (t->frame());
2170 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2171 cp->set_pulse (t->pulse());
2173 cp->set_active (t->active());
2174 cp->set_movable (t->movable());
2175 copy.push_back (cp);
2177 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2178 MeterSection* cp = 0;
2179 if (m->position_lock_style() == MusicTime) {
2180 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2181 cp->set_frame (m->frame());
2183 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2184 cp->set_pulse (m->pulse());
2186 cp->set_movable (m->movable());
2187 copy.push_back (cp);
2195 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2198 TempoSection* new_section = 0;
2201 Glib::Threads::RWLock::ReaderLock lm (lock);
2202 new_section = copy_metrics_and_point (copy, ts);
2205 double const beat = bbt_to_beats_locked (copy, bbt);
2206 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2208 Metrics::const_iterator d = copy.begin();
2209 while (d != copy.end()) {
2218 * 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,
2219 * taking any possible reordering as a consequence of this into account.
2220 * @param section - the section to be altered
2221 * @param bpm - the new Tempo
2222 * @param bbt - the bbt where the altered tempo will fall
2223 * @return returns - the position in frames where the new tempo section will lie.
2226 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2228 Glib::Threads::RWLock::ReaderLock lm (lock);
2231 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2233 const double beat = bbt_to_beats_locked (future_map, bbt);
2235 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2236 ret = new_section->frame();
2238 ret = frame_at_beat_locked (_metrics, beat);
2241 Metrics::const_iterator d = future_map.begin();
2242 while (d != future_map.end()) {
2250 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2252 Glib::Threads::RWLock::ReaderLock lm (lock);
2255 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2257 if (solve_map (future_map, new_section, frame)) {
2258 ret = new_section->pulse();
2260 ret = pulse_at_frame_locked (_metrics, frame);
2263 Metrics::const_iterator d = future_map.begin();
2264 while (d != future_map.end()) {
2272 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2276 Glib::Threads::RWLock::WriterLock lm (lock);
2277 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2278 if (solve_map (future_map, new_section, frame)) {
2279 solve_map (_metrics, ts, frame);
2283 Metrics::const_iterator d = future_map.begin();
2284 while (d != future_map.end()) {
2289 MetricPositionChanged (); // Emit Signal
2293 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2297 Glib::Threads::RWLock::WriterLock lm (lock);
2298 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2299 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2300 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2304 Metrics::const_iterator d = future_map.begin();
2305 while (d != future_map.end()) {
2310 MetricPositionChanged (); // Emit Signal
2314 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2317 Glib::Threads::RWLock::WriterLock lm (lock);
2318 solve_map (_metrics, ms, frame);
2321 MetricPositionChanged (); // Emit Signal
2325 TempoMap::gui_move_meter (MeterSection* ms, const double& pulse)
2328 Glib::Threads::RWLock::WriterLock lm (lock);
2329 solve_map (_metrics, ms, pulse);
2332 MetricPositionChanged (); // Emit Signal
2336 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2339 bool can_solve = false;
2341 Glib::Threads::RWLock::WriterLock lm (lock);
2342 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2343 new_section->set_beats_per_minute (bpm.beats_per_minute());
2344 recompute_tempos (future_map);
2346 if (check_solved (future_map, true)) {
2347 ts->set_beats_per_minute (bpm.beats_per_minute());
2348 recompute_map (_metrics);
2353 Metrics::const_iterator d = future_map.begin();
2354 while (d != future_map.end()) {
2359 MetricPositionChanged (); // Emit Signal
2365 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2367 Glib::Threads::RWLock::ReaderLock lm (lock);
2369 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2370 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2371 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2373 return frame_at_beat_locked (_metrics, total_beats);
2377 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2379 return round_to_type (fr, dir, Bar);
2383 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2385 return round_to_type (fr, dir, Beat);
2389 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2391 Glib::Threads::RWLock::ReaderLock lm (lock);
2392 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2393 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2394 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2396 ticks -= beats * BBT_Time::ticks_per_beat;
2399 /* round to next (or same iff dir == RoundUpMaybe) */
2401 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2403 if (mod == 0 && dir == RoundUpMaybe) {
2404 /* right on the subdivision, which is fine, so do nothing */
2406 } else if (mod == 0) {
2407 /* right on the subdivision, so the difference is just the subdivision ticks */
2408 ticks += ticks_one_subdivisions_worth;
2411 /* not on subdivision, compute distance to next subdivision */
2413 ticks += ticks_one_subdivisions_worth - mod;
2416 if (ticks >= BBT_Time::ticks_per_beat) {
2417 ticks -= BBT_Time::ticks_per_beat;
2419 } else if (dir < 0) {
2421 /* round to previous (or same iff dir == RoundDownMaybe) */
2423 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2425 if (difference == 0 && dir == RoundDownAlways) {
2426 /* right on the subdivision, but force-rounding down,
2427 so the difference is just the subdivision ticks */
2428 difference = ticks_one_subdivisions_worth;
2431 if (ticks < difference) {
2432 ticks = BBT_Time::ticks_per_beat - ticks;
2434 ticks -= difference;
2438 /* round to nearest */
2441 /* compute the distance to the previous and next subdivision */
2443 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2445 /* closer to the next subdivision, so shift forward */
2447 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2449 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2451 if (ticks > BBT_Time::ticks_per_beat) {
2453 ticks -= BBT_Time::ticks_per_beat;
2454 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2457 } else if (rem > 0) {
2459 /* closer to previous subdivision, so shift backward */
2463 /* can't go backwards past zero, so ... */
2466 /* step back to previous beat */
2468 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2469 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2471 ticks = lrint (ticks - rem);
2472 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2475 /* on the subdivision, do nothing */
2479 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2485 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2487 if (sub_num == -1) {
2488 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2489 if ((double) when.beats > bpb / 2.0) {
2495 } else if (sub_num == 0) {
2496 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2497 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2499 while ((double) when.beats > bpb) {
2501 when.beats -= (uint32_t) floor (bpb);
2507 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2509 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2510 /* closer to the next subdivision, so shift forward */
2512 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2514 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2516 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2519 } else if (rem > 0) {
2520 /* closer to previous subdivision, so shift backward */
2522 if (rem > when.ticks) {
2523 if (when.beats == 0) {
2524 /* can't go backwards past zero, so ... */
2526 /* step back to previous beat */
2528 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2530 when.ticks = when.ticks - rem;
2536 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2538 Glib::Threads::RWLock::ReaderLock lm (lock);
2540 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2541 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2546 /* find bar previous to 'frame' */
2549 return frame_time_locked (_metrics, bbt);
2551 } else if (dir > 0) {
2552 /* find bar following 'frame' */
2556 return frame_time_locked (_metrics, bbt);
2558 /* true rounding: find nearest bar */
2559 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2562 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2564 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2566 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2577 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2578 } else if (dir > 0) {
2579 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2581 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2590 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2591 framepos_t lower, framepos_t upper)
2593 Glib::Threads::RWLock::ReaderLock lm (lock);
2594 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2596 /* although the map handles negative beats, bbt doesn't. */
2600 while (pos < upper) {
2601 pos = frame_at_beat_locked (_metrics, cnt);
2602 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2603 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2604 const BBT_Time bbt = beats_to_bbt (cnt);
2605 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2611 TempoMap::tempo_section_at (framepos_t frame) const
2613 Glib::Threads::RWLock::ReaderLock lm (lock);
2614 return tempo_section_at_locked (_metrics, frame);
2618 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2620 Metrics::const_iterator i;
2621 TempoSection* prev = 0;
2623 for (i = metrics.begin(); i != metrics.end(); ++i) {
2626 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2630 if (prev && t->frame() > frame) {
2640 abort(); /*NOTREACHED*/
2647 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2649 TempoSection* prev_t = 0;
2650 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2652 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2654 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2655 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2665 /* don't use this to calculate length (the tempo is only correct for this frame).
2666 do that stuff based on the beat_at_frame and frame_at_beat api
2669 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2671 Glib::Threads::RWLock::ReaderLock lm (lock);
2673 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
2674 const TempoSection* ts_after = 0;
2675 Metrics::const_iterator i;
2677 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2680 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2684 if ((*i)->frame() > frame) {
2692 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2694 /* must be treated as constant tempo */
2695 return ts_at->frames_per_beat (_frame_rate);
2699 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
2701 TempoSection* prev_t = 0;
2703 Metrics::const_iterator i;
2705 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2707 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2711 if ((prev_t) && t->frame() > frame) {
2712 /* t is the section past frame */
2713 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2714 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2721 const double ret = prev_t->beats_per_minute();
2722 const Tempo ret_tempo (ret, prev_t->note_type ());
2728 TempoMap::tempo_at (const framepos_t& frame) const
2730 Glib::Threads::RWLock::ReaderLock lm (lock);
2731 return tempo_at_locked (_metrics, frame);
2735 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
2737 Metrics::const_iterator i;
2738 MeterSection* prev = 0;
2740 for (i = metrics.begin(); i != metrics.end(); ++i) {
2743 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2745 if (prev && (*i)->frame() > frame) {
2755 abort(); /*NOTREACHED*/
2763 TempoMap::meter_section_at (framepos_t frame) const
2765 Glib::Threads::RWLock::ReaderLock lm (lock);
2766 return meter_section_at_locked (_metrics, frame);
2770 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2772 MeterSection* prev_m = 0;
2774 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2776 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2777 if (prev_m && m->beat() > beat) {
2788 TempoMap::meter_section_at_beat (double beat) const
2790 Glib::Threads::RWLock::ReaderLock lm (lock);
2791 return meter_section_at_beat_locked (_metrics, beat);
2795 TempoMap::meter_at (framepos_t frame) const
2797 TempoMetric m (metric_at (frame));
2802 TempoMap::fix_legacy_session ()
2804 MeterSection* prev_m = 0;
2805 TempoSection* prev_t = 0;
2807 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2811 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2812 if (!m->movable()) {
2813 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2817 m->set_position_lock_style (AudioTime);
2822 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2823 + (m->bbt().beats - 1)
2824 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2826 m->set_beat (start);
2827 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2828 + (m->bbt().beats - 1)
2829 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2830 m->set_pulse (start_beat / prev_m->note_divisor());
2833 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2839 if (!t->movable()) {
2842 t->set_position_lock_style (AudioTime);
2848 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2849 + (t->legacy_bbt().beats - 1)
2850 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2852 t->set_pulse (beat / prev_m->note_divisor());
2854 /* really shouldn't happen but.. */
2855 t->set_pulse (beat / 4.0);
2864 TempoMap::get_state ()
2866 Metrics::const_iterator i;
2867 XMLNode *root = new XMLNode ("TempoMap");
2870 Glib::Threads::RWLock::ReaderLock lm (lock);
2871 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2872 root->add_child_nocopy ((*i)->get_state());
2880 TempoMap::set_state (const XMLNode& node, int /*version*/)
2883 Glib::Threads::RWLock::WriterLock lm (lock);
2886 XMLNodeConstIterator niter;
2887 Metrics old_metrics (_metrics);
2890 nlist = node.children();
2892 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2893 XMLNode* child = *niter;
2895 if (child->name() == TempoSection::xml_state_node_name) {
2898 TempoSection* ts = new TempoSection (*child);
2899 _metrics.push_back (ts);
2902 catch (failed_constructor& err){
2903 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2904 _metrics = old_metrics;
2908 } else if (child->name() == MeterSection::xml_state_node_name) {
2911 MeterSection* ms = new MeterSection (*child);
2912 _metrics.push_back (ms);
2915 catch (failed_constructor& err) {
2916 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2917 _metrics = old_metrics;
2923 if (niter == nlist.end()) {
2924 MetricSectionSorter cmp;
2925 _metrics.sort (cmp);
2928 /* check for legacy sessions where bbt was the base musical unit for tempo */
2929 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2931 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2932 if (t->legacy_bbt().bars != 0) {
2933 fix_legacy_session();
2940 /* check for multiple tempo/meters at the same location, which
2941 ardour2 somehow allowed.
2944 Metrics::iterator prev = _metrics.end();
2945 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2946 if (prev != _metrics.end()) {
2948 MeterSection* prev_m;
2950 TempoSection* prev_t;
2951 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2952 if (prev_m->pulse() == ms->pulse()) {
2953 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2954 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2957 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2958 if (prev_t->pulse() == ts->pulse()) {
2959 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2960 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2968 recompute_map (_metrics);
2971 PropertyChanged (PropertyChange ());
2977 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2979 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2980 const MeterSection* m;
2981 const TempoSection* t;
2982 const TempoSection* prev_t = 0;
2984 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2986 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2987 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2988 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2989 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2991 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
2992 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;
2995 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2996 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2997 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3000 o << "------" << std::endl;
3004 TempoMap::n_tempos() const
3006 Glib::Threads::RWLock::ReaderLock lm (lock);
3009 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3010 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3019 TempoMap::n_meters() const
3021 Glib::Threads::RWLock::ReaderLock lm (lock);
3024 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3025 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3034 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3037 Glib::Threads::RWLock::WriterLock lm (lock);
3038 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3039 if ((*i)->frame() >= where && (*i)->movable ()) {
3040 (*i)->set_frame ((*i)->frame() + amount);
3044 /* now reset the BBT time of all metrics, based on their new
3045 * audio time. This is the only place where we do this reverse
3049 Metrics::iterator i;
3050 const MeterSection* meter;
3051 const TempoSection* tempo;
3055 meter = &first_meter ();
3056 tempo = &first_tempo ();
3061 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3064 MetricSection* prev = 0;
3066 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3069 //TempoMetric metric (*meter, *tempo);
3070 MeterSection* ms = const_cast<MeterSection*>(meter);
3071 TempoSection* ts = const_cast<TempoSection*>(tempo);
3074 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3078 ts->set_pulse (t->pulse());
3080 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3081 ts->set_pulse (m->pulse());
3083 ts->set_frame (prev->frame());
3087 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3088 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3089 ms->set_beat (start);
3090 ms->set_pulse (m->pulse());
3092 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3096 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3097 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3098 ms->set_beat (start);
3099 ms->set_pulse (t->pulse());
3101 ms->set_frame (prev->frame());
3105 // metric will be at frames=0 bbt=1|1|0 by default
3106 // which is correct for our purpose
3109 // cerr << bbt << endl;
3111 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3115 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3117 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3118 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3119 bbt_time (m->frame(), bbt);
3121 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3127 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3128 /* round up to next beat */
3134 if (bbt.beats != 1) {
3135 /* round up to next bar */
3140 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3141 m->set_beat (start);
3142 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3144 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3146 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3147 abort(); /*NOTREACHED*/
3153 recompute_map (_metrics);
3157 PropertyChanged (PropertyChange ());
3160 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3164 std::list<MetricSection*> metric_kill_list;
3166 TempoSection* last_tempo = NULL;
3167 MeterSection* last_meter = NULL;
3168 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3169 bool meter_after = false; // is there a meter marker likewise?
3171 Glib::Threads::RWLock::WriterLock lm (lock);
3172 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3173 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3174 metric_kill_list.push_back(*i);
3175 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3178 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3182 else if ((*i)->frame() >= where) {
3183 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3184 (*i)->set_frame ((*i)->frame() - amount);
3185 if ((*i)->frame() == where) {
3186 // marker was immediately after end of range
3187 tempo_after = dynamic_cast<TempoSection*> (*i);
3188 meter_after = dynamic_cast<MeterSection*> (*i);
3194 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3195 if (last_tempo && !tempo_after) {
3196 metric_kill_list.remove(last_tempo);
3197 last_tempo->set_frame(where);
3200 if (last_meter && !meter_after) {
3201 metric_kill_list.remove(last_meter);
3202 last_meter->set_frame(where);
3206 //remove all the remaining metrics
3207 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3208 _metrics.remove(*i);
3213 recompute_map (_metrics);
3216 PropertyChanged (PropertyChange ());
3220 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3221 * pos can be -ve, if required.
3224 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3226 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3229 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3231 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3233 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3236 /** Add the BBT interval op to pos and return the result */
3238 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3240 Glib::Threads::RWLock::ReaderLock lm (lock);
3242 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3243 pos_bbt.ticks += op.ticks;
3244 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3246 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3248 pos_bbt.beats += op.beats;
3249 /* the meter in effect will start on the bar */
3250 double divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3251 while (pos_bbt.beats >= divisions_per_bar + 1) {
3253 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3254 pos_bbt.beats -= divisions_per_bar;
3256 pos_bbt.bars += op.bars;
3258 return frame_time_locked (_metrics, pos_bbt);
3261 /** Count the number of beats that are equivalent to distance when going forward,
3265 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3267 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3271 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3277 operator<< (std::ostream& o, const Meter& m) {
3278 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3282 operator<< (std::ostream& o, const Tempo& t) {
3283 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3287 operator<< (std::ostream& o, const MetricSection& section) {
3289 o << "MetricSection @ " << section.frame() << ' ';
3291 const TempoSection* ts;
3292 const MeterSection* ms;
3294 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3295 o << *((const Tempo*) ts);
3296 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3297 o << *((const Meter*) ms);