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)
76 : MetricSection (0.0), Tempo (TempoMap::default_tempo())
78 const XMLProperty *prop;
84 if ((prop = node.property ("start")) != 0) {
85 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
89 /* legacy session - start used to be in bbt*/
95 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
99 if ((prop = node.property ("beat")) != 0) {
100 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
101 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
106 if ((prop = node.property ("frame")) != 0) {
107 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
108 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
114 if ((prop = node.property ("beats-per-minute")) == 0) {
115 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
116 throw failed_constructor();
119 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
120 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
121 throw failed_constructor();
124 if ((prop = node.property ("note-type")) == 0) {
125 /* older session, make note type be quarter by default */
128 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
129 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
130 throw failed_constructor();
134 if ((prop = node.property ("movable")) == 0) {
135 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
136 throw failed_constructor();
139 set_movable (string_is_affirmative (prop->value()));
141 if ((prop = node.property ("bar-offset")) == 0) {
144 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
145 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
146 throw failed_constructor();
150 if ((prop = node.property ("tempo-type")) == 0) {
153 _type = Type (string_2_enum (prop->value(), _type));
156 if ((prop = node.property ("lock-style")) == 0) {
157 set_position_lock_style (MusicTime);
159 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
164 TempoSection::get_state() const
166 XMLNode *root = new XMLNode (xml_state_node_name);
170 snprintf (buf, sizeof (buf), "%f", beat());
171 root->add_property ("beat", buf);
172 snprintf (buf, sizeof (buf), "%li", frame());
173 root->add_property ("frame", buf);
174 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
175 root->add_property ("beats-per-minute", buf);
176 snprintf (buf, sizeof (buf), "%f", _note_type);
177 root->add_property ("note-type", buf);
178 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
179 // root->add_property ("bar-offset", buf);
180 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
181 root->add_property ("movable", buf);
182 root->add_property ("tempo-type", enum_2_string (_type));
183 root->add_property ("lock-style", enum_2_string (position_lock_style()));
190 TempoSection::update_bar_offset_from_bbt (const Meter& m)
192 _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
193 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
195 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
199 TempoSection::set_type (Type type)
204 /** returns the tempo at the zero-based (relative to session) frame.
207 TempoSection::tempo_at_frame (framepos_t f, framecnt_t frame_rate) const
210 if (_type == Constant) {
211 return beats_per_minute();
214 return tick_tempo_at_time (frame_to_minute (f - frame(), frame_rate)) / BBT_Time::ticks_per_beat;
217 /** returns the zero-based frame (relative to session)
218 where the tempo occurs in this section.
219 beat b is only used for constant tempos.
220 note that the tempo map may have multiple such values.
223 TempoSection::frame_at_tempo (double bpm, double b, framecnt_t frame_rate) const
225 if (_type == Constant) {
226 return ((b - beat()) * frames_per_beat (frame_rate)) + frame();
229 return minute_to_frame (time_at_tick_tempo (bpm * BBT_Time::ticks_per_beat), frame_rate) + frame();
231 /** returns the tempo at the zero-based (relative to session) beat.
234 TempoSection::tempo_at_beat (double b) const
237 if (_type == Constant) {
238 return beats_per_minute();
241 return tick_tempo_at_tick ((b - beat()) * BBT_Time::ticks_per_beat) / BBT_Time::ticks_per_beat;
244 /** returns the zero-based beat (relative to session)
245 where the tempo occurs given frame f. frame f is only used for constant tempos.
246 note that the session tempo map may have multiple beats at a given tempo.
249 TempoSection::beat_at_tempo (double bpm, framepos_t f, framecnt_t frame_rate) const
251 if (_type == Constant) {
252 double const ticks = (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
253 return ticks / BBT_Time::ticks_per_beat;
256 return (tick_at_tick_tempo (bpm * BBT_Time::ticks_per_beat) + tick()) / BBT_Time::ticks_per_beat;
259 /** returns the zero-based beat (relative to session origin)
260 where the zero-based frame (relative to session)
264 TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
266 return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
269 /** returns the zero-based frame (relative to session start frame)
270 where the zero-based beat (relative to session start)
275 TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
277 return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
280 /** returns the zero-based tick (relative to session origin)
281 where the zero-based frame (relative to tempo section)
285 TempoSection::tick_at_frame (framepos_t f, framecnt_t frame_rate) const
287 if (_type == Constant) {
288 return (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
291 return tick_at_time (frame_to_minute (f - frame(), frame_rate)) + tick();
294 /** returns the zero-based frame (relative to session origin)
295 where the zero-based tick (relative to session)
299 TempoSection::frame_at_tick (double t, framecnt_t frame_rate) const
301 if (_type == Constant) {
302 return (framepos_t) floor (((t - tick()) / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate)) + frame();
305 return minute_to_frame (time_at_tick (t - tick()), frame_rate) + frame();
313 Tt----|-----------------*|
314 Ta----|--------------|* |
320 _______________|___|____
321 time a t (next tempo)
324 Duration in beats at time a is the integral of some Tempo function.
325 In our case, the Tempo function (Tempo at time t) is
328 with function constant
333 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
334 b(t) = T0(e^(ct) - 1) / c
336 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:
337 t(b) = log((cb / T0) + 1) / c
339 The time t at which Tempo T occurs is a as above:
340 t(T) = log(T / T0) / c
342 The beat at which a Tempo T occurs is:
345 The Tempo at which beat b occurs is:
348 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
349 Our problem is that we usually don't know t.
350 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.
351 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
352 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
354 By substituting our expanded t as a in the c function above, our problem is reduced to:
355 c = T0 (e^(log (Ta / T0)) - 1) / b
357 We can now store c for future time calculations.
358 If the following tempo section (the one that defines c in conjunction with this one)
359 is changed or moved, c is no longer valid.
361 The public methods are session-relative.
363 Most of this stuff is taken from this paper:
366 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
369 Zurich University of Arts
370 Institute for Computer Music and Sound Technology
372 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
376 /* compute this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
378 TempoSection::compute_c_func_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
380 double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
381 return ticks_per_minute() * (exp (log_tempo_ratio) - 1) / ((end_beat - beat()) * BBT_Time::ticks_per_beat);
384 /* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from session origin */
386 TempoSection::compute_c_func_frame (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
388 return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame - frame(), frame_rate));
392 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
394 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
398 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
400 return (frame / (double) frame_rate) / 60.0;
403 /* position function */
405 TempoSection::a_func (double end_tpm, double c_func) const
407 return log (end_tpm / ticks_per_minute()) / c_func;
410 /*function constant*/
412 TempoSection::c_func (double end_tpm, double end_time) const
414 return log (end_tpm / ticks_per_minute()) / end_time;
417 /* tempo in tpm at time in minutes */
419 TempoSection::tick_tempo_at_time (double time) const
421 return exp (_c_func * time) * ticks_per_minute();
424 /* time in minutes at tempo in tpm */
426 TempoSection::time_at_tick_tempo (double tick_tempo) const
428 return log (tick_tempo / ticks_per_minute()) / _c_func;
431 /* tick at tempo in tpm */
433 TempoSection::tick_at_tick_tempo (double tick_tempo) const
435 return (tick_tempo - ticks_per_minute()) / _c_func;
438 /* tempo in tpm at tick */
440 TempoSection::tick_tempo_at_tick (double tick) const
442 return (tick * _c_func) + ticks_per_minute();
445 /* tick at time in minutes */
447 TempoSection::tick_at_time (double time) const
449 return ((exp (_c_func * time)) - 1) * (ticks_per_minute() / _c_func);
452 /* time in minutes at tick */
454 TempoSection::time_at_tick (double tick) const
456 return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
459 /* beat at time in minutes */
461 TempoSection::beat_at_time (double time) const
463 return tick_at_time (time) / BBT_Time::ticks_per_beat;
466 /* time in munutes at beat */
468 TempoSection::time_at_beat (double beat) const
470 return time_at_tick (beat * BBT_Time::ticks_per_beat);
474 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
478 if (_bar_offset < 0.0) {
485 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
486 new_beat = ticks / BBT_Time::ticks_per_beat;
488 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
489 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
494 /***********************************************************************/
497 if a meter section is position locked to frames, then it can only be at 1|1|0.
498 thus we can have multiple 1|1|0s in the session tempo map.
502 BBT 1|1|0 2|1|0 3|1|0 1|1|0
505 all other meter sections are locked to beats.
507 the beat of a meter section is used to find its position rather than the stored bbt.
510 const string MeterSection::xml_state_node_name = "Meter";
512 MeterSection::MeterSection (const XMLNode& node)
513 : MetricSection (0.0), Meter (TempoMap::default_meter())
515 XMLProperty const * prop;
518 const XMLProperty *prop;
521 framepos_t frame = 0;
522 pair<double, BBT_Time> start;
524 if ((prop = node.property ("start")) != 0) {
525 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
529 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
531 /* legacy session - start used to be in bbt*/
535 error << _("MeterSection XML node has no \"start\" property") << endmsg;
538 if ((prop = node.property ("beat")) != 0) {
539 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
540 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
546 if ((prop = node.property ("bbt")) == 0) {
547 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
548 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
552 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
553 //throw failed_constructor();
560 if ((prop = node.property ("frame")) != 0) {
561 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
562 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
568 /* beats-per-bar is old; divisions-per-bar is new */
570 if ((prop = node.property ("divisions-per-bar")) == 0) {
571 if ((prop = node.property ("beats-per-bar")) == 0) {
572 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
573 throw failed_constructor();
576 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
577 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
578 throw failed_constructor();
581 if ((prop = node.property ("note-type")) == 0) {
582 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
583 throw failed_constructor();
585 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
586 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
587 throw failed_constructor();
590 if ((prop = node.property ("lock-style")) == 0) {
591 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
592 set_position_lock_style (MusicTime);
594 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
597 if ((prop = node.property ("movable")) == 0) {
598 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
599 throw failed_constructor();
602 set_movable (string_is_affirmative (prop->value()));
606 MeterSection::get_state() const
608 XMLNode *root = new XMLNode (xml_state_node_name);
612 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
616 root->add_property ("bbt", buf);
617 snprintf (buf, sizeof (buf), "%lf", beat());
618 root->add_property ("beat", buf);
619 snprintf (buf, sizeof (buf), "%f", _note_type);
620 root->add_property ("note-type", buf);
621 snprintf (buf, sizeof (buf), "%li", frame());
622 root->add_property ("frame", buf);
623 root->add_property ("lock-style", enum_2_string (position_lock_style()));
624 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
625 root->add_property ("divisions-per-bar", buf);
626 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
627 root->add_property ("movable", buf);
632 /***********************************************************************/
634 struct MetricSectionSorter {
635 bool operator() (const MetricSection* a, const MetricSection* b) {
636 return a->beat() < b->beat();
640 struct MetricSectionFrameSorter {
641 bool operator() (const MetricSection* a, const MetricSection* b) {
642 return a->frame() < b->frame();
646 TempoMap::TempoMap (framecnt_t fr)
649 BBT_Time start (1, 1, 0);
651 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
652 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
654 t->set_movable (false);
655 m->set_movable (false);
657 /* note: frame time is correct (zero) for both of these */
659 _metrics.push_back (t);
660 _metrics.push_back (m);
664 TempoMap::~TempoMap ()
669 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
671 bool removed = false;
674 Glib::Threads::RWLock::WriterLock lm (lock);
675 if ((removed = remove_tempo_locked (tempo))) {
676 if (complete_operation) {
677 recompute_map (_metrics);
682 if (removed && complete_operation) {
683 PropertyChanged (PropertyChange ());
688 TempoMap::remove_tempo_locked (const TempoSection& tempo)
692 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
693 if (dynamic_cast<TempoSection*> (*i) != 0) {
694 if (tempo.frame() == (*i)->frame()) {
695 if ((*i)->movable()) {
707 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
709 bool removed = false;
712 Glib::Threads::RWLock::WriterLock lm (lock);
713 if ((removed = remove_meter_locked (tempo))) {
714 if (complete_operation) {
715 recompute_map (_metrics);
720 if (removed && complete_operation) {
721 PropertyChanged (PropertyChange ());
726 TempoMap::remove_meter_locked (const MeterSection& tempo)
730 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
731 if (dynamic_cast<MeterSection*> (*i) != 0) {
732 if (tempo.frame() == (*i)->frame()) {
733 if ((*i)->movable()) {
745 TempoMap::do_insert (MetricSection* section)
747 bool need_add = true;
748 /* we only allow new meters to be inserted on beat 1 of an existing
752 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
753 //assert (m->bbt().ticks == 0);
755 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
757 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
758 corrected.second.beats = 1;
759 corrected.second.ticks = 0;
760 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
761 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
762 m->bbt(), corrected.second) << endmsg;
763 m->set_beat (corrected);
767 /* Look for any existing MetricSection that is of the same type and
768 in the same bar as the new one, and remove it before adding
769 the new one. Note that this means that if we find a matching,
770 existing section, we can break out of the loop since we're
771 guaranteed that there is only one such match.
774 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
776 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
777 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
778 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
779 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
781 if (tempo && insert_tempo) {
784 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
785 if ((ipm && tempo->beat() == insert_tempo->beat()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
787 if (!tempo->movable()) {
789 /* can't (re)move this section, so overwrite
790 * its data content (but not its properties as
794 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
802 } else if (meter && insert_meter) {
806 bool const ipm = insert_meter->position_lock_style() == MusicTime;
808 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
810 if (!meter->movable()) {
812 /* can't (re)move this section, so overwrite
813 * its data content (but not its properties as
817 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
827 /* non-matching types, so we don't care */
831 /* Add the given MetricSection, if we didn't just reset an existing
836 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
837 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
840 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
841 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
844 bool const ipm = insert_meter->position_lock_style() == MusicTime;
845 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
850 } else if (insert_tempo) {
851 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
852 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
855 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
856 if ((ipm && tempo->beat() > insert_tempo->beat()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
863 _metrics.insert (i, section);
864 //dump (_metrics, std::cerr);
869 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
872 Glib::Threads::RWLock::WriterLock lm (lock);
873 TempoSection& first (first_tempo());
874 if (ts.beat() != first.beat()) {
875 remove_tempo_locked (ts);
876 add_tempo_locked (tempo, where, true, type);
878 first.set_type (type);
880 /* cannot move the first tempo section */
881 *static_cast<Tempo*>(&first) = tempo;
882 recompute_map (_metrics);
887 PropertyChanged (PropertyChange ());
891 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
894 Glib::Threads::RWLock::WriterLock lm (lock);
895 TempoSection& first (first_tempo());
896 if (ts.frame() != first.frame()) {
897 remove_tempo_locked (ts);
898 add_tempo_locked (tempo, frame, true, type);
900 first.set_type (type);
902 /* cannot move the first tempo section */
903 *static_cast<Tempo*>(&first) = tempo;
904 recompute_map (_metrics);
908 PropertyChanged (PropertyChange ());
912 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
915 Glib::Threads::RWLock::WriterLock lm (lock);
916 add_tempo_locked (tempo, where, true, type);
919 PropertyChanged (PropertyChange ());
923 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
926 Glib::Threads::RWLock::WriterLock lm (lock);
927 add_tempo_locked (tempo, frame, true, type);
931 PropertyChanged (PropertyChange ());
935 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
937 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
942 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->beat());
947 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
949 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
954 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
959 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
962 Glib::Threads::RWLock::WriterLock lm (lock);
963 MeterSection& first (first_meter());
964 if (ms.beat() != first.beat()) {
965 remove_meter_locked (ms);
966 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
968 /* cannot move the first meter section */
969 *static_cast<Meter*>(&first) = meter;
970 recompute_map (_metrics);
974 PropertyChanged (PropertyChange ());
978 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
981 Glib::Threads::RWLock::WriterLock lm (lock);
982 MeterSection& first (first_meter());
983 if (ms.beat() != first.beat()) {
984 remove_meter_locked (ms);
985 add_meter_locked (meter, frame, true);
987 /* cannot move the first meter section */
988 *static_cast<Meter*>(&first) = meter;
989 recompute_map (_metrics);
993 PropertyChanged (PropertyChange ());
998 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1001 Glib::Threads::RWLock::WriterLock lm (lock);
1002 add_meter_locked (meter, beat, where, true);
1007 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1008 dump (_metrics, std::cerr);
1012 PropertyChanged (PropertyChange ());
1016 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1019 Glib::Threads::RWLock::WriterLock lm (lock);
1020 add_meter_locked (meter, frame, true);
1025 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1026 dump (_metrics, std::cerr);
1030 PropertyChanged (PropertyChange ());
1034 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1036 /* a new meter always starts a new bar on the first beat. so
1037 round the start time appropriately. remember that
1038 `where' is based on the existing tempo map, not
1039 the result after we insert the new meter.
1043 if (where.beats != 1) {
1048 /* new meters *always* start on a beat. */
1050 MeterSection* new_meter = new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor());
1051 do_insert (new_meter);
1054 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), beat);
1060 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1063 MeterSection* new_meter = new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor());
1064 double baf = beat_at_frame_locked (_metrics, frame);
1065 pair<double, BBT_Time> beat = make_pair (baf, beats_to_bbt_locked (_metrics, baf));
1066 new_meter->set_beat (beat);
1067 do_insert (new_meter);
1070 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1076 * 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,
1077 * taking any possible reordering as a consequence of this into account.
1078 * @param section - the section to be altered
1079 * @param bpm - the new Tempo
1080 * @param bbt - the bbt where the altered tempo will fall
1081 * @return returns - the position in frames where the new tempo section will lie.
1084 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1086 Glib::Threads::RWLock::ReaderLock lm (lock);
1089 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1090 double const beat = bbt_to_beats_locked (future_map, bbt);
1091 if (solve_map (future_map, new_section, bpm, beat)) {
1092 ret = new_section->frame();
1094 ret = frame_at_beat_locked (_metrics, beat);
1097 Metrics::const_iterator d = future_map.begin();
1098 while (d != future_map.end()) {
1106 TempoMap::gui_move_tempo_frame (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1110 Glib::Threads::RWLock::WriterLock lm (lock);
1111 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1112 if (solve_map (future_map, new_section, bpm, frame)) {
1113 solve_map (_metrics, ts, bpm, frame);
1117 Metrics::const_iterator d = future_map.begin();
1118 while (d != future_map.end()) {
1123 MetricPositionChanged (); // Emit Signal
1127 TempoMap::gui_move_tempo_beat (TempoSection* ts, const Tempo& bpm, const double& beat)
1131 Glib::Threads::RWLock::WriterLock lm (lock);
1132 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1133 if (solve_map (future_map, new_section, bpm, beat)) {
1134 solve_map (_metrics, ts, bpm, beat);
1138 Metrics::const_iterator d = future_map.begin();
1139 while (d != future_map.end()) {
1144 MetricPositionChanged (); // Emit Signal
1148 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
1151 Glib::Threads::RWLock::WriterLock lm (lock);
1152 solve_map (_metrics, ms, mt, frame);
1155 MetricPositionChanged (); // Emit Signal
1159 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
1162 Glib::Threads::RWLock::WriterLock lm (lock);
1163 solve_map (_metrics, ms, mt, beat);
1166 MetricPositionChanged (); // Emit Signal
1170 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1173 TempoSection* ret = 0;
1176 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1177 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1179 if (t->position_lock_style() == MusicTime) {
1180 ret = new TempoSection (t->beat(), t->beats_per_minute(), t->note_type(), t->type());
1182 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1184 copy.push_back (ret);
1187 if (t->position_lock_style() == MusicTime) {
1188 copy.push_back (new TempoSection (t->beat(), t->beats_per_minute(), t->note_type(), t->type()));
1190 copy.push_back (new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type()));
1193 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1194 if (m->position_lock_style() == MusicTime) {
1195 copy.push_back (new MeterSection (m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1197 copy.push_back (new MeterSection (m->frame(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1202 recompute_map (copy);
1207 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
1210 TempoSection* new_section = 0;
1213 Glib::Threads::RWLock::ReaderLock lm (lock);
1214 new_section = copy_metrics_and_point (copy, ts);
1217 double const beat = bbt_to_beats_locked (copy, bbt);
1218 bool ret = solve_map (copy, new_section, bpm, beat);
1220 Metrics::const_iterator d = copy.begin();
1221 while (d != copy.end()) {
1230 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1232 Tempo newtempo (beats_per_minute, note_type);
1235 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1236 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1238 Glib::Threads::RWLock::WriterLock lm (lock);
1239 *((Tempo*) t) = newtempo;
1240 recompute_map (_metrics);
1242 PropertyChanged (PropertyChange ());
1249 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1251 Tempo newtempo (beats_per_minute, note_type);
1254 TempoSection* first;
1255 Metrics::iterator i;
1257 /* find the TempoSection immediately preceding "where"
1260 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1262 if ((*i)->frame() > where) {
1268 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1278 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1288 Glib::Threads::RWLock::WriterLock lm (lock);
1289 /* cannot move the first tempo section */
1290 *((Tempo*)prev) = newtempo;
1291 recompute_map (_metrics);
1294 PropertyChanged (PropertyChange ());
1298 TempoMap::first_meter () const
1300 const MeterSection *m = 0;
1302 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1303 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1308 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1309 abort(); /*NOTREACHED*/
1314 TempoMap::first_meter ()
1316 MeterSection *m = 0;
1318 /* CALLER MUST HOLD LOCK */
1320 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1321 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1326 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1327 abort(); /*NOTREACHED*/
1332 TempoMap::first_tempo () const
1334 const TempoSection *t = 0;
1336 /* CALLER MUST HOLD LOCK */
1338 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1339 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1344 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1345 abort(); /*NOTREACHED*/
1350 TempoMap::first_tempo ()
1352 TempoSection *t = 0;
1354 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1355 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1360 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1361 abort(); /*NOTREACHED*/
1365 TempoMap::recompute_tempos (Metrics& metrics)
1367 TempoSection* prev_ts = 0;
1369 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1372 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1374 if (t->position_lock_style() == AudioTime) {
1375 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->beats_per_minute(), t->frame(), _frame_rate));
1376 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1379 prev_ts->set_c_func (prev_ts->compute_c_func_beat (t->beats_per_minute(), t->beat(), _frame_rate));
1380 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1389 /* tempos must be positioned correctly */
1391 TempoMap::recompute_meters (Metrics& metrics)
1393 MeterSection* meter = 0;
1395 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1396 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1397 if (meter->position_lock_style() == AudioTime) {
1398 /* a frame based meter has to have a 1|1|0 bbt */
1399 pair<double, BBT_Time> pr;
1401 pr.first = ceil (beat_at_frame_locked (metrics, meter->frame()));
1402 BBT_Time const where = beats_to_bbt_locked (metrics, pr.first);
1405 meter->set_beat (pr);
1407 meter->set_frame (frame_at_tick_locked (metrics, meter->tick()));
1414 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1416 /* CALLER MUST HOLD WRITE LOCK */
1420 /* we will actually stop once we hit
1427 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1430 /* silly call from Session::process() during startup
1435 recompute_tempos (metrics);
1436 recompute_meters (metrics);
1441 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1443 Glib::Threads::RWLock::ReaderLock lm (lock);
1444 TempoMetric m (first_meter(), first_tempo());
1446 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1447 at something, because we insert the default tempo and meter during
1448 TempoMap construction.
1450 now see if we can find better candidates.
1453 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1455 if ((*i)->frame() > frame) {
1468 /* XX meters only */
1470 TempoMap::metric_at (BBT_Time bbt) const
1472 Glib::Threads::RWLock::ReaderLock lm (lock);
1473 TempoMetric m (first_meter(), first_tempo());
1475 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1476 at something, because we insert the default tempo and meter during
1477 TempoMap construction.
1479 now see if we can find better candidates.
1482 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1484 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1485 BBT_Time section_start (mw->bbt());
1487 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1499 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1506 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1509 Glib::Threads::RWLock::ReaderLock lm (lock);
1510 frameoffset_t const frame_off = frame_offset_at (_metrics, frame);
1511 double const beats = beat_at_frame_locked (_metrics, frame + frame_off);
1513 bbt = beats_to_bbt_locked (_metrics, beats);
1517 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1519 Glib::Threads::RWLock::ReaderLock lm (lock);
1521 return bbt_to_beats_locked (_metrics, bbt);
1525 TempoMap::bbt_to_beats_locked (const Metrics& metrics, Timecode::BBT_Time bbt)
1527 /* CALLER HOLDS READ LOCK */
1529 double accumulated_beats = 0.0;
1530 double accumulated_bars = 0.0;
1531 MeterSection* prev_ms = 0;
1532 /* because audio-locked meters have 'fake' integral beats,
1533 we don't have to worry about any offset here.
1535 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1537 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1538 double bars_to_m = 0.0;
1540 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1542 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1546 accumulated_beats += m->beat() - prev_ms->beat();
1547 accumulated_bars += bars_to_m;
1553 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1554 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1555 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1561 TempoMap::beats_to_bbt (double beats)
1563 Glib::Threads::RWLock::ReaderLock lm (lock);
1565 return beats_to_bbt_locked (_metrics, beats);
1569 TempoMap::beats_to_bbt_locked (Metrics& metrics, double beats)
1571 /* CALLER HOLDS READ LOCK */
1573 MeterSection* prev_ms = 0;
1574 uint32_t accumulated_bars = 0;
1576 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1577 MeterSection* m = 0;
1579 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1583 if (m->beat() > beats) {
1584 /* this is the meter after the one our beat is on*/
1588 /* we need a whole number of bars. */
1589 double const beats_dur = m->beat() - prev_ms->beat();
1590 accumulated_bars += (beats_dur + 1) / prev_ms->divisions_per_bar();
1597 double const beats_in_ms = beats - prev_ms->beat();
1598 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1599 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1600 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1601 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1605 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1606 ret.beats = (uint32_t) floor (remaining_beats);
1607 ret.bars = total_bars;
1609 /* 0 0 0 to 1 1 0 - based mapping*/
1613 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1615 ret.ticks -= BBT_Time::ticks_per_beat;
1618 if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1627 TempoMap::tick_at_frame_locked (const Metrics& metrics, framecnt_t frame) const
1629 /* HOLD (at least) THE READER LOCK */
1631 TempoSection* prev_ts = 0;
1632 double accumulated_ticks = 0.0;
1634 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1636 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1638 if ((prev_ts) && t->frame() > frame) {
1639 /*the previous ts is the one containing the frame */
1640 double const ret = prev_ts->tick_at_frame (frame, _frame_rate);
1644 accumulated_ticks = t->tick();
1649 /* treated as constant for this ts */
1650 double const ticks_in_section = (((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat);
1652 return ticks_in_section + accumulated_ticks;
1657 TempoMap::frame_at_tick_locked (const Metrics& metrics, double tick) const
1659 /* HOLD THE READER LOCK */
1661 const TempoSection* prev_ts = 0;
1662 double accumulated_ticks = 0.0;
1664 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1667 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1668 if (prev_ts && t->tick() > tick) {
1669 return prev_ts->frame_at_tick (tick, _frame_rate);
1672 accumulated_ticks = t->tick();
1676 /* must be treated as constant, irrespective of _type */
1677 double const ticks_in_section = tick - accumulated_ticks;
1678 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1680 framecnt_t const ret = (((framecnt_t) floor (dtime)) + prev_ts->frame());
1686 TempoMap::beat_offset_at (const Metrics& metrics, double beat) const
1688 MeterSection* prev_m = 0;
1689 double beat_off = 0.0;
1691 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1692 MeterSection* m = 0;
1693 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1694 if (m->beat() > beat) {
1698 if (prev_m && m->position_lock_style() == AudioTime) {
1699 beat_off += ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) - floor ((m->beat() - prev_m->beat()) / prev_m->note_divisor());
1710 TempoMap::frame_offset_at (const Metrics& metrics, framepos_t frame) const
1712 frameoffset_t frame_off = 0;
1714 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1715 MeterSection* m = 0;
1716 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1717 if (m->frame() > frame) {
1720 if (m->position_lock_style() == AudioTime) {
1721 frame_off += frame_at_beat_locked (metrics, m->beat()) - m->frame();
1730 TempoMap::beat_at_frame (framecnt_t frame) const
1732 Glib::Threads::RWLock::ReaderLock lm (lock);
1733 framecnt_t const offset_frame = frame + frame_offset_at (_metrics, frame);
1734 double const tick = tick_at_frame_locked (_metrics, offset_frame);
1736 return tick / BBT_Time::ticks_per_beat;
1740 TempoMap::beat_at_frame_locked (const Metrics& metrics, framecnt_t frame) const
1742 return tick_at_frame_locked (metrics, frame) / BBT_Time::ticks_per_beat;
1746 TempoMap::frame_at_beat (double beat) const
1748 Glib::Threads::RWLock::ReaderLock lm (lock);
1749 framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1750 frameoffset_t const frame_off = frame_offset_at (_metrics, frame);
1751 return frame - frame_off;
1755 TempoMap::frame_at_beat_locked (const Metrics& metrics, double beat) const
1758 return frame_at_tick_locked (metrics, beat * BBT_Time::ticks_per_beat);
1762 TempoMap::frame_time (const BBT_Time& bbt)
1765 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1769 if (bbt.beats < 1) {
1770 throw std::logic_error ("beats are counted from one");
1772 Glib::Threads::RWLock::ReaderLock lm (lock);
1774 framecnt_t const frame = frame_at_beat_locked (_metrics, bbt_to_beats_locked (_metrics, bbt));
1775 frameoffset_t const frame_off = frame_offset_at (_metrics, frame);
1776 return frame - frame_off;
1780 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt)
1782 /* HOLD THE READER LOCK */
1784 framepos_t const ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1790 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1792 TempoSection* prev_ts = 0;
1794 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1796 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1798 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->beat() < prev_ts->beat())) {
1801 if (by_frame && t->frame() != prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate)) {
1804 if (!by_frame && fabs (t->beat() - prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate)) > 0.00001) {
1805 std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
1806 " |error :" << t->beat() - prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << std::endl <<
1807 "|frame at beat :" << prev_ts->frame_at_beat (t->beat(), _frame_rate) << std::endl <<
1808 " |frame at tempo : " << prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
1820 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1822 TempoSection* prev_ts = 0;
1823 TempoSection* section_prev = 0;
1824 MetricSectionFrameSorter fcmp;
1825 MetricSectionSorter cmp;
1827 section->set_frame (frame);
1828 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1830 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1833 section_prev = prev_ts;
1836 if (t->position_lock_style() == MusicTime) {
1837 prev_ts->set_c_func (prev_ts->compute_c_func_beat (t->beats_per_minute(), t->beat(), _frame_rate));
1838 t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1840 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->beats_per_minute(), t->frame(), _frame_rate));
1841 t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1849 section_prev->set_c_func (section_prev->compute_c_func_beat (section->beats_per_minute(), section->beat(), _frame_rate));
1850 section->set_beat (section_prev->beat_at_frame (frame, _frame_rate));
1853 if (section->position_lock_style() == MusicTime) {
1854 /* we're setting the frame */
1855 section->set_position_lock_style (AudioTime);
1856 recompute_tempos (imaginary);
1857 section->set_position_lock_style (MusicTime);
1859 recompute_tempos (imaginary);
1862 if (check_solved (imaginary, true)) {
1863 recompute_meters (imaginary);
1867 imaginary.sort (fcmp);
1868 if (section->position_lock_style() == MusicTime) {
1869 /* we're setting the frame */
1870 section->set_position_lock_style (AudioTime);
1871 recompute_tempos (imaginary);
1872 section->set_position_lock_style (MusicTime);
1874 recompute_tempos (imaginary);
1876 if (check_solved (imaginary, true)) {
1877 recompute_meters (imaginary);
1881 imaginary.sort (cmp);
1882 if (section->position_lock_style() == MusicTime) {
1883 /* we're setting the frame */
1884 section->set_position_lock_style (AudioTime);
1885 recompute_tempos (imaginary);
1886 section->set_position_lock_style (MusicTime);
1888 recompute_tempos (imaginary);
1890 if (check_solved (imaginary, true)) {
1891 recompute_meters (imaginary);
1895 //dump (imaginary, std::cerr);
1900 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& beat)
1902 MetricSectionSorter cmp;
1903 MetricSectionFrameSorter fcmp;
1904 TempoSection* prev_ts = 0;
1905 TempoSection* section_prev = 0;
1907 section->set_beat (beat);
1909 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1911 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1914 section_prev = prev_ts;
1917 if (t->position_lock_style() == MusicTime) {
1918 prev_ts->set_c_func (prev_ts->compute_c_func_beat (t->beats_per_minute(), t->beat(), _frame_rate));
1919 t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1921 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->beats_per_minute(), t->frame(), _frame_rate));
1922 t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1929 section_prev->set_c_func (section_prev->compute_c_func_beat (section->beats_per_minute(), section->beat(), _frame_rate));
1930 section->set_frame (section_prev->frame_at_beat (section->beat(), _frame_rate));
1933 if (section->position_lock_style() == AudioTime) {
1934 /* we're setting the beat */
1935 section->set_position_lock_style (MusicTime);
1936 recompute_tempos (imaginary);
1937 section->set_position_lock_style (AudioTime);
1939 recompute_tempos (imaginary);
1941 if (check_solved (imaginary, false)) {
1942 recompute_meters (imaginary);
1946 imaginary.sort (cmp);
1947 if (section->position_lock_style() == AudioTime) {
1948 /* we're setting the beat */
1949 section->set_position_lock_style (MusicTime);
1950 recompute_tempos (imaginary);
1951 section->set_position_lock_style (AudioTime);
1953 recompute_tempos (imaginary);
1956 if (check_solved (imaginary, false)) {
1957 recompute_meters (imaginary);
1961 imaginary.sort (fcmp);
1962 if (section->position_lock_style() == AudioTime) {
1963 /* we're setting the beat */
1964 section->set_position_lock_style (MusicTime);
1965 recompute_tempos (imaginary);
1966 section->set_position_lock_style (AudioTime);
1968 recompute_tempos (imaginary);
1971 if (check_solved (imaginary, false)) {
1972 recompute_meters (imaginary);
1976 //dump (imaginary, std::cerr);
1982 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& beat)
1984 MeterSection* prev_ms = 0;
1986 pair<double, BBT_Time> b_bbt = make_pair (beat, beats_to_bbt_locked (imaginary, beat));
1987 section->set_beat (b_bbt);
1988 MetricSectionSorter cmp;
1989 imaginary.sort (cmp);
1991 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1993 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1996 section->set_frame (frame_at_beat_locked (imaginary, beat));
2000 if (m->position_lock_style() == MusicTime) {
2001 m->set_frame (frame_at_beat_locked (imaginary, m->beat()));
2003 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (imaginary, m->frame()), BBT_Time (1, 1, 0));
2004 b_bbt.second = beats_to_bbt_locked (imaginary, b_bbt.first);
2005 m->set_beat (b_bbt);
2012 if (section->position_lock_style() == AudioTime) {
2013 /* we're setting the beat */
2014 section->set_position_lock_style (MusicTime);
2015 recompute_meters (imaginary);
2016 section->set_position_lock_style (AudioTime);
2018 recompute_meters (imaginary);
2023 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
2025 MeterSection* prev_ms = 0;
2026 section->set_frame (frame);
2028 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2030 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2034 here we define the beat for this frame.
2035 we're going to set it 'incorrectly' to the next integer and use this 'error'
2036 as an offset to the map as far as users of the public methods are concerned.
2037 (meters should go on absolute beats to keep us sane)
2039 double const beat_at_f = ceil (beat_at_frame_locked (imaginary, m->frame()));
2040 pair<double, BBT_Time> b_bbt = make_pair (beat_at_f, beats_to_bbt_locked (imaginary, beat_at_f));
2041 m->set_beat (b_bbt);
2045 if (m->position_lock_style() == MusicTime) {
2046 m->set_frame (frame_at_beat_locked (imaginary, m->beat()));
2048 double const beat_at_f = ceil (beat_at_frame_locked (imaginary, frame));
2049 pair<double, BBT_Time> b_bbt = make_pair (beat_at_f, beats_to_bbt_locked (imaginary, beat_at_f));
2050 m->set_beat (b_bbt);
2057 if (section->position_lock_style() == MusicTime) {
2058 /* we're setting the frame */
2059 section->set_position_lock_style (AudioTime);
2060 recompute_meters (imaginary);
2061 section->set_position_lock_style (MusicTime);
2063 recompute_meters (imaginary);
2068 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2070 Glib::Threads::RWLock::ReaderLock lm (lock);
2072 Metrics::const_iterator i;
2073 TempoSection* first = 0;
2074 TempoSection* second = 0;
2075 framepos_t const offset_pos = pos + frame_offset_at (_metrics, pos);
2077 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2080 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2082 if ((*i)->frame() > offset_pos) {
2090 if (first && second) {
2091 double const tick_at_time = first->tick_at_frame (offset_pos, _frame_rate);
2092 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2093 framecnt_t const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
2094 framecnt_t const ret = time_at_bbt - offset_pos;
2095 return ret - frame_offset_at (_metrics, pos);
2098 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2099 framecnt_t const ret = (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
2101 return (offset_pos + ret) - frame_offset_at (_metrics, pos);
2105 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2107 return round_to_type (fr, dir, Bar);
2111 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2113 return round_to_type (fr, dir, Beat);
2117 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2119 Glib::Threads::RWLock::ReaderLock lm (lock);
2121 uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (_metrics, fr + frame_offset_at (_metrics, fr)) + 0.5);
2122 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2123 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
2125 ticks -= beats * BBT_Time::ticks_per_beat;
2128 /* round to next (or same iff dir == RoundUpMaybe) */
2130 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2132 if (mod == 0 && dir == RoundUpMaybe) {
2133 /* right on the subdivision, which is fine, so do nothing */
2135 } else if (mod == 0) {
2136 /* right on the subdivision, so the difference is just the subdivision ticks */
2137 ticks += ticks_one_subdivisions_worth;
2140 /* not on subdivision, compute distance to next subdivision */
2142 ticks += ticks_one_subdivisions_worth - mod;
2145 if (ticks >= BBT_Time::ticks_per_beat) {
2146 ticks -= BBT_Time::ticks_per_beat;
2148 } else if (dir < 0) {
2150 /* round to previous (or same iff dir == RoundDownMaybe) */
2152 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2154 if (difference == 0 && dir == RoundDownAlways) {
2155 /* right on the subdivision, but force-rounding down,
2156 so the difference is just the subdivision ticks */
2157 difference = ticks_one_subdivisions_worth;
2160 if (ticks < difference) {
2161 ticks = BBT_Time::ticks_per_beat - ticks;
2163 ticks -= difference;
2167 /* round to nearest */
2170 /* compute the distance to the previous and next subdivision */
2172 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2174 /* closer to the next subdivision, so shift forward */
2176 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2178 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2180 if (ticks > BBT_Time::ticks_per_beat) {
2182 ticks -= BBT_Time::ticks_per_beat;
2183 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2186 } else if (rem > 0) {
2188 /* closer to previous subdivision, so shift backward */
2192 /* can't go backwards past zero, so ... */
2195 /* step back to previous beat */
2197 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2198 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2200 ticks = lrint (ticks - rem);
2201 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2204 /* on the subdivision, do nothing */
2207 framepos_t ret_frame = frame_at_tick_locked (_metrics, (beats * BBT_Time::ticks_per_beat) + ticks);
2208 return ret_frame - frame_offset_at (_metrics, fr);
2209 //return frame_at_tick_locked (_metrics, (beats * BBT_Time::ticks_per_beat) + ticks);
2213 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2215 Glib::Threads::RWLock::ReaderLock lm (lock);
2217 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame + frame_offset_at (_metrics, frame));
2219 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2224 /* find bar previous to 'frame' */
2227 return frame_time (bbt);
2229 } else if (dir > 0) {
2230 /* find bar following 'frame' */
2234 return frame_time (bbt);
2236 /* true rounding: find nearest bar */
2237 framepos_t raw_ft = frame_time (bbt);
2240 framepos_t prev_ft = frame_time (bbt);
2242 framepos_t next_ft = frame_time (bbt);
2244 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2255 return frame_at_beat_locked (_metrics, floor (beat_at_framepos)) - frame_offset_at (_metrics, frame);
2256 } else if (dir > 0) {
2257 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos)) - frame_offset_at (_metrics, frame);
2259 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)) - frame_offset_at (_metrics, frame);
2268 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2269 framepos_t lower, framepos_t upper)
2271 Glib::Threads::RWLock::ReaderLock lm (lock);
2272 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame_locked (_metrics, upper));
2273 double cnt = ceil (beat_at_frame_locked (_metrics, lower));
2275 while (cnt <= upper_beat) {
2276 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2277 Tempo const tempo = tempo_at (pos);
2278 MeterSection const meter = meter_section_at (pos);
2280 frameoffset_t const frame_offset = frame_offset_at (_metrics, pos);
2281 pos -= frame_offset;
2283 BBT_Time const bbt = beats_to_bbt_locked (_metrics, (double) cnt);
2285 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
2291 TempoMap::tempo_section_at (framepos_t frame) const
2293 Glib::Threads::RWLock::ReaderLock lm (lock);
2295 Metrics::const_iterator i;
2296 TempoSection* prev = 0;
2298 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2301 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2303 if ((*i)->frame() > frame) {
2313 abort(); /*NOTREACHED*/
2319 /* don't use this to calculate length (the tempo is only correct for this frame).
2320 do that stuff based on the beat_at_frame and frame_at_beat api
2323 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2325 Glib::Threads::RWLock::ReaderLock lm (lock);
2327 const TempoSection* ts_at = &tempo_section_at (frame);
2328 const TempoSection* ts_after = 0;
2329 Metrics::const_iterator i;
2331 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2334 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2336 if ((*i)->frame() > frame) {
2344 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2346 /* must be treated as constant tempo */
2347 return ts_at->frames_per_beat (_frame_rate);
2351 TempoMap::tempo_at (framepos_t frame) const
2353 Glib::Threads::RWLock::ReaderLock lm (lock);
2354 frameoffset_t const frame_off = frame + frame_offset_at (_metrics, frame);
2355 TempoSection* prev_ts = 0;
2357 Metrics::const_iterator i;
2359 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2361 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2362 if ((prev_ts) && t->frame() > frame) {
2363 /* this is the one past frame */
2364 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
2365 Tempo const ret_tempo (ret, prev_ts->note_type());
2372 double const ret = prev_ts->beats_per_minute();
2373 Tempo const ret_tempo (ret, prev_ts->note_type ());
2379 TempoMap::meter_section_at (framepos_t frame) const
2381 Glib::Threads::RWLock::ReaderLock lm (lock);
2382 framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2383 Metrics::const_iterator i;
2384 MeterSection* prev = 0;
2386 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2389 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2391 if ((*i)->frame() > frame_off) {
2401 abort(); /*NOTREACHED*/
2408 TempoMap::meter_section_at (double beat) const
2410 Glib::Threads::RWLock::ReaderLock lm (lock);
2412 Metrics::const_iterator i;
2413 MeterSection* prev = 0;
2415 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2418 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2420 if ((*i)->beat() > beat) {
2430 abort(); /*NOTREACHED*/
2437 TempoMap::meter_at (framepos_t frame) const
2439 framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2440 TempoMetric m (metric_at (frame_off));
2446 TempoMap::get_state ()
2448 Metrics::const_iterator i;
2449 XMLNode *root = new XMLNode ("TempoMap");
2452 Glib::Threads::RWLock::ReaderLock lm (lock);
2453 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2454 root->add_child_nocopy ((*i)->get_state());
2462 TempoMap::set_state (const XMLNode& node, int /*version*/)
2465 Glib::Threads::RWLock::WriterLock lm (lock);
2468 XMLNodeConstIterator niter;
2469 Metrics old_metrics (_metrics);
2470 MeterSection* last_meter = 0;
2473 nlist = node.children();
2475 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2476 XMLNode* child = *niter;
2478 if (child->name() == TempoSection::xml_state_node_name) {
2481 TempoSection* ts = new TempoSection (*child);
2482 _metrics.push_back (ts);
2484 if (ts->bar_offset() < 0.0) {
2486 //ts->update_bar_offset_from_bbt (*last_meter);
2491 catch (failed_constructor& err){
2492 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2493 _metrics = old_metrics;
2497 } else if (child->name() == MeterSection::xml_state_node_name) {
2500 MeterSection* ms = new MeterSection (*child);
2501 _metrics.push_back (ms);
2505 catch (failed_constructor& err) {
2506 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2507 _metrics = old_metrics;
2513 if (niter == nlist.end()) {
2514 MetricSectionSorter cmp;
2515 _metrics.sort (cmp);
2517 /* check for legacy sessions where bbt was the base musical unit for tempo */
2518 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2519 MeterSection* prev_ms;
2520 TempoSection* prev_ts;
2521 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2522 if (prev_ms->beat() < 0.0) {
2523 /*XX we cannot possibly make this work??. */
2524 pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
2525 prev_ms->set_beat (start);
2527 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2528 if (prev_ts->beat() < 0.0) {
2529 double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2530 prev_ts->set_beat (start);
2535 /* check for multiple tempo/meters at the same location, which
2536 ardour2 somehow allowed.
2539 Metrics::iterator prev = _metrics.end();
2540 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2541 if (prev != _metrics.end()) {
2543 MeterSection* prev_ms;
2545 TempoSection* prev_ts;
2546 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2547 if (prev_ms->beat() == ms->beat()) {
2548 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2549 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2552 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2553 if (prev_ts->beat() == ts->beat()) {
2554 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2555 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2563 recompute_map (_metrics);
2566 PropertyChanged (PropertyChange ());
2572 TempoMap::dump (Metrics& metrics, std::ostream& o) const
2574 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2575 const MeterSection* m;
2576 const TempoSection* t;
2577 const TempoSection* prev_ts = 0;
2579 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2581 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2582 o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->beat() << " frame= " << t->frame() << " (movable? "
2583 << t->movable() << ')' << endl;
2585 o << "current : " << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
2586 o << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
2587 o << "calculated : " << prev_ts->tempo_at_beat (t->beat()) << " | " << prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << " | " << prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
2588 o << "------" << std::endl;
2590 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2591 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2592 << " (movable? " << m->movable() << ')' << endl;
2599 TempoMap::n_tempos() const
2601 Glib::Threads::RWLock::ReaderLock lm (lock);
2604 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2605 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2614 TempoMap::n_meters() const
2616 Glib::Threads::RWLock::ReaderLock lm (lock);
2619 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2620 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2629 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2632 Glib::Threads::RWLock::WriterLock lm (lock);
2633 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2634 if ((*i)->frame() >= where && (*i)->movable ()) {
2635 (*i)->set_frame ((*i)->frame() + amount);
2639 /* now reset the BBT time of all metrics, based on their new
2640 * audio time. This is the only place where we do this reverse
2644 Metrics::iterator i;
2645 const MeterSection* meter;
2646 const TempoSection* tempo;
2650 meter = &first_meter ();
2651 tempo = &first_tempo ();
2656 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2659 MetricSection* prev = 0;
2661 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2664 //TempoMetric metric (*meter, *tempo);
2665 MeterSection* ms = const_cast<MeterSection*>(meter);
2666 TempoSection* ts = const_cast<TempoSection*>(tempo);
2669 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2670 ts->set_beat (t->beat());
2672 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2673 ts->set_beat (m->beat());
2675 ts->set_frame (prev->frame());
2679 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2680 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2681 ms->set_beat (start);
2683 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2684 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (_metrics, t->beat()));
2685 ms->set_beat (start);
2687 ms->set_frame (prev->frame());
2691 // metric will be at frames=0 bbt=1|1|0 by default
2692 // which is correct for our purpose
2695 // cerr << bbt << endl;
2697 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2698 t->set_beat (beat_at_frame_locked (_metrics, m->frame()));
2700 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2701 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2702 bbt_time (m->frame(), bbt);
2704 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2710 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2711 /* round up to next beat */
2717 if (bbt.beats != 1) {
2718 /* round up to next bar */
2723 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
2724 m->set_beat (start);
2726 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2728 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2729 abort(); /*NOTREACHED*/
2735 recompute_map (_metrics);
2739 PropertyChanged (PropertyChange ());
2742 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2746 std::list<MetricSection*> metric_kill_list;
2748 TempoSection* last_tempo = NULL;
2749 MeterSection* last_meter = NULL;
2750 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2751 bool meter_after = false; // is there a meter marker likewise?
2753 Glib::Threads::RWLock::WriterLock lm (lock);
2754 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2755 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2756 metric_kill_list.push_back(*i);
2757 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2760 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2764 else if ((*i)->frame() >= where) {
2765 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2766 (*i)->set_frame ((*i)->frame() - amount);
2767 if ((*i)->frame() == where) {
2768 // marker was immediately after end of range
2769 tempo_after = dynamic_cast<TempoSection*> (*i);
2770 meter_after = dynamic_cast<MeterSection*> (*i);
2776 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2777 if (last_tempo && !tempo_after) {
2778 metric_kill_list.remove(last_tempo);
2779 last_tempo->set_frame(where);
2782 if (last_meter && !meter_after) {
2783 metric_kill_list.remove(last_meter);
2784 last_meter->set_frame(where);
2788 //remove all the remaining metrics
2789 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2790 _metrics.remove(*i);
2795 recompute_map (_metrics);
2798 PropertyChanged (PropertyChange ());
2802 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2803 * pos can be -ve, if required.
2806 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2808 return frame_at_beat (beat_at_frame_locked (_metrics, pos) + beats.to_double());
2811 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2813 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2815 return frame_at_beat (beat_at_frame_locked (_metrics, pos) - beats.to_double());
2818 /** Add the BBT interval op to pos and return the result */
2820 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2822 cerr << "framepos_plus_bbt - untested" << endl;
2823 Glib::Threads::RWLock::ReaderLock lm (lock);
2825 Metrics::const_iterator i;
2826 const MeterSection* meter;
2827 const MeterSection* m;
2828 const TempoSection* tempo;
2829 const TempoSection* next_tempo = 0;
2830 const TempoSection* t;
2831 double frames_per_beat;
2832 framepos_t effective_pos = max (pos, (framepos_t) 0);
2834 meter = &first_meter ();
2835 tempo = &first_tempo ();
2840 /* find the starting metrics for tempo & meter */
2842 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2844 if ((*i)->frame() > effective_pos) {
2848 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2850 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2855 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2856 if ((*i)->frame() > effective_pos) {
2857 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2866 meter -> the Meter for "pos"
2867 tempo -> the Tempo for "pos"
2868 next_tempo -> the Tempo after "pos" or 0
2869 i -> for first new metric after "pos", possibly metrics.end()
2872 /* now comes the complicated part. we have to add one beat a time,
2873 checking for a new metric on every beat.
2877 /* fpb is used for constant tempo */
2878 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2885 /* check if we need to use a new metric section: has adding frames moved us
2886 to or after the start of the next metric section? in which case, use it.
2889 if (i != _metrics.end()) {
2890 if ((*i)->frame() <= pos) {
2892 /* about to change tempo or meter, so add the
2893 * number of frames for the bars we've just
2894 * traversed before we change the
2895 * frames_per_beat value.
2898 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2903 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2905 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2910 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2912 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2916 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2923 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2925 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2932 /* given the current meter, have we gone past the end of the bar ? */
2937 /* check if we need to use a new metric section: has adding frames moved us
2938 to or after the start of the next metric section? in which case, use it.
2941 if (i != _metrics.end()) {
2942 if ((*i)->frame() <= pos) {
2944 /* about to change tempo or meter, so add the
2945 * number of frames for the beats we've just
2946 * traversed before we change the
2947 * frames_per_beat value.
2950 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2955 pos += tempo->frame_at_beat (beats, _frame_rate);
2957 pos += llrint (beats * frames_per_beat);
2962 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2964 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2968 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2974 pos += tempo->frame_at_beat (beats, _frame_rate);
2976 pos += llrint (beats * frames_per_beat);
2980 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2987 /** Count the number of beats that are equivalent to distance when going forward,
2991 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2993 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2997 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3003 operator<< (std::ostream& o, const Meter& m) {
3004 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3008 operator<< (std::ostream& o, const Tempo& t) {
3009 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3013 operator<< (std::ostream& o, const MetricSection& section) {
3015 o << "MetricSection @ " << section.frame() << ' ';
3017 const TempoSection* ts;
3018 const MeterSection* ms;
3020 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3021 o << *((const Tempo*) ts);
3022 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3023 //o << *((const Meter*) ms);