2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
72 /***********************************************************************/
74 const string TempoSection::xml_state_node_name = "Tempo";
76 TempoSection::TempoSection (const XMLNode& node)
77 : MetricSection (0.0), Tempo (TempoMap::default_tempo())
79 const XMLProperty *prop;
85 if ((prop = node.property ("start")) != 0) {
86 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
90 /* legacy session - start used to be in bbt*/
96 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
100 if ((prop = node.property ("beat")) != 0) {
101 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
102 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
107 if ((prop = node.property ("frame")) != 0) {
108 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
109 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
115 if ((prop = node.property ("beats-per-minute")) == 0) {
116 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
117 throw failed_constructor();
120 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
121 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
122 throw failed_constructor();
125 if ((prop = node.property ("note-type")) == 0) {
126 /* older session, make note type be quarter by default */
129 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
130 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
131 throw failed_constructor();
135 if ((prop = node.property ("movable")) == 0) {
136 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
137 throw failed_constructor();
140 set_movable (string_is_affirmative (prop->value()));
142 if ((prop = node.property ("bar-offset")) == 0) {
145 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
146 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
147 throw failed_constructor();
151 if ((prop = node.property ("tempo-type")) == 0) {
154 _type = Type (string_2_enum (prop->value(), _type));
157 if ((prop = node.property ("lock-style")) == 0) {
158 set_position_lock_style (MusicTime);
160 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
165 TempoSection::get_state() const
167 XMLNode *root = new XMLNode (xml_state_node_name);
171 snprintf (buf, sizeof (buf), "%f", beat());
172 root->add_property ("beat", buf);
173 snprintf (buf, sizeof (buf), "%li", frame());
174 root->add_property ("frame", buf);
175 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
176 root->add_property ("beats-per-minute", buf);
177 snprintf (buf, sizeof (buf), "%f", _note_type);
178 root->add_property ("note-type", buf);
179 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
180 // root->add_property ("bar-offset", buf);
181 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
182 root->add_property ("movable", buf);
183 root->add_property ("tempo-type", enum_2_string (_type));
184 root->add_property ("lock-style", enum_2_string (position_lock_style()));
191 TempoSection::update_bar_offset_from_bbt (const Meter& m)
193 _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
194 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
196 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
200 TempoSection::set_type (Type type)
205 /** returns the tempo at the zero-based (relative to session) frame.
208 TempoSection::tempo_at_frame (framepos_t f, framecnt_t frame_rate) const
211 if (_type == Constant) {
212 return beats_per_minute();
215 return tick_tempo_at_time (frame_to_minute (f - frame(), frame_rate)) / BBT_Time::ticks_per_beat;
218 /** returns the zero-based frame (relative to session)
219 where the tempo occurs in this section.
220 beat b is only used for constant tempos.
221 note that the tempo map may have multiple such values.
224 TempoSection::frame_at_tempo (double bpm, double b, framecnt_t frame_rate) const
226 if (_type == Constant) {
227 return ((b - beat()) * frames_per_beat (frame_rate)) + frame();
230 return minute_to_frame (time_at_tick_tempo (bpm * BBT_Time::ticks_per_beat), frame_rate) + frame();
232 /** returns the tempo at the zero-based (relative to session) beat.
235 TempoSection::tempo_at_beat (double b) const
238 if (_type == Constant) {
239 return beats_per_minute();
242 return tick_tempo_at_tick ((b - beat()) * BBT_Time::ticks_per_beat) / BBT_Time::ticks_per_beat;
245 /** returns the zero-based beat (relative to session)
246 where the tempo occurs given frame f. frame f is only used for constant tempos.
247 note that the session tempo map may have multiple beats at a given tempo.
250 TempoSection::beat_at_tempo (double bpm, framepos_t f, framecnt_t frame_rate) const
252 if (_type == Constant) {
253 double const ticks = (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
254 return ticks / BBT_Time::ticks_per_beat;
257 return (tick_at_tick_tempo (bpm * BBT_Time::ticks_per_beat) + tick()) / BBT_Time::ticks_per_beat;
260 /** returns the zero-based beat (relative to session origin)
261 where the zero-based frame (relative to session)
265 TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
267 return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
270 /** returns the zero-based frame (relative to session start frame)
271 where the zero-based beat (relative to session start)
276 TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
278 return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
281 /** returns the zero-based tick (relative to session origin)
282 where the zero-based frame (relative to tempo section)
286 TempoSection::tick_at_frame (framepos_t f, framecnt_t frame_rate) const
288 if (_type == Constant) {
289 return (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
292 return tick_at_time (frame_to_minute (f - frame(), frame_rate)) + tick();
295 /** returns the zero-based frame (relative to session origin)
296 where the zero-based tick (relative to session)
300 TempoSection::frame_at_tick (double t, framecnt_t frame_rate) const
302 if (_type == Constant) {
303 return (framepos_t) floor (((t - tick()) / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate)) + frame();
306 return minute_to_frame (time_at_tick (t - tick()), frame_rate) + frame();
314 Tt----|-----------------*|
315 Ta----|--------------|* |
321 _______________|___|____
322 time a t (next tempo)
325 Duration in beats at time a is the integral of some Tempo function.
326 In our case, the Tempo function (Tempo at time t) is
329 with function constant
334 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
335 b(t) = T0(e^(ct) - 1) / c
337 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
338 t(b) = log((cb / T0) + 1) / c
340 The time t at which Tempo T occurs is a as above:
341 t(T) = log(T / T0) / c
343 The beat at which a Tempo T occurs is:
346 The Tempo at which beat b occurs is:
349 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
350 Our problem is that we usually don't know t.
351 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
352 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
353 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
355 By substituting our expanded t as a in the c function above, our problem is reduced to:
356 c = T0 (e^(log (Ta / T0)) - 1) / b
358 We can now store c for future time calculations.
359 If the following tempo section (the one that defines c in conjunction with this one)
360 is changed or moved, c is no longer valid.
362 The public methods are session-relative.
364 Most of this stuff is taken from this paper:
367 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
370 Zurich University of Arts
371 Institute for Computer Music and Sound Technology
373 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
377 /* set this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
379 TempoSection::set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
381 double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
382 _c_func = ticks_per_minute() * (exp (log_tempo_ratio) - 1) / ((end_beat - beat()) * BBT_Time::ticks_per_beat);
385 /* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from session origin */
387 TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
389 return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame - frame(), frame_rate));
393 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
395 return (framecnt_t) floor ((time * 60.0 * frame_rate));
399 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
401 return (frame / (double) frame_rate) / 60.0;
404 /* position function */
406 TempoSection::a_func (double end_tpm, double c_func) const
408 return log (end_tpm / ticks_per_minute()) / c_func;
411 /*function constant*/
413 TempoSection::c_func (double end_tpm, double end_time) const
415 return log (end_tpm / ticks_per_minute()) / end_time;
418 /* tempo in tpm at time in minutes */
420 TempoSection::tick_tempo_at_time (double time) const
422 return exp (_c_func * time) * ticks_per_minute();
425 /* time in minutes at tempo in tpm */
427 TempoSection::time_at_tick_tempo (double tick_tempo) const
429 return log (tick_tempo / ticks_per_minute()) / _c_func;
432 /* tick at tempo in tpm */
434 TempoSection::tick_at_tick_tempo (double tick_tempo) const
436 return (tick_tempo - ticks_per_minute()) / _c_func;
439 /* tempo in tpm at tick */
441 TempoSection::tick_tempo_at_tick (double tick) const
443 return (tick * _c_func) + ticks_per_minute();
446 /* tick at time in minutes */
448 TempoSection::tick_at_time (double time) const
450 return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
453 /* time in minutes at tick */
455 TempoSection::time_at_tick (double tick) const
457 return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
460 /* beat at time in minutes */
462 TempoSection::beat_at_time (double time) const
464 return tick_at_time (time) / BBT_Time::ticks_per_beat;
467 /* time in munutes at beat */
469 TempoSection::time_at_beat (double beat) const
471 return time_at_tick (beat * BBT_Time::ticks_per_beat);
475 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
479 if (_bar_offset < 0.0) {
486 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
487 new_beat = ticks / BBT_Time::ticks_per_beat;
489 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
490 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
495 /***********************************************************************/
498 if a meter section is position locked to frames, then it can only be at 1|1|0.
499 thus we can have multiple 1|1|0s in the session tempo map.
503 BBT 1|1|0 2|1|0 3|1|0 1|1|0
506 all other meter sections are locked to beats.
508 the beat of a meter section is used to find its position rather than the stored bbt.
511 const string MeterSection::xml_state_node_name = "Meter";
513 MeterSection::MeterSection (const XMLNode& node)
514 : MetricSection (0.0), Meter (TempoMap::default_meter())
516 XMLProperty const * prop;
519 const XMLProperty *prop;
522 framepos_t frame = 0;
523 pair<double, BBT_Time> start;
525 if ((prop = node.property ("start")) != 0) {
526 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
530 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
532 /* legacy session - start used to be in bbt*/
536 error << _("MeterSection XML node has no \"start\" property") << endmsg;
539 if ((prop = node.property ("beat")) != 0) {
540 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
541 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
547 if ((prop = node.property ("bbt")) == 0) {
548 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
549 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
553 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
554 //throw failed_constructor();
561 if ((prop = node.property ("frame")) != 0) {
562 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
563 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
569 /* beats-per-bar is old; divisions-per-bar is new */
571 if ((prop = node.property ("divisions-per-bar")) == 0) {
572 if ((prop = node.property ("beats-per-bar")) == 0) {
573 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
574 throw failed_constructor();
577 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
578 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
579 throw failed_constructor();
582 if ((prop = node.property ("note-type")) == 0) {
583 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
584 throw failed_constructor();
586 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
587 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
588 throw failed_constructor();
591 if ((prop = node.property ("lock-style")) == 0) {
592 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
593 set_position_lock_style (MusicTime);
595 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
598 if ((prop = node.property ("movable")) == 0) {
599 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
600 throw failed_constructor();
603 set_movable (string_is_affirmative (prop->value()));
607 MeterSection::get_state() const
609 XMLNode *root = new XMLNode (xml_state_node_name);
613 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
617 root->add_property ("bbt", buf);
618 snprintf (buf, sizeof (buf), "%lf", beat());
619 root->add_property ("beat", buf);
620 snprintf (buf, sizeof (buf), "%f", _note_type);
621 root->add_property ("note-type", buf);
622 snprintf (buf, sizeof (buf), "%li", frame());
623 root->add_property ("frame", buf);
624 root->add_property ("lock-style", enum_2_string (position_lock_style()));
625 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
626 root->add_property ("divisions-per-bar", buf);
627 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
628 root->add_property ("movable", buf);
633 /***********************************************************************/
635 struct MetricSectionSorter {
636 bool operator() (const MetricSection* a, const MetricSection* b) {
637 return a->beat() < b->beat();
641 struct MetricSectionFrameSorter {
642 bool operator() (const MetricSection* a, const MetricSection* b) {
643 return a->frame() < b->frame();
647 TempoMap::TempoMap (framecnt_t fr)
650 BBT_Time start (1, 1, 0);
652 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
653 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
655 t->set_movable (false);
656 m->set_movable (false);
658 /* note: frame time is correct (zero) for both of these */
660 metrics.push_back (t);
661 metrics.push_back (m);
665 TempoMap::~TempoMap ()
670 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
672 bool removed = false;
675 Glib::Threads::RWLock::WriterLock lm (lock);
676 if ((removed = remove_tempo_locked (tempo))) {
677 if (complete_operation) {
678 recompute_map (true);
683 if (removed && complete_operation) {
684 PropertyChanged (PropertyChange ());
689 TempoMap::remove_tempo_locked (const TempoSection& tempo)
693 for (i = metrics.begin(); i != metrics.end(); ++i) {
694 if (dynamic_cast<TempoSection*> (*i) != 0) {
695 if (tempo.frame() == (*i)->frame()) {
696 if ((*i)->movable()) {
708 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
710 bool removed = false;
713 Glib::Threads::RWLock::WriterLock lm (lock);
714 if ((removed = remove_meter_locked (tempo))) {
715 if (complete_operation) {
716 recompute_map (true);
721 if (removed && complete_operation) {
722 PropertyChanged (PropertyChange ());
727 TempoMap::remove_meter_locked (const MeterSection& tempo)
731 for (i = metrics.begin(); i != metrics.end(); ++i) {
732 if (dynamic_cast<MeterSection*> (*i) != 0) {
733 if (tempo.frame() == (*i)->frame()) {
734 if ((*i)->movable()) {
746 TempoMap::do_insert (MetricSection* section)
748 bool need_add = true;
750 /* we only allow new meters to be inserted on beat 1 of an existing
754 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
755 assert (m->bbt().ticks == 0);
757 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
759 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
760 corrected.second.beats = 1;
761 corrected.second.ticks = 0;
762 corrected.first = bbt_to_beats_locked (corrected.second);
763 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
764 m->bbt(), corrected.second) << endmsg;
765 m->set_beat (corrected);
769 /* Look for any existing MetricSection that is of the same type and
770 in the same bar as the new one, and remove it before adding
771 the new one. Note that this means that if we find a matching,
772 existing section, we can break out of the loop since we're
773 guaranteed that there is only one such match.
776 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
778 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
779 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
781 if (tempo && insert_tempo) {
784 PositionLockStyle const ipl = insert_tempo->position_lock_style();
785 if ((ipl == MusicTime && tempo->beat() == insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() == insert_tempo->frame())) {
787 if (!tempo->movable()) {
789 /* can't (re)move this section, so overwrite
790 * its data content (but not its properties as
794 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
802 } else if (!tempo && !insert_tempo) {
805 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
806 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
807 PositionLockStyle const ipl = insert_meter->position_lock_style();
809 if ((ipl == MusicTime && meter->beat() == insert_meter->beat()) || (ipl == AudioTime && meter->frame() == insert_meter->frame())) {
811 if (!meter->movable()) {
813 /* can't (re)move this section, so overwrite
814 * its data content (but not its properties as
818 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
828 /* non-matching types, so we don't care */
832 /* Add the given MetricSection, if we didn't just reset an existing
837 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
838 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
842 for (i = metrics.begin(); i != metrics.end(); ++i) {
843 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
846 PositionLockStyle const ipl = insert_meter->position_lock_style();
847 if ((ipl == MusicTime && meter->beat() > insert_meter->beat()) || (ipl == AudioTime && meter->frame() > insert_meter->frame())) {
852 } else if (insert_tempo) {
853 for (i = metrics.begin(); i != metrics.end(); ++i) {
854 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
857 PositionLockStyle const ipl = insert_tempo->position_lock_style();
858 if ((ipl == MusicTime && tempo->beat() > insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() > insert_tempo->frame())) {
865 metrics.insert (i, section);
871 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
874 Glib::Threads::RWLock::WriterLock lm (lock);
875 TempoSection& first (first_tempo());
876 if (ts.beat() != first.beat()) {
877 remove_tempo_locked (ts);
878 add_tempo_locked (tempo, where, true, type);
880 first.set_type (type);
882 /* cannot move the first tempo section */
883 *static_cast<Tempo*>(&first) = tempo;
884 recompute_map (false);
889 PropertyChanged (PropertyChange ());
893 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
896 Glib::Threads::RWLock::WriterLock lm (lock);
897 TempoSection& first (first_tempo());
898 if (ts.beat() != first.beat()) {
899 remove_tempo_locked (ts);
900 add_tempo_locked (tempo, frame, true, type);
902 first.set_type (type);
904 /* cannot move the first tempo section */
905 *static_cast<Tempo*>(&first) = tempo;
906 recompute_map (false);
911 PropertyChanged (PropertyChange ());
915 TempoMap::get_new_order (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
917 Metrics imaginary (metrics);
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_map (false);
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_frame (t->frame(), _frame_rate));
1462 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1464 prev_ts->set_c_func (0.0);
1465 //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1466 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1469 if (prev_ts->type() == TempoSection::Ramp) {
1470 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
1471 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1472 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1474 prev_ts->set_c_func (0.0);
1475 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1476 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1486 TempoMap::recompute_meters ()
1488 MeterSection* meter = 0;
1490 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1491 /* Now we have the tempos mapped to position, set meter positions.*/
1492 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1493 if (meter->position_lock_style() == AudioTime) {
1494 /* a frame based meter has to have a 1|1|0 bbt */
1495 pair<double, BBT_Time> pr;
1502 pr.first = beat_at_frame_locked (meter->frame());
1504 meter->set_beat (pr);
1506 meter->set_frame (frame_at_tick_locked (meter->tick()));
1513 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1515 /* CALLER MUST HOLD WRITE LOCK */
1519 /* we will actually stop once we hit
1526 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1529 /* silly call from Session::process() during startup
1540 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1542 Glib::Threads::RWLock::ReaderLock lm (lock);
1543 TempoMetric m (first_meter(), first_tempo());
1545 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1546 at something, because we insert the default tempo and meter during
1547 TempoMap construction.
1549 now see if we can find better candidates.
1552 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1554 if ((*i)->frame() > frame) {
1567 /* XX meters only */
1569 TempoMap::metric_at (BBT_Time bbt) const
1571 Glib::Threads::RWLock::ReaderLock lm (lock);
1572 TempoMetric m (first_meter(), first_tempo());
1574 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1575 at something, because we insert the default tempo and meter during
1576 TempoMap construction.
1578 now see if we can find better candidates.
1581 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1583 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1584 BBT_Time section_start (mw->bbt());
1586 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1598 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1600 Glib::Threads::RWLock::ReaderLock lm (lock);
1606 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1609 bbt = beats_to_bbt_locked (beat_at_frame_locked (frame));
1613 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1615 Glib::Threads::RWLock::ReaderLock lm (lock);
1616 return bbt_to_beats_locked (bbt);
1620 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1622 /* CALLER HOLDS READ LOCK */
1624 double accumulated_beats = 0.0;
1625 double accumulated_bars = 0.0;
1626 double bars_offset = 0.0;
1627 MeterSection* prev_ms = 0;
1629 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1631 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1632 double bars_to_m = 0.0;
1634 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1636 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1640 if (m->position_lock_style() == AudioTime) {
1641 accumulated_beats = 0.0;
1642 accumulated_bars = 0;
1643 bars_offset += bars_to_m;
1645 accumulated_beats += m->beat() - prev_ms->beat();
1646 accumulated_bars += bars_to_m;
1653 double const remaining_bars = (bbt.bars - bars_offset - 1) - accumulated_bars;
1654 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1655 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1656 std::cerr << "ret : " << ret << " bbt : " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << std::endl;
1662 TempoMap::beats_to_bbt (double beats)
1664 Glib::Threads::RWLock::ReaderLock lm (lock);
1665 return beats_to_bbt_locked (beats);
1669 TempoMap::beats_to_bbt_locked (double beats)
1671 /* CALLER HOLDS READ LOCK */
1673 MeterSection* prev_ms = 0;
1674 uint32_t accumulated_bars = 0;
1676 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1677 MeterSection* m = 0;
1679 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1681 if (m->beat() > beats) {
1682 /* this is the meter after the one our beat is on*/
1687 if(m->position_lock_style() == AudioTime) {
1688 accumulated_bars = 0;
1690 /* we need a whole number of bars. */
1691 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1699 double const beats_in_ms = beats - prev_ms->beat();
1700 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1701 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1702 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1703 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1707 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1708 ret.beats = (uint32_t) floor (remaining_beats);
1709 ret.bars = total_bars;
1711 /* 0 0 0 to 1 1 0 - based mapping*/
1715 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1717 ret.ticks -= BBT_Time::ticks_per_beat;
1720 if (ret.beats > prev_ms->divisions_per_bar()) {
1729 TempoMap::tick_at_frame_locked (framecnt_t frame) const
1731 /* HOLD (at least) THE READER LOCK */
1733 TempoSection* prev_ts = 0;
1734 double accumulated_ticks = 0.0;
1736 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1738 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1739 if ((prev_ts) && frame < t->frame()) {
1740 /*the previous ts is the one containing the frame */
1741 return prev_ts->tick_at_frame (frame, _frame_rate);
1744 accumulated_ticks = t->tick();
1749 /* treated as constant for this ts */
1750 double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1752 return ticks_in_section + accumulated_ticks;
1757 TempoMap::frame_at_tick_locked (double tick) const
1759 /* HOLD THE READER LOCK */
1761 const TempoSection* prev_ts = 0;
1762 double accumulated_ticks = 0.0;
1764 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1766 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1767 if (prev_ts && tick < t->tick()) {
1768 /* prev_ts is the one affecting us. */
1769 return prev_ts->frame_at_tick (tick, _frame_rate);
1772 accumulated_ticks = t->tick();
1776 /* must be treated as constant, irrespective of _type */
1777 double const ticks_in_section = tick - accumulated_ticks;
1778 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1780 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1786 TempoMap::beat_at_frame (framecnt_t frame) const
1788 Glib::Threads::RWLock::ReaderLock lm (lock);
1789 return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1793 TempoMap::beat_at_frame_locked (framecnt_t frame) const
1795 return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1799 TempoMap::frame_at_beat (double beat) const
1801 Glib::Threads::RWLock::ReaderLock lm (lock);
1802 return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1806 TempoMap::frame_at_beat_locked (double beat) const
1809 return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1813 TempoMap::frame_time (const BBT_Time& bbt)
1816 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1820 if (bbt.beats < 1) {
1821 throw std::logic_error ("beats are counted from one");
1823 Glib::Threads::RWLock::ReaderLock lm (lock);
1825 return frame_time_locked (bbt);;
1829 TempoMap::frame_time_locked (const BBT_Time& bbt)
1831 /* HOLD THE READER LOCK */
1833 framepos_t const ret = frame_at_beat_locked (bbt_to_beats_locked (bbt));
1840 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1842 Glib::Threads::RWLock::ReaderLock lm (lock);
1844 Metrics::const_iterator i;
1845 TempoSection* first = 0;
1846 TempoSection* second = 0;
1848 for (i = metrics.begin(); i != metrics.end(); ++i) {
1851 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1853 if ((*i)->frame() > pos) {
1861 if (first && second) {
1862 double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
1863 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1864 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1866 return time_at_bbt - pos;
1868 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1870 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1874 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1876 return round_to_type (fr, dir, Bar);
1880 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1882 return round_to_type (fr, dir, Beat);
1886 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1888 Glib::Threads::RWLock::ReaderLock lm (lock);
1890 uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (fr) + 0.5);
1891 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1892 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1894 ticks -= beats * BBT_Time::ticks_per_beat;
1897 /* round to next (or same iff dir == RoundUpMaybe) */
1899 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1901 if (mod == 0 && dir == RoundUpMaybe) {
1902 /* right on the subdivision, which is fine, so do nothing */
1904 } else if (mod == 0) {
1905 /* right on the subdivision, so the difference is just the subdivision ticks */
1906 ticks += ticks_one_subdivisions_worth;
1909 /* not on subdivision, compute distance to next subdivision */
1911 ticks += ticks_one_subdivisions_worth - mod;
1914 if (ticks >= BBT_Time::ticks_per_beat) {
1915 ticks -= BBT_Time::ticks_per_beat;
1917 } else if (dir < 0) {
1919 /* round to previous (or same iff dir == RoundDownMaybe) */
1921 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1923 if (difference == 0 && dir == RoundDownAlways) {
1924 /* right on the subdivision, but force-rounding down,
1925 so the difference is just the subdivision ticks */
1926 difference = ticks_one_subdivisions_worth;
1929 if (ticks < difference) {
1930 ticks = BBT_Time::ticks_per_beat - ticks;
1932 ticks -= difference;
1936 /* round to nearest */
1939 /* compute the distance to the previous and next subdivision */
1941 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1943 /* closer to the next subdivision, so shift forward */
1945 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1947 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1949 if (ticks > BBT_Time::ticks_per_beat) {
1951 ticks -= BBT_Time::ticks_per_beat;
1952 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1955 } else if (rem > 0) {
1957 /* closer to previous subdivision, so shift backward */
1961 /* can't go backwards past zero, so ... */
1964 /* step back to previous beat */
1966 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1967 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1969 ticks = lrint (ticks - rem);
1970 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1973 /* on the subdivision, do nothing */
1976 return frame_at_tick_locked ((beats * BBT_Time::ticks_per_beat) + ticks);
1980 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1982 Glib::Threads::RWLock::ReaderLock lm (lock);
1984 double const beat_at_framepos = beat_at_frame_locked (frame);
1986 BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1991 /* find bar previous to 'frame' */
1994 return frame_time (bbt);
1996 } else if (dir > 0) {
1997 /* find bar following 'frame' */
2001 return frame_time (bbt);
2003 /* true rounding: find nearest bar */
2004 framepos_t raw_ft = frame_time (bbt);
2007 framepos_t prev_ft = frame_time (bbt);
2009 framepos_t next_ft = frame_time (bbt);
2011 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2022 return frame_at_beat_locked (floor (beat_at_framepos));
2023 } else if (dir > 0) {
2024 return frame_at_beat_locked (ceil (beat_at_framepos));
2026 return frame_at_beat_locked (floor (beat_at_framepos + 0.5));
2035 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2036 framepos_t lower, framepos_t upper)
2038 Glib::Threads::RWLock::ReaderLock lm (lock);
2039 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame_locked (upper));
2040 uint32_t cnt = (uint32_t) ceil (beat_at_frame_locked (lower));
2042 while (cnt <= upper_beat) {
2043 framecnt_t const pos = frame_at_beat (cnt);
2044 MeterSection const meter = meter_section_at (pos);
2045 Tempo const tempo = tempo_at (pos);
2046 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
2048 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
2054 TempoMap::tempo_section_at (framepos_t frame) const
2056 Glib::Threads::RWLock::ReaderLock lm (lock);
2058 Metrics::const_iterator i;
2059 TempoSection* prev = 0;
2061 for (i = metrics.begin(); i != metrics.end(); ++i) {
2064 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2066 if ((*i)->frame() > frame) {
2076 abort(); /*NOTREACHED*/
2082 /* don't use this to calculate length (the tempo is only correct for this frame).
2083 do that stuff based on the beat_at_frame and frame_at_beat api
2086 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2088 Glib::Threads::RWLock::ReaderLock lm (lock);
2090 const TempoSection* ts_at = &tempo_section_at (frame);
2091 const TempoSection* ts_after = 0;
2092 Metrics::const_iterator i;
2094 for (i = metrics.begin(); i != metrics.end(); ++i) {
2097 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2099 if ((*i)->frame() > frame) {
2107 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2109 /* must be treated as constant tempo */
2110 return ts_at->frames_per_beat (_frame_rate);
2114 TempoMap::tempo_at (framepos_t frame) const
2116 Glib::Threads::RWLock::ReaderLock lm (lock);
2118 TempoMetric m (metric_at (frame));
2119 TempoSection* prev_ts = 0;
2121 Metrics::const_iterator i;
2123 for (i = metrics.begin(); i != metrics.end(); ++i) {
2125 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2126 if ((prev_ts) && t->frame() > frame) {
2127 /* this is the one past frame */
2128 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
2129 Tempo const ret_tempo (ret, m.tempo().note_type ());
2140 TempoMap::meter_section_at (framepos_t frame) const
2142 Glib::Threads::RWLock::ReaderLock lm (lock);
2144 Metrics::const_iterator i;
2145 MeterSection* prev = 0;
2147 for (i = metrics.begin(); i != metrics.end(); ++i) {
2150 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2152 if ((*i)->frame() > frame) {
2162 abort(); /*NOTREACHED*/
2169 TempoMap::meter_at (framepos_t frame) const
2171 TempoMetric m (metric_at (frame));
2176 TempoMap::get_state ()
2178 Metrics::const_iterator i;
2179 XMLNode *root = new XMLNode ("TempoMap");
2182 Glib::Threads::RWLock::ReaderLock lm (lock);
2183 for (i = metrics.begin(); i != metrics.end(); ++i) {
2184 root->add_child_nocopy ((*i)->get_state());
2192 TempoMap::set_state (const XMLNode& node, int /*version*/)
2195 Glib::Threads::RWLock::WriterLock lm (lock);
2198 XMLNodeConstIterator niter;
2199 Metrics old_metrics (metrics);
2200 MeterSection* last_meter = 0;
2203 nlist = node.children();
2205 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2206 XMLNode* child = *niter;
2208 if (child->name() == TempoSection::xml_state_node_name) {
2211 TempoSection* ts = new TempoSection (*child);
2212 metrics.push_back (ts);
2214 if (ts->bar_offset() < 0.0) {
2216 //ts->update_bar_offset_from_bbt (*last_meter);
2221 catch (failed_constructor& err){
2222 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2223 metrics = old_metrics;
2227 } else if (child->name() == MeterSection::xml_state_node_name) {
2230 MeterSection* ms = new MeterSection (*child);
2231 metrics.push_back (ms);
2235 catch (failed_constructor& err) {
2236 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2237 metrics = old_metrics;
2243 if (niter == nlist.end()) {
2244 MetricSectionSorter cmp;
2247 /* check for legacy sessions where bbt was the base musical unit for tempo */
2248 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2249 MeterSection* prev_ms;
2250 TempoSection* prev_ts;
2251 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2252 if (prev_ms->beat() < 0.0) {
2253 /*XX we cannot possibly make this work??. */
2254 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());
2255 prev_ms->set_beat (start);
2257 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2258 if (prev_ts->beat() < 0.0) {
2259 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);
2260 prev_ts->set_beat (start);
2265 /* check for multiple tempo/meters at the same location, which
2266 ardour2 somehow allowed.
2269 Metrics::iterator prev = metrics.end();
2270 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2271 if (prev != metrics.end()) {
2273 MeterSection* prev_ms;
2275 TempoSection* prev_ts;
2276 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2277 if (prev_ms->beat() == ms->beat()) {
2278 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2279 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2282 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2283 if (prev_ts->beat() == ts->beat()) {
2284 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2285 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2293 recompute_map (true, -1);
2296 PropertyChanged (PropertyChange ());
2302 TempoMap::dump (std::ostream& o) const
2304 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2305 const MeterSection* m;
2306 const TempoSection* t;
2308 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2310 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2311 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? "
2312 << t->movable() << ')' << endl;
2313 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2314 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2315 << " (movable? " << m->movable() << ')' << endl;
2321 TempoMap::n_tempos() const
2323 Glib::Threads::RWLock::ReaderLock lm (lock);
2326 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2327 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2336 TempoMap::n_meters() const
2338 Glib::Threads::RWLock::ReaderLock lm (lock);
2341 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2342 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2351 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2354 Glib::Threads::RWLock::WriterLock lm (lock);
2355 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2356 if ((*i)->frame() >= where && (*i)->movable ()) {
2357 (*i)->set_frame ((*i)->frame() + amount);
2361 /* now reset the BBT time of all metrics, based on their new
2362 * audio time. This is the only place where we do this reverse
2366 Metrics::iterator i;
2367 const MeterSection* meter;
2368 const TempoSection* tempo;
2372 meter = &first_meter ();
2373 tempo = &first_tempo ();
2378 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2381 MetricSection* prev = 0;
2383 for (i = metrics.begin(); i != metrics.end(); ++i) {
2386 //TempoMetric metric (*meter, *tempo);
2387 MeterSection* ms = const_cast<MeterSection*>(meter);
2388 TempoSection* ts = const_cast<TempoSection*>(tempo);
2391 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2392 ts->set_beat (t->beat());
2394 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2395 ts->set_beat (m->beat());
2397 ts->set_frame (prev->frame());
2401 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2402 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2403 ms->set_beat (start);
2405 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2406 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2407 ms->set_beat (start);
2409 ms->set_frame (prev->frame());
2413 // metric will be at frames=0 bbt=1|1|0 by default
2414 // which is correct for our purpose
2417 // cerr << bbt << endl;
2419 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2420 t->set_beat (beat_at_frame_locked (m->frame()));
2422 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2423 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2424 bbt_time (m->frame(), bbt);
2426 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2432 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2433 /* round up to next beat */
2439 if (bbt.beats != 1) {
2440 /* round up to next bar */
2445 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (m->frame()), bbt);
2446 m->set_beat (start);
2448 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2450 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2451 abort(); /*NOTREACHED*/
2457 recompute_map (true);
2461 PropertyChanged (PropertyChange ());
2464 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2468 std::list<MetricSection*> metric_kill_list;
2470 TempoSection* last_tempo = NULL;
2471 MeterSection* last_meter = NULL;
2472 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2473 bool meter_after = false; // is there a meter marker likewise?
2475 Glib::Threads::RWLock::WriterLock lm (lock);
2476 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2477 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2478 metric_kill_list.push_back(*i);
2479 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2482 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2486 else if ((*i)->frame() >= where) {
2487 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2488 (*i)->set_frame ((*i)->frame() - amount);
2489 if ((*i)->frame() == where) {
2490 // marker was immediately after end of range
2491 tempo_after = dynamic_cast<TempoSection*> (*i);
2492 meter_after = dynamic_cast<MeterSection*> (*i);
2498 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2499 if (last_tempo && !tempo_after) {
2500 metric_kill_list.remove(last_tempo);
2501 last_tempo->set_frame(where);
2504 if (last_meter && !meter_after) {
2505 metric_kill_list.remove(last_meter);
2506 last_meter->set_frame(where);
2510 //remove all the remaining metrics
2511 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2517 recompute_map (true);
2520 PropertyChanged (PropertyChange ());
2524 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2525 * pos can be -ve, if required.
2528 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2530 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2533 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2535 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2537 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2540 /** Add the BBT interval op to pos and return the result */
2542 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2544 cerr << "framepos_plus_bbt - untested" << endl;
2545 Glib::Threads::RWLock::ReaderLock lm (lock);
2547 Metrics::const_iterator i;
2548 const MeterSection* meter;
2549 const MeterSection* m;
2550 const TempoSection* tempo;
2551 const TempoSection* next_tempo = 0;
2552 const TempoSection* t;
2553 double frames_per_beat;
2554 framepos_t effective_pos = max (pos, (framepos_t) 0);
2556 meter = &first_meter ();
2557 tempo = &first_tempo ();
2562 /* find the starting metrics for tempo & meter */
2564 for (i = metrics.begin(); i != metrics.end(); ++i) {
2566 if ((*i)->frame() > effective_pos) {
2570 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2572 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2577 for (i = metrics.begin(); i != metrics.end(); ++i) {
2578 if ((*i)->frame() > effective_pos) {
2579 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2588 meter -> the Meter for "pos"
2589 tempo -> the Tempo for "pos"
2590 next_tempo -> the Tempo after "pos" or 0
2591 i -> for first new metric after "pos", possibly metrics.end()
2594 /* now comes the complicated part. we have to add one beat a time,
2595 checking for a new metric on every beat.
2599 /* fpb is used for constant tempo */
2600 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2607 /* check if we need to use a new metric section: has adding frames moved us
2608 to or after the start of the next metric section? in which case, use it.
2611 if (i != metrics.end()) {
2612 if ((*i)->frame() <= pos) {
2614 /* about to change tempo or meter, so add the
2615 * number of frames for the bars we've just
2616 * traversed before we change the
2617 * frames_per_beat value.
2620 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2625 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2627 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2632 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2634 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2638 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2645 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2647 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2654 /* given the current meter, have we gone past the end of the bar ? */
2659 /* check if we need to use a new metric section: has adding frames moved us
2660 to or after the start of the next metric section? in which case, use it.
2663 if (i != metrics.end()) {
2664 if ((*i)->frame() <= pos) {
2666 /* about to change tempo or meter, so add the
2667 * number of frames for the beats we've just
2668 * traversed before we change the
2669 * frames_per_beat value.
2672 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2677 pos += tempo->frame_at_beat (beats, _frame_rate);
2679 pos += llrint (beats * frames_per_beat);
2684 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2686 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2690 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2696 pos += tempo->frame_at_beat (beats, _frame_rate);
2698 pos += llrint (beats * frames_per_beat);
2702 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2709 /** Count the number of beats that are equivalent to distance when going forward,
2713 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2715 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2719 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2725 operator<< (std::ostream& o, const Meter& m) {
2726 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2730 operator<< (std::ostream& o, const Tempo& t) {
2731 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2735 operator<< (std::ostream& o, const MetricSection& section) {
2737 o << "MetricSection @ " << section.frame() << ' ';
2739 const TempoSection* ts;
2740 const MeterSection* ms;
2742 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2743 o << *((const Tempo*) ts);
2744 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2745 //o << *((const Meter*) ms);