2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
77 , Tempo (TempoMap::default_tempo())
81 const XMLProperty *prop;
87 if ((prop = node.property ("start")) != 0) {
88 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
92 /* legacy session - start used to be in bbt*/
98 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
104 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
109 if ((prop = node.property ("frame")) != 0) {
110 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
111 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
117 if ((prop = node.property ("beats-per-minute")) == 0) {
118 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
119 throw failed_constructor();
122 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
123 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
124 throw failed_constructor();
127 if ((prop = node.property ("note-type")) == 0) {
128 /* older session, make note type be quarter by default */
131 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
132 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
133 throw failed_constructor();
137 if ((prop = node.property ("movable")) == 0) {
138 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
139 throw failed_constructor();
142 set_movable (string_is_affirmative (prop->value()));
144 if ((prop = node.property ("active")) == 0) {
145 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
148 set_active (string_is_affirmative (prop->value()));
151 if ((prop = node.property ("tempo-type")) == 0) {
154 _type = Type (string_2_enum (prop->value(), _type));
157 if ((prop = node.property ("lock-style")) == 0) {
158 set_position_lock_style (MusicTime);
160 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
165 TempoSection::get_state() const
167 XMLNode *root = new XMLNode (xml_state_node_name);
171 snprintf (buf, sizeof (buf), "%f", pulse());
172 root->add_property ("pulse", buf);
173 snprintf (buf, sizeof (buf), "%li", frame());
174 root->add_property ("frame", buf);
175 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
176 root->add_property ("beats-per-minute", buf);
177 snprintf (buf, sizeof (buf), "%f", _note_type);
178 root->add_property ("note-type", buf);
179 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
180 root->add_property ("movable", buf);
181 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
182 root->add_property ("active", buf);
183 root->add_property ("tempo-type", enum_2_string (_type));
184 root->add_property ("lock-style", enum_2_string (position_lock_style()));
190 TempoSection::set_type (Type type)
195 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
198 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
201 if (_type == Constant || _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*/
460 error << _("MeterSection XML node has no \"start\" property") << endmsg;
463 if ((prop = node.property ("pulse")) != 0) {
464 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
465 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
470 if ((prop = node.property ("beat")) != 0) {
471 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
472 error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
478 if ((prop = node.property ("bbt")) == 0) {
479 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
480 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
484 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
485 //throw failed_constructor();
491 if ((prop = node.property ("frame")) != 0) {
492 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
493 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
499 /* beats-per-bar is old; divisions-per-bar is new */
501 if ((prop = node.property ("divisions-per-bar")) == 0) {
502 if ((prop = node.property ("beats-per-bar")) == 0) {
503 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
504 throw failed_constructor();
507 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
508 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
509 throw failed_constructor();
512 if ((prop = node.property ("note-type")) == 0) {
513 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
514 throw failed_constructor();
516 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
517 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
518 throw failed_constructor();
521 if ((prop = node.property ("lock-style")) == 0) {
522 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
523 set_position_lock_style (MusicTime);
525 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
528 if ((prop = node.property ("movable")) == 0) {
529 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
530 throw failed_constructor();
533 set_movable (string_is_affirmative (prop->value()));
537 MeterSection::get_state() const
539 XMLNode *root = new XMLNode (xml_state_node_name);
543 snprintf (buf, sizeof (buf), "%lf", pulse());
544 root->add_property ("pulse", buf);
545 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
549 root->add_property ("bbt", buf);
550 snprintf (buf, sizeof (buf), "%lf", beat());
551 root->add_property ("beat", buf);
552 snprintf (buf, sizeof (buf), "%f", _note_type);
553 root->add_property ("note-type", buf);
554 snprintf (buf, sizeof (buf), "%li", frame());
555 root->add_property ("frame", buf);
556 root->add_property ("lock-style", enum_2_string (position_lock_style()));
557 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
558 root->add_property ("divisions-per-bar", buf);
559 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
560 root->add_property ("movable", buf);
565 /***********************************************************************/
569 Tempo can be thought of as a source of the musical pulse.
570 Meters divide that pulse into measures and beats.
571 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
572 at any particular time.
573 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
574 It should rather be thought of as tempo note divisions per minute.
576 TempoSections, which are nice to think of in whole pulses per minute,
577 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
578 and beats (via note_divisor) are used to form a tempo map.
579 TempoSections and MeterSections may be locked to either audio or music (position lock style).
580 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
581 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
583 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
585 The first tempo and first meter are special. they must move together, and must be locked to audio.
586 Audio locked tempos which lie before the first meter are made inactive.
587 They will be re-activated if the first meter is again placed before them.
589 Both tempos and meters have a pulse position and a frame position.
590 Meters also have a beat position, which is always 0.0 for the first meter.
592 A tempo locked to music is locked to musical pulses.
593 A meter locked to music is locked to beats.
595 Recomputing the tempo map is the process where the 'missing' position
596 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
598 It is important to keep the _metrics in an order that makes sense.
599 Because ramped MusicTime and AudioTime tempos can interact with each other,
600 reordering is frequent. Care must be taken to keep _metrics in a solved state.
601 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
603 struct MetricSectionSorter {
604 bool operator() (const MetricSection* a, const MetricSection* b) {
605 return a->pulse() < b->pulse();
609 struct MetricSectionFrameSorter {
610 bool operator() (const MetricSection* a, const MetricSection* b) {
611 return a->frame() < b->frame();
615 TempoMap::TempoMap (framecnt_t fr)
618 BBT_Time start (1, 1, 0);
620 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
621 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
623 t->set_movable (false);
624 m->set_movable (false);
626 /* note: frame time is correct (zero) for both of these */
628 _metrics.push_back (t);
629 _metrics.push_back (m);
633 TempoMap::~TempoMap ()
638 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
640 bool removed = false;
643 Glib::Threads::RWLock::WriterLock lm (lock);
644 if ((removed = remove_tempo_locked (tempo))) {
645 if (complete_operation) {
646 recompute_map (_metrics);
651 if (removed && complete_operation) {
652 PropertyChanged (PropertyChange ());
657 TempoMap::remove_tempo_locked (const TempoSection& tempo)
661 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
662 if (dynamic_cast<TempoSection*> (*i) != 0) {
663 if (tempo.frame() == (*i)->frame()) {
664 if ((*i)->movable()) {
676 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
678 bool removed = false;
681 Glib::Threads::RWLock::WriterLock lm (lock);
682 if ((removed = remove_meter_locked (tempo))) {
683 if (complete_operation) {
684 recompute_map (_metrics);
689 if (removed && complete_operation) {
690 PropertyChanged (PropertyChange ());
695 TempoMap::remove_meter_locked (const MeterSection& tempo)
699 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
700 if (dynamic_cast<MeterSection*> (*i) != 0) {
701 if (tempo.frame() == (*i)->frame()) {
702 if ((*i)->movable()) {
714 TempoMap::do_insert (MetricSection* section)
716 bool need_add = true;
717 /* we only allow new meters to be inserted on beat 1 of an existing
721 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
722 //assert (m->bbt().ticks == 0);
724 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
726 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
727 corrected.second.beats = 1;
728 corrected.second.ticks = 0;
729 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
730 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
731 m->bbt(), corrected.second) << endmsg;
732 //m->set_pulse (corrected);
736 /* Look for any existing MetricSection that is of the same type and
737 in the same bar as the new one, and remove it before adding
738 the new one. Note that this means that if we find a matching,
739 existing section, we can break out of the loop since we're
740 guaranteed that there is only one such match.
743 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
745 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
746 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
747 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
748 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
750 if (tempo && insert_tempo) {
753 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
754 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
756 if (!tempo->movable()) {
758 /* can't (re)move this section, so overwrite
759 * its data content (but not its properties as
763 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
764 (*i)->set_position_lock_style (AudioTime);
766 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
767 t->set_type (insert_tempo->type());
776 } else if (meter && insert_meter) {
780 bool const ipm = insert_meter->position_lock_style() == MusicTime;
782 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
784 if (!meter->movable()) {
786 /* can't (re)move this section, so overwrite
787 * its data content (but not its properties as
791 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
792 (*i)->set_position_lock_style (insert_meter->position_lock_style());
801 /* non-matching types, so we don't care */
805 /* Add the given MetricSection, if we didn't just reset an existing
810 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
811 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
814 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
815 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
818 bool const ipm = insert_meter->position_lock_style() == MusicTime;
819 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
824 } else if (insert_tempo) {
825 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
826 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
829 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
830 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
837 _metrics.insert (i, section);
838 //dump (_metrics, std::cerr);
843 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
846 Glib::Threads::RWLock::WriterLock lm (lock);
847 TempoSection& first (first_tempo());
848 if (ts.pulse() != first.pulse()) {
849 remove_tempo_locked (ts);
850 add_tempo_locked (tempo, pulse, true, type);
852 first.set_type (type);
854 /* cannot move the first tempo section */
855 *static_cast<Tempo*>(&first) = tempo;
856 recompute_map (_metrics);
861 PropertyChanged (PropertyChange ());
865 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
868 Glib::Threads::RWLock::WriterLock lm (lock);
869 TempoSection& first (first_tempo());
870 if (ts.frame() != first.frame()) {
871 remove_tempo_locked (ts);
872 add_tempo_locked (tempo, frame, true, type);
874 first.set_type (type);
875 first.set_pulse (0.0);
876 first.set_position_lock_style (AudioTime);
878 /* cannot move the first tempo section */
879 *static_cast<Tempo*>(&first) = tempo;
880 recompute_map (_metrics);
885 PropertyChanged (PropertyChange ());
889 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
891 TempoSection* ts = 0;
893 Glib::Threads::RWLock::WriterLock lm (lock);
894 ts = add_tempo_locked (tempo, pulse, true, type);
897 PropertyChanged (PropertyChange ());
903 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
905 TempoSection* ts = 0;
907 Glib::Threads::RWLock::WriterLock lm (lock);
908 ts = add_tempo_locked (tempo, frame, true, type);
912 PropertyChanged (PropertyChange ());
918 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
920 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
925 solve_map (_metrics, t, t->pulse());
932 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
934 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
939 solve_map (_metrics, t, t->frame());
946 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
949 Glib::Threads::RWLock::WriterLock lm (lock);
952 remove_meter_locked (ms);
953 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
955 MeterSection& first (first_meter());
956 const PositionLockStyle pl = ms.position_lock_style();
957 /* cannot move the first meter section */
958 *static_cast<Meter*>(&first) = meter;
959 first.set_position_lock_style (pl);
961 recompute_map (_metrics);
964 PropertyChanged (PropertyChange ());
968 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
971 Glib::Threads::RWLock::WriterLock lm (lock);
973 const double beat = ms.beat();
974 const BBT_Time bbt = ms.bbt();
977 remove_meter_locked (ms);
978 add_meter_locked (meter, frame, beat, bbt, true);
980 MeterSection& first (first_meter());
981 TempoSection& first_t (first_tempo());
982 /* cannot move the first meter section */
983 *static_cast<Meter*>(&first) = meter;
984 first.set_position_lock_style (AudioTime);
985 first.set_pulse (0.0);
986 first.set_frame (frame);
987 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
988 first.set_beat (beat);
989 first_t.set_frame (first.frame());
990 first_t.set_pulse (0.0);
991 first_t.set_position_lock_style (AudioTime);
993 recompute_map (_metrics);
995 PropertyChanged (PropertyChange ());
1000 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1002 MeterSection* m = 0;
1004 Glib::Threads::RWLock::WriterLock lm (lock);
1005 m = add_meter_locked (meter, beat, where, true);
1010 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1011 dump (_metrics, std::cerr);
1015 PropertyChanged (PropertyChange ());
1021 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1023 MeterSection* m = 0;
1025 Glib::Threads::RWLock::WriterLock lm (lock);
1026 m = add_meter_locked (meter, frame, beat, where, true);
1031 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1032 dump (_metrics, std::cerr);
1036 PropertyChanged (PropertyChange ());
1042 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1044 /* a new meter always starts a new bar on the first beat. so
1045 round the start time appropriately. remember that
1046 `where' is based on the existing tempo map, not
1047 the result after we insert the new meter.
1051 if (where.beats != 1) {
1055 /* new meters *always* start on a beat. */
1057 const double pulse = pulse_at_beat_locked (_metrics, beat);
1058 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1059 do_insert (new_meter);
1062 solve_map (_metrics, new_meter, pulse);
1069 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1071 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1073 double pulse = pulse_at_frame_locked (_metrics, frame);
1074 new_meter->set_pulse (pulse);
1076 do_insert (new_meter);
1079 solve_map (_metrics, new_meter, frame);
1086 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1088 Tempo newtempo (beats_per_minute, note_type);
1091 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1092 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1097 Glib::Threads::RWLock::WriterLock lm (lock);
1098 *((Tempo*) t) = newtempo;
1099 recompute_map (_metrics);
1101 PropertyChanged (PropertyChange ());
1108 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1110 Tempo newtempo (beats_per_minute, note_type);
1113 TempoSection* first;
1114 Metrics::iterator i;
1116 /* find the TempoSection immediately preceding "where"
1119 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1121 if ((*i)->frame() > where) {
1127 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1140 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1150 Glib::Threads::RWLock::WriterLock lm (lock);
1151 /* cannot move the first tempo section */
1152 *((Tempo*)prev) = newtempo;
1153 recompute_map (_metrics);
1156 PropertyChanged (PropertyChange ());
1160 TempoMap::first_meter () const
1162 const MeterSection *m = 0;
1164 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1165 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1170 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1171 abort(); /*NOTREACHED*/
1176 TempoMap::first_meter ()
1178 MeterSection *m = 0;
1180 /* CALLER MUST HOLD LOCK */
1182 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1183 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1188 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1189 abort(); /*NOTREACHED*/
1194 TempoMap::first_tempo () const
1196 const TempoSection *t = 0;
1198 /* CALLER MUST HOLD LOCK */
1200 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1201 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1205 if (!t->movable()) {
1211 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1212 abort(); /*NOTREACHED*/
1217 TempoMap::first_tempo ()
1219 TempoSection *t = 0;
1221 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1222 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1226 if (!t->movable()) {
1232 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1233 abort(); /*NOTREACHED*/
1237 TempoMap::recompute_tempos (Metrics& metrics)
1239 TempoSection* prev_t = 0;
1241 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1244 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1248 if (!t->movable()) {
1254 if (t->position_lock_style() == AudioTime) {
1255 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1256 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1259 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1260 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1267 prev_t->set_c_func (0.0);
1270 /* tempos must be positioned correctly */
1272 TempoMap::recompute_meters (Metrics& metrics)
1274 MeterSection* meter = 0;
1275 MeterSection* prev_m = 0;
1277 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1278 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1279 if (meter->position_lock_style() == AudioTime) {
1281 pair<double, BBT_Time> b_bbt;
1282 if (meter->movable()) {
1283 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor());
1284 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1285 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1286 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1287 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1288 pulse = true_pulse - pulse_off;
1290 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1292 meter->set_beat (b_bbt);
1293 meter->set_pulse (pulse);
1297 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1299 /* shouldn't happen - the first is audio-locked */
1300 pulse = pulse_at_beat_locked (metrics, meter->beat());
1302 pair<double, BBT_Time> new_beat (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1303 meter->set_beat (new_beat);
1304 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1305 meter->set_pulse (pulse);
1311 //dump (_metrics, std::cerr;
1315 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1317 /* CALLER MUST HOLD WRITE LOCK */
1321 /* we will actually stop once we hit
1328 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1331 /* silly call from Session::process() during startup
1336 recompute_tempos (metrics);
1337 recompute_meters (metrics);
1341 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1343 Glib::Threads::RWLock::ReaderLock lm (lock);
1344 TempoMetric m (first_meter(), first_tempo());
1346 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1347 at something, because we insert the default tempo and meter during
1348 TempoMap construction.
1350 now see if we can find better candidates.
1353 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1355 if ((*i)->frame() > frame) {
1369 /* XX meters only */
1371 TempoMap::metric_at (BBT_Time bbt) const
1373 Glib::Threads::RWLock::ReaderLock lm (lock);
1374 TempoMetric m (first_meter(), first_tempo());
1376 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1377 at something, because we insert the default tempo and meter during
1378 TempoMap construction.
1380 now see if we can find better candidates.
1383 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1385 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1386 BBT_Time section_start (mw->bbt());
1388 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1400 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1402 MeterSection* prev_m = 0;
1404 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1406 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1407 if (prev_m && m->beat() > beat) {
1414 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1419 TempoMap::pulse_at_beat (const double& beat) const
1421 Glib::Threads::RWLock::ReaderLock lm (lock);
1422 return pulse_at_beat_locked (_metrics, beat);
1426 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1428 MeterSection* prev_m = 0;
1430 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1432 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1433 if (prev_m && m->pulse() > pulse) {
1440 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1442 return beats_in_section + prev_m->beat();
1446 TempoMap::beat_at_pulse (const double& pulse) const
1448 Glib::Threads::RWLock::ReaderLock lm (lock);
1449 return beat_at_pulse_locked (_metrics, pulse);
1453 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1455 /* HOLD (at least) THE READER LOCK */
1456 TempoSection* prev_t = 0;
1458 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1460 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1464 if (prev_t && t->frame() > frame) {
1465 /*the previous ts is the one containing the frame */
1466 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1473 /* treated as constant for this ts */
1474 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1476 return pulses_in_section + prev_t->pulse();
1480 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1482 /* HOLD THE READER LOCK */
1484 const TempoSection* prev_t = 0;
1486 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1489 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1493 if (prev_t && t->pulse() > pulse) {
1494 return prev_t->frame_at_pulse (pulse, _frame_rate);
1500 /* must be treated as constant, irrespective of _type */
1501 double const pulses_in_section = pulse - prev_t->pulse();
1502 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1504 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1510 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1512 const double pulse = pulse_at_frame_locked (metrics, frame);
1513 return beat_at_pulse_locked (metrics, pulse);
1517 TempoMap::beat_at_frame (const framecnt_t& frame) const
1519 Glib::Threads::RWLock::ReaderLock lm (lock);
1520 return beat_at_frame_locked (_metrics, frame);
1524 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1526 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1531 TempoMap::frame_at_beat (const double& beat) const
1533 Glib::Threads::RWLock::ReaderLock lm (lock);
1534 return frame_at_beat_locked (_metrics, beat);
1538 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1540 /* CALLER HOLDS READ LOCK */
1542 MeterSection* prev_m = 0;
1544 /* because audio-locked meters have 'fake' integral beats,
1545 there is no pulse offset here.
1547 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1549 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1551 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1552 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1560 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1561 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1562 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1568 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1570 Glib::Threads::RWLock::ReaderLock lm (lock);
1571 return bbt_to_beats_locked (_metrics, bbt);
1575 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1577 /* CALLER HOLDS READ LOCK */
1578 MeterSection* prev_m = 0;
1579 const double beats = (b < 0.0) ? 0.0 : b;
1581 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1582 MeterSection* m = 0;
1584 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1586 if (m->beat() > beats) {
1587 /* this is the meter after the one our beat is on*/
1596 const double beats_in_ms = beats - prev_m->beat();
1597 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1598 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1599 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1600 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1604 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1605 ret.beats = (uint32_t) floor (remaining_beats);
1606 ret.bars = total_bars;
1608 /* 0 0 0 to 1 1 0 - based mapping*/
1612 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1614 ret.ticks -= BBT_Time::ticks_per_beat;
1617 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1626 TempoMap::beats_to_bbt (const double& beats)
1628 Glib::Threads::RWLock::ReaderLock lm (lock);
1629 return beats_to_bbt_locked (_metrics, beats);
1633 TempoMap::pulse_to_bbt (const double& pulse)
1635 Glib::Threads::RWLock::ReaderLock lm (lock);
1636 MeterSection* prev_m = 0;
1638 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1639 MeterSection* m = 0;
1641 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1644 double const pulses_to_m = m->pulse() - prev_m->pulse();
1645 if (prev_m->pulse() + pulses_to_m > pulse) {
1646 /* this is the meter after the one our beat is on*/
1655 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1656 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1657 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1658 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1659 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1663 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1664 ret.beats = (uint32_t) floor (remaining_beats);
1665 ret.bars = total_bars;
1667 /* 0 0 0 to 1 1 0 mapping*/
1671 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1673 ret.ticks -= BBT_Time::ticks_per_beat;
1676 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1685 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1692 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1695 Glib::Threads::RWLock::ReaderLock lm (lock);
1696 const double beat = beat_at_frame_locked (_metrics, frame);
1698 bbt = beats_to_bbt_locked (_metrics, beat);
1702 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1704 /* HOLD THE READER LOCK */
1706 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1711 TempoMap::frame_time (const BBT_Time& bbt)
1714 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1718 if (bbt.beats < 1) {
1719 throw std::logic_error ("beats are counted from one");
1721 Glib::Threads::RWLock::ReaderLock lm (lock);
1723 return frame_time_locked (_metrics, bbt);
1727 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1729 TempoSection* prev_t = 0;
1731 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1733 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1738 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1742 if (t->frame() == prev_t->frame()) {
1746 /* precision check ensures pulses and frames align.*/
1747 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1759 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1761 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1763 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1764 if (!t->movable()) {
1765 t->set_active (true);
1768 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1769 t->set_active (false);
1771 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1772 t->set_active (true);
1773 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1782 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1784 TempoSection* prev_t = 0;
1785 TempoSection* section_prev = 0;
1786 framepos_t first_m_frame = 0;
1788 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1790 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1791 if (!m->movable()) {
1792 first_m_frame = m->frame();
1797 if (section->movable() && frame <= first_m_frame) {
1801 section->set_active (true);
1802 section->set_frame (frame);
1804 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1806 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1813 section_prev = prev_t;
1816 if (t->position_lock_style() == MusicTime) {
1817 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1818 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1820 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1821 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1829 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1830 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1833 if (section->position_lock_style() == MusicTime) {
1834 /* we're setting the frame */
1835 section->set_position_lock_style (AudioTime);
1836 recompute_tempos (imaginary);
1837 section->set_position_lock_style (MusicTime);
1839 recompute_tempos (imaginary);
1842 if (check_solved (imaginary, true)) {
1843 recompute_meters (imaginary);
1847 MetricSectionFrameSorter fcmp;
1848 imaginary.sort (fcmp);
1849 if (section->position_lock_style() == MusicTime) {
1850 /* we're setting the frame */
1851 section->set_position_lock_style (AudioTime);
1852 recompute_tempos (imaginary);
1853 section->set_position_lock_style (MusicTime);
1855 recompute_tempos (imaginary);
1858 if (check_solved (imaginary, true)) {
1859 recompute_meters (imaginary);
1863 MetricSectionSorter cmp;
1864 imaginary.sort (cmp);
1865 if (section->position_lock_style() == MusicTime) {
1866 /* we're setting the frame */
1867 section->set_position_lock_style (AudioTime);
1868 recompute_tempos (imaginary);
1869 section->set_position_lock_style (MusicTime);
1871 recompute_tempos (imaginary);
1874 if (check_solved (imaginary, true)) {
1875 recompute_meters (imaginary);
1879 //dump (imaginary, std::cerr);
1885 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1887 TempoSection* prev_t = 0;
1888 TempoSection* section_prev = 0;
1890 section->set_pulse (pulse);
1892 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1894 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1898 if (!t->movable()) {
1905 section_prev = prev_t;
1908 if (t->position_lock_style() == MusicTime) {
1909 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1910 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1912 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1913 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1920 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1921 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1924 if (section->position_lock_style() == AudioTime) {
1925 /* we're setting the pulse */
1926 section->set_position_lock_style (MusicTime);
1927 recompute_tempos (imaginary);
1928 section->set_position_lock_style (AudioTime);
1930 recompute_tempos (imaginary);
1933 if (check_solved (imaginary, false)) {
1934 recompute_meters (imaginary);
1938 MetricSectionSorter cmp;
1939 imaginary.sort (cmp);
1940 if (section->position_lock_style() == AudioTime) {
1941 /* we're setting the pulse */
1942 section->set_position_lock_style (MusicTime);
1943 recompute_tempos (imaginary);
1944 section->set_position_lock_style (AudioTime);
1946 recompute_tempos (imaginary);
1949 if (check_solved (imaginary, false)) {
1950 recompute_meters (imaginary);
1954 MetricSectionFrameSorter fcmp;
1955 imaginary.sort (fcmp);
1956 if (section->position_lock_style() == AudioTime) {
1957 /* we're setting the pulse */
1958 section->set_position_lock_style (MusicTime);
1959 recompute_tempos (imaginary);
1960 section->set_position_lock_style (AudioTime);
1962 recompute_tempos (imaginary);
1965 if (check_solved (imaginary, false)) {
1966 recompute_meters (imaginary);
1970 //dump (imaginary, std::cerr);
1976 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
1978 MeterSection* prev_m = 0;
1980 if (!section->movable()) {
1981 /* lock the first tempo to our first meter */
1982 if (!set_active_tempos (imaginary, frame)) {
1985 TempoSection* first_t = &first_tempo();
1987 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1989 new_section->set_frame (frame);
1990 new_section->set_pulse (0.0);
1991 new_section->set_active (true);
1993 if (solve_map (future_map, new_section, frame)) {
1994 first_t->set_frame (frame);
1995 first_t->set_pulse (0.0);
1996 first_t->set_active (true);
1997 solve_map (imaginary, first_t, frame);
2003 section->set_frame (frame);
2005 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2007 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2010 here we set the beat for this frame.
2011 we set it 'incorrectly' to the next bar's first beat
2012 and use the delat to find the meter's pulse.
2014 double new_pulse = 0.0;
2015 pair<double, BBT_Time> b_bbt;
2017 if (section->movable()) {
2018 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2019 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2020 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2021 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2022 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2023 new_pulse = true_pulse - pulse_off;
2025 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2028 section->set_beat (b_bbt);
2029 section->set_pulse (new_pulse);
2035 double new_pulse = 0.0;
2036 if (m->position_lock_style() == MusicTime) {
2037 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2038 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2039 if (m->frame() > section->frame()) {
2040 /* moving 'section' will affect later meters' beat (but not bbt).*/
2041 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2042 m->set_beat (new_beat);
2045 pair<double, BBT_Time> b_bbt;
2047 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2048 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2049 b_bbt = make_pair (floor_beats + prev_m->beat()
2050 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2051 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2052 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2053 new_pulse = true_pulse - pulse_off;
2055 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2058 m->set_beat (b_bbt);
2060 m->set_pulse (new_pulse);
2066 MetricSectionFrameSorter fcmp;
2067 imaginary.sort (fcmp);
2068 if (section->position_lock_style() == MusicTime) {
2069 /* we're setting the frame */
2070 section->set_position_lock_style (AudioTime);
2071 recompute_meters (imaginary);
2072 section->set_position_lock_style (MusicTime);
2074 recompute_meters (imaginary);
2076 //dump (imaginary, std::cerr);
2080 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2082 MeterSection* prev_m = 0;
2083 MeterSection* section_prev = 0;
2085 section->set_pulse (pulse);
2087 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2089 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2091 section_prev = prev_m;
2095 double new_pulse = 0.0;
2096 if (m->position_lock_style() == MusicTime) {
2097 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2098 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2100 if (new_pulse > section->pulse()) {
2101 /* moving 'section' will affect later meters' beat (but not bbt).*/
2102 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2103 m->set_beat (new_beat);
2106 pair<double, BBT_Time> b_bbt;
2108 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2109 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2110 b_bbt = make_pair (floor_beats + prev_m->beat()
2111 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2112 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2113 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2114 new_pulse = true_pulse - pulse_off;
2116 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2118 m->set_beat (b_bbt);
2120 m->set_pulse (new_pulse);
2127 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2128 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2129 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2130 section->set_beat (b_bbt);
2131 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2134 MetricSectionSorter cmp;
2135 imaginary.sort (cmp);
2136 if (section->position_lock_style() == AudioTime) {
2137 /* we're setting the pulse */
2138 section->set_position_lock_style (MusicTime);
2139 recompute_meters (imaginary);
2140 section->set_position_lock_style (AudioTime);
2142 recompute_meters (imaginary);
2146 /** places a copy of _metrics into copy and returns a pointer
2147 * to section's equivalent in copy.
2150 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2154 TempoSection* ret = 0;
2156 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2157 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2159 if (t->position_lock_style() == MusicTime) {
2160 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2161 ret->set_frame (t->frame());
2163 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2164 ret->set_pulse (t->pulse());
2166 ret->set_active (t->active());
2167 ret->set_movable (t->movable());
2168 copy.push_back (ret);
2171 TempoSection* cp = 0;
2172 if (t->position_lock_style() == MusicTime) {
2173 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2174 cp->set_frame (t->frame());
2176 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2177 cp->set_pulse (t->pulse());
2179 cp->set_active (t->active());
2180 cp->set_movable (t->movable());
2181 copy.push_back (cp);
2183 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2184 MeterSection* cp = 0;
2185 if (m->position_lock_style() == MusicTime) {
2186 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2187 cp->set_frame (m->frame());
2189 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2190 cp->set_pulse (m->pulse());
2192 cp->set_movable (m->movable());
2193 copy.push_back (cp);
2201 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2204 TempoSection* new_section = 0;
2207 Glib::Threads::RWLock::ReaderLock lm (lock);
2208 new_section = copy_metrics_and_point (copy, ts);
2211 double const beat = bbt_to_beats_locked (copy, bbt);
2212 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2214 Metrics::const_iterator d = copy.begin();
2215 while (d != copy.end()) {
2224 * 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,
2225 * taking any possible reordering as a consequence of this into account.
2226 * @param section - the section to be altered
2227 * @param bpm - the new Tempo
2228 * @param bbt - the bbt where the altered tempo will fall
2229 * @return returns - the position in frames where the new tempo section will lie.
2232 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2234 Glib::Threads::RWLock::ReaderLock lm (lock);
2237 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2239 const double beat = bbt_to_beats_locked (future_map, bbt);
2241 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2242 ret = new_section->frame();
2244 ret = frame_at_beat_locked (_metrics, beat);
2247 Metrics::const_iterator d = future_map.begin();
2248 while (d != future_map.end()) {
2256 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2258 Glib::Threads::RWLock::ReaderLock lm (lock);
2261 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2263 if (solve_map (future_map, new_section, frame)) {
2264 ret = new_section->pulse();
2266 ret = pulse_at_frame_locked (_metrics, frame);
2269 Metrics::const_iterator d = future_map.begin();
2270 while (d != future_map.end()) {
2278 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2282 Glib::Threads::RWLock::WriterLock lm (lock);
2283 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2284 if (solve_map (future_map, new_section, frame)) {
2285 solve_map (_metrics, ts, frame);
2289 Metrics::const_iterator d = future_map.begin();
2290 while (d != future_map.end()) {
2295 MetricPositionChanged (); // Emit Signal
2299 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2303 Glib::Threads::RWLock::WriterLock lm (lock);
2304 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2305 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2306 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2310 Metrics::const_iterator d = future_map.begin();
2311 while (d != future_map.end()) {
2316 MetricPositionChanged (); // Emit Signal
2320 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2323 Glib::Threads::RWLock::WriterLock lm (lock);
2324 solve_map (_metrics, ms, frame);
2327 MetricPositionChanged (); // Emit Signal
2331 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2334 Glib::Threads::RWLock::WriterLock lm (lock);
2335 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2338 MetricPositionChanged (); // Emit Signal
2342 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2345 bool can_solve = false;
2347 Glib::Threads::RWLock::WriterLock lm (lock);
2348 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2349 new_section->set_beats_per_minute (bpm.beats_per_minute());
2350 recompute_tempos (future_map);
2352 if (check_solved (future_map, true)) {
2353 ts->set_beats_per_minute (bpm.beats_per_minute());
2354 recompute_map (_metrics);
2359 Metrics::const_iterator d = future_map.begin();
2360 while (d != future_map.end()) {
2365 MetricPositionChanged (); // Emit Signal
2371 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2373 Glib::Threads::RWLock::ReaderLock lm (lock);
2375 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2376 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2377 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2379 return frame_at_beat_locked (_metrics, total_beats);
2383 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2385 return round_to_type (fr, dir, Bar);
2389 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2391 return round_to_type (fr, dir, Beat);
2395 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2397 Glib::Threads::RWLock::ReaderLock lm (lock);
2398 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2399 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2400 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2402 ticks -= beats * BBT_Time::ticks_per_beat;
2405 /* round to next (or same iff dir == RoundUpMaybe) */
2407 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2409 if (mod == 0 && dir == RoundUpMaybe) {
2410 /* right on the subdivision, which is fine, so do nothing */
2412 } else if (mod == 0) {
2413 /* right on the subdivision, so the difference is just the subdivision ticks */
2414 ticks += ticks_one_subdivisions_worth;
2417 /* not on subdivision, compute distance to next subdivision */
2419 ticks += ticks_one_subdivisions_worth - mod;
2422 if (ticks >= BBT_Time::ticks_per_beat) {
2423 ticks -= BBT_Time::ticks_per_beat;
2425 } else if (dir < 0) {
2427 /* round to previous (or same iff dir == RoundDownMaybe) */
2429 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2431 if (difference == 0 && dir == RoundDownAlways) {
2432 /* right on the subdivision, but force-rounding down,
2433 so the difference is just the subdivision ticks */
2434 difference = ticks_one_subdivisions_worth;
2437 if (ticks < difference) {
2438 ticks = BBT_Time::ticks_per_beat - ticks;
2440 ticks -= difference;
2444 /* round to nearest */
2447 /* compute the distance to the previous and next subdivision */
2449 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2451 /* closer to the next subdivision, so shift forward */
2453 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2455 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2457 if (ticks > BBT_Time::ticks_per_beat) {
2459 ticks -= BBT_Time::ticks_per_beat;
2460 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2463 } else if (rem > 0) {
2465 /* closer to previous subdivision, so shift backward */
2469 /* can't go backwards past zero, so ... */
2472 /* step back to previous beat */
2474 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2475 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2477 ticks = lrint (ticks - rem);
2478 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2481 /* on the subdivision, do nothing */
2485 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2491 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2493 if (sub_num == -1) {
2494 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2495 if ((double) when.beats > bpb / 2.0) {
2501 } else if (sub_num == 0) {
2502 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2503 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2505 while ((double) when.beats > bpb) {
2507 when.beats -= (uint32_t) floor (bpb);
2513 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2515 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2516 /* closer to the next subdivision, so shift forward */
2518 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2520 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2522 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2525 } else if (rem > 0) {
2526 /* closer to previous subdivision, so shift backward */
2528 if (rem > when.ticks) {
2529 if (when.beats == 0) {
2530 /* can't go backwards past zero, so ... */
2532 /* step back to previous beat */
2534 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2536 when.ticks = when.ticks - rem;
2542 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2544 Glib::Threads::RWLock::ReaderLock lm (lock);
2546 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2547 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2552 /* find bar previous to 'frame' */
2555 return frame_time_locked (_metrics, bbt);
2557 } else if (dir > 0) {
2558 /* find bar following 'frame' */
2562 return frame_time_locked (_metrics, bbt);
2564 /* true rounding: find nearest bar */
2565 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2568 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2570 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2572 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2583 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2584 } else if (dir > 0) {
2585 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2587 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2596 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2597 framepos_t lower, framepos_t upper)
2599 Glib::Threads::RWLock::ReaderLock lm (lock);
2600 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2601 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2602 /* although the map handles negative beats, bbt doesn't. */
2606 while (cnt <= upper_beat) {
2607 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2608 const TempoSection tempo = tempo_section_at_locked (pos);
2609 const MeterSection meter = meter_section_at_locked (pos);
2610 const BBT_Time bbt = beats_to_bbt (cnt);
2611 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
2617 TempoMap::tempo_section_at (framepos_t frame) const
2619 Glib::Threads::RWLock::ReaderLock lm (lock);
2620 return tempo_section_at_locked (frame);
2624 TempoMap::tempo_section_at_locked (framepos_t frame) const
2626 Metrics::const_iterator i;
2627 TempoSection* prev = 0;
2629 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2632 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2636 if (t->frame() > frame) {
2646 abort(); /*NOTREACHED*/
2653 /* don't use this to calculate length (the tempo is only correct for this frame).
2654 do that stuff based on the beat_at_frame and frame_at_beat api
2657 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2659 Glib::Threads::RWLock::ReaderLock lm (lock);
2661 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2662 const TempoSection* ts_after = 0;
2663 Metrics::const_iterator i;
2665 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2668 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2672 if ((*i)->frame() > frame) {
2680 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2682 /* must be treated as constant tempo */
2683 return ts_at->frames_per_beat (_frame_rate);
2687 TempoMap::tempo_at (const framepos_t& frame) const
2689 Glib::Threads::RWLock::ReaderLock lm (lock);
2690 return tempo_at_locked (frame);
2694 TempoMap::tempo_at_locked (const framepos_t& frame) const
2696 TempoSection* prev_t = 0;
2698 Metrics::const_iterator i;
2700 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2702 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2706 if ((prev_t) && t->frame() > frame) {
2707 /* t is the section past frame */
2708 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2709 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2716 const double ret = prev_t->beats_per_minute();
2717 const Tempo ret_tempo (ret, prev_t->note_type ());
2723 TempoMap::meter_section_at (framepos_t frame) const
2725 Glib::Threads::RWLock::ReaderLock lm (lock);
2726 return meter_section_at_locked (frame);
2730 TempoMap::meter_section_at_locked (framepos_t frame) const
2732 Metrics::const_iterator i;
2733 MeterSection* prev = 0;
2735 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2738 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2740 if (prev && (*i)->frame() > frame) {
2750 abort(); /*NOTREACHED*/
2757 TempoMap::meter_at (framepos_t frame) const
2759 TempoMetric m (metric_at (frame));
2764 TempoMap::meter_section_at (const double& beat) const
2766 MeterSection* prev_m = 0;
2767 Glib::Threads::RWLock::ReaderLock lm (lock);
2769 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2771 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2772 if (prev_m && m->beat() > beat) {
2783 TempoMap::get_state ()
2785 Metrics::const_iterator i;
2786 XMLNode *root = new XMLNode ("TempoMap");
2789 Glib::Threads::RWLock::ReaderLock lm (lock);
2790 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2791 root->add_child_nocopy ((*i)->get_state());
2799 TempoMap::set_state (const XMLNode& node, int /*version*/)
2802 Glib::Threads::RWLock::WriterLock lm (lock);
2805 XMLNodeConstIterator niter;
2806 Metrics old_metrics (_metrics);
2809 nlist = node.children();
2811 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2812 XMLNode* child = *niter;
2814 if (child->name() == TempoSection::xml_state_node_name) {
2817 TempoSection* ts = new TempoSection (*child);
2818 _metrics.push_back (ts);
2821 catch (failed_constructor& err){
2822 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2823 _metrics = old_metrics;
2827 } else if (child->name() == MeterSection::xml_state_node_name) {
2830 MeterSection* ms = new MeterSection (*child);
2831 _metrics.push_back (ms);
2834 catch (failed_constructor& err) {
2835 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2836 _metrics = old_metrics;
2842 if (niter == nlist.end()) {
2843 MetricSectionSorter cmp;
2844 _metrics.sort (cmp);
2846 /* check for legacy sessions where bbt was the base musical unit for tempo */
2847 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2850 MeterSection* prev_m = 0;
2851 TempoSection* prev_t = 0;
2853 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2854 if (!m->movable()) {
2855 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2859 m->set_position_lock_style (AudioTime);
2863 if (prev_m && prev_m->pulse() < 0.0) {
2864 /*XX we cannot possibly make this work??. */
2865 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * m->note_divisor())
2866 + (m->bbt().beats - 1)
2867 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2869 m->set_beat (start);
2870 const double start_beat = ((m->bbt().bars - 1) * m->note_divisor())
2871 + (m->bbt().beats - 1)
2872 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2873 m->set_pulse (start_beat / prev_m->note_divisor());
2876 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2880 if (!t->movable()) {
2883 t->set_position_lock_style (AudioTime);
2887 if (prev_t && prev_t->pulse() < 0.0) {
2888 double const beat = ((t->legacy_bbt().bars - 1) * prev_m->note_divisor()) + (t->legacy_bbt().beats - 1) + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2890 t->set_pulse (beat / prev_m->note_divisor());
2892 /* really shouldn't happen but.. */
2893 t->set_pulse (beat / 4.0);
2899 /* check for multiple tempo/meters at the same location, which
2900 ardour2 somehow allowed.
2903 Metrics::iterator prev = _metrics.end();
2904 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2905 if (prev != _metrics.end()) {
2907 MeterSection* prev_m;
2909 TempoSection* prev_t;
2910 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2911 if (prev_m->pulse() == ms->pulse()) {
2912 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2913 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2916 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2917 if (prev_t->pulse() == ts->pulse()) {
2918 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2919 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2927 recompute_map (_metrics);
2930 PropertyChanged (PropertyChange ());
2936 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2938 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2939 const MeterSection* m;
2940 const TempoSection* t;
2941 const TempoSection* prev_t = 0;
2943 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2945 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2946 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2947 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2948 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2950 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
2951 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;
2954 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2955 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2956 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2959 o << "------" << std::endl;
2963 TempoMap::n_tempos() const
2965 Glib::Threads::RWLock::ReaderLock lm (lock);
2968 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2969 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2978 TempoMap::n_meters() const
2980 Glib::Threads::RWLock::ReaderLock lm (lock);
2983 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2984 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2993 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2996 Glib::Threads::RWLock::WriterLock lm (lock);
2997 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2998 if ((*i)->frame() >= where && (*i)->movable ()) {
2999 (*i)->set_frame ((*i)->frame() + amount);
3003 /* now reset the BBT time of all metrics, based on their new
3004 * audio time. This is the only place where we do this reverse
3008 Metrics::iterator i;
3009 const MeterSection* meter;
3010 const TempoSection* tempo;
3014 meter = &first_meter ();
3015 tempo = &first_tempo ();
3020 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3023 MetricSection* prev = 0;
3025 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3028 //TempoMetric metric (*meter, *tempo);
3029 MeterSection* ms = const_cast<MeterSection*>(meter);
3030 TempoSection* ts = const_cast<TempoSection*>(tempo);
3033 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3037 ts->set_pulse (t->pulse());
3039 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3040 ts->set_pulse (m->pulse());
3042 ts->set_frame (prev->frame());
3046 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3047 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3048 ms->set_beat (start);
3049 ms->set_pulse (m->pulse());
3051 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3055 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3056 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3057 ms->set_beat (start);
3058 ms->set_pulse (t->pulse());
3060 ms->set_frame (prev->frame());
3064 // metric will be at frames=0 bbt=1|1|0 by default
3065 // which is correct for our purpose
3068 // cerr << bbt << endl;
3070 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3074 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3076 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3077 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3078 bbt_time (m->frame(), bbt);
3080 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3086 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3087 /* round up to next beat */
3093 if (bbt.beats != 1) {
3094 /* round up to next bar */
3099 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3100 m->set_beat (start);
3101 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3103 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3105 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3106 abort(); /*NOTREACHED*/
3112 recompute_map (_metrics);
3116 PropertyChanged (PropertyChange ());
3119 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3123 std::list<MetricSection*> metric_kill_list;
3125 TempoSection* last_tempo = NULL;
3126 MeterSection* last_meter = NULL;
3127 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3128 bool meter_after = false; // is there a meter marker likewise?
3130 Glib::Threads::RWLock::WriterLock lm (lock);
3131 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3132 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3133 metric_kill_list.push_back(*i);
3134 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3137 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3141 else if ((*i)->frame() >= where) {
3142 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3143 (*i)->set_frame ((*i)->frame() - amount);
3144 if ((*i)->frame() == where) {
3145 // marker was immediately after end of range
3146 tempo_after = dynamic_cast<TempoSection*> (*i);
3147 meter_after = dynamic_cast<MeterSection*> (*i);
3153 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3154 if (last_tempo && !tempo_after) {
3155 metric_kill_list.remove(last_tempo);
3156 last_tempo->set_frame(where);
3159 if (last_meter && !meter_after) {
3160 metric_kill_list.remove(last_meter);
3161 last_meter->set_frame(where);
3165 //remove all the remaining metrics
3166 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3167 _metrics.remove(*i);
3172 recompute_map (_metrics);
3175 PropertyChanged (PropertyChange ());
3179 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3180 * pos can be -ve, if required.
3183 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3185 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3188 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3190 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3192 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3195 /** Add the BBT interval op to pos and return the result */
3197 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3199 Glib::Threads::RWLock::ReaderLock lm (lock);
3201 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3202 pos_bbt.ticks += op.ticks;
3203 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3205 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3207 pos_bbt.beats += op.beats;
3208 /* the meter in effect will start on the bar */
3209 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3210 while (pos_bbt.beats >= divisions_per_bar + 1) {
3212 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3213 pos_bbt.beats -= divisions_per_bar;
3215 pos_bbt.bars += op.bars;
3217 return frame_time_locked (_metrics, pos_bbt);
3220 /** Count the number of beats that are equivalent to distance when going forward,
3224 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3226 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3230 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3236 operator<< (std::ostream& o, const Meter& m) {
3237 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3241 operator<< (std::ostream& o, const Tempo& t) {
3242 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3246 operator<< (std::ostream& o, const MetricSection& section) {
3248 o << "MetricSection @ " << section.frame() << ' ';
3250 const TempoSection* ts;
3251 const MeterSection* ms;
3253 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3254 o << *((const Tempo*) ts);
3255 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3256 o << *((const Meter*) ms);