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;
72 /***********************************************************************/
74 const string TempoSection::xml_state_node_name = "Tempo";
76 TempoSection::TempoSection (const XMLNode& node)
77 : MetricSection (0.0), Tempo (TempoMap::default_tempo())
79 const XMLProperty *prop;
85 if ((prop = node.property ("start")) != 0) {
86 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
90 /* legacy session - start used to be in bbt*/
96 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
100 if ((prop = node.property ("beat")) != 0) {
101 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
102 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
107 if ((prop = node.property ("frame")) != 0) {
108 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
109 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
115 if ((prop = node.property ("beats-per-minute")) == 0) {
116 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
117 throw failed_constructor();
120 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
121 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
122 throw failed_constructor();
125 if ((prop = node.property ("note-type")) == 0) {
126 /* older session, make note type be quarter by default */
129 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
130 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
131 throw failed_constructor();
135 if ((prop = node.property ("movable")) == 0) {
136 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
137 throw failed_constructor();
140 set_movable (string_is_affirmative (prop->value()));
142 if ((prop = node.property ("bar-offset")) == 0) {
145 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
146 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
147 throw failed_constructor();
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", beat());
172 root->add_property ("beat", 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), "%f", _bar_offset);
180 // root->add_property ("bar-offset", buf);
181 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
182 root->add_property ("movable", buf);
183 root->add_property ("tempo-type", enum_2_string (_type));
184 root->add_property ("lock-style", enum_2_string (position_lock_style()));
191 TempoSection::update_bar_offset_from_bbt (const Meter& m)
193 _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
194 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
196 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
200 TempoSection::set_type (Type type)
205 /** returns the tempo at the zero-based (relative to session) frame.
208 TempoSection::tempo_at_frame (framepos_t f, framecnt_t frame_rate) const
211 if (_type == Constant) {
212 return beats_per_minute();
215 return tick_tempo_at_time (frame_to_minute (f - frame(), frame_rate)) / BBT_Time::ticks_per_beat;
218 /** returns the zero-based frame (relative to session)
219 where the tempo occurs in this section.
220 beat b is only used for constant tempos.
221 note that the tempo map may have multiple such values.
224 TempoSection::frame_at_tempo (double bpm, double b, framecnt_t frame_rate) const
226 if (_type == Constant) {
227 return ((b - beat()) * frames_per_beat (frame_rate)) + frame();
230 return minute_to_frame (time_at_tick_tempo (bpm * BBT_Time::ticks_per_beat), frame_rate) + frame();
232 /** returns the tempo at the zero-based (relative to session) beat.
235 TempoSection::tempo_at_beat (double b) const
238 if (_type == Constant) {
239 return beats_per_minute();
242 return tick_tempo_at_tick ((b - beat()) * BBT_Time::ticks_per_beat) / BBT_Time::ticks_per_beat;
245 /** returns the zero-based beat (relative to session)
246 where the tempo occurs given frame f. frame f is only used for constant tempos.
247 note that the session tempo map may have multiple beats at a given tempo.
250 TempoSection::beat_at_tempo (double bpm, framepos_t f, framecnt_t frame_rate) const
252 if (_type == Constant) {
253 double const ticks = (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
254 return ticks / BBT_Time::ticks_per_beat;
257 return (tick_at_tick_tempo (bpm * BBT_Time::ticks_per_beat) + tick()) / BBT_Time::ticks_per_beat;
260 /** returns the zero-based beat (relative to session origin)
261 where the zero-based frame (relative to session)
265 TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
267 return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
270 /** returns the zero-based frame (relative to session start frame)
271 where the zero-based beat (relative to session start)
276 TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
278 return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
281 /** returns the zero-based tick (relative to session origin)
282 where the zero-based frame (relative to tempo section)
286 TempoSection::tick_at_frame (framepos_t f, framecnt_t frame_rate) const
288 if (_type == Constant) {
289 return (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
292 return tick_at_time (frame_to_minute (f - frame(), frame_rate)) + tick();
295 /** returns the zero-based frame (relative to session origin)
296 where the zero-based tick (relative to session)
300 TempoSection::frame_at_tick (double t, framecnt_t frame_rate) const
302 if (_type == Constant) {
303 return (framepos_t) floor (((t - tick()) / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate)) + frame();
306 return minute_to_frame (time_at_tick (t - tick()), frame_rate) + frame();
314 Tt----|-----------------*|
315 Ta----|--------------|* |
321 _______________|___|____
322 time a t (next tempo)
325 Duration in beats at time a is the integral of some Tempo function.
326 In our case, the Tempo function (Tempo at time t) is
329 with function constant
334 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
335 b(t) = T0(e^(ct) - 1) / c
337 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:
338 t(b) = log((cb / T0) + 1) / c
340 The time t at which Tempo T occurs is a as above:
341 t(T) = log(T / T0) / c
343 The beat at which a Tempo T occurs is:
346 The Tempo at which beat b occurs is:
349 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
350 Our problem is that we usually don't know t.
351 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.
352 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
353 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
355 By substituting our expanded t as a in the c function above, our problem is reduced to:
356 c = T0 (e^(log (Ta / T0)) - 1) / b
358 We can now store c for future time calculations.
359 If the following tempo section (the one that defines c in conjunction with this one)
360 is changed or moved, c is no longer valid.
362 The public methods are session-relative.
364 Most of this stuff is taken from this paper:
367 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
370 Zurich University of Arts
371 Institute for Computer Music and Sound Technology
373 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
377 /* set this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
379 TempoSection::set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
381 double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
382 _c_func = ticks_per_minute() * (exp (log_tempo_ratio) - 1) / ((end_beat - beat()) * BBT_Time::ticks_per_beat);
385 /* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from session origin */
387 TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
389 return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame - frame(), frame_rate));
393 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
395 return (framecnt_t) floor ((time * 60.0 * frame_rate));
399 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
401 return (frame / (double) frame_rate) / 60.0;
404 /* position function */
406 TempoSection::a_func (double end_tpm, double c_func) const
408 return log (end_tpm / ticks_per_minute()) / c_func;
411 /*function constant*/
413 TempoSection::c_func (double end_tpm, double end_time) const
415 return log (end_tpm / ticks_per_minute()) / end_time;
418 /* tempo in tpm at time in minutes */
420 TempoSection::tick_tempo_at_time (double time) const
422 return exp (_c_func * time) * ticks_per_minute();
425 /* time in minutes at tempo in tpm */
427 TempoSection::time_at_tick_tempo (double tick_tempo) const
429 return log (tick_tempo / ticks_per_minute()) / _c_func;
432 /* tick at tempo in tpm */
434 TempoSection::tick_at_tick_tempo (double tick_tempo) const
436 return (tick_tempo - ticks_per_minute()) / _c_func;
439 /* tempo in tpm at tick */
441 TempoSection::tick_tempo_at_tick (double tick) const
443 return (tick * _c_func) + ticks_per_minute();
446 /* tick at time in minutes */
448 TempoSection::tick_at_time (double time) const
450 return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
453 /* time in minutes at tick */
455 TempoSection::time_at_tick (double tick) const
457 return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
460 /* beat at time in minutes */
462 TempoSection::beat_at_time (double time) const
464 return tick_at_time (time) / BBT_Time::ticks_per_beat;
467 /* time in munutes at beat */
469 TempoSection::time_at_beat (double beat) const
471 return time_at_tick (beat * BBT_Time::ticks_per_beat);
475 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
479 if (_bar_offset < 0.0) {
486 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
487 new_beat = ticks / BBT_Time::ticks_per_beat;
489 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
490 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
495 /***********************************************************************/
498 if a meter section is position locked to frames, then it can only be at 1|1|0.
499 thus we can have multiple 1|1|0s in the session tempo map.
503 BBT 1|1|0 2|1|0 3|1|0 1|1|0
506 all other meter sections are locked to beats.
508 the beat of a meter section is used to find its position rather than the stored bbt.
511 const string MeterSection::xml_state_node_name = "Meter";
513 MeterSection::MeterSection (const XMLNode& node)
514 : MetricSection (0.0), Meter (TempoMap::default_meter())
516 XMLProperty const * prop;
519 const XMLProperty *prop;
522 framepos_t frame = 0;
523 pair<double, BBT_Time> start;
525 if ((prop = node.property ("start")) != 0) {
526 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
530 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
532 /* legacy session - start used to be in bbt*/
536 error << _("MeterSection XML node has no \"start\" property") << endmsg;
539 if ((prop = node.property ("beat")) != 0) {
540 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
541 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
547 if ((prop = node.property ("bbt")) == 0) {
548 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
549 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
553 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
554 //throw failed_constructor();
561 if ((prop = node.property ("frame")) != 0) {
562 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
563 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
569 /* beats-per-bar is old; divisions-per-bar is new */
571 if ((prop = node.property ("divisions-per-bar")) == 0) {
572 if ((prop = node.property ("beats-per-bar")) == 0) {
573 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
574 throw failed_constructor();
577 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
578 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
579 throw failed_constructor();
582 if ((prop = node.property ("note-type")) == 0) {
583 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
584 throw failed_constructor();
586 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
587 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
588 throw failed_constructor();
591 if ((prop = node.property ("lock-style")) == 0) {
592 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
593 set_position_lock_style (MusicTime);
595 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
598 if ((prop = node.property ("movable")) == 0) {
599 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
600 throw failed_constructor();
603 set_movable (string_is_affirmative (prop->value()));
607 MeterSection::get_state() const
609 XMLNode *root = new XMLNode (xml_state_node_name);
613 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
617 root->add_property ("bbt", buf);
618 snprintf (buf, sizeof (buf), "%lf", beat());
619 root->add_property ("beat", buf);
620 snprintf (buf, sizeof (buf), "%f", _note_type);
621 root->add_property ("note-type", buf);
622 snprintf (buf, sizeof (buf), "%li", frame());
623 root->add_property ("frame", buf);
624 root->add_property ("lock-style", enum_2_string (position_lock_style()));
625 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
626 root->add_property ("divisions-per-bar", buf);
627 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
628 root->add_property ("movable", buf);
633 /***********************************************************************/
635 struct MetricSectionSorter {
636 bool operator() (const MetricSection* a, const MetricSection* b) {
637 return a->beat() < b->beat();
641 struct MetricSectionFrameSorter {
642 bool operator() (const MetricSection* a, const MetricSection* b) {
643 return a->frame() < b->frame();
647 TempoMap::TempoMap (framecnt_t fr)
650 BBT_Time start (1, 1, 0);
652 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
653 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
655 t->set_movable (false);
656 m->set_movable (false);
658 /* note: frame time is correct (zero) for both of these */
660 metrics.push_back (t);
661 metrics.push_back (m);
665 TempoMap::~TempoMap ()
670 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
672 bool removed = false;
675 Glib::Threads::RWLock::WriterLock lm (lock);
676 if ((removed = remove_tempo_locked (tempo))) {
677 if (complete_operation) {
678 recompute_map (true);
683 if (removed && complete_operation) {
684 PropertyChanged (PropertyChange ());
689 TempoMap::remove_tempo_locked (const TempoSection& tempo)
693 for (i = metrics.begin(); i != metrics.end(); ++i) {
694 if (dynamic_cast<TempoSection*> (*i) != 0) {
695 if (tempo.frame() == (*i)->frame()) {
696 if ((*i)->movable()) {
708 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
710 bool removed = false;
713 Glib::Threads::RWLock::WriterLock lm (lock);
714 if ((removed = remove_meter_locked (tempo))) {
715 if (complete_operation) {
716 recompute_map (true);
721 if (removed && complete_operation) {
722 PropertyChanged (PropertyChange ());
727 TempoMap::remove_meter_locked (const MeterSection& tempo)
731 for (i = metrics.begin(); i != metrics.end(); ++i) {
732 if (dynamic_cast<MeterSection*> (*i) != 0) {
733 if (tempo.frame() == (*i)->frame()) {
734 if ((*i)->movable()) {
746 TempoMap::do_insert (MetricSection* section)
748 bool need_add = true;
750 /* we only allow new meters to be inserted on beat 1 of an existing
754 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
755 assert (m->bbt().ticks == 0);
757 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
759 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
760 corrected.second.beats = 1;
761 corrected.second.ticks = 0;
762 corrected.first = bbt_to_beats_locked (corrected.second);
763 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
764 m->bbt(), corrected.second) << endmsg;
765 m->set_beat (corrected);
769 /* Look for any existing MetricSection that is of the same type and
770 in the same bar as the new one, and remove it before adding
771 the new one. Note that this means that if we find a matching,
772 existing section, we can break out of the loop since we're
773 guaranteed that there is only one such match.
776 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
778 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
779 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
781 if (tempo && insert_tempo) {
784 PositionLockStyle const ipl = insert_tempo->position_lock_style();
785 if ((ipl == MusicTime && tempo->beat() == insert_tempo->beat()) || (ipl == AudioTime && 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 (!tempo && !insert_tempo) {
805 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
806 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
807 PositionLockStyle const ipl = insert_meter->position_lock_style();
809 if ((ipl == MusicTime && meter->beat() == insert_meter->beat()) || (ipl == AudioTime && meter->frame() == insert_meter->frame())) {
811 if (!meter->movable()) {
813 /* can't (re)move this section, so overwrite
814 * its data content (but not its properties as
818 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
828 /* non-matching types, so we don't care */
832 /* Add the given MetricSection, if we didn't just reset an existing
837 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
838 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
842 for (i = metrics.begin(); i != metrics.end(); ++i) {
843 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
846 PositionLockStyle const ipl = insert_meter->position_lock_style();
847 if ((ipl == MusicTime && meter->beat() > insert_meter->beat()) || (ipl == AudioTime && meter->frame() > insert_meter->frame())) {
852 } else if (insert_tempo) {
853 for (i = metrics.begin(); i != metrics.end(); ++i) {
854 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
857 PositionLockStyle const ipl = insert_tempo->position_lock_style();
858 if ((ipl == MusicTime && tempo->beat() > insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() > insert_tempo->frame())) {
865 metrics.insert (i, section);
871 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
874 Glib::Threads::RWLock::WriterLock lm (lock);
875 TempoSection& first (first_tempo());
876 if (ts.beat() != first.beat()) {
877 remove_tempo_locked (ts);
878 add_tempo_locked (tempo, where, true, type);
880 first.set_type (type);
882 /* cannot move the first tempo section */
883 *static_cast<Tempo*>(&first) = tempo;
884 recompute_map (false);
889 PropertyChanged (PropertyChange ());
893 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
896 Glib::Threads::RWLock::WriterLock lm (lock);
897 TempoSection& first (first_tempo());
898 if (ts.beat() != first.beat()) {
899 remove_tempo_locked (ts);
900 add_tempo_locked (tempo, frame, true, type);
902 first.set_type (type);
904 /* cannot move the first tempo section */
905 *static_cast<Tempo*>(&first) = tempo;
906 recompute_map (false);
911 PropertyChanged (PropertyChange ());
915 TempoMap::get_new_order (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
917 Metrics imaginary (metrics);
919 TempoSection* prev_ts = 0;
922 /*set frame and sort */
923 section->set_frame (frame);
924 MetricSectionFrameSorter fcmp;
925 imaginary.sort (fcmp);
927 /* recompute tempos */
928 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
929 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
932 /* we have already set the frame - set the beat */
933 prev_ts->set_c_func (prev_ts->compute_c_func (bpm.beats_per_minute(), frame, _frame_rate));
934 section->set_beat (prev_ts->beat_at_tempo (bpm.beats_per_minute(), frame, _frame_rate));
938 if (t->position_lock_style() == MusicTime) {
939 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
940 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
942 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
943 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
949 MetricSectionSorter cmp;
950 imaginary.sort (cmp);
953 std::cerr << "dumping imaginary order ------" << std::endl;;
954 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
955 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
958 std::cerr << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
959 std::cerr << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
960 std::cerr << t->beats_per_minute() << " | " << prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame, _frame_rate) << " | " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
961 std::cerr << " ------" << std::endl;;
967 std::cerr << "end dump ------";
973 TempoMap::get_new_order (TempoSection* section, const Tempo& bpm, const double& beat)
975 Metrics imaginary (metrics);
977 TempoSection* prev_ts = 0;
980 /*set beat and sort */
981 section->set_beat (beat);
982 MetricSectionSorter cmp;
983 imaginary.sort (cmp);
985 /* recompute tempo positions */
986 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
987 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
990 /* we've already set the beat - set the frame */
991 prev_ts->set_c_func_from_tempo_and_beat (bpm.beats_per_minute(), beat, _frame_rate);
992 section->set_frame (prev_ts->frame_at_tempo (bpm.beats_per_minute(), beat, _frame_rate));
996 if (t->position_lock_style() == MusicTime) {
997 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
998 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1000 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
1001 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1008 MetricSectionFrameSorter fcmp;
1009 imaginary.sort (fcmp);
1012 std::cerr << "dumping imaginary order ------" << std::endl;;
1013 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1014 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1017 std::cerr << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
1018 std::cerr << "prev : " << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
1019 std::cerr << "calculated : " << t->beats_per_minute() << " | " << prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << " | " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), t->frame(), _frame_rate) << std::endl;
1020 std::cerr << " ------" << std::endl;;
1026 std::cerr << "end dump ------";
1032 TempoMap::get_new_order(MeterSection* section, const Meter& mt, const framepos_t& frame, const double& beat)
1035 Metrics imaginary (metrics);
1037 MeterSection* prev_ms = 0;
1039 MeterSection* our_section = 0;
1041 MetricSectionSorter cmp;
1043 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1044 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1046 if (section->beat() == m->beat()) {
1050 if (beat < m->beat()){
1051 pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1052 our_section->set_beat (b_bbt);
1053 our_section->set_frame (frame_at_beat_locked (beat));
1057 if (m->position_lock_style() == MusicTime) {
1058 m->set_frame (frame_at_beat_locked (m->beat()));
1060 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
1061 m->set_beat (b_bbt);
1067 /* now we do the whole thing again because audio-locked sections will have caused a re-order */
1071 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1072 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1074 if (section->beat() == m->beat()) {
1077 if (beat < m->beat()){
1078 ret = frame_at_beat (beat);
1079 section->set_frame (ret);
1083 if (m->position_lock_style() == MusicTime) {
1084 m->set_frame (frame_at_beat (m->beat()));
1086 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
1087 m->set_beat (b_bbt);
1095 pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1096 section->set_beat (b_bbt);
1097 section->set_frame (frame_at_beat_locked (beat));
1103 * This is for a gui that needs to know the frame of a beat if a tempo section were to be moved or altered.
1104 * It actually reorders and partially recomputes tha ramps, so calling this commits you to replacing the section immediately.
1105 * It will not emit a signal as you probably want to replace the tempo or do somethig else before that happens.
1106 * @param section - the section you want to alter
1107 * @param bpm - the new tempo
1108 * @param beat - the beat where the altered tempo will fall
1109 * @return returns - the position in frames where the new tempo section will lie.
1112 TempoMap::compute_new_tempo_frame (TempoSection* section, const Tempo& bpm, const double& beat)
1114 Glib::Threads::RWLock::WriterLock lm (lock);
1115 Metrics new_order = get_new_order (section, bpm, beat);
1117 return section->frame();
1121 TempoMap::gui_move_tempo (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1124 Glib::Threads::RWLock::WriterLock lm (lock);
1125 Metrics new_order = get_new_order (ts, bpm, frame);
1128 metrics = new_order;
1129 recompute_map (false);
1132 MetricPositionChanged (); // Emit Signal
1136 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame, const double& beat_where)
1138 Glib::Threads::RWLock::WriterLock lm (lock);
1139 Metrics imaginary = get_new_order (ms, mt, frame, beat_where);
1143 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
1146 Glib::Threads::RWLock::WriterLock lm (lock);
1147 add_tempo_locked (tempo, where, true, type);
1150 PropertyChanged (PropertyChange ());
1154 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
1157 Glib::Threads::RWLock::WriterLock lm (lock);
1158 add_tempo_locked (tempo, frame, true, type);
1162 PropertyChanged (PropertyChange ());
1166 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
1168 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
1173 recompute_map (false);
1178 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
1180 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
1185 recompute_map (false);
1190 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
1193 Glib::Threads::RWLock::WriterLock lm (lock);
1194 MeterSection& first (first_meter());
1195 if (ms.beat() != first.beat()) {
1196 remove_meter_locked (ms);
1197 add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
1199 /* cannot move the first meter section */
1200 *static_cast<Meter*>(&first) = meter;
1201 recompute_map (true);
1205 PropertyChanged (PropertyChange ());
1209 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1212 Glib::Threads::RWLock::WriterLock lm (lock);
1213 MeterSection& first (first_meter());
1214 if (ms.beat() != first.beat()) {
1215 remove_meter_locked (ms);
1216 add_meter_locked (meter, frame, true);
1218 /* cannot move the first meter section */
1219 *static_cast<Meter*>(&first) = meter;
1220 recompute_map (true);
1224 PropertyChanged (PropertyChange ());
1229 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1232 Glib::Threads::RWLock::WriterLock lm (lock);
1233 add_meter_locked (meter, beat, where, true);
1238 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1243 PropertyChanged (PropertyChange ());
1247 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1250 Glib::Threads::RWLock::WriterLock lm (lock);
1251 add_meter_locked (meter, frame, true);
1256 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1261 PropertyChanged (PropertyChange ());
1265 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1267 /* a new meter always starts a new bar on the first beat. so
1268 round the start time appropriately. remember that
1269 `where' is based on the existing tempo map, not
1270 the result after we insert the new meter.
1274 if (where.beats != 1) {
1279 /* new meters *always* start on a beat. */
1282 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
1285 recompute_map (true);
1291 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1294 /* MusicTime meters always start on 1|1|0. */
1295 do_insert (new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor()));
1298 recompute_map (true);
1304 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1306 Tempo newtempo (beats_per_minute, note_type);
1309 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1310 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1312 Glib::Threads::RWLock::WriterLock lm (lock);
1313 *((Tempo*) t) = newtempo;
1314 recompute_map (false);
1316 PropertyChanged (PropertyChange ());
1323 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1325 Tempo newtempo (beats_per_minute, note_type);
1328 TempoSection* first;
1329 Metrics::iterator i;
1331 /* find the TempoSection immediately preceding "where"
1334 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
1336 if ((*i)->frame() > where) {
1342 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1352 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1362 Glib::Threads::RWLock::WriterLock lm (lock);
1363 /* cannot move the first tempo section */
1364 *((Tempo*)prev) = newtempo;
1365 recompute_map (false);
1368 PropertyChanged (PropertyChange ());
1372 TempoMap::first_meter () const
1374 const MeterSection *m = 0;
1376 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1377 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1382 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1383 abort(); /*NOTREACHED*/
1388 TempoMap::first_meter ()
1390 MeterSection *m = 0;
1392 /* CALLER MUST HOLD LOCK */
1394 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1395 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1400 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1401 abort(); /*NOTREACHED*/
1406 TempoMap::first_tempo () const
1408 const TempoSection *t = 0;
1410 /* CALLER MUST HOLD LOCK */
1412 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1413 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1418 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1419 abort(); /*NOTREACHED*/
1424 TempoMap::first_tempo ()
1426 TempoSection *t = 0;
1428 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1429 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1434 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1435 abort(); /*NOTREACHED*/
1439 TempoMap::recompute_tempos ()
1441 TempoSection* prev_ts = 0;
1443 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1446 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1448 if (t->position_lock_style() == AudioTime) {
1449 if (prev_ts->type() == TempoSection::Ramp) {
1450 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
1451 //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1452 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1454 prev_ts->set_c_func (0.0);
1455 //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1456 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1459 if (prev_ts->type() == TempoSection::Ramp) {
1460 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
1461 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1462 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1464 prev_ts->set_c_func (0.0);
1465 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1466 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1476 TempoMap::recompute_meters ()
1478 MeterSection* meter = 0;
1480 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1481 /* Now we have the tempos mapped to position, set meter positions.*/
1482 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1483 if (meter->position_lock_style() == AudioTime) {
1484 /* a frame based meter has to have a 1|1|0 bbt */
1485 pair<double, BBT_Time> pr;
1492 pr.first = beat_at_frame_locked (meter->frame());
1494 meter->set_beat (pr);
1496 meter->set_frame (frame_at_tick_locked (meter->tick()));
1503 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1505 /* CALLER MUST HOLD WRITE LOCK */
1509 /* we will actually stop once we hit
1516 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1519 /* silly call from Session::process() during startup
1530 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1532 Glib::Threads::RWLock::ReaderLock lm (lock);
1533 TempoMetric m (first_meter(), first_tempo());
1535 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1536 at something, because we insert the default tempo and meter during
1537 TempoMap construction.
1539 now see if we can find better candidates.
1542 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1544 if ((*i)->frame() > frame) {
1557 /* XX meters only */
1559 TempoMap::metric_at (BBT_Time bbt) const
1561 Glib::Threads::RWLock::ReaderLock lm (lock);
1562 TempoMetric m (first_meter(), first_tempo());
1564 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1565 at something, because we insert the default tempo and meter during
1566 TempoMap construction.
1568 now see if we can find better candidates.
1571 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1573 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1574 BBT_Time section_start (mw->bbt());
1576 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1588 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1590 Glib::Threads::RWLock::ReaderLock lm (lock);
1596 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1599 bbt = beats_to_bbt_locked (beat_at_frame_locked (frame));
1603 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1605 Glib::Threads::RWLock::ReaderLock lm (lock);
1606 return bbt_to_beats_locked (bbt);
1610 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1612 /* CALLER HOLDS READ LOCK */
1614 double accumulated_beats = 0.0;
1615 double accumulated_bars = 0.0;
1616 MeterSection* prev_ms = 0;
1618 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1620 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1621 double bars_to_m = 0.0;
1623 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1625 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1629 accumulated_beats += m->beat() - prev_ms->beat();
1630 accumulated_bars += bars_to_m;
1636 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1637 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1638 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1643 TempoMap::beats_to_bbt (double beats)
1645 Glib::Threads::RWLock::ReaderLock lm (lock);
1646 return beats_to_bbt_locked (beats);
1650 TempoMap::beats_to_bbt_locked (double beats)
1652 /* CALLER HOLDS READ LOCK */
1654 MeterSection* prev_ms = 0;
1655 uint32_t accumulated_bars = 0;
1657 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1658 MeterSection* m = 0;
1660 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1662 if (beats < m->beat()) {
1663 /* this is the meter after the one our beat is on*/
1668 /* we need a whole number of bars. */
1669 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1676 double const beats_in_ms = beats - prev_ms->beat();
1677 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1678 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1679 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1680 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1684 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1685 ret.beats = (uint32_t) floor (remaining_beats);
1686 ret.bars = total_bars;
1688 /* 0 0 0 to 1 1 0 - based mapping*/
1692 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1694 ret.ticks -= BBT_Time::ticks_per_beat;
1697 if (ret.beats > prev_ms->divisions_per_bar()) {
1706 TempoMap::tick_at_frame_locked (framecnt_t frame) const
1708 /* HOLD (at least) THE READER LOCK */
1710 TempoSection* prev_ts = 0;
1711 double accumulated_ticks = 0.0;
1713 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1715 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1716 if ((prev_ts) && frame < t->frame()) {
1717 /*the previous ts is the one containing the frame */
1718 return prev_ts->tick_at_frame (frame, _frame_rate);
1721 accumulated_ticks = t->tick();
1726 /* treated as constant for this ts */
1727 double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1729 return ticks_in_section + accumulated_ticks;
1734 TempoMap::frame_at_tick_locked (double tick) const
1736 /* HOLD THE READER LOCK */
1738 const TempoSection* prev_ts = 0;
1739 double accumulated_ticks = 0.0;
1741 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1743 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1744 if (prev_ts && tick < t->tick()) {
1745 /* prev_ts is the one affecting us. */
1746 return prev_ts->frame_at_tick (tick, _frame_rate);
1749 accumulated_ticks = t->tick();
1753 /* must be treated as constant, irrespective of _type */
1754 double const ticks_in_section = tick - accumulated_ticks;
1755 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1757 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1763 TempoMap::beat_at_frame (framecnt_t frame) const
1765 Glib::Threads::RWLock::ReaderLock lm (lock);
1766 return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1770 TempoMap::beat_at_frame_locked (framecnt_t frame) const
1772 return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1776 TempoMap::frame_at_beat (double beat) const
1778 Glib::Threads::RWLock::ReaderLock lm (lock);
1779 return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1783 TempoMap::frame_at_beat_locked (double beat) const
1786 return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1790 TempoMap::frame_time (const BBT_Time& bbt)
1793 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1797 if (bbt.beats < 1) {
1798 throw std::logic_error ("beats are counted from one");
1800 Glib::Threads::RWLock::ReaderLock lm (lock);
1802 return frame_time_locked (bbt);;
1806 TempoMap::frame_time_locked (const BBT_Time& bbt)
1808 /* HOLD THE READER LOCK */
1810 framepos_t const ret = frame_at_beat_locked (bbt_to_beats_locked (bbt));
1817 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1819 Glib::Threads::RWLock::ReaderLock lm (lock);
1821 Metrics::const_iterator i;
1822 TempoSection* first = 0;
1823 TempoSection* second = 0;
1825 for (i = metrics.begin(); i != metrics.end(); ++i) {
1828 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1830 if ((*i)->frame() > pos) {
1838 if (first && second) {
1839 double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
1840 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1841 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1843 return time_at_bbt - pos;
1845 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1847 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1851 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1853 return round_to_type (fr, dir, Bar);
1857 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1859 return round_to_type (fr, dir, Beat);
1863 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1865 Glib::Threads::RWLock::ReaderLock lm (lock);
1867 uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (fr) + 0.5);
1868 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1869 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1871 ticks -= beats * BBT_Time::ticks_per_beat;
1874 /* round to next (or same iff dir == RoundUpMaybe) */
1876 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1878 if (mod == 0 && dir == RoundUpMaybe) {
1879 /* right on the subdivision, which is fine, so do nothing */
1881 } else if (mod == 0) {
1882 /* right on the subdivision, so the difference is just the subdivision ticks */
1883 ticks += ticks_one_subdivisions_worth;
1886 /* not on subdivision, compute distance to next subdivision */
1888 ticks += ticks_one_subdivisions_worth - mod;
1891 if (ticks >= BBT_Time::ticks_per_beat) {
1892 ticks -= BBT_Time::ticks_per_beat;
1894 } else if (dir < 0) {
1896 /* round to previous (or same iff dir == RoundDownMaybe) */
1898 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1900 if (difference == 0 && dir == RoundDownAlways) {
1901 /* right on the subdivision, but force-rounding down,
1902 so the difference is just the subdivision ticks */
1903 difference = ticks_one_subdivisions_worth;
1906 if (ticks < difference) {
1907 ticks = BBT_Time::ticks_per_beat - ticks;
1909 ticks -= difference;
1913 /* round to nearest */
1916 /* compute the distance to the previous and next subdivision */
1918 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1920 /* closer to the next subdivision, so shift forward */
1922 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1924 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1926 if (ticks > BBT_Time::ticks_per_beat) {
1928 ticks -= BBT_Time::ticks_per_beat;
1929 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1932 } else if (rem > 0) {
1934 /* closer to previous subdivision, so shift backward */
1938 /* can't go backwards past zero, so ... */
1941 /* step back to previous beat */
1943 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1944 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1946 ticks = lrint (ticks - rem);
1947 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1950 /* on the subdivision, do nothing */
1953 return frame_at_tick_locked ((beats * BBT_Time::ticks_per_beat) + ticks);
1957 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1959 Glib::Threads::RWLock::ReaderLock lm (lock);
1961 double const beat_at_framepos = beat_at_frame_locked (frame);
1963 BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1968 /* find bar previous to 'frame' */
1971 return frame_time (bbt);
1973 } else if (dir > 0) {
1974 /* find bar following 'frame' */
1978 return frame_time (bbt);
1980 /* true rounding: find nearest bar */
1981 framepos_t raw_ft = frame_time (bbt);
1984 framepos_t prev_ft = frame_time (bbt);
1986 framepos_t next_ft = frame_time (bbt);
1988 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1999 return frame_at_beat_locked (floor (beat_at_framepos));
2000 } else if (dir > 0) {
2001 return frame_at_beat_locked (ceil (beat_at_framepos));
2003 return frame_at_beat_locked (floor (beat_at_framepos + 0.5));
2012 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2013 framepos_t lower, framepos_t upper)
2015 Glib::Threads::RWLock::ReaderLock lm (lock);
2016 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame_locked (upper));
2017 uint32_t cnt = (uint32_t) ceil (beat_at_frame_locked (lower));
2019 while (cnt <= upper_beat) {
2020 framecnt_t const pos = frame_at_beat (cnt);
2021 MeterSection const meter = meter_section_at (pos);
2022 Tempo const tempo = tempo_at (pos);
2023 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
2025 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
2031 TempoMap::tempo_section_at (framepos_t frame) const
2033 Glib::Threads::RWLock::ReaderLock lm (lock);
2035 Metrics::const_iterator i;
2036 TempoSection* prev = 0;
2038 for (i = metrics.begin(); i != metrics.end(); ++i) {
2041 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2043 if ((*i)->frame() > frame) {
2053 abort(); /*NOTREACHED*/
2059 /* don't use this to calculate length (the tempo is only correct for this frame).
2060 do that stuff based on the beat_at_frame and frame_at_beat api
2063 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2065 Glib::Threads::RWLock::ReaderLock lm (lock);
2067 const TempoSection* ts_at = &tempo_section_at (frame);
2068 const TempoSection* ts_after = 0;
2069 Metrics::const_iterator i;
2071 for (i = metrics.begin(); i != metrics.end(); ++i) {
2074 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2076 if ((*i)->frame() > frame) {
2084 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2086 /* must be treated as constant tempo */
2087 return ts_at->frames_per_beat (_frame_rate);
2091 TempoMap::tempo_at (framepos_t frame) const
2093 Glib::Threads::RWLock::ReaderLock lm (lock);
2095 TempoMetric m (metric_at (frame));
2096 TempoSection* prev_ts = 0;
2098 Metrics::const_iterator i;
2100 for (i = metrics.begin(); i != metrics.end(); ++i) {
2102 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2103 if ((prev_ts) && t->frame() > frame) {
2104 /* this is the one past frame */
2105 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
2106 Tempo const ret_tempo (ret, m.tempo().note_type ());
2117 TempoMap::meter_section_at (framepos_t frame) const
2119 Glib::Threads::RWLock::ReaderLock lm (lock);
2121 Metrics::const_iterator i;
2122 MeterSection* prev = 0;
2124 for (i = metrics.begin(); i != metrics.end(); ++i) {
2127 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2129 if ((*i)->frame() > frame) {
2139 abort(); /*NOTREACHED*/
2146 TempoMap::meter_at (framepos_t frame) const
2148 TempoMetric m (metric_at (frame));
2153 TempoMap::get_state ()
2155 Metrics::const_iterator i;
2156 XMLNode *root = new XMLNode ("TempoMap");
2159 Glib::Threads::RWLock::ReaderLock lm (lock);
2160 for (i = metrics.begin(); i != metrics.end(); ++i) {
2161 root->add_child_nocopy ((*i)->get_state());
2169 TempoMap::set_state (const XMLNode& node, int /*version*/)
2172 Glib::Threads::RWLock::WriterLock lm (lock);
2175 XMLNodeConstIterator niter;
2176 Metrics old_metrics (metrics);
2177 MeterSection* last_meter = 0;
2180 nlist = node.children();
2182 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2183 XMLNode* child = *niter;
2185 if (child->name() == TempoSection::xml_state_node_name) {
2188 TempoSection* ts = new TempoSection (*child);
2189 metrics.push_back (ts);
2191 if (ts->bar_offset() < 0.0) {
2193 //ts->update_bar_offset_from_bbt (*last_meter);
2198 catch (failed_constructor& err){
2199 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2200 metrics = old_metrics;
2204 } else if (child->name() == MeterSection::xml_state_node_name) {
2207 MeterSection* ms = new MeterSection (*child);
2208 metrics.push_back (ms);
2212 catch (failed_constructor& err) {
2213 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2214 metrics = old_metrics;
2220 if (niter == nlist.end()) {
2221 MetricSectionSorter cmp;
2224 /* check for legacy sessions where bbt was the base musical unit for tempo */
2225 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2226 MeterSection* prev_ms;
2227 TempoSection* prev_ts;
2228 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2229 if (prev_ms->beat() < 0.0) {
2230 /*XX we cannot possibly make this work??. */
2231 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());
2232 prev_ms->set_beat (start);
2234 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2235 if (prev_ts->beat() < 0.0) {
2236 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);
2237 prev_ts->set_beat (start);
2242 /* check for multiple tempo/meters at the same location, which
2243 ardour2 somehow allowed.
2246 Metrics::iterator prev = metrics.end();
2247 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2248 if (prev != metrics.end()) {
2250 MeterSection* prev_ms;
2252 TempoSection* prev_ts;
2253 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2254 if (prev_ms->beat() == ms->beat()) {
2255 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2256 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2259 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2260 if (prev_ts->beat() == ts->beat()) {
2261 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2262 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2270 recompute_map (true, -1);
2273 PropertyChanged (PropertyChange ());
2279 TempoMap::dump (std::ostream& o) const
2281 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2282 const MeterSection* m;
2283 const TempoSection* t;
2285 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2287 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2288 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? "
2289 << t->movable() << ')' << endl;
2290 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2291 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2292 << " (movable? " << m->movable() << ')' << endl;
2298 TempoMap::n_tempos() const
2300 Glib::Threads::RWLock::ReaderLock lm (lock);
2303 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2304 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2313 TempoMap::n_meters() const
2315 Glib::Threads::RWLock::ReaderLock lm (lock);
2318 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2319 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2328 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2331 Glib::Threads::RWLock::WriterLock lm (lock);
2332 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2333 if ((*i)->frame() >= where && (*i)->movable ()) {
2334 (*i)->set_frame ((*i)->frame() + amount);
2338 /* now reset the BBT time of all metrics, based on their new
2339 * audio time. This is the only place where we do this reverse
2343 Metrics::iterator i;
2344 const MeterSection* meter;
2345 const TempoSection* tempo;
2349 meter = &first_meter ();
2350 tempo = &first_tempo ();
2355 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2358 MetricSection* prev = 0;
2360 for (i = metrics.begin(); i != metrics.end(); ++i) {
2363 //TempoMetric metric (*meter, *tempo);
2364 MeterSection* ms = const_cast<MeterSection*>(meter);
2365 TempoSection* ts = const_cast<TempoSection*>(tempo);
2368 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2369 ts->set_beat (t->beat());
2371 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2372 ts->set_beat (m->beat());
2374 ts->set_frame (prev->frame());
2378 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2379 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2380 ms->set_beat (start);
2382 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2383 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2384 ms->set_beat (start);
2386 ms->set_frame (prev->frame());
2390 // metric will be at frames=0 bbt=1|1|0 by default
2391 // which is correct for our purpose
2394 // cerr << bbt << endl;
2396 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2397 t->set_beat (beat_at_frame_locked (m->frame()));
2399 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2400 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2401 bbt_time (m->frame(), bbt);
2403 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2409 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2410 /* round up to next beat */
2416 if (bbt.beats != 1) {
2417 /* round up to next bar */
2422 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (m->frame()), bbt);
2423 m->set_beat (start);
2425 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2427 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2428 abort(); /*NOTREACHED*/
2434 recompute_map (true);
2438 PropertyChanged (PropertyChange ());
2441 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2445 std::list<MetricSection*> metric_kill_list;
2447 TempoSection* last_tempo = NULL;
2448 MeterSection* last_meter = NULL;
2449 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2450 bool meter_after = false; // is there a meter marker likewise?
2452 Glib::Threads::RWLock::WriterLock lm (lock);
2453 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2454 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2455 metric_kill_list.push_back(*i);
2456 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2459 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2463 else if ((*i)->frame() >= where) {
2464 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2465 (*i)->set_frame ((*i)->frame() - amount);
2466 if ((*i)->frame() == where) {
2467 // marker was immediately after end of range
2468 tempo_after = dynamic_cast<TempoSection*> (*i);
2469 meter_after = dynamic_cast<MeterSection*> (*i);
2475 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2476 if (last_tempo && !tempo_after) {
2477 metric_kill_list.remove(last_tempo);
2478 last_tempo->set_frame(where);
2481 if (last_meter && !meter_after) {
2482 metric_kill_list.remove(last_meter);
2483 last_meter->set_frame(where);
2487 //remove all the remaining metrics
2488 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2494 recompute_map (true);
2497 PropertyChanged (PropertyChange ());
2501 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2502 * pos can be -ve, if required.
2505 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2507 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2510 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2512 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2514 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2517 /** Add the BBT interval op to pos and return the result */
2519 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2521 cerr << "framepos_plus_bbt - untested" << endl;
2522 Glib::Threads::RWLock::ReaderLock lm (lock);
2524 Metrics::const_iterator i;
2525 const MeterSection* meter;
2526 const MeterSection* m;
2527 const TempoSection* tempo;
2528 const TempoSection* next_tempo = 0;
2529 const TempoSection* t;
2530 double frames_per_beat;
2531 framepos_t effective_pos = max (pos, (framepos_t) 0);
2533 meter = &first_meter ();
2534 tempo = &first_tempo ();
2539 /* find the starting metrics for tempo & meter */
2541 for (i = metrics.begin(); i != metrics.end(); ++i) {
2543 if ((*i)->frame() > effective_pos) {
2547 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2549 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2554 for (i = metrics.begin(); i != metrics.end(); ++i) {
2555 if ((*i)->frame() > effective_pos) {
2556 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2565 meter -> the Meter for "pos"
2566 tempo -> the Tempo for "pos"
2567 next_tempo -> the Tempo after "pos" or 0
2568 i -> for first new metric after "pos", possibly metrics.end()
2571 /* now comes the complicated part. we have to add one beat a time,
2572 checking for a new metric on every beat.
2576 /* fpb is used for constant tempo */
2577 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2584 /* check if we need to use a new metric section: has adding frames moved us
2585 to or after the start of the next metric section? in which case, use it.
2588 if (i != metrics.end()) {
2589 if ((*i)->frame() <= pos) {
2591 /* about to change tempo or meter, so add the
2592 * number of frames for the bars we've just
2593 * traversed before we change the
2594 * frames_per_beat value.
2597 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2602 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2604 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2609 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2611 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2615 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2622 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2624 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2631 /* given the current meter, have we gone past the end of the bar ? */
2636 /* check if we need to use a new metric section: has adding frames moved us
2637 to or after the start of the next metric section? in which case, use it.
2640 if (i != metrics.end()) {
2641 if ((*i)->frame() <= pos) {
2643 /* about to change tempo or meter, so add the
2644 * number of frames for the beats we've just
2645 * traversed before we change the
2646 * frames_per_beat value.
2649 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2654 pos += tempo->frame_at_beat (beats, _frame_rate);
2656 pos += llrint (beats * frames_per_beat);
2661 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2663 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2667 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2673 pos += tempo->frame_at_beat (beats, _frame_rate);
2675 pos += llrint (beats * frames_per_beat);
2679 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2686 /** Count the number of beats that are equivalent to distance when going forward,
2690 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2692 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2696 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2702 operator<< (std::ostream& o, const Meter& m) {
2703 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2707 operator<< (std::ostream& o, const Tempo& t) {
2708 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2712 operator<< (std::ostream& o, const MetricSection& section) {
2714 o << "MetricSection @ " << section.frame() << ' ';
2716 const TempoSection* ts;
2717 const MeterSection* ms;
2719 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2720 o << *((const Tempo*) ts);
2721 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2722 //o << *((const Meter*) ms);