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->position_lock_style() == MusicTime && ((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);
918 TempoSection* prev_ts = 0;
920 /*set frame and sort */
921 section->set_frame (frame);
922 MetricSectionFrameSorter fcmp;
923 imaginary.sort (fcmp);
925 /* recompute tempos */
926 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
928 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
931 /* we have already set the frame - set the beat */
932 prev_ts->set_c_func (prev_ts->compute_c_func (bpm.beats_per_minute(), frame, _frame_rate));
933 section->set_beat (prev_ts->beat_at_tempo (bpm.beats_per_minute(), frame, _frame_rate));
937 if (t->position_lock_style() == MusicTime) {
938 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
939 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
941 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
942 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
948 MetricSectionSorter cmp;
949 imaginary.sort (cmp);
952 std::cerr << "dumping imaginary order ------" << std::endl;;
953 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
954 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
957 std::cerr << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
958 std::cerr << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
959 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;
960 std::cerr << " ------" << std::endl;;
966 std::cerr << "end dump ------";
972 TempoMap::get_new_order (TempoSection* section, const Tempo& bpm, const double& beat)
974 Metrics imaginary (metrics);
975 TempoSection* prev_ts = 0;
977 /*set beat and sort */
978 section->set_beat (beat);
979 MetricSectionSorter cmp;
980 imaginary.sort (cmp);
982 /* recompute tempo positions */
983 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
985 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
988 /* we've already set the beat - set the frame */
989 prev_ts->set_c_func_from_tempo_and_beat (bpm.beats_per_minute(), beat, _frame_rate);
990 section->set_frame (prev_ts->frame_at_tempo (bpm.beats_per_minute(), beat, _frame_rate));
994 if (t->position_lock_style() == MusicTime) {
995 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
996 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
998 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
999 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1006 MetricSectionFrameSorter fcmp;
1007 imaginary.sort (fcmp);
1010 std::cerr << "dumping imaginary order ------" << std::endl;;
1011 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1012 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1015 std::cerr << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
1016 std::cerr << "prev : " << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
1017 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;
1018 std::cerr << " ------" << std::endl;;
1024 std::cerr << "end dump ------";
1030 TempoMap::get_new_order(MeterSection* section, const Meter& mt, const double& beat)
1032 Metrics imaginary (metrics);
1033 MeterSection* prev_ms = 0;
1035 pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1036 section->set_beat (b_bbt);
1037 MetricSectionSorter cmp;
1038 imaginary.sort (cmp);
1040 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1042 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1044 if (m->beat() > beat){
1045 section->set_frame (frame_at_beat_locked (beat));
1049 if (m->position_lock_style() == MusicTime) {
1050 m->set_frame (frame_at_beat_locked (m->beat()));
1052 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
1053 m->set_beat (b_bbt);
1060 MetricSectionFrameSorter fcmp;
1061 imaginary.sort (fcmp);
1067 TempoMap::get_new_order(MeterSection* section, const Meter& mt, const framepos_t& frame)
1069 Metrics imaginary (metrics);
1070 MeterSection* prev_ms = 0;
1072 section->set_frame (frame);
1073 MetricSectionFrameSorter fcmp;
1074 imaginary.sort (fcmp);
1076 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1078 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1080 if (m->frame() > frame){
1081 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (frame), BBT_Time (1, 1, 0));
1083 section->set_beat (b_bbt);
1087 if (m->position_lock_style() == MusicTime) {
1088 m->set_frame (frame_at_beat_locked (m->beat()));
1090 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
1091 m->set_beat (b_bbt);
1098 MetricSectionSorter cmp;
1099 imaginary.sort (cmp);
1105 * This is for a gui that needs to know the frame of an audio-locked tempo section if it were to be placed at some beat.
1106 * It actually reorders and partially recomputes tha ramps, so calling this commits you to replacing the section immediately.
1107 * It will not emit a signal as you probably want to replace the tempo or do somethig else before that happens.
1108 * @param section - the section you want to alter
1109 * @param bpm - the new Tempo
1110 * @param beat - the beat where the altered tempo will fall
1111 * @return returns - the position in frames where the new tempo section will lie.
1114 TempoMap::compute_new_tempo_frame (TempoSection* section, const Tempo& bpm, const double& beat)
1116 Glib::Threads::RWLock::WriterLock lm (lock);
1117 Metrics new_order = get_new_order (section, bpm, beat);
1119 return section->frame();
1123 TempoMap::gui_move_tempo (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1126 Glib::Threads::RWLock::WriterLock lm (lock);
1127 Metrics new_order = get_new_order (ts, bpm, frame);
1130 metrics = new_order;
1131 recompute_map (false);
1134 MetricPositionChanged (); // Emit Signal
1138 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
1141 Glib::Threads::RWLock::WriterLock lm (lock);
1142 Metrics new_order = get_new_order (ms, mt, frame);
1145 metrics = new_order;
1146 recompute_meters ();
1149 MetricPositionChanged (); // Emit Signal
1153 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
1156 Glib::Threads::RWLock::WriterLock lm (lock);
1157 add_tempo_locked (tempo, where, true, type);
1160 PropertyChanged (PropertyChange ());
1164 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
1167 Glib::Threads::RWLock::WriterLock lm (lock);
1168 add_tempo_locked (tempo, frame, true, type);
1172 PropertyChanged (PropertyChange ());
1176 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
1178 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
1183 recompute_map (false);
1188 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
1190 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
1195 recompute_map (false);
1200 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
1203 Glib::Threads::RWLock::WriterLock lm (lock);
1204 MeterSection& first (first_meter());
1205 if (ms.beat() != first.beat()) {
1206 remove_meter_locked (ms);
1207 add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
1209 /* cannot move the first meter section */
1210 *static_cast<Meter*>(&first) = meter;
1211 recompute_map (true);
1215 PropertyChanged (PropertyChange ());
1219 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1222 Glib::Threads::RWLock::WriterLock lm (lock);
1223 MeterSection& first (first_meter());
1224 if (ms.beat() != first.beat()) {
1225 remove_meter_locked (ms);
1226 add_meter_locked (meter, frame, true);
1228 /* cannot move the first meter section */
1229 *static_cast<Meter*>(&first) = meter;
1230 recompute_map (true);
1234 PropertyChanged (PropertyChange ());
1239 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1242 Glib::Threads::RWLock::WriterLock lm (lock);
1243 add_meter_locked (meter, beat, where, true);
1248 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1253 PropertyChanged (PropertyChange ());
1257 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1260 Glib::Threads::RWLock::WriterLock lm (lock);
1261 add_meter_locked (meter, frame, true);
1266 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1271 PropertyChanged (PropertyChange ());
1275 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1277 /* a new meter always starts a new bar on the first beat. so
1278 round the start time appropriately. remember that
1279 `where' is based on the existing tempo map, not
1280 the result after we insert the new meter.
1284 if (where.beats != 1) {
1289 /* new meters *always* start on a beat. */
1292 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
1295 recompute_map (true);
1301 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1304 /* MusicTime meters always start on 1|1|0. */
1305 do_insert (new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor()));
1308 recompute_map (true);
1314 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1316 Tempo newtempo (beats_per_minute, note_type);
1319 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1320 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1322 Glib::Threads::RWLock::WriterLock lm (lock);
1323 *((Tempo*) t) = newtempo;
1324 recompute_map (false);
1326 PropertyChanged (PropertyChange ());
1333 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1335 Tempo newtempo (beats_per_minute, note_type);
1338 TempoSection* first;
1339 Metrics::iterator i;
1341 /* find the TempoSection immediately preceding "where"
1344 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
1346 if ((*i)->frame() > where) {
1352 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1362 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1372 Glib::Threads::RWLock::WriterLock lm (lock);
1373 /* cannot move the first tempo section */
1374 *((Tempo*)prev) = newtempo;
1375 recompute_map (false);
1378 PropertyChanged (PropertyChange ());
1382 TempoMap::first_meter () const
1384 const MeterSection *m = 0;
1386 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1387 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1392 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1393 abort(); /*NOTREACHED*/
1398 TempoMap::first_meter ()
1400 MeterSection *m = 0;
1402 /* CALLER MUST HOLD LOCK */
1404 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1405 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1410 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1411 abort(); /*NOTREACHED*/
1416 TempoMap::first_tempo () const
1418 const TempoSection *t = 0;
1420 /* CALLER MUST HOLD LOCK */
1422 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1423 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1428 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1429 abort(); /*NOTREACHED*/
1434 TempoMap::first_tempo ()
1436 TempoSection *t = 0;
1438 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1439 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1444 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1445 abort(); /*NOTREACHED*/
1449 TempoMap::recompute_tempos ()
1451 TempoSection* prev_ts = 0;
1453 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1456 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1458 if (t->position_lock_style() == AudioTime) {
1459 if (prev_ts->type() == TempoSection::Ramp) {
1460 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
1461 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1463 prev_ts->set_c_func (0.0);
1464 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1467 if (prev_ts->type() == TempoSection::Ramp) {
1468 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
1469 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1471 prev_ts->set_c_func (0.0);
1472 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1481 /* tempos must be positioned correctly */
1483 TempoMap::recompute_meters ()
1485 MeterSection* meter = 0;
1487 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1488 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1489 if (meter->position_lock_style() == AudioTime) {
1490 /* a frame based meter has to have a 1|1|0 bbt */
1491 pair<double, BBT_Time> pr;
1492 BBT_Time const where (1, 1, 0);
1494 pr.first = beat_at_frame_locked (meter->frame());
1496 meter->set_beat (pr);
1498 meter->set_frame (frame_at_tick_locked (meter->tick()));
1505 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1507 /* CALLER MUST HOLD WRITE LOCK */
1511 /* we will actually stop once we hit
1518 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1521 /* silly call from Session::process() during startup
1532 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1534 Glib::Threads::RWLock::ReaderLock lm (lock);
1535 TempoMetric m (first_meter(), first_tempo());
1537 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1538 at something, because we insert the default tempo and meter during
1539 TempoMap construction.
1541 now see if we can find better candidates.
1544 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1546 if ((*i)->frame() > frame) {
1559 /* XX meters only */
1561 TempoMap::metric_at (BBT_Time bbt) const
1563 Glib::Threads::RWLock::ReaderLock lm (lock);
1564 TempoMetric m (first_meter(), first_tempo());
1566 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1567 at something, because we insert the default tempo and meter during
1568 TempoMap construction.
1570 now see if we can find better candidates.
1573 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1575 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1576 BBT_Time section_start (mw->bbt());
1578 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1590 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1592 Glib::Threads::RWLock::ReaderLock lm (lock);
1598 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1601 bbt = beats_to_bbt_locked (beat_at_frame_locked (frame));
1605 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1607 Glib::Threads::RWLock::ReaderLock lm (lock);
1608 return bbt_to_beats_locked (bbt);
1612 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1614 /* CALLER HOLDS READ LOCK */
1616 double accumulated_beats = 0.0;
1617 double accumulated_bars = 0.0;
1618 double bars_offset = 0.0;
1619 MeterSection* prev_ms = 0;
1621 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1623 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1624 double bars_to_m = 0.0;
1626 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1628 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1632 if (m->position_lock_style() == AudioTime) {
1633 accumulated_beats = 0.0;
1634 accumulated_bars = 0;
1635 bars_offset += bars_to_m;
1637 accumulated_beats += m->beat() - prev_ms->beat();
1638 accumulated_bars += bars_to_m;
1645 double const remaining_bars = (bbt.bars - bars_offset - 1) - accumulated_bars;
1646 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1647 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1648 std::cerr << "ret : " << ret << " bbt : " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << std::endl;
1654 TempoMap::beats_to_bbt (double beats)
1656 Glib::Threads::RWLock::ReaderLock lm (lock);
1657 return beats_to_bbt_locked (beats);
1661 TempoMap::beats_to_bbt_locked (double beats)
1663 /* CALLER HOLDS READ LOCK */
1665 MeterSection* prev_ms = 0;
1666 uint32_t accumulated_bars = 0;
1668 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1669 MeterSection* m = 0;
1671 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1673 if (m->beat() > beats) {
1674 /* this is the meter after the one our beat is on*/
1679 if(m->position_lock_style() == AudioTime) {
1680 accumulated_bars = 0;
1682 /* we need a whole number of bars. */
1683 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1691 double const beats_in_ms = beats - prev_ms->beat();
1692 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1693 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1694 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1695 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1699 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1700 ret.beats = (uint32_t) floor (remaining_beats);
1701 ret.bars = total_bars;
1703 /* 0 0 0 to 1 1 0 - based mapping*/
1707 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1709 ret.ticks -= BBT_Time::ticks_per_beat;
1712 if (ret.beats > prev_ms->divisions_per_bar()) {
1721 TempoMap::tick_at_frame_locked (framecnt_t frame) const
1723 /* HOLD (at least) THE READER LOCK */
1725 TempoSection* prev_ts = 0;
1726 double accumulated_ticks = 0.0;
1728 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1730 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1731 if ((prev_ts) && frame < t->frame()) {
1732 /*the previous ts is the one containing the frame */
1733 return prev_ts->tick_at_frame (frame, _frame_rate);
1736 accumulated_ticks = t->tick();
1741 /* treated as constant for this ts */
1742 double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1744 return ticks_in_section + accumulated_ticks;
1749 TempoMap::frame_at_tick_locked (double tick) const
1751 /* HOLD THE READER LOCK */
1753 const TempoSection* prev_ts = 0;
1754 double accumulated_ticks = 0.0;
1756 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1758 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1759 if (prev_ts && tick < t->tick()) {
1760 /* prev_ts is the one affecting us. */
1761 return prev_ts->frame_at_tick (tick, _frame_rate);
1764 accumulated_ticks = t->tick();
1768 /* must be treated as constant, irrespective of _type */
1769 double const ticks_in_section = tick - accumulated_ticks;
1770 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1772 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1778 TempoMap::beat_at_frame (framecnt_t frame) const
1780 Glib::Threads::RWLock::ReaderLock lm (lock);
1781 return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1785 TempoMap::beat_at_frame_locked (framecnt_t frame) const
1787 return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1791 TempoMap::frame_at_beat (double beat) const
1793 Glib::Threads::RWLock::ReaderLock lm (lock);
1794 return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1798 TempoMap::frame_at_beat_locked (double beat) const
1801 return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1805 TempoMap::frame_time (const BBT_Time& bbt)
1808 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1812 if (bbt.beats < 1) {
1813 throw std::logic_error ("beats are counted from one");
1815 Glib::Threads::RWLock::ReaderLock lm (lock);
1817 return frame_time_locked (bbt);;
1821 TempoMap::frame_time_locked (const BBT_Time& bbt)
1823 /* HOLD THE READER LOCK */
1825 framepos_t const ret = frame_at_beat_locked (bbt_to_beats_locked (bbt));
1832 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1834 Glib::Threads::RWLock::ReaderLock lm (lock);
1836 Metrics::const_iterator i;
1837 TempoSection* first = 0;
1838 TempoSection* second = 0;
1840 for (i = metrics.begin(); i != metrics.end(); ++i) {
1843 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1845 if ((*i)->frame() > pos) {
1853 if (first && second) {
1854 double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
1855 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1856 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1858 return time_at_bbt - pos;
1860 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1862 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1866 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1868 return round_to_type (fr, dir, Bar);
1872 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1874 return round_to_type (fr, dir, Beat);
1878 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1880 Glib::Threads::RWLock::ReaderLock lm (lock);
1882 uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (fr) + 0.5);
1883 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1884 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1886 ticks -= beats * BBT_Time::ticks_per_beat;
1889 /* round to next (or same iff dir == RoundUpMaybe) */
1891 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1893 if (mod == 0 && dir == RoundUpMaybe) {
1894 /* right on the subdivision, which is fine, so do nothing */
1896 } else if (mod == 0) {
1897 /* right on the subdivision, so the difference is just the subdivision ticks */
1898 ticks += ticks_one_subdivisions_worth;
1901 /* not on subdivision, compute distance to next subdivision */
1903 ticks += ticks_one_subdivisions_worth - mod;
1906 if (ticks >= BBT_Time::ticks_per_beat) {
1907 ticks -= BBT_Time::ticks_per_beat;
1909 } else if (dir < 0) {
1911 /* round to previous (or same iff dir == RoundDownMaybe) */
1913 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1915 if (difference == 0 && dir == RoundDownAlways) {
1916 /* right on the subdivision, but force-rounding down,
1917 so the difference is just the subdivision ticks */
1918 difference = ticks_one_subdivisions_worth;
1921 if (ticks < difference) {
1922 ticks = BBT_Time::ticks_per_beat - ticks;
1924 ticks -= difference;
1928 /* round to nearest */
1931 /* compute the distance to the previous and next subdivision */
1933 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1935 /* closer to the next subdivision, so shift forward */
1937 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1939 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1941 if (ticks > BBT_Time::ticks_per_beat) {
1943 ticks -= BBT_Time::ticks_per_beat;
1944 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1947 } else if (rem > 0) {
1949 /* closer to previous subdivision, so shift backward */
1953 /* can't go backwards past zero, so ... */
1956 /* step back to previous beat */
1958 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1959 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1961 ticks = lrint (ticks - rem);
1962 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1965 /* on the subdivision, do nothing */
1968 return frame_at_tick_locked ((beats * BBT_Time::ticks_per_beat) + ticks);
1972 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1974 Glib::Threads::RWLock::ReaderLock lm (lock);
1976 double const beat_at_framepos = beat_at_frame_locked (frame);
1978 BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1983 /* find bar previous to 'frame' */
1986 return frame_time (bbt);
1988 } else if (dir > 0) {
1989 /* find bar following 'frame' */
1993 return frame_time (bbt);
1995 /* true rounding: find nearest bar */
1996 framepos_t raw_ft = frame_time (bbt);
1999 framepos_t prev_ft = frame_time (bbt);
2001 framepos_t next_ft = frame_time (bbt);
2003 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2014 return frame_at_beat_locked (floor (beat_at_framepos));
2015 } else if (dir > 0) {
2016 return frame_at_beat_locked (ceil (beat_at_framepos));
2018 return frame_at_beat_locked (floor (beat_at_framepos + 0.5));
2027 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2028 framepos_t lower, framepos_t upper)
2030 Glib::Threads::RWLock::ReaderLock lm (lock);
2031 double const upper_beat = floor (beat_at_frame_locked (upper));
2032 double cnt = ceil (beat_at_frame_locked (lower));
2033 MeterSection old_meter = meter_section_at (lower);
2035 while (cnt <= upper_beat) {
2036 MeterSection const meter = meter_section_at (cnt);
2037 if (meter.beat() != old_meter.beat()) {
2038 if (meter.position_lock_style () == AudioTime) {
2043 framecnt_t const pos = frame_at_beat_locked (cnt);
2044 Tempo const tempo = tempo_at (pos);
2045 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
2047 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
2053 TempoMap::tempo_section_at (framepos_t frame) const
2055 Glib::Threads::RWLock::ReaderLock lm (lock);
2057 Metrics::const_iterator i;
2058 TempoSection* prev = 0;
2060 for (i = metrics.begin(); i != metrics.end(); ++i) {
2063 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2065 if ((*i)->frame() > frame) {
2075 abort(); /*NOTREACHED*/
2081 /* don't use this to calculate length (the tempo is only correct for this frame).
2082 do that stuff based on the beat_at_frame and frame_at_beat api
2085 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2087 Glib::Threads::RWLock::ReaderLock lm (lock);
2089 const TempoSection* ts_at = &tempo_section_at (frame);
2090 const TempoSection* ts_after = 0;
2091 Metrics::const_iterator i;
2093 for (i = metrics.begin(); i != metrics.end(); ++i) {
2096 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2098 if ((*i)->frame() > frame) {
2106 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2108 /* must be treated as constant tempo */
2109 return ts_at->frames_per_beat (_frame_rate);
2113 TempoMap::tempo_at (framepos_t frame) const
2115 Glib::Threads::RWLock::ReaderLock lm (lock);
2117 TempoMetric m (metric_at (frame));
2118 TempoSection* prev_ts = 0;
2120 Metrics::const_iterator i;
2122 for (i = metrics.begin(); i != metrics.end(); ++i) {
2124 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2125 if ((prev_ts) && t->frame() > frame) {
2126 /* this is the one past frame */
2127 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
2128 Tempo const ret_tempo (ret, m.tempo().note_type ());
2139 TempoMap::meter_section_at (framepos_t frame) const
2141 Glib::Threads::RWLock::ReaderLock lm (lock);
2143 Metrics::const_iterator i;
2144 MeterSection* prev = 0;
2146 for (i = metrics.begin(); i != metrics.end(); ++i) {
2149 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2151 if ((*i)->frame() > frame) {
2161 abort(); /*NOTREACHED*/
2168 TempoMap::meter_section_at (double beat) const
2170 Glib::Threads::RWLock::ReaderLock lm (lock);
2172 Metrics::const_iterator i;
2173 MeterSection* prev = 0;
2175 for (i = metrics.begin(); i != metrics.end(); ++i) {
2178 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2180 if ((*i)->beat() > beat) {
2190 abort(); /*NOTREACHED*/
2197 TempoMap::meter_at (framepos_t frame) const
2199 TempoMetric m (metric_at (frame));
2204 TempoMap::get_state ()
2206 Metrics::const_iterator i;
2207 XMLNode *root = new XMLNode ("TempoMap");
2210 Glib::Threads::RWLock::ReaderLock lm (lock);
2211 for (i = metrics.begin(); i != metrics.end(); ++i) {
2212 root->add_child_nocopy ((*i)->get_state());
2220 TempoMap::set_state (const XMLNode& node, int /*version*/)
2223 Glib::Threads::RWLock::WriterLock lm (lock);
2226 XMLNodeConstIterator niter;
2227 Metrics old_metrics (metrics);
2228 MeterSection* last_meter = 0;
2231 nlist = node.children();
2233 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2234 XMLNode* child = *niter;
2236 if (child->name() == TempoSection::xml_state_node_name) {
2239 TempoSection* ts = new TempoSection (*child);
2240 metrics.push_back (ts);
2242 if (ts->bar_offset() < 0.0) {
2244 //ts->update_bar_offset_from_bbt (*last_meter);
2249 catch (failed_constructor& err){
2250 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2251 metrics = old_metrics;
2255 } else if (child->name() == MeterSection::xml_state_node_name) {
2258 MeterSection* ms = new MeterSection (*child);
2259 metrics.push_back (ms);
2263 catch (failed_constructor& err) {
2264 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2265 metrics = old_metrics;
2271 if (niter == nlist.end()) {
2272 MetricSectionSorter cmp;
2275 /* check for legacy sessions where bbt was the base musical unit for tempo */
2276 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2277 MeterSection* prev_ms;
2278 TempoSection* prev_ts;
2279 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2280 if (prev_ms->beat() < 0.0) {
2281 /*XX we cannot possibly make this work??. */
2282 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());
2283 prev_ms->set_beat (start);
2285 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2286 if (prev_ts->beat() < 0.0) {
2287 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);
2288 prev_ts->set_beat (start);
2293 /* check for multiple tempo/meters at the same location, which
2294 ardour2 somehow allowed.
2297 Metrics::iterator prev = metrics.end();
2298 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2299 if (prev != metrics.end()) {
2301 MeterSection* prev_ms;
2303 TempoSection* prev_ts;
2304 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2305 if (prev_ms->beat() == ms->beat()) {
2306 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2307 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2310 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2311 if (prev_ts->beat() == ts->beat()) {
2312 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2313 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2321 recompute_map (true, -1);
2324 PropertyChanged (PropertyChange ());
2330 TempoMap::dump (std::ostream& o) const
2332 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2333 const MeterSection* m;
2334 const TempoSection* t;
2336 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2338 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2339 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? "
2340 << t->movable() << ')' << endl;
2341 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2342 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2343 << " (movable? " << m->movable() << ')' << endl;
2349 TempoMap::n_tempos() const
2351 Glib::Threads::RWLock::ReaderLock lm (lock);
2354 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2355 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2364 TempoMap::n_meters() const
2366 Glib::Threads::RWLock::ReaderLock lm (lock);
2369 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2370 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2379 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2382 Glib::Threads::RWLock::WriterLock lm (lock);
2383 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2384 if ((*i)->frame() >= where && (*i)->movable ()) {
2385 (*i)->set_frame ((*i)->frame() + amount);
2389 /* now reset the BBT time of all metrics, based on their new
2390 * audio time. This is the only place where we do this reverse
2394 Metrics::iterator i;
2395 const MeterSection* meter;
2396 const TempoSection* tempo;
2400 meter = &first_meter ();
2401 tempo = &first_tempo ();
2406 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2409 MetricSection* prev = 0;
2411 for (i = metrics.begin(); i != metrics.end(); ++i) {
2414 //TempoMetric metric (*meter, *tempo);
2415 MeterSection* ms = const_cast<MeterSection*>(meter);
2416 TempoSection* ts = const_cast<TempoSection*>(tempo);
2419 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2420 ts->set_beat (t->beat());
2422 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2423 ts->set_beat (m->beat());
2425 ts->set_frame (prev->frame());
2429 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2430 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2431 ms->set_beat (start);
2433 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2434 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2435 ms->set_beat (start);
2437 ms->set_frame (prev->frame());
2441 // metric will be at frames=0 bbt=1|1|0 by default
2442 // which is correct for our purpose
2445 // cerr << bbt << endl;
2447 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2448 t->set_beat (beat_at_frame_locked (m->frame()));
2450 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2451 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2452 bbt_time (m->frame(), bbt);
2454 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2460 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2461 /* round up to next beat */
2467 if (bbt.beats != 1) {
2468 /* round up to next bar */
2473 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (m->frame()), bbt);
2474 m->set_beat (start);
2476 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2478 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2479 abort(); /*NOTREACHED*/
2485 recompute_map (true);
2489 PropertyChanged (PropertyChange ());
2492 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2496 std::list<MetricSection*> metric_kill_list;
2498 TempoSection* last_tempo = NULL;
2499 MeterSection* last_meter = NULL;
2500 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2501 bool meter_after = false; // is there a meter marker likewise?
2503 Glib::Threads::RWLock::WriterLock lm (lock);
2504 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2505 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2506 metric_kill_list.push_back(*i);
2507 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2510 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2514 else if ((*i)->frame() >= where) {
2515 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2516 (*i)->set_frame ((*i)->frame() - amount);
2517 if ((*i)->frame() == where) {
2518 // marker was immediately after end of range
2519 tempo_after = dynamic_cast<TempoSection*> (*i);
2520 meter_after = dynamic_cast<MeterSection*> (*i);
2526 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2527 if (last_tempo && !tempo_after) {
2528 metric_kill_list.remove(last_tempo);
2529 last_tempo->set_frame(where);
2532 if (last_meter && !meter_after) {
2533 metric_kill_list.remove(last_meter);
2534 last_meter->set_frame(where);
2538 //remove all the remaining metrics
2539 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2545 recompute_map (true);
2548 PropertyChanged (PropertyChange ());
2552 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2553 * pos can be -ve, if required.
2556 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2558 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2561 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2563 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2565 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2568 /** Add the BBT interval op to pos and return the result */
2570 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2572 cerr << "framepos_plus_bbt - untested" << endl;
2573 Glib::Threads::RWLock::ReaderLock lm (lock);
2575 Metrics::const_iterator i;
2576 const MeterSection* meter;
2577 const MeterSection* m;
2578 const TempoSection* tempo;
2579 const TempoSection* next_tempo = 0;
2580 const TempoSection* t;
2581 double frames_per_beat;
2582 framepos_t effective_pos = max (pos, (framepos_t) 0);
2584 meter = &first_meter ();
2585 tempo = &first_tempo ();
2590 /* find the starting metrics for tempo & meter */
2592 for (i = metrics.begin(); i != metrics.end(); ++i) {
2594 if ((*i)->frame() > effective_pos) {
2598 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2600 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2605 for (i = metrics.begin(); i != metrics.end(); ++i) {
2606 if ((*i)->frame() > effective_pos) {
2607 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2616 meter -> the Meter for "pos"
2617 tempo -> the Tempo for "pos"
2618 next_tempo -> the Tempo after "pos" or 0
2619 i -> for first new metric after "pos", possibly metrics.end()
2622 /* now comes the complicated part. we have to add one beat a time,
2623 checking for a new metric on every beat.
2627 /* fpb is used for constant tempo */
2628 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2635 /* check if we need to use a new metric section: has adding frames moved us
2636 to or after the start of the next metric section? in which case, use it.
2639 if (i != metrics.end()) {
2640 if ((*i)->frame() <= pos) {
2642 /* about to change tempo or meter, so add the
2643 * number of frames for the bars we've just
2644 * traversed before we change the
2645 * frames_per_beat value.
2648 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2653 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2655 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2660 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2662 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2666 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2673 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2675 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2682 /* given the current meter, have we gone past the end of the bar ? */
2687 /* check if we need to use a new metric section: has adding frames moved us
2688 to or after the start of the next metric section? in which case, use it.
2691 if (i != metrics.end()) {
2692 if ((*i)->frame() <= pos) {
2694 /* about to change tempo or meter, so add the
2695 * number of frames for the beats we've just
2696 * traversed before we change the
2697 * frames_per_beat value.
2700 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2705 pos += tempo->frame_at_beat (beats, _frame_rate);
2707 pos += llrint (beats * frames_per_beat);
2712 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2714 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2718 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2724 pos += tempo->frame_at_beat (beats, _frame_rate);
2726 pos += llrint (beats * frames_per_beat);
2730 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2737 /** Count the number of beats that are equivalent to distance when going forward,
2741 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2743 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2747 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2753 operator<< (std::ostream& o, const Meter& m) {
2754 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2758 operator<< (std::ostream& o, const Tempo& t) {
2759 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2763 operator<< (std::ostream& o, const MetricSection& section) {
2765 o << "MetricSection @ " << section.frame() << ' ';
2767 const TempoSection* ts;
2768 const MeterSection* ms;
2770 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2771 o << *((const Tempo*) ts);
2772 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2773 //o << *((const Meter*) ms);