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) {
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 || _c_func == 0.0) {
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 || _c_func == 0.0) {
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 || _c_func == 0.0) {
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 || _c_func == 0.0) {
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 || _c_func == 0.0) {
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 || _c_func == 0.0) {
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*/
457 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
462 if ((prop = node.property ("pulse")) != 0) {
463 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
464 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
469 if ((prop = node.property ("beat")) != 0) {
470 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
471 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
477 if ((prop = node.property ("bbt")) == 0) {
478 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
479 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
483 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
484 throw failed_constructor();
490 if ((prop = node.property ("frame")) != 0) {
491 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
492 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
498 /* beats-per-bar is old; divisions-per-bar is new */
500 if ((prop = node.property ("divisions-per-bar")) == 0) {
501 if ((prop = node.property ("beats-per-bar")) == 0) {
502 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
503 throw failed_constructor();
506 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
507 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
508 throw failed_constructor();
511 if ((prop = node.property ("note-type")) == 0) {
512 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
513 throw failed_constructor();
515 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
516 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
517 throw failed_constructor();
520 if ((prop = node.property ("lock-style")) == 0) {
521 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
522 set_position_lock_style (MusicTime);
524 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
527 if ((prop = node.property ("movable")) == 0) {
528 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
529 throw failed_constructor();
532 set_movable (string_is_affirmative (prop->value()));
536 MeterSection::get_state() const
538 XMLNode *root = new XMLNode (xml_state_node_name);
542 snprintf (buf, sizeof (buf), "%lf", pulse());
543 root->add_property ("pulse", buf);
544 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
548 root->add_property ("bbt", buf);
549 snprintf (buf, sizeof (buf), "%lf", beat());
550 root->add_property ("beat", buf);
551 snprintf (buf, sizeof (buf), "%f", _note_type);
552 root->add_property ("note-type", buf);
553 snprintf (buf, sizeof (buf), "%li", frame());
554 root->add_property ("frame", buf);
555 root->add_property ("lock-style", enum_2_string (position_lock_style()));
556 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
557 root->add_property ("divisions-per-bar", buf);
558 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
559 root->add_property ("movable", buf);
564 /***********************************************************************/
568 Tempo can be thought of as a source of the musical pulse.
569 Meters divide that pulse into measures and beats.
570 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
571 at any particular time.
572 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
573 It should rather be thought of as tempo note divisions per minute.
575 TempoSections, which are nice to think of in whole pulses per minute,
576 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
577 and beats (via note_divisor) are used to form a tempo map.
578 TempoSections and MeterSections may be locked to either audio or music (position lock style).
579 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
580 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
582 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
584 The first tempo and first meter are special. they must move together, and must be locked to audio.
585 Audio locked tempos which lie before the first meter are made inactive.
586 They will be re-activated if the first meter is again placed before them.
588 Both tempos and meters have a pulse position and a frame position.
589 Meters also have a beat position, which is always 0.0 for the first meter.
591 A tempo locked to music is locked to musical pulses.
592 A meter locked to music is locked to beats.
594 Recomputing the tempo map is the process where the 'missing' position
595 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
597 It is important to keep the _metrics in an order that makes sense.
598 Because ramped MusicTime and AudioTime tempos can interact with each other,
599 reordering is frequent. Care must be taken to keep _metrics in a solved state.
600 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
602 struct MetricSectionSorter {
603 bool operator() (const MetricSection* a, const MetricSection* b) {
604 return a->pulse() < b->pulse();
608 struct MetricSectionFrameSorter {
609 bool operator() (const MetricSection* a, const MetricSection* b) {
610 return a->frame() < b->frame();
614 TempoMap::TempoMap (framecnt_t fr)
617 BBT_Time start (1, 1, 0);
619 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
620 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
622 t->set_movable (false);
623 m->set_movable (false);
625 /* note: frame time is correct (zero) for both of these */
627 _metrics.push_back (t);
628 _metrics.push_back (m);
632 TempoMap::~TempoMap ()
637 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
639 bool removed = false;
642 Glib::Threads::RWLock::WriterLock lm (lock);
643 if ((removed = remove_tempo_locked (tempo))) {
644 if (complete_operation) {
645 recompute_map (_metrics);
650 if (removed && complete_operation) {
651 PropertyChanged (PropertyChange ());
656 TempoMap::remove_tempo_locked (const TempoSection& tempo)
660 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
661 if (dynamic_cast<TempoSection*> (*i) != 0) {
662 if (tempo.frame() == (*i)->frame()) {
663 if ((*i)->movable()) {
675 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
677 bool removed = false;
680 Glib::Threads::RWLock::WriterLock lm (lock);
681 if ((removed = remove_meter_locked (tempo))) {
682 if (complete_operation) {
683 recompute_map (_metrics);
688 if (removed && complete_operation) {
689 PropertyChanged (PropertyChange ());
694 TempoMap::remove_meter_locked (const MeterSection& tempo)
698 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
699 if (dynamic_cast<MeterSection*> (*i) != 0) {
700 if (tempo.frame() == (*i)->frame()) {
701 if ((*i)->movable()) {
713 TempoMap::do_insert (MetricSection* section)
715 bool need_add = true;
716 /* we only allow new meters to be inserted on beat 1 of an existing
720 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
721 //assert (m->bbt().ticks == 0);
723 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
725 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
726 corrected.second.beats = 1;
727 corrected.second.ticks = 0;
728 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
729 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
730 m->bbt(), corrected.second) << endmsg;
731 //m->set_pulse (corrected);
735 /* Look for any existing MetricSection that is of the same type and
736 in the same bar as the new one, and remove it before adding
737 the new one. Note that this means that if we find a matching,
738 existing section, we can break out of the loop since we're
739 guaranteed that there is only one such match.
742 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
744 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
745 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
746 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
747 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
749 if (tempo && insert_tempo) {
752 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
753 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
755 if (!tempo->movable()) {
757 /* can't (re)move this section, so overwrite
758 * its data content (but not its properties as
762 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
763 (*i)->set_position_lock_style (AudioTime);
765 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
766 t->set_type (insert_tempo->type());
775 } else if (meter && insert_meter) {
779 bool const ipm = insert_meter->position_lock_style() == MusicTime;
781 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
783 if (!meter->movable()) {
785 /* can't (re)move this section, so overwrite
786 * its data content (but not its properties as
790 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
791 (*i)->set_position_lock_style (insert_meter->position_lock_style());
800 /* non-matching types, so we don't care */
804 /* Add the given MetricSection, if we didn't just reset an existing
809 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
810 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
813 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
814 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
817 bool const ipm = insert_meter->position_lock_style() == MusicTime;
818 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
823 } else if (insert_tempo) {
824 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
825 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
828 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
829 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
836 _metrics.insert (i, section);
837 //dump (_metrics, std::cerr);
842 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
845 Glib::Threads::RWLock::WriterLock lm (lock);
846 TempoSection& first (first_tempo());
847 if (ts.pulse() != first.pulse()) {
848 remove_tempo_locked (ts);
849 add_tempo_locked (tempo, pulse, true, type);
851 first.set_type (type);
853 /* cannot move the first tempo section */
854 *static_cast<Tempo*>(&first) = tempo;
855 recompute_map (_metrics);
860 PropertyChanged (PropertyChange ());
864 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
867 Glib::Threads::RWLock::WriterLock lm (lock);
868 TempoSection& first (first_tempo());
869 if (ts.frame() != first.frame()) {
870 remove_tempo_locked (ts);
871 add_tempo_locked (tempo, frame, true, type);
873 first.set_type (type);
874 first.set_pulse (0.0);
875 first.set_position_lock_style (AudioTime);
877 /* cannot move the first tempo section */
878 *static_cast<Tempo*>(&first) = tempo;
879 recompute_map (_metrics);
884 PropertyChanged (PropertyChange ());
888 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
890 TempoSection* ts = 0;
892 Glib::Threads::RWLock::WriterLock lm (lock);
893 ts = add_tempo_locked (tempo, pulse, true, type);
896 PropertyChanged (PropertyChange ());
902 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
904 TempoSection* ts = 0;
906 Glib::Threads::RWLock::WriterLock lm (lock);
907 ts = add_tempo_locked (tempo, frame, true, type);
911 PropertyChanged (PropertyChange ());
917 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
919 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
924 solve_map (_metrics, t, t->pulse());
931 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
933 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
938 solve_map (_metrics, t, t->frame());
945 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
948 Glib::Threads::RWLock::WriterLock lm (lock);
951 remove_meter_locked (ms);
952 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
954 MeterSection& first (first_meter());
955 const PositionLockStyle pl = ms.position_lock_style();
956 /* cannot move the first meter section */
957 *static_cast<Meter*>(&first) = meter;
958 first.set_position_lock_style (pl);
960 recompute_map (_metrics);
963 PropertyChanged (PropertyChange ());
967 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
970 Glib::Threads::RWLock::WriterLock lm (lock);
972 const double beat = ms.beat();
973 const BBT_Time bbt = ms.bbt();
976 remove_meter_locked (ms);
977 add_meter_locked (meter, frame, beat, bbt, true);
979 MeterSection& first (first_meter());
980 TempoSection& first_t (first_tempo());
981 /* cannot move the first meter section */
982 *static_cast<Meter*>(&first) = meter;
983 first.set_position_lock_style (AudioTime);
984 first.set_pulse (0.0);
985 first.set_frame (frame);
986 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
987 first.set_beat (beat);
988 first_t.set_frame (first.frame());
989 first_t.set_pulse (0.0);
990 first_t.set_position_lock_style (AudioTime);
992 recompute_map (_metrics);
994 PropertyChanged (PropertyChange ());
999 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1001 MeterSection* m = 0;
1003 Glib::Threads::RWLock::WriterLock lm (lock);
1004 m = add_meter_locked (meter, beat, where, true);
1009 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1010 dump (_metrics, std::cerr);
1014 PropertyChanged (PropertyChange ());
1020 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1022 MeterSection* m = 0;
1024 Glib::Threads::RWLock::WriterLock lm (lock);
1025 m = add_meter_locked (meter, frame, beat, where, true);
1030 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1031 dump (_metrics, std::cerr);
1035 PropertyChanged (PropertyChange ());
1041 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1043 /* a new meter always starts a new bar on the first beat. so
1044 round the start time appropriately. remember that
1045 `where' is based on the existing tempo map, not
1046 the result after we insert the new meter.
1050 if (where.beats != 1) {
1054 /* new meters *always* start on a beat. */
1056 const double pulse = pulse_at_beat_locked (_metrics, beat);
1057 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1058 do_insert (new_meter);
1061 solve_map (_metrics, new_meter, pulse);
1068 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1070 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1072 double pulse = pulse_at_frame_locked (_metrics, frame);
1073 new_meter->set_pulse (pulse);
1075 do_insert (new_meter);
1078 solve_map (_metrics, new_meter, frame);
1085 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1087 Tempo newtempo (beats_per_minute, note_type);
1090 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1091 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1096 Glib::Threads::RWLock::WriterLock lm (lock);
1097 *((Tempo*) t) = newtempo;
1098 recompute_map (_metrics);
1100 PropertyChanged (PropertyChange ());
1107 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1109 Tempo newtempo (beats_per_minute, note_type);
1112 TempoSection* first;
1113 Metrics::iterator i;
1115 /* find the TempoSection immediately preceding "where"
1118 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1120 if ((*i)->frame() > where) {
1126 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1139 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1149 Glib::Threads::RWLock::WriterLock lm (lock);
1150 /* cannot move the first tempo section */
1151 *((Tempo*)prev) = newtempo;
1152 recompute_map (_metrics);
1155 PropertyChanged (PropertyChange ());
1159 TempoMap::first_meter () const
1161 const MeterSection *m = 0;
1163 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1164 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1169 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1170 abort(); /*NOTREACHED*/
1175 TempoMap::first_meter ()
1177 MeterSection *m = 0;
1179 /* CALLER MUST HOLD LOCK */
1181 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1182 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1187 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1188 abort(); /*NOTREACHED*/
1193 TempoMap::first_tempo () const
1195 const TempoSection *t = 0;
1197 /* CALLER MUST HOLD LOCK */
1199 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1200 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1204 if (!t->movable()) {
1210 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1211 abort(); /*NOTREACHED*/
1216 TempoMap::first_tempo ()
1218 TempoSection *t = 0;
1220 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1221 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1225 if (!t->movable()) {
1231 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1232 abort(); /*NOTREACHED*/
1236 TempoMap::recompute_tempos (Metrics& metrics)
1238 TempoSection* prev_t = 0;
1240 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1243 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1247 if (!t->movable()) {
1253 if (t->position_lock_style() == AudioTime) {
1254 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1255 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1258 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1259 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1266 prev_t->set_c_func (0.0);
1269 /* tempos must be positioned correctly */
1271 TempoMap::recompute_meters (Metrics& metrics)
1273 MeterSection* meter = 0;
1274 MeterSection* prev_m = 0;
1276 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1277 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1278 if (meter->position_lock_style() == AudioTime) {
1280 pair<double, BBT_Time> b_bbt;
1281 if (meter->movable()) {
1282 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor());
1283 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1284 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1285 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1286 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1287 pulse = true_pulse - pulse_off;
1289 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1291 meter->set_beat (b_bbt);
1292 meter->set_pulse (pulse);
1295 pair<double, BBT_Time> new_beat;
1297 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1298 new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1300 /* shouldn't happen - the first is audio-locked */
1301 pulse = pulse_at_beat_locked (metrics, meter->beat());
1302 new_beat = make_pair (pulse, meter->bbt());
1305 meter->set_beat (new_beat);
1306 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1307 meter->set_pulse (pulse);
1313 //dump (_metrics, std::cerr;
1317 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1319 /* CALLER MUST HOLD WRITE LOCK */
1323 /* we will actually stop once we hit
1330 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1333 /* silly call from Session::process() during startup
1338 recompute_tempos (metrics);
1339 recompute_meters (metrics);
1343 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1345 Glib::Threads::RWLock::ReaderLock lm (lock);
1346 TempoMetric m (first_meter(), first_tempo());
1348 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1349 at something, because we insert the default tempo and meter during
1350 TempoMap construction.
1352 now see if we can find better candidates.
1355 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1357 if ((*i)->frame() > frame) {
1371 /* XX meters only */
1373 TempoMap::metric_at (BBT_Time bbt) const
1375 Glib::Threads::RWLock::ReaderLock lm (lock);
1376 TempoMetric m (first_meter(), first_tempo());
1378 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1379 at something, because we insert the default tempo and meter during
1380 TempoMap construction.
1382 now see if we can find better candidates.
1385 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1387 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1388 BBT_Time section_start (mw->bbt());
1390 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1402 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1404 MeterSection* prev_m = 0;
1406 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1408 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1409 if (prev_m && m->beat() > beat) {
1416 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1421 TempoMap::pulse_at_beat (const double& beat) const
1423 Glib::Threads::RWLock::ReaderLock lm (lock);
1424 return pulse_at_beat_locked (_metrics, beat);
1428 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1430 MeterSection* prev_m = 0;
1432 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1434 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1435 if (prev_m && m->pulse() > pulse) {
1442 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1444 return beats_in_section + prev_m->beat();
1448 TempoMap::beat_at_pulse (const double& pulse) const
1450 Glib::Threads::RWLock::ReaderLock lm (lock);
1451 return beat_at_pulse_locked (_metrics, pulse);
1455 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1457 /* HOLD (at least) THE READER LOCK */
1458 TempoSection* prev_t = 0;
1460 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1462 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1466 if (prev_t && t->frame() > frame) {
1467 /*the previous ts is the one containing the frame */
1468 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1475 /* treated as constant for this ts */
1476 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1478 return pulses_in_section + prev_t->pulse();
1482 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1484 /* HOLD THE READER LOCK */
1486 const TempoSection* prev_t = 0;
1488 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1491 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1495 if (prev_t && t->pulse() > pulse) {
1496 return prev_t->frame_at_pulse (pulse, _frame_rate);
1502 /* must be treated as constant, irrespective of _type */
1503 double const pulses_in_section = pulse - prev_t->pulse();
1504 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1506 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1512 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1514 const double pulse = pulse_at_frame_locked (metrics, frame);
1515 return beat_at_pulse_locked (metrics, pulse);
1519 TempoMap::beat_at_frame (const framecnt_t& frame) const
1521 Glib::Threads::RWLock::ReaderLock lm (lock);
1522 return beat_at_frame_locked (_metrics, frame);
1526 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1528 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1533 TempoMap::frame_at_beat (const double& beat) const
1535 Glib::Threads::RWLock::ReaderLock lm (lock);
1536 return frame_at_beat_locked (_metrics, beat);
1540 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1542 /* CALLER HOLDS READ LOCK */
1544 MeterSection* prev_m = 0;
1546 /* because audio-locked meters have 'fake' integral beats,
1547 there is no pulse offset here.
1549 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1551 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1553 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1554 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1562 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1563 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1564 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1570 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1572 Glib::Threads::RWLock::ReaderLock lm (lock);
1573 return bbt_to_beats_locked (_metrics, bbt);
1577 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1579 /* CALLER HOLDS READ LOCK */
1580 MeterSection* prev_m = 0;
1581 const double beats = (b < 0.0) ? 0.0 : b;
1583 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1584 MeterSection* m = 0;
1586 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1588 if (m->beat() > beats) {
1589 /* this is the meter after the one our beat is on*/
1598 const double beats_in_ms = beats - prev_m->beat();
1599 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1600 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1601 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1602 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1606 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1607 ret.beats = (uint32_t) floor (remaining_beats);
1608 ret.bars = total_bars;
1610 /* 0 0 0 to 1 1 0 - based mapping*/
1614 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1616 ret.ticks -= BBT_Time::ticks_per_beat;
1619 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1628 TempoMap::beats_to_bbt (const double& beats)
1630 Glib::Threads::RWLock::ReaderLock lm (lock);
1631 return beats_to_bbt_locked (_metrics, beats);
1635 TempoMap::pulse_to_bbt (const double& pulse)
1637 Glib::Threads::RWLock::ReaderLock lm (lock);
1638 MeterSection* prev_m = 0;
1640 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1641 MeterSection* m = 0;
1643 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1646 double const pulses_to_m = m->pulse() - prev_m->pulse();
1647 if (prev_m->pulse() + pulses_to_m > pulse) {
1648 /* this is the meter after the one our beat is on*/
1657 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1658 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1659 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1660 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1661 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1665 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1666 ret.beats = (uint32_t) floor (remaining_beats);
1667 ret.bars = total_bars;
1669 /* 0 0 0 to 1 1 0 mapping*/
1673 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1675 ret.ticks -= BBT_Time::ticks_per_beat;
1678 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1687 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1694 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1697 Glib::Threads::RWLock::ReaderLock lm (lock);
1698 const double beat = beat_at_frame_locked (_metrics, frame);
1700 bbt = beats_to_bbt_locked (_metrics, beat);
1704 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1706 /* HOLD THE READER LOCK */
1708 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1713 TempoMap::frame_time (const BBT_Time& bbt)
1716 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1720 if (bbt.beats < 1) {
1721 throw std::logic_error ("beats are counted from one");
1723 Glib::Threads::RWLock::ReaderLock lm (lock);
1725 return frame_time_locked (_metrics, bbt);
1729 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1731 TempoSection* prev_t = 0;
1733 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1735 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1740 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1744 if (t->frame() == prev_t->frame()) {
1748 /* precision check ensures pulses and frames align.*/
1749 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1761 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1763 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1765 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1766 if (!t->movable()) {
1767 t->set_active (true);
1770 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1771 t->set_active (false);
1773 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1774 t->set_active (true);
1775 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1784 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1786 TempoSection* prev_t = 0;
1787 TempoSection* section_prev = 0;
1788 framepos_t first_m_frame = 0;
1790 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1792 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1793 if (!m->movable()) {
1794 first_m_frame = m->frame();
1799 if (section->movable() && frame <= first_m_frame) {
1803 section->set_active (true);
1804 section->set_frame (frame);
1806 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1808 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1815 section_prev = prev_t;
1818 if (t->position_lock_style() == MusicTime) {
1819 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1820 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1822 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1823 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1831 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1832 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
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);
1844 if (check_solved (imaginary, true)) {
1845 recompute_meters (imaginary);
1849 MetricSectionFrameSorter fcmp;
1850 imaginary.sort (fcmp);
1851 if (section->position_lock_style() == MusicTime) {
1852 /* we're setting the frame */
1853 section->set_position_lock_style (AudioTime);
1854 recompute_tempos (imaginary);
1855 section->set_position_lock_style (MusicTime);
1857 recompute_tempos (imaginary);
1860 if (check_solved (imaginary, true)) {
1861 recompute_meters (imaginary);
1865 MetricSectionSorter cmp;
1866 imaginary.sort (cmp);
1867 if (section->position_lock_style() == MusicTime) {
1868 /* we're setting the frame */
1869 section->set_position_lock_style (AudioTime);
1870 recompute_tempos (imaginary);
1871 section->set_position_lock_style (MusicTime);
1873 recompute_tempos (imaginary);
1876 if (check_solved (imaginary, true)) {
1877 recompute_meters (imaginary);
1881 //dump (imaginary, std::cerr);
1887 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1889 TempoSection* prev_t = 0;
1890 TempoSection* section_prev = 0;
1892 section->set_pulse (pulse);
1894 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1896 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1900 if (!t->movable()) {
1907 section_prev = prev_t;
1910 if (t->position_lock_style() == MusicTime) {
1911 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1912 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1914 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1915 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1922 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1923 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1926 if (section->position_lock_style() == AudioTime) {
1927 /* we're setting the pulse */
1928 section->set_position_lock_style (MusicTime);
1929 recompute_tempos (imaginary);
1930 section->set_position_lock_style (AudioTime);
1932 recompute_tempos (imaginary);
1935 if (check_solved (imaginary, false)) {
1936 recompute_meters (imaginary);
1940 MetricSectionSorter cmp;
1941 imaginary.sort (cmp);
1942 if (section->position_lock_style() == AudioTime) {
1943 /* we're setting the pulse */
1944 section->set_position_lock_style (MusicTime);
1945 recompute_tempos (imaginary);
1946 section->set_position_lock_style (AudioTime);
1948 recompute_tempos (imaginary);
1951 if (check_solved (imaginary, false)) {
1952 recompute_meters (imaginary);
1956 MetricSectionFrameSorter fcmp;
1957 imaginary.sort (fcmp);
1958 if (section->position_lock_style() == AudioTime) {
1959 /* we're setting the pulse */
1960 section->set_position_lock_style (MusicTime);
1961 recompute_tempos (imaginary);
1962 section->set_position_lock_style (AudioTime);
1964 recompute_tempos (imaginary);
1967 if (check_solved (imaginary, false)) {
1968 recompute_meters (imaginary);
1972 //dump (imaginary, std::cerr);
1978 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
1980 MeterSection* prev_m = 0;
1982 if (!section->movable()) {
1983 /* lock the first tempo to our first meter */
1984 if (!set_active_tempos (imaginary, frame)) {
1987 TempoSection* first_t = &first_tempo();
1989 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1991 new_section->set_frame (frame);
1992 new_section->set_pulse (0.0);
1993 new_section->set_active (true);
1995 if (solve_map (future_map, new_section, frame)) {
1996 first_t->set_frame (frame);
1997 first_t->set_pulse (0.0);
1998 first_t->set_active (true);
1999 solve_map (imaginary, first_t, frame);
2005 section->set_frame (frame);
2007 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2009 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2012 here we set the beat for this frame.
2013 we set it 'incorrectly' to the next bar's first beat
2014 and use the delat to find the meter's pulse.
2016 double new_pulse = 0.0;
2017 pair<double, BBT_Time> b_bbt;
2019 if (section->movable()) {
2020 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2021 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2022 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2023 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2024 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2025 new_pulse = true_pulse - pulse_off;
2027 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2030 section->set_beat (b_bbt);
2031 section->set_pulse (new_pulse);
2037 double new_pulse = 0.0;
2038 if (m->position_lock_style() == MusicTime) {
2039 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2040 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2041 if (m->frame() > section->frame()) {
2042 /* moving 'section' will affect later meters' beat (but not bbt).*/
2043 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2044 m->set_beat (new_beat);
2047 pair<double, BBT_Time> b_bbt;
2049 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2050 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2051 b_bbt = make_pair (floor_beats + prev_m->beat()
2052 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2053 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2054 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2055 new_pulse = true_pulse - pulse_off;
2057 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2060 m->set_beat (b_bbt);
2062 m->set_pulse (new_pulse);
2068 MetricSectionFrameSorter fcmp;
2069 imaginary.sort (fcmp);
2070 if (section->position_lock_style() == MusicTime) {
2071 /* we're setting the frame */
2072 section->set_position_lock_style (AudioTime);
2073 recompute_meters (imaginary);
2074 section->set_position_lock_style (MusicTime);
2076 recompute_meters (imaginary);
2078 //dump (imaginary, std::cerr);
2082 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2084 MeterSection* prev_m = 0;
2085 MeterSection* section_prev = 0;
2087 section->set_pulse (pulse);
2089 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2091 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2093 section_prev = prev_m;
2097 double new_pulse = 0.0;
2098 if (m->position_lock_style() == MusicTime) {
2099 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2100 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2102 if (new_pulse > section->pulse()) {
2103 /* moving 'section' will affect later meters' beat (but not bbt).*/
2104 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2105 m->set_beat (new_beat);
2108 pair<double, BBT_Time> b_bbt;
2110 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2111 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2112 b_bbt = make_pair (floor_beats + prev_m->beat()
2113 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2114 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2115 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2116 new_pulse = true_pulse - pulse_off;
2118 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2120 m->set_beat (b_bbt);
2122 m->set_pulse (new_pulse);
2129 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2130 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2131 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2132 section->set_beat (b_bbt);
2133 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2136 MetricSectionSorter cmp;
2137 imaginary.sort (cmp);
2138 if (section->position_lock_style() == AudioTime) {
2139 /* we're setting the pulse */
2140 section->set_position_lock_style (MusicTime);
2141 recompute_meters (imaginary);
2142 section->set_position_lock_style (AudioTime);
2144 recompute_meters (imaginary);
2148 /** places a copy of _metrics into copy and returns a pointer
2149 * to section's equivalent in copy.
2152 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2156 TempoSection* ret = 0;
2158 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2159 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2161 if (t->position_lock_style() == MusicTime) {
2162 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2163 ret->set_frame (t->frame());
2165 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2166 ret->set_pulse (t->pulse());
2168 ret->set_active (t->active());
2169 ret->set_movable (t->movable());
2170 copy.push_back (ret);
2173 TempoSection* cp = 0;
2174 if (t->position_lock_style() == MusicTime) {
2175 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2176 cp->set_frame (t->frame());
2178 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2179 cp->set_pulse (t->pulse());
2181 cp->set_active (t->active());
2182 cp->set_movable (t->movable());
2183 copy.push_back (cp);
2185 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2186 MeterSection* cp = 0;
2187 if (m->position_lock_style() == MusicTime) {
2188 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2189 cp->set_frame (m->frame());
2191 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2192 cp->set_pulse (m->pulse());
2194 cp->set_movable (m->movable());
2195 copy.push_back (cp);
2203 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2206 TempoSection* new_section = 0;
2209 Glib::Threads::RWLock::ReaderLock lm (lock);
2210 new_section = copy_metrics_and_point (copy, ts);
2213 double const beat = bbt_to_beats_locked (copy, bbt);
2214 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2216 Metrics::const_iterator d = copy.begin();
2217 while (d != copy.end()) {
2226 * 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,
2227 * taking any possible reordering as a consequence of this into account.
2228 * @param section - the section to be altered
2229 * @param bpm - the new Tempo
2230 * @param bbt - the bbt where the altered tempo will fall
2231 * @return returns - the position in frames where the new tempo section will lie.
2234 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2236 Glib::Threads::RWLock::ReaderLock lm (lock);
2239 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2241 const double beat = bbt_to_beats_locked (future_map, bbt);
2243 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2244 ret = new_section->frame();
2246 ret = frame_at_beat_locked (_metrics, beat);
2249 Metrics::const_iterator d = future_map.begin();
2250 while (d != future_map.end()) {
2258 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2260 Glib::Threads::RWLock::ReaderLock lm (lock);
2263 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2265 if (solve_map (future_map, new_section, frame)) {
2266 ret = new_section->pulse();
2268 ret = pulse_at_frame_locked (_metrics, frame);
2271 Metrics::const_iterator d = future_map.begin();
2272 while (d != future_map.end()) {
2280 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2284 Glib::Threads::RWLock::WriterLock lm (lock);
2285 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2286 if (solve_map (future_map, new_section, frame)) {
2287 solve_map (_metrics, ts, frame);
2291 Metrics::const_iterator d = future_map.begin();
2292 while (d != future_map.end()) {
2297 MetricPositionChanged (); // Emit Signal
2301 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2305 Glib::Threads::RWLock::WriterLock lm (lock);
2306 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2307 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2308 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2312 Metrics::const_iterator d = future_map.begin();
2313 while (d != future_map.end()) {
2318 MetricPositionChanged (); // Emit Signal
2322 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2325 Glib::Threads::RWLock::WriterLock lm (lock);
2326 solve_map (_metrics, ms, frame);
2329 MetricPositionChanged (); // Emit Signal
2333 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2336 Glib::Threads::RWLock::WriterLock lm (lock);
2337 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2340 MetricPositionChanged (); // Emit Signal
2344 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2347 bool can_solve = false;
2349 Glib::Threads::RWLock::WriterLock lm (lock);
2350 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2351 new_section->set_beats_per_minute (bpm.beats_per_minute());
2352 recompute_tempos (future_map);
2354 if (check_solved (future_map, true)) {
2355 ts->set_beats_per_minute (bpm.beats_per_minute());
2356 recompute_map (_metrics);
2361 Metrics::const_iterator d = future_map.begin();
2362 while (d != future_map.end()) {
2367 MetricPositionChanged (); // Emit Signal
2373 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2375 Glib::Threads::RWLock::ReaderLock lm (lock);
2377 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2378 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2379 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2381 return frame_at_beat_locked (_metrics, total_beats);
2385 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2387 return round_to_type (fr, dir, Bar);
2391 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2393 return round_to_type (fr, dir, Beat);
2397 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2399 Glib::Threads::RWLock::ReaderLock lm (lock);
2400 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2401 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2402 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2404 ticks -= beats * BBT_Time::ticks_per_beat;
2407 /* round to next (or same iff dir == RoundUpMaybe) */
2409 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2411 if (mod == 0 && dir == RoundUpMaybe) {
2412 /* right on the subdivision, which is fine, so do nothing */
2414 } else if (mod == 0) {
2415 /* right on the subdivision, so the difference is just the subdivision ticks */
2416 ticks += ticks_one_subdivisions_worth;
2419 /* not on subdivision, compute distance to next subdivision */
2421 ticks += ticks_one_subdivisions_worth - mod;
2424 if (ticks >= BBT_Time::ticks_per_beat) {
2425 ticks -= BBT_Time::ticks_per_beat;
2427 } else if (dir < 0) {
2429 /* round to previous (or same iff dir == RoundDownMaybe) */
2431 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2433 if (difference == 0 && dir == RoundDownAlways) {
2434 /* right on the subdivision, but force-rounding down,
2435 so the difference is just the subdivision ticks */
2436 difference = ticks_one_subdivisions_worth;
2439 if (ticks < difference) {
2440 ticks = BBT_Time::ticks_per_beat - ticks;
2442 ticks -= difference;
2446 /* round to nearest */
2449 /* compute the distance to the previous and next subdivision */
2451 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2453 /* closer to the next subdivision, so shift forward */
2455 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2457 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2459 if (ticks > BBT_Time::ticks_per_beat) {
2461 ticks -= BBT_Time::ticks_per_beat;
2462 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2465 } else if (rem > 0) {
2467 /* closer to previous subdivision, so shift backward */
2471 /* can't go backwards past zero, so ... */
2474 /* step back to previous beat */
2476 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2477 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2479 ticks = lrint (ticks - rem);
2480 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2483 /* on the subdivision, do nothing */
2487 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2493 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2495 if (sub_num == -1) {
2496 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2497 if ((double) when.beats > bpb / 2.0) {
2503 } else if (sub_num == 0) {
2504 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2505 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2507 while ((double) when.beats > bpb) {
2509 when.beats -= (uint32_t) floor (bpb);
2515 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2517 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2518 /* closer to the next subdivision, so shift forward */
2520 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2522 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2524 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2527 } else if (rem > 0) {
2528 /* closer to previous subdivision, so shift backward */
2530 if (rem > when.ticks) {
2531 if (when.beats == 0) {
2532 /* can't go backwards past zero, so ... */
2534 /* step back to previous beat */
2536 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2538 when.ticks = when.ticks - rem;
2544 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2546 Glib::Threads::RWLock::ReaderLock lm (lock);
2548 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2549 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2554 /* find bar previous to 'frame' */
2557 return frame_time_locked (_metrics, bbt);
2559 } else if (dir > 0) {
2560 /* find bar following 'frame' */
2564 return frame_time_locked (_metrics, bbt);
2566 /* true rounding: find nearest bar */
2567 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2570 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2572 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2574 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2585 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2586 } else if (dir > 0) {
2587 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2589 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2598 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2599 framepos_t lower, framepos_t upper)
2601 Glib::Threads::RWLock::ReaderLock lm (lock);
2602 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2603 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2604 /* although the map handles negative beats, bbt doesn't. */
2608 while (cnt <= upper_beat) {
2609 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2610 const TempoSection tempo = tempo_section_at_locked (pos);
2611 const MeterSection meter = meter_section_at_locked (pos);
2612 const BBT_Time bbt = beats_to_bbt (cnt);
2613 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2619 TempoMap::tempo_section_at (framepos_t frame) const
2621 Glib::Threads::RWLock::ReaderLock lm (lock);
2622 return tempo_section_at_locked (frame);
2626 TempoMap::tempo_section_at_locked (framepos_t frame) const
2628 Metrics::const_iterator i;
2629 TempoSection* prev = 0;
2631 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2634 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2638 if (t->frame() > frame) {
2648 abort(); /*NOTREACHED*/
2655 /* don't use this to calculate length (the tempo is only correct for this frame).
2656 do that stuff based on the beat_at_frame and frame_at_beat api
2659 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2661 Glib::Threads::RWLock::ReaderLock lm (lock);
2663 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2664 const TempoSection* ts_after = 0;
2665 Metrics::const_iterator i;
2667 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2670 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2674 if ((*i)->frame() > frame) {
2682 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2684 /* must be treated as constant tempo */
2685 return ts_at->frames_per_beat (_frame_rate);
2689 TempoMap::tempo_at (const framepos_t& frame) const
2691 Glib::Threads::RWLock::ReaderLock lm (lock);
2692 return tempo_at_locked (frame);
2696 TempoMap::tempo_at_locked (const framepos_t& frame) const
2698 TempoSection* prev_t = 0;
2700 Metrics::const_iterator i;
2702 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2704 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2708 if ((prev_t) && t->frame() > frame) {
2709 /* t is the section past frame */
2710 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2711 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2718 const double ret = prev_t->beats_per_minute();
2719 const Tempo ret_tempo (ret, prev_t->note_type ());
2725 TempoMap::meter_section_at (framepos_t frame) const
2727 Glib::Threads::RWLock::ReaderLock lm (lock);
2728 return meter_section_at_locked (frame);
2732 TempoMap::meter_section_at_locked (framepos_t frame) const
2734 Metrics::const_iterator i;
2735 MeterSection* prev = 0;
2737 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2740 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2742 if (prev && (*i)->frame() > frame) {
2752 abort(); /*NOTREACHED*/
2759 TempoMap::meter_at (framepos_t frame) const
2761 TempoMetric m (metric_at (frame));
2766 TempoMap::meter_section_at (const double& beat) const
2768 MeterSection* prev_m = 0;
2769 Glib::Threads::RWLock::ReaderLock lm (lock);
2771 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2773 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2774 if (prev_m && m->beat() > beat) {
2785 TempoMap::fix_legacy_session ()
2787 MeterSection* prev_m = 0;
2788 TempoSection* prev_t = 0;
2790 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2794 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2795 if (!m->movable()) {
2796 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2800 m->set_position_lock_style (AudioTime);
2805 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2806 + (m->bbt().beats - 1)
2807 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2809 m->set_beat (start);
2810 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2811 + (m->bbt().beats - 1)
2812 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2813 m->set_pulse (start_beat / prev_m->note_divisor());
2816 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2822 if (!t->movable()) {
2825 t->set_position_lock_style (AudioTime);
2831 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2832 + (t->legacy_bbt().beats - 1)
2833 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2835 t->set_pulse (beat / prev_m->note_divisor());
2837 /* really shouldn't happen but.. */
2838 t->set_pulse (beat / 4.0);
2847 TempoMap::get_state ()
2849 Metrics::const_iterator i;
2850 XMLNode *root = new XMLNode ("TempoMap");
2853 Glib::Threads::RWLock::ReaderLock lm (lock);
2854 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2855 root->add_child_nocopy ((*i)->get_state());
2863 TempoMap::set_state (const XMLNode& node, int /*version*/)
2866 Glib::Threads::RWLock::WriterLock lm (lock);
2869 XMLNodeConstIterator niter;
2870 Metrics old_metrics (_metrics);
2873 nlist = node.children();
2875 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2876 XMLNode* child = *niter;
2878 if (child->name() == TempoSection::xml_state_node_name) {
2881 TempoSection* ts = new TempoSection (*child);
2882 _metrics.push_back (ts);
2885 catch (failed_constructor& err){
2886 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2887 _metrics = old_metrics;
2891 } else if (child->name() == MeterSection::xml_state_node_name) {
2894 MeterSection* ms = new MeterSection (*child);
2895 _metrics.push_back (ms);
2898 catch (failed_constructor& err) {
2899 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2900 _metrics = old_metrics;
2906 if (niter == nlist.end()) {
2907 MetricSectionSorter cmp;
2908 _metrics.sort (cmp);
2911 /* check for multiple tempo/meters at the same location, which
2912 ardour2 somehow allowed.
2915 Metrics::iterator prev = _metrics.end();
2916 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2917 if (prev != _metrics.end()) {
2919 MeterSection* prev_m;
2921 TempoSection* prev_t;
2922 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2923 if (prev_m->pulse() == ms->pulse()) {
2924 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2925 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2928 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2929 if (prev_t->pulse() == ts->pulse()) {
2930 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2931 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2939 /* check for legacy sessions where bbt was the base musical unit for tempo */
2940 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2942 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2943 if (t->legacy_bbt().bars != 0) {
2944 fix_legacy_session();
2951 recompute_map (_metrics);
2954 PropertyChanged (PropertyChange ());
2960 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2962 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2963 const MeterSection* m;
2964 const TempoSection* t;
2965 const TempoSection* prev_t = 0;
2967 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2969 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2970 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2971 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2972 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2974 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
2975 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;
2978 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2979 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2980 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2983 o << "------" << std::endl;
2987 TempoMap::n_tempos() const
2989 Glib::Threads::RWLock::ReaderLock lm (lock);
2992 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2993 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3002 TempoMap::n_meters() const
3004 Glib::Threads::RWLock::ReaderLock lm (lock);
3007 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3008 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3017 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3020 Glib::Threads::RWLock::WriterLock lm (lock);
3021 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3022 if ((*i)->frame() >= where && (*i)->movable ()) {
3023 (*i)->set_frame ((*i)->frame() + amount);
3027 /* now reset the BBT time of all metrics, based on their new
3028 * audio time. This is the only place where we do this reverse
3032 Metrics::iterator i;
3033 const MeterSection* meter;
3034 const TempoSection* tempo;
3038 meter = &first_meter ();
3039 tempo = &first_tempo ();
3044 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3047 MetricSection* prev = 0;
3049 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3052 //TempoMetric metric (*meter, *tempo);
3053 MeterSection* ms = const_cast<MeterSection*>(meter);
3054 TempoSection* ts = const_cast<TempoSection*>(tempo);
3057 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3061 ts->set_pulse (t->pulse());
3063 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3064 ts->set_pulse (m->pulse());
3066 ts->set_frame (prev->frame());
3070 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3071 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3072 ms->set_beat (start);
3073 ms->set_pulse (m->pulse());
3075 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3079 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3080 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3081 ms->set_beat (start);
3082 ms->set_pulse (t->pulse());
3084 ms->set_frame (prev->frame());
3088 // metric will be at frames=0 bbt=1|1|0 by default
3089 // which is correct for our purpose
3092 // cerr << bbt << endl;
3094 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3098 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3100 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3101 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3102 bbt_time (m->frame(), bbt);
3104 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3110 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3111 /* round up to next beat */
3117 if (bbt.beats != 1) {
3118 /* round up to next bar */
3123 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3124 m->set_beat (start);
3125 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3127 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3129 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3130 abort(); /*NOTREACHED*/
3136 recompute_map (_metrics);
3140 PropertyChanged (PropertyChange ());
3143 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3147 std::list<MetricSection*> metric_kill_list;
3149 TempoSection* last_tempo = NULL;
3150 MeterSection* last_meter = NULL;
3151 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3152 bool meter_after = false; // is there a meter marker likewise?
3154 Glib::Threads::RWLock::WriterLock lm (lock);
3155 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3156 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3157 metric_kill_list.push_back(*i);
3158 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3161 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3165 else if ((*i)->frame() >= where) {
3166 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3167 (*i)->set_frame ((*i)->frame() - amount);
3168 if ((*i)->frame() == where) {
3169 // marker was immediately after end of range
3170 tempo_after = dynamic_cast<TempoSection*> (*i);
3171 meter_after = dynamic_cast<MeterSection*> (*i);
3177 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3178 if (last_tempo && !tempo_after) {
3179 metric_kill_list.remove(last_tempo);
3180 last_tempo->set_frame(where);
3183 if (last_meter && !meter_after) {
3184 metric_kill_list.remove(last_meter);
3185 last_meter->set_frame(where);
3189 //remove all the remaining metrics
3190 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3191 _metrics.remove(*i);
3196 recompute_map (_metrics);
3199 PropertyChanged (PropertyChange ());
3203 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3204 * pos can be -ve, if required.
3207 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3209 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3212 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3214 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3216 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3219 /** Add the BBT interval op to pos and return the result */
3221 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3223 Glib::Threads::RWLock::ReaderLock lm (lock);
3225 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3226 pos_bbt.ticks += op.ticks;
3227 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3229 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3231 pos_bbt.beats += op.beats;
3232 /* the meter in effect will start on the bar */
3233 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3234 while (pos_bbt.beats >= divisions_per_bar + 1) {
3236 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3237 pos_bbt.beats -= divisions_per_bar;
3239 pos_bbt.bars += op.bars;
3241 return frame_time_locked (_metrics, pos_bbt);
3244 /** Count the number of beats that are equivalent to distance when going forward,
3248 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3250 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3254 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3260 operator<< (std::ostream& o, const Meter& m) {
3261 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3265 operator<< (std::ostream& o, const Tempo& t) {
3266 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3270 operator<< (std::ostream& o, const MetricSection& section) {
3272 o << "MetricSection @ " << section.frame() << ' ';
3274 const TempoSection* ts;
3275 const MeterSection* ms;
3277 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3278 o << *((const Tempo*) ts);
3279 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3280 o << *((const Meter*) ms);