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) + 0.5);
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 * This is for a gui that needs to know the frame of a beat if a tempo section were to be moved or altered.
872 * It actually alters tha ramps up to the beat parameter, so calling this commits you to replacing the section immediately.
873 * It will not emit a signal or recompute the map, as you probably want to replace the tempo or do somethig else before that happens.
874 * @param section - the section you want to alter
875 * @param bpm - the new tempo
876 * @param beat - the beat where the altered tempo will fall
877 * @return returns - the position in frames where the new tempo section will lie
880 TempoMap::compute_replacement_tempo_section (TempoSection* section, const double& bpm, const double& beat)
882 TempoSection* prev_ts = 0;
885 MetricSectionSorter cmp;
887 Glib::Threads::RWLock::WriterLock lm (lock);
889 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
890 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
892 if (section->beat() == t->beat()) {
895 if (beat < t->beat()){
896 prev_ts->set_c_func_from_tempo_and_beat (bpm, beat, _frame_rate);
897 section->set_beat (beat);
898 section->set_frame (prev_ts->frame_at_beat (beat, _frame_rate));
902 if (t->position_lock_style() == MusicTime) {
903 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
904 t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
906 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
907 t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
913 /* now we do the whole thing again because audio-locked sections will have caused a re-order */
917 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
918 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
920 if (section->beat() == t->beat()) {
923 if (beat < t->beat()){
924 prev_ts->set_c_func_from_tempo_and_beat (bpm, beat, _frame_rate);
925 ret = prev_ts->frame_at_beat (beat, _frame_rate);
926 section->set_frame (ret);
930 if (t->position_lock_style() == MusicTime) {
931 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
932 t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
934 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
935 t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
943 prev_ts->set_c_func_from_tempo_and_beat (bpm, beat, _frame_rate);
944 section->set_beat (beat);
945 section->set_frame (prev_ts->frame_at_beat (beat, _frame_rate));
947 ret = prev_ts->frame_at_beat (beat, _frame_rate);
954 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
957 Glib::Threads::RWLock::WriterLock lm (lock);
958 TempoSection& first (first_tempo());
959 if (ts.beat() != first.beat()) {
960 remove_tempo_locked (ts);
961 add_tempo_locked (tempo, where, true, type);
963 first.set_type (type);
965 /* cannot move the first tempo section */
966 *static_cast<Tempo*>(&first) = tempo;
967 recompute_map (false);
972 PropertyChanged (PropertyChange ());
976 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
979 Glib::Threads::RWLock::WriterLock lm (lock);
980 TempoSection& first (first_tempo());
981 if (ts.beat() != first.beat()) {
982 remove_tempo_locked (ts);
983 add_tempo_locked (tempo, frame, true, type);
985 first.set_type (type);
987 /* cannot move the first tempo section */
988 *static_cast<Tempo*>(&first) = tempo;
989 recompute_map (false);
994 PropertyChanged (PropertyChange ());
998 TempoMap::imagine_new_order (TempoSection* section, const Tempo& bpm, const framepos_t& frame, const double& beat)
1000 Metrics imaginary (metrics);
1002 TempoSection* prev_ts = 0;
1005 /*set frame and sort */
1006 section->set_frame (frame);
1007 MetricSectionFrameSorter fcmp;
1008 imaginary.sort (fcmp);
1011 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1012 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1015 /* we have already set the frame - set the beat */
1016 prev_ts->set_c_func (prev_ts->compute_c_func (bpm.beats_per_minute(), frame, _frame_rate));
1017 //section->set_beat (prev_ts->beat_at_frame (frame, _frame_rate));
1018 section->set_beat (prev_ts->beat_at_tempo (bpm.beats_per_minute(), frame, _frame_rate));
1022 if (t->position_lock_style() == MusicTime) {
1023 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
1024 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1025 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1027 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
1028 //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1029 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1036 MetricSectionSorter cmp;
1037 imaginary.sort (cmp);
1039 /* to do - check precision using _at_tempo() methods */
1042 std::cerr << "dumping imaginary order ------" << std::endl;;
1043 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1044 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1047 std::cerr << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
1048 std::cerr << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
1049 std::cerr << t->beats_per_minute() << " | " << prev_ts->beat_at_tempo (t->beats_per_minute()) << " | " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), _frame_rate) << std::endl;
1050 std::cerr << " ------" << std::endl;;
1056 std::cerr << "end dump ------";
1062 TempoMap::imagine_new_order(MeterSection* section, const Meter& mt, const framepos_t& frame, const double& beat)
1065 Metrics imaginary (metrics);
1067 MeterSection* prev_ms = 0;
1069 MeterSection* our_section = 0;
1071 MetricSectionSorter cmp;
1073 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1074 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1076 if (section->beat() == m->beat()) {
1080 if (beat < m->beat()){
1081 pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1082 our_section->set_beat (b_bbt);
1083 our_section->set_frame (frame_at_beat_locked (beat));
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);
1097 /* now we do the whole thing again because audio-locked sections will have caused a re-order */
1101 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1102 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1104 if (section->beat() == m->beat()) {
1107 if (beat < m->beat()){
1108 ret = frame_at_beat (beat);
1109 section->set_frame (ret);
1113 if (m->position_lock_style() == MusicTime) {
1114 m->set_frame (frame_at_beat (m->beat()));
1116 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
1117 m->set_beat (b_bbt);
1125 pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1126 section->set_beat (b_bbt);
1127 section->set_frame (frame_at_beat_locked (beat));
1133 TempoMap::gui_move_tempo (TempoSection* ts, const Tempo& bpm, const framepos_t& frame, const double& beat_where)
1136 Glib::Threads::RWLock::WriterLock lm (lock);
1137 Metrics new_order = imagine_new_order (ts, bpm, frame, beat_where);
1139 metrics = new_order;
1141 recompute_map (false);
1144 MetricPositionChanged (); // Emit Signal
1148 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame, const double& beat_where)
1150 Glib::Threads::RWLock::WriterLock lm (lock);
1151 Metrics imaginary = imagine_new_order (ms, mt, frame, beat_where);
1155 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
1158 Glib::Threads::RWLock::WriterLock lm (lock);
1159 add_tempo_locked (tempo, where, true, type);
1163 PropertyChanged (PropertyChange ());
1167 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
1170 Glib::Threads::RWLock::WriterLock lm (lock);
1171 add_tempo_locked (tempo, frame, true, type);
1175 PropertyChanged (PropertyChange ());
1179 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
1181 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
1186 recompute_map (false);
1191 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
1193 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
1198 recompute_map (false);
1203 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
1206 Glib::Threads::RWLock::WriterLock lm (lock);
1207 MeterSection& first (first_meter());
1208 if (ms.beat() != first.beat()) {
1209 remove_meter_locked (ms);
1210 add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
1212 /* cannot move the first meter section */
1213 *static_cast<Meter*>(&first) = meter;
1214 recompute_map (true);
1218 PropertyChanged (PropertyChange ());
1222 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1225 Glib::Threads::RWLock::WriterLock lm (lock);
1226 MeterSection& first (first_meter());
1227 if (ms.beat() != first.beat()) {
1228 remove_meter_locked (ms);
1229 add_meter_locked (meter, frame, true);
1231 /* cannot move the first meter section */
1232 *static_cast<Meter*>(&first) = meter;
1233 recompute_map (true);
1237 PropertyChanged (PropertyChange ());
1242 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1245 Glib::Threads::RWLock::WriterLock lm (lock);
1246 add_meter_locked (meter, beat, where, true);
1251 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1256 PropertyChanged (PropertyChange ());
1260 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1263 Glib::Threads::RWLock::WriterLock lm (lock);
1264 add_meter_locked (meter, frame, true);
1269 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1274 PropertyChanged (PropertyChange ());
1278 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1280 /* a new meter always starts a new bar on the first beat. so
1281 round the start time appropriately. remember that
1282 `where' is based on the existing tempo map, not
1283 the result after we insert the new meter.
1287 if (where.beats != 1) {
1292 /* new meters *always* start on a beat. */
1295 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
1298 recompute_map (true);
1304 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1307 /* MusicTime meters always start on 1|1|0. */
1308 do_insert (new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor()));
1311 recompute_map (true);
1317 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1319 Tempo newtempo (beats_per_minute, note_type);
1322 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1323 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1325 Glib::Threads::RWLock::WriterLock lm (lock);
1326 *((Tempo*) t) = newtempo;
1327 recompute_map (false);
1329 PropertyChanged (PropertyChange ());
1336 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1338 Tempo newtempo (beats_per_minute, note_type);
1341 TempoSection* first;
1342 Metrics::iterator i;
1344 /* find the TempoSection immediately preceding "where"
1347 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
1349 if ((*i)->frame() > where) {
1355 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1365 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1375 Glib::Threads::RWLock::WriterLock lm (lock);
1376 /* cannot move the first tempo section */
1377 *((Tempo*)prev) = newtempo;
1378 recompute_map (false);
1381 PropertyChanged (PropertyChange ());
1385 TempoMap::first_meter () const
1387 const MeterSection *m = 0;
1389 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1390 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1395 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1396 abort(); /*NOTREACHED*/
1401 TempoMap::first_meter ()
1403 MeterSection *m = 0;
1405 /* CALLER MUST HOLD LOCK */
1407 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1408 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1413 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1414 abort(); /*NOTREACHED*/
1419 TempoMap::first_tempo () const
1421 const TempoSection *t = 0;
1423 /* CALLER MUST HOLD LOCK */
1425 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1426 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1431 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1432 abort(); /*NOTREACHED*/
1437 TempoMap::first_tempo ()
1439 TempoSection *t = 0;
1441 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1442 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1447 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1448 abort(); /*NOTREACHED*/
1453 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1455 /* CALLER MUST HOLD WRITE LOCK */
1459 /* we will actually stop once we hit
1466 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1469 /* silly call from Session::process() during startup
1474 Metrics::const_iterator i;
1475 TempoSection* prev_ts = 0;
1477 for (i = metrics.begin(); i != metrics.end(); ++i) {
1480 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1482 if (t->position_lock_style() == AudioTime) {
1483 if (prev_ts->type() == TempoSection::Ramp) {
1484 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
1485 //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1486 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1488 prev_ts->set_c_func (0.0);
1489 //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1490 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1493 if (prev_ts->type() == TempoSection::Ramp) {
1494 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
1495 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1496 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1498 prev_ts->set_c_func (0.0);
1499 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1500 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1508 MeterSection* meter = 0;
1510 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1511 /* Now we have the tempos mapped to position, set meter positions.*/
1512 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1513 if (meter->position_lock_style() == AudioTime) {
1514 /* a frame based meter has to have a 1|1|0 bbt */
1515 pair<double, BBT_Time> pr;
1522 pr.first = beat_at_frame_locked (meter->frame());
1524 meter->set_beat (pr);
1526 meter->set_frame (frame_at_tick_locked (meter->tick()));
1534 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1536 Glib::Threads::RWLock::ReaderLock lm (lock);
1537 TempoMetric m (first_meter(), first_tempo());
1539 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1540 at something, because we insert the default tempo and meter during
1541 TempoMap construction.
1543 now see if we can find better candidates.
1546 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1548 if ((*i)->frame() > frame) {
1561 /* XX meters only */
1563 TempoMap::metric_at (BBT_Time bbt) const
1565 Glib::Threads::RWLock::ReaderLock lm (lock);
1566 TempoMetric m (first_meter(), first_tempo());
1568 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1569 at something, because we insert the default tempo and meter during
1570 TempoMap construction.
1572 now see if we can find better candidates.
1575 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1577 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1578 BBT_Time section_start (mw->bbt());
1580 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1592 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1594 Glib::Threads::RWLock::ReaderLock lm (lock);
1600 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1603 bbt = beats_to_bbt_locked (beat_at_frame_locked (frame));
1607 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1609 Glib::Threads::RWLock::ReaderLock lm (lock);
1610 return bbt_to_beats_locked (bbt);
1614 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1616 /* CALLER HOLDS READ LOCK */
1618 double accumulated_beats = 0.0;
1619 double accumulated_bars = 0.0;
1620 MeterSection* prev_ms = 0;
1622 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1624 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1625 double bars_to_m = 0.0;
1627 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1629 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1633 accumulated_beats += m->beat() - prev_ms->beat();
1634 accumulated_bars += bars_to_m;
1640 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1641 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1642 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1647 TempoMap::beats_to_bbt (double beats)
1649 Glib::Threads::RWLock::ReaderLock lm (lock);
1650 return beats_to_bbt_locked (beats);
1654 TempoMap::beats_to_bbt_locked (double beats)
1656 /* CALLER HOLDS READ LOCK */
1658 MeterSection* prev_ms = 0;
1659 uint32_t accumulated_bars = 0;
1661 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1662 MeterSection* m = 0;
1664 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1666 if (beats < m->beat()) {
1667 /* this is the meter after the one our beat is on*/
1672 /* we need a whole number of bars. */
1673 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1680 double const beats_in_ms = beats - prev_ms->beat();
1681 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1682 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1683 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1684 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1688 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1689 ret.beats = (uint32_t) floor (remaining_beats);
1690 ret.bars = total_bars;
1692 /* 0 0 0 to 1 1 0 - based mapping*/
1696 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1698 ret.ticks -= BBT_Time::ticks_per_beat;
1701 if (ret.beats > prev_ms->divisions_per_bar()) {
1710 TempoMap::tick_at_frame_locked (framecnt_t frame) const
1712 /* HOLD (at least) THE READER LOCK */
1714 TempoSection* prev_ts = 0;
1715 double accumulated_ticks = 0.0;
1717 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1719 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1720 if ((prev_ts) && frame < t->frame()) {
1721 /*the previous ts is the one containing the frame */
1722 return prev_ts->tick_at_frame (frame, _frame_rate);
1725 accumulated_ticks = t->tick();
1730 /* treated as constant for this ts */
1731 double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1733 return ticks_in_section + accumulated_ticks;
1738 TempoMap::frame_at_tick_locked (double tick) const
1740 /* HOLD THE READER LOCK */
1742 const TempoSection* prev_ts = 0;
1743 double accumulated_ticks = 0.0;
1745 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1747 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1748 if (prev_ts && tick < t->tick()) {
1749 /* prev_ts is the one affecting us. */
1750 return prev_ts->frame_at_tick (tick, _frame_rate);
1753 accumulated_ticks = t->tick();
1757 /* must be treated as constant, irrespective of _type */
1758 double const ticks_in_section = tick - accumulated_ticks;
1759 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1761 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1767 TempoMap::beat_at_frame (framecnt_t frame) const
1769 Glib::Threads::RWLock::ReaderLock lm (lock);
1770 return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1774 TempoMap::beat_at_frame_locked (framecnt_t frame) const
1776 return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1780 TempoMap::frame_at_beat (double beat) const
1782 Glib::Threads::RWLock::ReaderLock lm (lock);
1783 return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1787 TempoMap::frame_at_beat_locked (double beat) const
1790 return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1794 TempoMap::frame_time (const BBT_Time& bbt)
1797 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1801 if (bbt.beats < 1) {
1802 throw std::logic_error ("beats are counted from one");
1804 Glib::Threads::RWLock::ReaderLock lm (lock);
1806 return frame_time_locked (bbt);;
1810 TempoMap::frame_time_locked (const BBT_Time& bbt)
1812 /* HOLD THE READER LOCK */
1814 framepos_t const ret = frame_at_beat_locked (bbt_to_beats_locked (bbt));
1821 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1823 Glib::Threads::RWLock::ReaderLock lm (lock);
1825 Metrics::const_iterator i;
1826 TempoSection* first = 0;
1827 TempoSection* second = 0;
1829 for (i = metrics.begin(); i != metrics.end(); ++i) {
1832 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1834 if ((*i)->frame() > pos) {
1842 if (first && second) {
1843 double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
1844 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1845 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1847 return time_at_bbt - pos;
1849 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1851 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1855 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1857 return round_to_type (fr, dir, Bar);
1861 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1863 return round_to_type (fr, dir, Beat);
1867 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1869 Glib::Threads::RWLock::ReaderLock lm (lock);
1871 uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (fr) + 0.5);
1872 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1873 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1875 ticks -= beats * BBT_Time::ticks_per_beat;
1878 /* round to next (or same iff dir == RoundUpMaybe) */
1880 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1882 if (mod == 0 && dir == RoundUpMaybe) {
1883 /* right on the subdivision, which is fine, so do nothing */
1885 } else if (mod == 0) {
1886 /* right on the subdivision, so the difference is just the subdivision ticks */
1887 ticks += ticks_one_subdivisions_worth;
1890 /* not on subdivision, compute distance to next subdivision */
1892 ticks += ticks_one_subdivisions_worth - mod;
1895 if (ticks >= BBT_Time::ticks_per_beat) {
1896 ticks -= BBT_Time::ticks_per_beat;
1898 } else if (dir < 0) {
1900 /* round to previous (or same iff dir == RoundDownMaybe) */
1902 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1904 if (difference == 0 && dir == RoundDownAlways) {
1905 /* right on the subdivision, but force-rounding down,
1906 so the difference is just the subdivision ticks */
1907 difference = ticks_one_subdivisions_worth;
1910 if (ticks < difference) {
1911 ticks = BBT_Time::ticks_per_beat - ticks;
1913 ticks -= difference;
1917 /* round to nearest */
1920 /* compute the distance to the previous and next subdivision */
1922 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1924 /* closer to the next subdivision, so shift forward */
1926 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1928 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1930 if (ticks > BBT_Time::ticks_per_beat) {
1932 ticks -= BBT_Time::ticks_per_beat;
1933 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1936 } else if (rem > 0) {
1938 /* closer to previous subdivision, so shift backward */
1942 /* can't go backwards past zero, so ... */
1945 /* step back to previous beat */
1947 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1948 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1950 ticks = lrint (ticks - rem);
1951 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1954 /* on the subdivision, do nothing */
1957 return frame_at_tick_locked ((beats * BBT_Time::ticks_per_beat) + ticks);
1961 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1963 Glib::Threads::RWLock::ReaderLock lm (lock);
1965 double const beat_at_framepos = beat_at_frame_locked (frame);
1967 BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1972 /* find bar previous to 'frame' */
1975 return frame_time (bbt);
1977 } else if (dir > 0) {
1978 /* find bar following 'frame' */
1982 return frame_time (bbt);
1984 /* true rounding: find nearest bar */
1985 framepos_t raw_ft = frame_time (bbt);
1988 framepos_t prev_ft = frame_time (bbt);
1990 framepos_t next_ft = frame_time (bbt);
1992 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2003 return frame_at_beat_locked (floor (beat_at_framepos));
2004 } else if (dir > 0) {
2005 return frame_at_beat_locked (ceil (beat_at_framepos));
2007 return frame_at_beat_locked (floor (beat_at_framepos + 0.5));
2016 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2017 framepos_t lower, framepos_t upper)
2019 Glib::Threads::RWLock::ReaderLock lm (lock);
2020 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame_locked (upper));
2021 uint32_t cnt = (uint32_t) ceil (beat_at_frame_locked (lower));
2023 while (cnt <= upper_beat) {
2024 framecnt_t const pos = frame_at_beat (cnt);
2025 MeterSection const meter = meter_section_at (pos);
2026 Tempo const tempo = tempo_at (pos);
2027 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
2029 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
2035 TempoMap::tempo_section_at (framepos_t frame) const
2037 Glib::Threads::RWLock::ReaderLock lm (lock);
2039 Metrics::const_iterator i;
2040 TempoSection* prev = 0;
2042 for (i = metrics.begin(); i != metrics.end(); ++i) {
2045 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2047 if ((*i)->frame() > frame) {
2057 abort(); /*NOTREACHED*/
2063 /* don't use this to calculate length (the tempo is only correct for this frame).
2064 do that stuff based on the beat_at_frame and frame_at_beat api
2067 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2069 Glib::Threads::RWLock::ReaderLock lm (lock);
2071 const TempoSection* ts_at = &tempo_section_at (frame);
2072 const TempoSection* ts_after = 0;
2073 Metrics::const_iterator i;
2075 for (i = metrics.begin(); i != metrics.end(); ++i) {
2078 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2080 if ((*i)->frame() > frame) {
2088 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2090 /* must be treated as constant tempo */
2091 return ts_at->frames_per_beat (_frame_rate);
2095 TempoMap::tempo_at (framepos_t frame) const
2097 Glib::Threads::RWLock::ReaderLock lm (lock);
2099 TempoMetric m (metric_at (frame));
2100 TempoSection* prev_ts = 0;
2102 Metrics::const_iterator i;
2104 for (i = metrics.begin(); i != metrics.end(); ++i) {
2106 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2107 if ((prev_ts) && t->frame() > frame) {
2108 /* this is the one past frame */
2109 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
2110 Tempo const ret_tempo (ret, m.tempo().note_type ());
2121 TempoMap::meter_section_at (framepos_t frame) const
2123 Glib::Threads::RWLock::ReaderLock lm (lock);
2125 Metrics::const_iterator i;
2126 MeterSection* prev = 0;
2128 for (i = metrics.begin(); i != metrics.end(); ++i) {
2131 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2133 if ((*i)->frame() > frame) {
2143 abort(); /*NOTREACHED*/
2150 TempoMap::meter_at (framepos_t frame) const
2152 TempoMetric m (metric_at (frame));
2157 TempoMap::get_state ()
2159 Metrics::const_iterator i;
2160 XMLNode *root = new XMLNode ("TempoMap");
2163 Glib::Threads::RWLock::ReaderLock lm (lock);
2164 for (i = metrics.begin(); i != metrics.end(); ++i) {
2165 root->add_child_nocopy ((*i)->get_state());
2173 TempoMap::set_state (const XMLNode& node, int /*version*/)
2176 Glib::Threads::RWLock::WriterLock lm (lock);
2179 XMLNodeConstIterator niter;
2180 Metrics old_metrics (metrics);
2181 MeterSection* last_meter = 0;
2184 nlist = node.children();
2186 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2187 XMLNode* child = *niter;
2189 if (child->name() == TempoSection::xml_state_node_name) {
2192 TempoSection* ts = new TempoSection (*child);
2193 metrics.push_back (ts);
2195 if (ts->bar_offset() < 0.0) {
2197 //ts->update_bar_offset_from_bbt (*last_meter);
2202 catch (failed_constructor& err){
2203 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2204 metrics = old_metrics;
2208 } else if (child->name() == MeterSection::xml_state_node_name) {
2211 MeterSection* ms = new MeterSection (*child);
2212 metrics.push_back (ms);
2216 catch (failed_constructor& err) {
2217 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2218 metrics = old_metrics;
2224 if (niter == nlist.end()) {
2225 MetricSectionSorter cmp;
2228 /* check for legacy sessions where bbt was the base musical unit for tempo */
2229 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2230 MeterSection* prev_ms;
2231 TempoSection* prev_ts;
2232 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2233 if (prev_ms->beat() < 0.0) {
2234 /*XX we cannot possibly make this work??. */
2235 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());
2236 prev_ms->set_beat (start);
2238 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2239 if (prev_ts->beat() < 0.0) {
2240 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);
2241 prev_ts->set_beat (start);
2246 /* check for multiple tempo/meters at the same location, which
2247 ardour2 somehow allowed.
2250 Metrics::iterator prev = metrics.end();
2251 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2252 if (prev != metrics.end()) {
2254 MeterSection* prev_ms;
2256 TempoSection* prev_ts;
2257 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2258 if (prev_ms->beat() == ms->beat()) {
2259 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2260 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2263 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2264 if (prev_ts->beat() == ts->beat()) {
2265 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2266 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2274 recompute_map (true, -1);
2277 PropertyChanged (PropertyChange ());
2283 TempoMap::dump (std::ostream& o) const
2285 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2286 const MeterSection* m;
2287 const TempoSection* t;
2289 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2291 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2292 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? "
2293 << t->movable() << ')' << endl;
2294 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2295 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2296 << " (movable? " << m->movable() << ')' << endl;
2302 TempoMap::n_tempos() const
2304 Glib::Threads::RWLock::ReaderLock lm (lock);
2307 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2308 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2317 TempoMap::n_meters() const
2319 Glib::Threads::RWLock::ReaderLock lm (lock);
2322 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2323 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2332 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2335 Glib::Threads::RWLock::WriterLock lm (lock);
2336 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2337 if ((*i)->frame() >= where && (*i)->movable ()) {
2338 (*i)->set_frame ((*i)->frame() + amount);
2342 /* now reset the BBT time of all metrics, based on their new
2343 * audio time. This is the only place where we do this reverse
2347 Metrics::iterator i;
2348 const MeterSection* meter;
2349 const TempoSection* tempo;
2353 meter = &first_meter ();
2354 tempo = &first_tempo ();
2359 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2362 MetricSection* prev = 0;
2364 for (i = metrics.begin(); i != metrics.end(); ++i) {
2367 //TempoMetric metric (*meter, *tempo);
2368 MeterSection* ms = const_cast<MeterSection*>(meter);
2369 TempoSection* ts = const_cast<TempoSection*>(tempo);
2372 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2373 ts->set_beat (t->beat());
2375 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2376 ts->set_beat (m->beat());
2378 ts->set_frame (prev->frame());
2382 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2383 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2384 ms->set_beat (start);
2386 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2387 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2388 ms->set_beat (start);
2390 ms->set_frame (prev->frame());
2394 // metric will be at frames=0 bbt=1|1|0 by default
2395 // which is correct for our purpose
2398 // cerr << bbt << endl;
2400 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2401 t->set_beat (beat_at_frame_locked (m->frame()));
2403 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2404 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2405 bbt_time (m->frame(), bbt);
2407 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2413 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2414 /* round up to next beat */
2420 if (bbt.beats != 1) {
2421 /* round up to next bar */
2426 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (m->frame()), bbt);
2427 m->set_beat (start);
2429 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2431 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2432 abort(); /*NOTREACHED*/
2438 recompute_map (true);
2442 PropertyChanged (PropertyChange ());
2445 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2449 std::list<MetricSection*> metric_kill_list;
2451 TempoSection* last_tempo = NULL;
2452 MeterSection* last_meter = NULL;
2453 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2454 bool meter_after = false; // is there a meter marker likewise?
2456 Glib::Threads::RWLock::WriterLock lm (lock);
2457 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2458 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2459 metric_kill_list.push_back(*i);
2460 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2463 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2467 else if ((*i)->frame() >= where) {
2468 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2469 (*i)->set_frame ((*i)->frame() - amount);
2470 if ((*i)->frame() == where) {
2471 // marker was immediately after end of range
2472 tempo_after = dynamic_cast<TempoSection*> (*i);
2473 meter_after = dynamic_cast<MeterSection*> (*i);
2479 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2480 if (last_tempo && !tempo_after) {
2481 metric_kill_list.remove(last_tempo);
2482 last_tempo->set_frame(where);
2485 if (last_meter && !meter_after) {
2486 metric_kill_list.remove(last_meter);
2487 last_meter->set_frame(where);
2491 //remove all the remaining metrics
2492 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2498 recompute_map (true);
2501 PropertyChanged (PropertyChange ());
2505 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2506 * pos can be -ve, if required.
2509 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2511 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2514 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2516 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2518 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2521 /** Add the BBT interval op to pos and return the result */
2523 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2525 cerr << "framepos_plus_bbt - untested" << endl;
2526 Glib::Threads::RWLock::ReaderLock lm (lock);
2528 Metrics::const_iterator i;
2529 const MeterSection* meter;
2530 const MeterSection* m;
2531 const TempoSection* tempo;
2532 const TempoSection* next_tempo = 0;
2533 const TempoSection* t;
2534 double frames_per_beat;
2535 framepos_t effective_pos = max (pos, (framepos_t) 0);
2537 meter = &first_meter ();
2538 tempo = &first_tempo ();
2543 /* find the starting metrics for tempo & meter */
2545 for (i = metrics.begin(); i != metrics.end(); ++i) {
2547 if ((*i)->frame() > effective_pos) {
2551 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2553 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2558 for (i = metrics.begin(); i != metrics.end(); ++i) {
2559 if ((*i)->frame() > effective_pos) {
2560 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2569 meter -> the Meter for "pos"
2570 tempo -> the Tempo for "pos"
2571 next_tempo -> the Tempo after "pos" or 0
2572 i -> for first new metric after "pos", possibly metrics.end()
2575 /* now comes the complicated part. we have to add one beat a time,
2576 checking for a new metric on every beat.
2580 /* fpb is used for constant tempo */
2581 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2588 /* check if we need to use a new metric section: has adding frames moved us
2589 to or after the start of the next metric section? in which case, use it.
2592 if (i != metrics.end()) {
2593 if ((*i)->frame() <= pos) {
2595 /* about to change tempo or meter, so add the
2596 * number of frames for the bars we've just
2597 * traversed before we change the
2598 * frames_per_beat value.
2601 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2606 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2608 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2613 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2615 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2619 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2626 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2628 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2635 /* given the current meter, have we gone past the end of the bar ? */
2640 /* check if we need to use a new metric section: has adding frames moved us
2641 to or after the start of the next metric section? in which case, use it.
2644 if (i != metrics.end()) {
2645 if ((*i)->frame() <= pos) {
2647 /* about to change tempo or meter, so add the
2648 * number of frames for the beats we've just
2649 * traversed before we change the
2650 * frames_per_beat value.
2653 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2658 pos += tempo->frame_at_beat (beats, _frame_rate);
2660 pos += llrint (beats * frames_per_beat);
2665 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2667 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2671 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2677 pos += tempo->frame_at_beat (beats, _frame_rate);
2679 pos += llrint (beats * frames_per_beat);
2683 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2690 /** Count the number of beats that are equivalent to distance when going forward,
2694 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2696 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2700 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2706 operator<< (std::ostream& o, const Meter& m) {
2707 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2711 operator<< (std::ostream& o, const Tempo& t) {
2712 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2716 operator<< (std::ostream& o, const MetricSection& section) {
2718 o << "MetricSection @ " << section.frame() << ' ';
2720 const TempoSection* ts;
2721 const MeterSection* ms;
2723 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2724 o << *((const Tempo*) ts);
2725 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2726 //o << *((const Meter*) ms);