2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
77 , Tempo (TempoMap::default_tempo())
81 const XMLProperty *prop;
87 if ((prop = node.property ("start")) != 0) {
88 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
92 /* legacy session - start used to be in bbt*/
98 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
104 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
109 if ((prop = node.property ("frame")) != 0) {
110 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
111 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
117 if ((prop = node.property ("beats-per-minute")) == 0) {
118 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
119 throw failed_constructor();
122 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
123 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
124 throw failed_constructor();
127 if ((prop = node.property ("note-type")) == 0) {
128 /* older session, make note type be quarter by default */
131 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
132 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
133 throw failed_constructor();
137 if ((prop = node.property ("movable")) == 0) {
138 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
139 throw failed_constructor();
142 set_movable (string_is_affirmative (prop->value()));
144 if ((prop = node.property ("active")) == 0) {
145 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
148 set_active (string_is_affirmative (prop->value()));
151 if ((prop = node.property ("tempo-type")) == 0) {
154 _type = Type (string_2_enum (prop->value(), _type));
157 if ((prop = node.property ("lock-style")) == 0) {
158 set_position_lock_style (MusicTime);
160 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
165 TempoSection::get_state() const
167 XMLNode *root = new XMLNode (xml_state_node_name);
171 snprintf (buf, sizeof (buf), "%f", pulse());
172 root->add_property ("pulse", buf);
173 snprintf (buf, sizeof (buf), "%li", frame());
174 root->add_property ("frame", buf);
175 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
176 root->add_property ("beats-per-minute", buf);
177 snprintf (buf, sizeof (buf), "%f", _note_type);
178 root->add_property ("note-type", buf);
179 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
180 root->add_property ("movable", buf);
181 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
182 root->add_property ("active", buf);
183 root->add_property ("tempo-type", enum_2_string (_type));
184 root->add_property ("lock-style", enum_2_string (position_lock_style()));
190 TempoSection::set_type (Type type)
195 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
198 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
201 if (_type == Constant) {
202 return pulses_per_minute();
205 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
208 /** returns the zero-based frame (relative to session)
209 where the tempo in whole pulses per minute occurs in this section.
210 beat b is only used for constant tempos.
211 note that the tempo map may have multiple such values.
214 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
216 if (_type == Constant) {
217 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
220 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
222 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
225 TempoSection::tempo_at_pulse (const double& p) const
228 if (_type == Constant) {
229 return pulses_per_minute();
231 double const ppm = pulse_tempo_at_pulse (p - pulse());
235 /** returns the zero-based beat (relative to session)
236 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
237 note that the session tempo map may have multiple beats at a given tempo.
240 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
242 if (_type == Constant) {
243 double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
247 return pulse_at_pulse_tempo (ppm) + pulse();
250 /** returns the zero-based pulse (relative to session origin)
251 where the zero-based frame (relative to session)
255 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
257 if (_type == Constant) {
258 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
261 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
264 /** returns the zero-based frame (relative to session start frame)
265 where the zero-based pulse (relative to session start)
270 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
272 if (_type == Constant) {
273 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
276 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
284 Tt----|-----------------*|
285 Ta----|--------------|* |
291 _______________|___|____
292 time a t (next tempo)
295 Duration in beats at time a is the integral of some Tempo function.
296 In our case, the Tempo function (Tempo at time t) is
299 with function constant
304 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
305 b(t) = T0(e^(ct) - 1) / c
307 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
308 t(b) = log((cb / T0) + 1) / c
310 The time t at which Tempo T occurs is a as above:
311 t(T) = log(T / T0) / c
313 The beat at which a Tempo T occurs is:
316 The Tempo at which beat b occurs is:
319 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
320 Our problem is that we usually don't know t.
321 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
322 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
323 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
325 By substituting our expanded t as a in the c function above, our problem is reduced to:
326 c = T0 (e^(log (Ta / T0)) - 1) / b
328 We can now store c for future time calculations.
329 If the following tempo section (the one that defines c in conjunction with this one)
330 is changed or moved, c is no longer valid.
332 The public methods are session-relative.
334 Most of this stuff is taken from this paper:
337 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
340 Zurich University of Arts
341 Institute for Computer Music and Sound Technology
343 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
348 compute this ramp's function constant using the end tempo (in whole pulses per minute)
349 and duration (pulses into global start) of some later tempo section.
352 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
354 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
355 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
358 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
360 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
362 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
366 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
368 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
372 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
374 return (frame / (double) frame_rate) / 60.0;
377 /* position function */
379 TempoSection::a_func (double end_bpm, double c_func) const
381 return log (end_bpm / pulses_per_minute()) / c_func;
384 /*function constant*/
386 TempoSection::c_func (double end_bpm, double end_time) const
388 return log (end_bpm / pulses_per_minute()) / end_time;
391 /* tempo in ppm at time in minutes */
393 TempoSection::pulse_tempo_at_time (const double& time) const
395 return exp (_c_func * time) * pulses_per_minute();
398 /* time in minutes at tempo in ppm */
400 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
402 return log (pulse_tempo / pulses_per_minute()) / _c_func;
405 /* tick at tempo in ppm */
407 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
409 return (pulse_tempo - pulses_per_minute()) / _c_func;
412 /* tempo in ppm at tick */
414 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
416 return (pulse * _c_func) + pulses_per_minute();
419 /* pulse at time in minutes */
421 TempoSection::pulse_at_time (const double& time) const
423 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
426 /* time in minutes at pulse */
428 TempoSection::time_at_pulse (const double& pulse) const
430 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
433 /***********************************************************************/
435 const string MeterSection::xml_state_node_name = "Meter";
437 MeterSection::MeterSection (const XMLNode& node)
438 : MetricSection (0.0), Meter (TempoMap::default_meter())
440 XMLProperty const * prop;
443 const XMLProperty *prop;
447 framepos_t frame = 0;
448 pair<double, BBT_Time> start;
450 if ((prop = node.property ("start")) != 0) {
451 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
455 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
457 /* legacy session - start used to be in bbt*/
461 error << _("MeterSection XML node has no \"start\" property") << endmsg;
464 if ((prop = node.property ("pulse")) != 0) {
465 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
466 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
471 if ((prop = node.property ("beat")) != 0) {
472 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
473 error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
479 if ((prop = node.property ("bbt")) == 0) {
480 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
481 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
485 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
486 //throw failed_constructor();
492 if ((prop = node.property ("frame")) != 0) {
493 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
494 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
500 /* beats-per-bar is old; divisions-per-bar is new */
502 if ((prop = node.property ("divisions-per-bar")) == 0) {
503 if ((prop = node.property ("beats-per-bar")) == 0) {
504 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
505 throw failed_constructor();
508 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
509 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
510 throw failed_constructor();
513 if ((prop = node.property ("note-type")) == 0) {
514 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
515 throw failed_constructor();
517 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
518 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
519 throw failed_constructor();
522 if ((prop = node.property ("lock-style")) == 0) {
523 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
524 set_position_lock_style (MusicTime);
526 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
529 if ((prop = node.property ("movable")) == 0) {
530 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
531 throw failed_constructor();
534 set_movable (string_is_affirmative (prop->value()));
538 MeterSection::get_state() const
540 XMLNode *root = new XMLNode (xml_state_node_name);
544 snprintf (buf, sizeof (buf), "%lf", pulse());
545 root->add_property ("pulse", buf);
546 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
550 root->add_property ("bbt", buf);
551 snprintf (buf, sizeof (buf), "%lf", beat());
552 root->add_property ("beat", buf);
553 snprintf (buf, sizeof (buf), "%f", _note_type);
554 root->add_property ("note-type", buf);
555 snprintf (buf, sizeof (buf), "%li", frame());
556 root->add_property ("frame", buf);
557 root->add_property ("lock-style", enum_2_string (position_lock_style()));
558 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
559 root->add_property ("divisions-per-bar", buf);
560 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
561 root->add_property ("movable", buf);
566 /***********************************************************************/
570 Tempos can be thought of as a source of the musical pulse.
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);
771 } else if (meter && insert_meter) {
775 bool const ipm = insert_meter->position_lock_style() == MusicTime;
777 if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
779 if (!meter->movable()) {
781 /* can't (re)move this section, so overwrite
782 * its data content (but not its properties as
786 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
787 (*i)->set_position_lock_style (insert_meter->position_lock_style());
797 /* non-matching types, so we don't care */
801 /* Add the given MetricSection, if we didn't just reset an existing
806 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
807 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
810 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
811 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
814 bool const ipm = insert_meter->position_lock_style() == MusicTime;
815 if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
820 } else if (insert_tempo) {
821 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
822 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
825 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
826 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
833 _metrics.insert (i, section);
834 //dump (_metrics, std::cerr);
839 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
842 Glib::Threads::RWLock::WriterLock lm (lock);
843 TempoSection& first (first_tempo());
844 if (ts.pulse() != first.pulse()) {
845 remove_tempo_locked (ts);
846 add_tempo_locked (tempo, pulse, true, type);
848 first.set_type (type);
850 /* cannot move the first tempo section */
851 *static_cast<Tempo*>(&first) = tempo;
852 recompute_map (_metrics);
857 PropertyChanged (PropertyChange ());
861 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
864 Glib::Threads::RWLock::WriterLock lm (lock);
865 TempoSection& first (first_tempo());
866 if (ts.frame() != first.frame()) {
867 remove_tempo_locked (ts);
868 add_tempo_locked (tempo, frame, true, type);
870 first.set_type (type);
871 first.set_pulse (0.0);
872 first.set_position_lock_style (AudioTime);
874 /* cannot move the first tempo section */
875 *static_cast<Tempo*>(&first) = tempo;
876 recompute_map (_metrics);
881 PropertyChanged (PropertyChange ());
885 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
888 Glib::Threads::RWLock::WriterLock lm (lock);
889 add_tempo_locked (tempo, pulse, true, type);
892 PropertyChanged (PropertyChange ());
896 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
899 Glib::Threads::RWLock::WriterLock lm (lock);
900 add_tempo_locked (tempo, frame, true, type);
904 PropertyChanged (PropertyChange ());
908 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
910 TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
915 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
920 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
922 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
927 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
932 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
935 Glib::Threads::RWLock::WriterLock lm (lock);
936 MeterSection& first (first_meter());
939 remove_meter_locked (ms);
940 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
942 const PositionLockStyle pl = ms.position_lock_style();
943 /* cannot move the first meter section */
944 *static_cast<Meter*>(&first) = meter;
945 first.set_position_lock_style (pl);
946 recompute_map (_metrics);
950 PropertyChanged (PropertyChange ());
954 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
957 Glib::Threads::RWLock::WriterLock lm (lock);
959 const double beat = ms.beat();
960 const BBT_Time bbt = ms.bbt();
962 remove_meter_locked (ms);
963 add_meter_locked (meter, frame, beat, bbt, true);
965 MeterSection& first (first_meter());
966 TempoSection& first_t (first_tempo());
967 /* cannot move the first meter section */
968 *static_cast<Meter*>(&first) = meter;
969 first.set_position_lock_style (AudioTime);
970 first.set_pulse (0.0);
971 first.set_frame (frame);
972 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
973 first.set_beat (beat);
974 first_t.set_frame (first.frame());
975 first_t.set_pulse (0.0);
976 first_t.set_position_lock_style (AudioTime);
978 recompute_map (_metrics);
981 PropertyChanged (PropertyChange ());
986 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
989 Glib::Threads::RWLock::WriterLock lm (lock);
990 add_meter_locked (meter, beat, where, true);
995 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
996 dump (_metrics, std::cerr);
1000 PropertyChanged (PropertyChange ());
1004 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1007 Glib::Threads::RWLock::WriterLock lm (lock);
1008 add_meter_locked (meter, frame, beat, where, true);
1013 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1014 dump (_metrics, std::cerr);
1018 PropertyChanged (PropertyChange ());
1022 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1024 /* a new meter always starts a new bar on the first beat. so
1025 round the start time appropriately. remember that
1026 `where' is based on the existing tempo map, not
1027 the result after we insert the new meter.
1031 if (where.beats != 1) {
1035 /* new meters *always* start on a beat. */
1037 double pulse = pulse_at_beat_locked (_metrics, beat);
1039 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1040 do_insert (new_meter);
1043 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1049 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1052 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1054 double pulse = pulse_at_frame_locked (_metrics, frame);
1055 new_meter->set_pulse (pulse);
1057 do_insert (new_meter);
1060 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1066 * 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,
1067 * taking any possible reordering as a consequence of this into account.
1068 * @param section - the section to be altered
1069 * @param bpm - the new Tempo
1070 * @param bbt - the bbt where the altered tempo will fall
1071 * @return returns - the position in frames where the new tempo section will lie.
1074 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1076 Glib::Threads::RWLock::ReaderLock lm (lock);
1079 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1081 double const beat = bbt_to_beats_locked (future_map, bbt);
1082 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
1083 ret = new_section->frame();
1085 ret = frame_at_beat_locked (_metrics, beat);
1088 Metrics::const_iterator d = future_map.begin();
1089 while (d != future_map.end()) {
1097 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1099 Glib::Threads::RWLock::ReaderLock lm (lock);
1102 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1104 if (solve_map (future_map, new_section, bpm, frame)) {
1105 ret = new_section->pulse();
1107 ret = pulse_at_frame_locked (_metrics, frame);
1110 Metrics::const_iterator d = future_map.begin();
1111 while (d != future_map.end()) {
1119 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1123 Glib::Threads::RWLock::WriterLock lm (lock);
1124 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1125 if (solve_map (future_map, new_section, bpm, frame)) {
1126 solve_map (_metrics, ts, bpm, frame);
1130 Metrics::const_iterator d = future_map.begin();
1131 while (d != future_map.end()) {
1136 MetricPositionChanged (); // Emit Signal
1140 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
1144 Glib::Threads::RWLock::WriterLock lm (lock);
1145 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1146 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
1147 solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
1151 Metrics::const_iterator d = future_map.begin();
1152 while (d != future_map.end()) {
1157 MetricPositionChanged (); // Emit Signal
1161 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
1164 Glib::Threads::RWLock::WriterLock lm (lock);
1165 solve_map (_metrics, ms, mt, frame);
1168 MetricPositionChanged (); // Emit Signal
1172 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
1175 Glib::Threads::RWLock::WriterLock lm (lock);
1176 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
1179 MetricPositionChanged (); // Emit Signal
1183 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
1186 bool can_solve = false;
1188 Glib::Threads::RWLock::WriterLock lm (lock);
1189 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1190 new_section->set_beats_per_minute (bpm.beats_per_minute());
1191 recompute_tempos (future_map);
1193 if (check_solved (future_map, true)) {
1194 ts->set_beats_per_minute (bpm.beats_per_minute());
1195 recompute_map (_metrics);
1200 Metrics::const_iterator d = future_map.begin();
1201 while (d != future_map.end()) {
1206 MetricPositionChanged (); // Emit Signal
1212 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1215 TempoSection* ret = 0;
1218 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1219 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1221 if (t->position_lock_style() == MusicTime) {
1222 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1224 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1226 ret->set_active (t->active());
1227 ret->set_movable (t->movable());
1228 copy.push_back (ret);
1231 TempoSection* cp = 0;
1232 if (t->position_lock_style() == MusicTime) {
1233 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1235 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1237 cp->set_active (t->active());
1238 cp->set_movable (t->movable());
1239 copy.push_back (cp);
1241 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1242 MeterSection* cp = 0;
1243 if (m->position_lock_style() == MusicTime) {
1244 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
1246 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
1248 cp->set_movable (m->movable());
1249 copy.push_back (cp);
1252 //recompute_map (copy);
1257 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
1260 TempoSection* new_section = 0;
1263 Glib::Threads::RWLock::ReaderLock lm (lock);
1264 new_section = copy_metrics_and_point (copy, ts);
1267 double const beat = bbt_to_beats_locked (copy, bbt);
1268 bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
1270 Metrics::const_iterator d = copy.begin();
1271 while (d != copy.end()) {
1280 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1282 Tempo newtempo (beats_per_minute, note_type);
1285 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1286 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1291 Glib::Threads::RWLock::WriterLock lm (lock);
1292 *((Tempo*) t) = newtempo;
1293 recompute_map (_metrics);
1295 PropertyChanged (PropertyChange ());
1302 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1304 Tempo newtempo (beats_per_minute, note_type);
1307 TempoSection* first;
1308 Metrics::iterator i;
1310 /* find the TempoSection immediately preceding "where"
1313 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1315 if ((*i)->frame() > where) {
1321 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1334 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1344 Glib::Threads::RWLock::WriterLock lm (lock);
1345 /* cannot move the first tempo section */
1346 *((Tempo*)prev) = newtempo;
1347 recompute_map (_metrics);
1350 PropertyChanged (PropertyChange ());
1354 TempoMap::first_meter () const
1356 const MeterSection *m = 0;
1358 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1359 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1364 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1365 abort(); /*NOTREACHED*/
1370 TempoMap::first_meter ()
1372 MeterSection *m = 0;
1374 /* CALLER MUST HOLD LOCK */
1376 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1377 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1382 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1383 abort(); /*NOTREACHED*/
1388 TempoMap::first_tempo () const
1390 const TempoSection *t = 0;
1392 /* CALLER MUST HOLD LOCK */
1394 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1395 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1399 if (!t->movable()) {
1405 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1406 abort(); /*NOTREACHED*/
1411 TempoMap::first_tempo ()
1413 TempoSection *t = 0;
1415 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1416 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1420 if (!t->movable()) {
1426 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1427 abort(); /*NOTREACHED*/
1431 TempoMap::recompute_tempos (Metrics& metrics)
1433 TempoSection* prev_ts = 0;
1435 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1438 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1443 if (t->position_lock_style() == AudioTime) {
1444 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1445 t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1448 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1449 t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1456 prev_ts->set_c_func (0.0);
1459 /* tempos must be positioned correctly */
1461 TempoMap::recompute_meters (Metrics& metrics)
1463 MeterSection* meter = 0;
1464 MeterSection* prev_m = 0;
1465 uint32_t accumulated_bars = 0;
1467 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1468 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1470 const double beats_in_m = (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
1471 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1473 if (meter->position_lock_style() == AudioTime) {
1475 pair<double, BBT_Time> b_bbt;
1477 double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1478 b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
1479 const double true_pulse = prev_m->pulse() + (ceil (beats) - prev_m->beat()) / prev_m->note_divisor();
1480 const double pulse_off = true_pulse - ((beats - prev_m->beat()) / prev_m->note_divisor());
1481 pulse = true_pulse - pulse_off;
1483 if (!meter->movable()) {
1484 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1486 meter->set_beat (b_bbt);
1487 meter->set_pulse (pulse);
1491 pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1493 pulse = pulse_at_beat_locked (metrics, meter->beat());
1495 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1496 meter->set_pulse (pulse);
1505 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1507 /* CALLER MUST HOLD WRITE LOCK */
1511 /* we will actually stop once we hit
1518 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1521 /* silly call from Session::process() during startup
1526 recompute_tempos (metrics);
1527 recompute_meters (metrics);
1531 TempoMap::pulse_at_beat (const double& beat) const
1533 Glib::Threads::RWLock::ReaderLock lm (lock);
1534 return pulse_at_beat_locked (_metrics, beat);
1538 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1540 MeterSection* prev_ms = 0;
1541 double accumulated_beats = 0.0;
1543 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1545 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1546 if (prev_ms && m->beat() > beat) {
1549 accumulated_beats = m->beat();
1554 double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1559 TempoMap::beat_at_pulse (const double& pulse) const
1561 Glib::Threads::RWLock::ReaderLock lm (lock);
1562 return beat_at_pulse_locked (_metrics, pulse);
1566 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1568 MeterSection* prev_ms = 0;
1569 double accumulated_beats = 0.0;
1571 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1573 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1574 if (prev_ms && m->pulse() > pulse) {
1577 accumulated_beats = m->beat();
1582 double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1584 return beats_in_section + accumulated_beats;
1588 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1590 Glib::Threads::RWLock::ReaderLock lm (lock);
1591 TempoMetric m (first_meter(), first_tempo());
1593 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1594 at something, because we insert the default tempo and meter during
1595 TempoMap construction.
1597 now see if we can find better candidates.
1600 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1602 if ((*i)->frame() > frame) {
1616 /* XX meters only */
1618 TempoMap::metric_at (BBT_Time bbt) const
1620 Glib::Threads::RWLock::ReaderLock lm (lock);
1621 TempoMetric m (first_meter(), first_tempo());
1623 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1624 at something, because we insert the default tempo and meter during
1625 TempoMap construction.
1627 now see if we can find better candidates.
1630 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1632 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1633 BBT_Time section_start (mw->bbt());
1635 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1647 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1654 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1657 Glib::Threads::RWLock::ReaderLock lm (lock);
1658 double const beat = beat_at_frame_locked (_metrics, frame);
1660 bbt = beats_to_bbt_locked (_metrics, beat);
1664 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1666 Glib::Threads::RWLock::ReaderLock lm (lock);
1668 return bbt_to_beats_locked (_metrics, bbt);
1672 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1674 /* CALLER HOLDS READ LOCK */
1676 double accumulated_beats = 0.0;
1677 double accumulated_bars = 0.0;
1678 MeterSection* prev_ms = 0;
1679 /* because audio-locked meters have 'fake' integral beats,
1680 there is no pulse offset here.
1682 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1684 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1685 double bars_to_m = 0.0;
1687 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1688 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1691 accumulated_beats = m->beat();
1692 accumulated_bars += bars_to_m;
1698 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1699 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1700 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1706 TempoMap::beats_to_bbt (const double& beats)
1708 Glib::Threads::RWLock::ReaderLock lm (lock);
1710 return beats_to_bbt_locked (_metrics, beats);
1714 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1716 /* CALLER HOLDS READ LOCK */
1717 MeterSection* prev_ms = 0;
1718 const double beats = (b < 0.0) ? 0.0 : b;
1719 uint32_t accumulated_bars = 0;
1720 double accumulated_beats = 0.0;
1722 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1723 MeterSection* m = 0;
1725 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1728 double const beats_to_m = m->beat() - prev_ms->beat();
1729 if (accumulated_beats + beats_to_m > beats) {
1730 /* this is the meter after the one our beat is on*/
1734 /* we need a whole number of bars. */
1735 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1736 accumulated_beats += beats_to_m;
1743 double const beats_in_ms = beats - accumulated_beats;
1744 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1745 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1746 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1747 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1751 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1752 ret.beats = (uint32_t) floor (remaining_beats);
1753 ret.bars = total_bars;
1755 /* 0 0 0 to 1 1 0 - based mapping*/
1759 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1761 ret.ticks -= BBT_Time::ticks_per_beat;
1764 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1773 TempoMap::pulse_to_bbt (const double& pulse)
1775 Glib::Threads::RWLock::ReaderLock lm (lock);
1776 MeterSection* prev_ms = 0;
1777 uint32_t accumulated_bars = 0;
1778 double accumulated_pulses = 0.0;
1780 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1781 MeterSection* m = 0;
1783 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1786 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1787 if (accumulated_pulses + pulses_to_m > pulse) {
1788 /* this is the meter after the one our beat is on*/
1792 /* we need a whole number of bars. */
1793 accumulated_pulses += pulses_to_m;
1794 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1800 double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1801 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1802 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1803 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1804 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1808 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1809 ret.beats = (uint32_t) floor (remaining_beats);
1810 ret.bars = total_bars;
1812 /* 0 0 0 to 1 1 0 mapping*/
1816 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1818 ret.ticks -= BBT_Time::ticks_per_beat;
1821 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1830 TempoMap::beat_at_frame (const framecnt_t& frame) const
1832 Glib::Threads::RWLock::ReaderLock lm (lock);
1833 return beat_at_frame_locked (_metrics, frame);
1837 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1839 //framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
1840 double const pulse = pulse_at_frame_locked (metrics, frame);
1842 return beat_at_pulse_locked (metrics, pulse);
1846 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1848 /* HOLD (at least) THE READER LOCK */
1849 TempoSection* prev_ts = 0;
1850 double accumulated_pulses = 0.0;
1852 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1854 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1858 if (prev_ts && t->frame() > frame) {
1859 /*the previous ts is the one containing the frame */
1860 double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1863 accumulated_pulses = t->pulse();
1868 /* treated as constant for this ts */
1869 double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1871 return pulses_in_section + accumulated_pulses;
1875 TempoMap::frame_at_beat (const double& beat) const
1877 Glib::Threads::RWLock::ReaderLock lm (lock);
1878 return frame_at_beat_locked (_metrics, beat);
1882 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1884 framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1885 //frameoffset_t const frame_off = frame_offset_at (metrics, frame);
1890 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1892 /* HOLD THE READER LOCK */
1894 const TempoSection* prev_ts = 0;
1895 double accumulated_pulses = 0.0;
1897 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1900 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1904 if (prev_ts && t->pulse() > pulse) {
1905 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1908 accumulated_pulses = t->pulse();
1912 /* must be treated as constant, irrespective of _type */
1913 double const pulses_in_section = pulse - accumulated_pulses;
1914 double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1916 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1922 TempoMap::beat_offset_at (const Metrics& metrics, const double& beat) const
1924 MeterSection* prev_m = 0;
1925 double beat_off = first_meter().pulse();
1927 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1928 MeterSection* m = 0;
1929 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1931 if (m->beat() > beat) {
1935 if (m->position_lock_style() == AudioTime) {
1936 beat_off += ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) - floor (m->pulse() - prev_m->pulse());
1947 TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
1949 frameoffset_t frame_off = 0;
1950 MeterSection* prev_m = 0;
1952 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1953 MeterSection* m = 0;
1954 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1955 if (m->frame() > frame) {
1958 if (prev_m && m->position_lock_style() == AudioTime) {
1959 const double pulse = prev_m->pulse() + ((m->beat() - prev_m->beat()) / prev_m->note_divisor());
1960 frame_off += frame_at_pulse_locked (metrics, pulse) - m->frame();
1970 TempoMap::frame_time (const BBT_Time& bbt)
1973 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1977 if (bbt.beats < 1) {
1978 throw std::logic_error ("beats are counted from one");
1980 Glib::Threads::RWLock::ReaderLock lm (lock);
1981 double const beat = bbt_to_beats_locked (_metrics, bbt);
1982 framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1987 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1989 /* HOLD THE READER LOCK */
1991 framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt)));
1997 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1999 TempoSection* prev_ts = 0;
2001 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2003 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2008 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
2012 if (t->frame() == prev_ts->frame()) {
2016 /* precision check ensures pulses and frames align independent of lock style.*/
2017 if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
2029 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2031 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2033 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2034 if (!t->movable()) {
2035 t->set_active (true);
2038 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2039 t->set_active (false);
2041 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2042 t->set_active (true);
2043 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2052 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2054 TempoSection* prev_ts = 0;
2055 TempoSection* section_prev = 0;
2056 framepos_t first_m_frame = 0;
2058 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2060 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2061 if (!m->movable()) {
2062 first_m_frame = m->frame();
2067 if (section->movable() && frame <= first_m_frame) {
2070 section->set_active (true);
2072 section->set_frame (frame);
2074 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2076 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2083 section_prev = prev_ts;
2086 if (t->position_lock_style() == MusicTime) {
2087 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2088 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2090 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2091 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2099 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2100 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2103 if (section->position_lock_style() == MusicTime) {
2104 /* we're setting the frame */
2105 section->set_position_lock_style (AudioTime);
2106 recompute_tempos (imaginary);
2107 section->set_position_lock_style (MusicTime);
2109 recompute_tempos (imaginary);
2112 if (check_solved (imaginary, true)) {
2113 recompute_meters (imaginary);
2117 MetricSectionFrameSorter fcmp;
2118 imaginary.sort (fcmp);
2119 if (section->position_lock_style() == MusicTime) {
2120 /* we're setting the frame */
2121 section->set_position_lock_style (AudioTime);
2122 recompute_tempos (imaginary);
2123 section->set_position_lock_style (MusicTime);
2125 recompute_tempos (imaginary);
2127 if (check_solved (imaginary, true)) {
2128 recompute_meters (imaginary);
2132 MetricSectionSorter cmp;
2133 imaginary.sort (cmp);
2134 if (section->position_lock_style() == MusicTime) {
2135 /* we're setting the frame */
2136 section->set_position_lock_style (AudioTime);
2137 recompute_tempos (imaginary);
2138 section->set_position_lock_style (MusicTime);
2140 recompute_tempos (imaginary);
2142 if (check_solved (imaginary, true)) {
2143 recompute_meters (imaginary);
2146 //dump (imaginary, std::cerr);
2152 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
2154 TempoSection* prev_ts = 0;
2155 TempoSection* section_prev = 0;
2157 section->set_pulse (pulse);
2159 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2161 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2167 section_prev = prev_ts;
2170 if (t->position_lock_style() == MusicTime) {
2171 prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2172 t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2174 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2175 t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2182 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2183 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2186 if (section->position_lock_style() == AudioTime) {
2187 /* we're setting the pulse */
2188 section->set_position_lock_style (MusicTime);
2189 recompute_tempos (imaginary);
2190 section->set_position_lock_style (AudioTime);
2192 recompute_tempos (imaginary);
2194 if (check_solved (imaginary, false)) {
2195 recompute_meters (imaginary);
2199 MetricSectionSorter cmp;
2200 imaginary.sort (cmp);
2201 if (section->position_lock_style() == AudioTime) {
2202 /* we're setting the pulse */
2203 section->set_position_lock_style (MusicTime);
2204 recompute_tempos (imaginary);
2205 section->set_position_lock_style (AudioTime);
2207 recompute_tempos (imaginary);
2210 if (check_solved (imaginary, false)) {
2211 recompute_meters (imaginary);
2215 MetricSectionFrameSorter fcmp;
2216 imaginary.sort (fcmp);
2217 if (section->position_lock_style() == AudioTime) {
2218 /* we're setting the pulse */
2219 section->set_position_lock_style (MusicTime);
2220 recompute_tempos (imaginary);
2221 section->set_position_lock_style (AudioTime);
2223 recompute_tempos (imaginary);
2226 if (check_solved (imaginary, false)) {
2227 recompute_meters (imaginary);
2231 //dump (imaginary, std::cerr);
2237 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
2239 MeterSection* prev_ms = 0;
2241 if (!section->movable()) {
2242 /* lock the first tempo to our first meter */
2243 if (!set_active_tempos (imaginary, frame)) {
2246 TempoSection* first_t = &first_tempo();
2248 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2250 new_section->set_frame (frame);
2251 new_section->set_pulse (0.0);
2252 new_section->set_active (true);
2254 if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
2255 first_t->set_frame (frame);
2256 first_t->set_pulse (0.0);
2257 first_t->set_active (true);
2258 solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
2264 uint32_t accumulated_bars = 0;
2266 section->set_frame (frame);
2268 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2270 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2272 const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2273 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2277 here we set the beat for this frame.
2278 we're going to set it 'incorrectly' to the next integer and use this difference
2279 to find the meter's pulse later.
2280 (meters should fall on absolute beats to keep us sane)
2283 pair<double, BBT_Time> b_bbt;
2285 double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2286 b_bbt = make_pair (ceil (beats), BBT_Time (accumulated_bars + 1, 1, 0));
2287 const double true_pulse = prev_ms->pulse() + ((ceil (beats) - prev_ms->beat()) / prev_ms->note_divisor());
2288 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2289 pulse = true_pulse - pulse_off;
2291 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2293 m->set_beat (b_bbt);
2294 m->set_pulse (pulse);
2299 if (m->position_lock_style() == MusicTime) {
2300 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2301 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2302 m->set_pulse (pulse);
2304 if (!m->movable()) {
2305 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2306 m->set_beat (b_bbt);
2308 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2309 m->set_pulse (pulse);
2316 if (section->position_lock_style() == MusicTime) {
2317 /* we're setting the frame */
2318 section->set_position_lock_style (AudioTime);
2319 recompute_meters (imaginary);
2320 section->set_position_lock_style (MusicTime);
2322 recompute_meters (imaginary);
2324 //dump (imaginary, std::cerr);
2328 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2330 MeterSection* prev_ms = 0;
2331 double accumulated_beats = 0.0;
2332 uint32_t accumulated_bars = 0;
2334 section->set_pulse (pulse);
2336 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2338 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2340 double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2341 accumulated_beats += beats_in_m;
2342 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2345 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2346 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2347 section->set_beat (b_bbt);
2352 if (m->position_lock_style() == MusicTime) {
2353 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2354 m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2355 m->set_pulse (pulse);
2357 if (!m->movable()) {
2358 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2359 m->set_beat (b_bbt);
2361 const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2362 m->set_pulse (pulse);
2369 if (section->position_lock_style() == AudioTime) {
2370 /* we're setting the pulse */
2371 section->set_position_lock_style (MusicTime);
2372 recompute_meters (imaginary);
2373 section->set_position_lock_style (AudioTime);
2375 recompute_meters (imaginary);
2380 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2382 Glib::Threads::RWLock::ReaderLock lm (lock);
2384 double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2385 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2386 double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2387 framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2388 framecnt_t const ret = time_at_bbt;
2394 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2396 return round_to_type (fr, dir, Bar);
2400 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2402 return round_to_type (fr, dir, Beat);
2406 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2408 Glib::Threads::RWLock::ReaderLock lm (lock);
2409 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2410 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2411 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2413 ticks -= beats * BBT_Time::ticks_per_beat;
2416 /* round to next (or same iff dir == RoundUpMaybe) */
2418 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2420 if (mod == 0 && dir == RoundUpMaybe) {
2421 /* right on the subdivision, which is fine, so do nothing */
2423 } else if (mod == 0) {
2424 /* right on the subdivision, so the difference is just the subdivision ticks */
2425 ticks += ticks_one_subdivisions_worth;
2428 /* not on subdivision, compute distance to next subdivision */
2430 ticks += ticks_one_subdivisions_worth - mod;
2433 if (ticks >= BBT_Time::ticks_per_beat) {
2434 ticks -= BBT_Time::ticks_per_beat;
2436 } else if (dir < 0) {
2438 /* round to previous (or same iff dir == RoundDownMaybe) */
2440 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2442 if (difference == 0 && dir == RoundDownAlways) {
2443 /* right on the subdivision, but force-rounding down,
2444 so the difference is just the subdivision ticks */
2445 difference = ticks_one_subdivisions_worth;
2448 if (ticks < difference) {
2449 ticks = BBT_Time::ticks_per_beat - ticks;
2451 ticks -= difference;
2455 /* round to nearest */
2458 /* compute the distance to the previous and next subdivision */
2460 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2462 /* closer to the next subdivision, so shift forward */
2464 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2466 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2468 if (ticks > BBT_Time::ticks_per_beat) {
2470 ticks -= BBT_Time::ticks_per_beat;
2471 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2474 } else if (rem > 0) {
2476 /* closer to previous subdivision, so shift backward */
2480 /* can't go backwards past zero, so ... */
2483 /* step back to previous beat */
2485 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2486 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2488 ticks = lrint (ticks - rem);
2489 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2492 /* on the subdivision, do nothing */
2496 framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2502 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2504 if (sub_num == -1) {
2505 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2506 if ((double) when.beats > bpb / 2.0) {
2512 } else if (sub_num == 0) {
2513 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2514 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2516 while ((double) when.beats > bpb) {
2518 when.beats -= (uint32_t) floor (bpb);
2524 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2526 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2527 /* closer to the next subdivision, so shift forward */
2529 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2531 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2533 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2536 } else if (rem > 0) {
2537 /* closer to previous subdivision, so shift backward */
2539 if (rem > when.ticks) {
2540 if (when.beats == 0) {
2541 /* can't go backwards past zero, so ... */
2543 /* step back to previous beat */
2545 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2547 when.ticks = when.ticks - rem;
2553 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2555 Glib::Threads::RWLock::ReaderLock lm (lock);
2557 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2558 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2563 /* find bar previous to 'frame' */
2566 return frame_time (bbt);
2568 } else if (dir > 0) {
2569 /* find bar following 'frame' */
2573 return frame_time (bbt);
2575 /* true rounding: find nearest bar */
2576 framepos_t raw_ft = frame_time (bbt);
2579 framepos_t prev_ft = frame_time (bbt);
2581 framepos_t next_ft = frame_time (bbt);
2583 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2594 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2595 } else if (dir > 0) {
2596 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2598 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2607 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2608 framepos_t lower, framepos_t upper)
2610 Glib::Threads::RWLock::ReaderLock lm (lock);
2611 int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2612 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2613 /* although the map handles negative beats, bbt doesn't. */
2617 while (cnt <= upper_beat) {
2618 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2619 TempoSection const tempo = tempo_section_at_locked (pos);
2620 MeterSection const meter = meter_section_at_locked (pos);
2621 BBT_Time const bbt = beats_to_bbt (cnt);
2622 BBTPoint point = BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func());
2623 points.push_back (point);
2629 TempoMap::tempo_section_at (framepos_t frame) const
2631 Glib::Threads::RWLock::ReaderLock lm (lock);
2632 return tempo_section_at_locked (frame);
2636 TempoMap::tempo_section_at_locked (framepos_t frame) const
2638 Metrics::const_iterator i;
2639 TempoSection* prev = 0;
2641 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2644 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2648 if (t->frame() > frame) {
2658 abort(); /*NOTREACHED*/
2665 /* don't use this to calculate length (the tempo is only correct for this frame).
2666 do that stuff based on the beat_at_frame and frame_at_beat api
2669 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2671 Glib::Threads::RWLock::ReaderLock lm (lock);
2673 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2674 const TempoSection* ts_after = 0;
2675 Metrics::const_iterator i;
2677 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2680 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2684 if ((*i)->frame() > frame) {
2692 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2694 /* must be treated as constant tempo */
2695 return ts_at->frames_per_beat (_frame_rate);
2699 TempoMap::tempo_at (const framepos_t& frame) const
2701 Glib::Threads::RWLock::ReaderLock lm (lock);
2702 return tempo_at_locked (frame);
2706 TempoMap::tempo_at_locked (const framepos_t& frame) const
2708 TempoSection* prev_ts = 0;
2710 Metrics::const_iterator i;
2712 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2714 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2718 if ((prev_ts) && t->frame() > frame) {
2719 /* t is the section past frame */
2720 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2721 Tempo const ret_tempo (ret, prev_ts->note_type());
2728 double const ret = prev_ts->beats_per_minute();
2729 Tempo const ret_tempo (ret, prev_ts->note_type ());
2735 TempoMap::meter_section_at (framepos_t frame) const
2737 Glib::Threads::RWLock::ReaderLock lm (lock);
2738 return meter_section_at_locked (frame);
2742 TempoMap::meter_section_at_locked (framepos_t frame) const
2744 //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2745 Metrics::const_iterator i;
2746 MeterSection* prev = 0;
2748 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2751 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2753 if (prev && (*i)->frame() > frame) {
2763 abort(); /*NOTREACHED*/
2770 TempoMap::meter_at (framepos_t frame) const
2772 TempoMetric m (metric_at (frame));
2777 TempoMap::meter_section_at (const double& beat) const
2779 MeterSection* prev_ms = 0;
2780 Glib::Threads::RWLock::ReaderLock lm (lock);
2782 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2784 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2785 if (prev_ms && m->beat() > beat) {
2796 TempoMap::get_state ()
2798 Metrics::const_iterator i;
2799 XMLNode *root = new XMLNode ("TempoMap");
2802 Glib::Threads::RWLock::ReaderLock lm (lock);
2803 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2804 root->add_child_nocopy ((*i)->get_state());
2812 TempoMap::set_state (const XMLNode& node, int /*version*/)
2815 Glib::Threads::RWLock::WriterLock lm (lock);
2818 XMLNodeConstIterator niter;
2819 Metrics old_metrics (_metrics);
2822 nlist = node.children();
2824 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2825 XMLNode* child = *niter;
2827 if (child->name() == TempoSection::xml_state_node_name) {
2830 TempoSection* ts = new TempoSection (*child);
2831 _metrics.push_back (ts);
2834 catch (failed_constructor& err){
2835 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2836 _metrics = old_metrics;
2840 } else if (child->name() == MeterSection::xml_state_node_name) {
2843 MeterSection* ms = new MeterSection (*child);
2844 _metrics.push_back (ms);
2847 catch (failed_constructor& err) {
2848 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2849 _metrics = old_metrics;
2855 if (niter == nlist.end()) {
2856 MetricSectionSorter cmp;
2857 _metrics.sort (cmp);
2859 /* check for legacy sessions where bbt was the base musical unit for tempo */
2860 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2863 MeterSection* prev_ms = 0;
2864 TempoSection* prev_ts = 0;
2865 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2866 if (prev_ms && prev_ms->pulse() < 0.0) {
2867 /*XX we cannot possibly make this work??. */
2868 pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
2869 prev_ms->set_beat (start);
2870 const double start_pulse = ((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat);
2871 prev_ms->set_pulse (start_pulse);
2874 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2878 if (prev_ts && prev_ts->pulse() < 0.0) {
2879 double const start = ((prev_ts->legacy_bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2880 prev_ts->set_pulse (start);
2885 /* check for multiple tempo/meters at the same location, which
2886 ardour2 somehow allowed.
2889 Metrics::iterator prev = _metrics.end();
2890 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2891 if (prev != _metrics.end()) {
2893 MeterSection* prev_ms;
2895 TempoSection* prev_ts;
2896 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2897 if (prev_ms->pulse() == ms->pulse()) {
2898 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2899 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2902 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2903 if (prev_ts->pulse() == ts->pulse()) {
2904 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2905 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2913 recompute_map (_metrics);
2916 PropertyChanged (PropertyChange ());
2922 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2924 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2925 const MeterSection* m;
2926 const TempoSection* t;
2927 const TempoSection* prev_ts = 0;
2929 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2931 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2932 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2933 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2934 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2936 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2937 o << "calculated : " << prev_ts->tempo_at_pulse (t->pulse()) * prev_ts->note_type() << " | " << prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << " | " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2940 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2941 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2942 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2945 o << "------" << std::endl;
2949 TempoMap::n_tempos() const
2951 Glib::Threads::RWLock::ReaderLock lm (lock);
2954 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2955 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2964 TempoMap::n_meters() const
2966 Glib::Threads::RWLock::ReaderLock lm (lock);
2969 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2970 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2979 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2982 Glib::Threads::RWLock::WriterLock lm (lock);
2983 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2984 if ((*i)->frame() >= where && (*i)->movable ()) {
2985 (*i)->set_frame ((*i)->frame() + amount);
2989 /* now reset the BBT time of all metrics, based on their new
2990 * audio time. This is the only place where we do this reverse
2994 Metrics::iterator i;
2995 const MeterSection* meter;
2996 const TempoSection* tempo;
3000 meter = &first_meter ();
3001 tempo = &first_tempo ();
3006 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3009 MetricSection* prev = 0;
3011 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3014 //TempoMetric metric (*meter, *tempo);
3015 MeterSection* ms = const_cast<MeterSection*>(meter);
3016 TempoSection* ts = const_cast<TempoSection*>(tempo);
3019 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3023 ts->set_pulse (t->pulse());
3025 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3026 ts->set_pulse (m->pulse());
3028 ts->set_frame (prev->frame());
3032 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3033 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3034 ms->set_beat (start);
3035 ms->set_pulse (m->pulse());
3037 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3041 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3042 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3043 ms->set_beat (start);
3044 ms->set_pulse (t->pulse());
3046 ms->set_frame (prev->frame());
3050 // metric will be at frames=0 bbt=1|1|0 by default
3051 // which is correct for our purpose
3054 // cerr << bbt << endl;
3056 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3060 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3062 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3063 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3064 bbt_time (m->frame(), bbt);
3066 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3072 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3073 /* round up to next beat */
3079 if (bbt.beats != 1) {
3080 /* round up to next bar */
3085 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3086 m->set_beat (start);
3087 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3089 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3091 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3092 abort(); /*NOTREACHED*/
3098 recompute_map (_metrics);
3102 PropertyChanged (PropertyChange ());
3105 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3109 std::list<MetricSection*> metric_kill_list;
3111 TempoSection* last_tempo = NULL;
3112 MeterSection* last_meter = NULL;
3113 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3114 bool meter_after = false; // is there a meter marker likewise?
3116 Glib::Threads::RWLock::WriterLock lm (lock);
3117 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3118 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3119 metric_kill_list.push_back(*i);
3120 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3123 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3127 else if ((*i)->frame() >= where) {
3128 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3129 (*i)->set_frame ((*i)->frame() - amount);
3130 if ((*i)->frame() == where) {
3131 // marker was immediately after end of range
3132 tempo_after = dynamic_cast<TempoSection*> (*i);
3133 meter_after = dynamic_cast<MeterSection*> (*i);
3139 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3140 if (last_tempo && !tempo_after) {
3141 metric_kill_list.remove(last_tempo);
3142 last_tempo->set_frame(where);
3145 if (last_meter && !meter_after) {
3146 metric_kill_list.remove(last_meter);
3147 last_meter->set_frame(where);
3151 //remove all the remaining metrics
3152 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3153 _metrics.remove(*i);
3158 recompute_map (_metrics);
3161 PropertyChanged (PropertyChange ());
3165 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3166 * pos can be -ve, if required.
3169 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3171 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3174 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3176 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3178 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3181 /** Add the BBT interval op to pos and return the result */
3183 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3185 Glib::Threads::RWLock::ReaderLock lm (lock);
3187 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3188 pos_bbt.ticks += op.ticks;
3189 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3191 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3193 pos_bbt.beats += op.beats;
3194 /* the meter in effect will start on the bar */
3195 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3196 while (pos_bbt.beats >= divisions_per_bar + 1) {
3198 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3199 pos_bbt.beats -= divisions_per_bar;
3201 pos_bbt.bars += op.bars;
3203 return frame_time_locked (_metrics, pos_bbt);
3206 /** Count the number of beats that are equivalent to distance when going forward,
3210 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3212 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3216 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3222 operator<< (std::ostream& o, const Meter& m) {
3223 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3227 operator<< (std::ostream& o, const Tempo& t) {
3228 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3232 operator<< (std::ostream& o, const MetricSection& section) {
3234 o << "MetricSection @ " << section.frame() << ' ';
3236 const TempoSection* ts;
3237 const MeterSection* ms;
3239 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3240 o << *((const Tempo*) ts);
3241 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3242 o << *((const Meter*) ms);