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 /* compute this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
379 TempoSection::compute_c_func_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 return 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_frame (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 (_metrics);
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 (_metrics);
721 if (removed && complete_operation) {
722 PropertyChanged (PropertyChange ());
727 TempoMap::remove_meter_locked (const MeterSection& tempo)
731 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
732 if (dynamic_cast<MeterSection*> (*i) != 0) {
733 if (tempo.frame() == (*i)->frame()) {
734 if ((*i)->movable()) {
746 TempoMap::do_insert (MetricSection* section)
748 bool need_add = true;
750 /* we only allow new meters to be inserted on beat 1 of an existing
754 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
755 assert (m->bbt().ticks == 0);
757 if (m->position_lock_style() == MusicTime && ((m->bbt().beats != 1) || (m->bbt().ticks != 0))) {
759 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
760 corrected.second.beats = 1;
761 corrected.second.ticks = 0;
762 corrected.first = bbt_to_beats_locked (_metrics, 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 (_metrics);
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.frame() != first.frame()) {
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 (_metrics);
910 PropertyChanged (PropertyChange ());
914 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
917 Glib::Threads::RWLock::WriterLock lm (lock);
918 add_tempo_locked (tempo, where, true, type);
921 PropertyChanged (PropertyChange ());
925 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
928 Glib::Threads::RWLock::WriterLock lm (lock);
929 add_tempo_locked (tempo, frame, true, type);
933 PropertyChanged (PropertyChange ());
937 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
939 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
944 recompute_map (_metrics);
949 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
951 TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
956 recompute_map (_metrics);
961 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
964 Glib::Threads::RWLock::WriterLock lm (lock);
965 MeterSection& first (first_meter());
966 if (ms.beat() != first.beat()) {
967 remove_meter_locked (ms);
968 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
970 /* cannot move the first meter section */
971 *static_cast<Meter*>(&first) = meter;
972 recompute_map (_metrics);
976 PropertyChanged (PropertyChange ());
980 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
983 Glib::Threads::RWLock::WriterLock lm (lock);
984 MeterSection& first (first_meter());
985 if (ms.beat() != first.beat()) {
986 remove_meter_locked (ms);
987 add_meter_locked (meter, frame, true);
989 /* cannot move the first meter section */
990 *static_cast<Meter*>(&first) = meter;
991 recompute_map (_metrics);
995 PropertyChanged (PropertyChange ());
1000 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1003 Glib::Threads::RWLock::WriterLock lm (lock);
1004 add_meter_locked (meter, beat, where, true);
1009 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1014 PropertyChanged (PropertyChange ());
1018 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1021 Glib::Threads::RWLock::WriterLock lm (lock);
1022 add_meter_locked (meter, frame, true);
1027 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1032 PropertyChanged (PropertyChange ());
1036 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1038 /* a new meter always starts a new bar on the first beat. so
1039 round the start time appropriately. remember that
1040 `where' is based on the existing tempo map, not
1041 the result after we insert the new meter.
1045 if (where.beats != 1) {
1050 /* new meters *always* start on a beat. */
1053 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
1056 recompute_map (_metrics);
1062 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1065 /* MusicTime meters always start on 1|1|0. */
1066 do_insert (new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor()));
1069 recompute_map (_metrics);
1075 * This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
1076 * taking any possible reordering as a consequence of this into account.
1077 * @param section - the section to be altered
1078 * @param bpm - the new Tempo
1079 * @param bbt - the bbt where the altered tempo will fall
1080 * @return returns - the position in frames where the new tempo section will lie.
1083 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1085 Glib::Threads::RWLock::ReaderLock lm (lock);
1088 TempoSection* new_section = copy_metrics_and_point (future_map, section);
1089 double const beat = bbt_to_beats_locked (future_map, bbt);
1090 if (solve_map (future_map, new_section, bpm, beat)) {
1091 ret = new_section->frame();
1093 ret = frame_at_beat_locked (_metrics, beat);
1096 Metrics::const_iterator d = future_map.begin();
1097 while (d != future_map.end()) {
1105 TempoMap::gui_move_tempo (TempoSection* ts, const Tempo& bpm, const framepos_t& frame)
1109 Glib::Threads::RWLock::WriterLock lm (lock);
1110 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1111 if (solve_map (future_map, new_section, bpm, frame)) {
1112 solve_map (_metrics, ts, bpm, frame);
1116 Metrics::const_iterator d = future_map.begin();
1117 while (d != future_map.end()) {
1122 MetricPositionChanged (); // Emit Signal
1126 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame)
1129 Glib::Threads::RWLock::WriterLock lm (lock);
1130 solve_map (_metrics, ms, mt, frame);
1133 MetricPositionChanged (); // Emit Signal
1137 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double& beat)
1140 Glib::Threads::RWLock::WriterLock lm (lock);
1141 solve_map (_metrics, ms, mt, beat);
1144 MetricPositionChanged (); // Emit Signal
1148 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1151 TempoSection* ret = 0;
1154 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1155 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1157 if (t->position_lock_style() == MusicTime) {
1158 ret = new TempoSection (t->beat(), t->beats_per_minute(), t->note_type(), t->type());
1160 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1162 copy.push_back (ret);
1165 if (t->position_lock_style() == MusicTime) {
1166 copy.push_back (new TempoSection (t->beat(), t->beats_per_minute(), t->note_type(), t->type()));
1168 copy.push_back (new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type()));
1171 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1172 if (m->position_lock_style() == MusicTime) {
1173 copy.push_back (new MeterSection (m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1175 copy.push_back (new MeterSection (m->frame(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1180 recompute_map (copy);
1185 TempoMap::can_solve_bbt (TempoSection* ts, const Tempo& bpm, const BBT_Time& bbt)
1188 TempoSection* new_section = 0;
1191 Glib::Threads::RWLock::ReaderLock lm (lock);
1192 new_section = copy_metrics_and_point (copy, ts);
1195 double const beat = bbt_to_beats_locked (copy, bbt);
1196 bool ret = solve_map (copy, new_section, bpm, beat);
1198 Metrics::const_iterator d = copy.begin();
1199 while (d != copy.end()) {
1208 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1210 Tempo newtempo (beats_per_minute, note_type);
1213 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1214 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1216 Glib::Threads::RWLock::WriterLock lm (lock);
1217 *((Tempo*) t) = newtempo;
1218 recompute_map (_metrics);
1220 PropertyChanged (PropertyChange ());
1227 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1229 Tempo newtempo (beats_per_minute, note_type);
1232 TempoSection* first;
1233 Metrics::iterator i;
1235 /* find the TempoSection immediately preceding "where"
1238 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1240 if ((*i)->frame() > where) {
1246 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1256 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1266 Glib::Threads::RWLock::WriterLock lm (lock);
1267 /* cannot move the first tempo section */
1268 *((Tempo*)prev) = newtempo;
1269 recompute_map (_metrics);
1272 PropertyChanged (PropertyChange ());
1276 TempoMap::first_meter () const
1278 const MeterSection *m = 0;
1280 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1281 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1286 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1287 abort(); /*NOTREACHED*/
1292 TempoMap::first_meter ()
1294 MeterSection *m = 0;
1296 /* CALLER MUST HOLD LOCK */
1298 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1299 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1304 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1305 abort(); /*NOTREACHED*/
1310 TempoMap::first_tempo () const
1312 const TempoSection *t = 0;
1314 /* CALLER MUST HOLD LOCK */
1316 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1317 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1322 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1323 abort(); /*NOTREACHED*/
1328 TempoMap::first_tempo ()
1330 TempoSection *t = 0;
1332 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1333 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1338 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1339 abort(); /*NOTREACHED*/
1343 TempoMap::recompute_tempos (Metrics& metrics)
1345 TempoSection* prev_ts = 0;
1347 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1350 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1352 if (t->position_lock_style() == AudioTime) {
1353 prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->beats_per_minute(), t->frame(), _frame_rate));
1354 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1357 prev_ts->set_c_func (prev_ts->compute_c_func_beat (t->beats_per_minute(), t->beat(), _frame_rate));
1358 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1367 /* tempos must be positioned correctly */
1369 TempoMap::recompute_meters (Metrics& metrics)
1371 MeterSection* meter = 0;
1373 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1374 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1375 if (meter->position_lock_style() == AudioTime) {
1376 /* a frame based meter has to have a 1|1|0 bbt */
1377 pair<double, BBT_Time> pr;
1378 BBT_Time const where (1, 1, 0);
1380 pr.first = beat_at_frame_locked (metrics, meter->frame());
1382 meter->set_beat (pr);
1384 meter->set_frame (frame_at_tick_locked (metrics, meter->tick()));
1391 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1393 /* CALLER MUST HOLD WRITE LOCK */
1397 /* we will actually stop once we hit
1404 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1407 /* silly call from Session::process() during startup
1412 recompute_tempos (metrics);
1413 recompute_meters (metrics);
1418 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1420 Glib::Threads::RWLock::ReaderLock lm (lock);
1421 TempoMetric m (first_meter(), first_tempo());
1423 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1424 at something, because we insert the default tempo and meter during
1425 TempoMap construction.
1427 now see if we can find better candidates.
1430 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1432 if ((*i)->frame() > frame) {
1445 /* XX meters only */
1447 TempoMap::metric_at (BBT_Time bbt) const
1449 Glib::Threads::RWLock::ReaderLock lm (lock);
1450 TempoMetric m (first_meter(), first_tempo());
1452 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1453 at something, because we insert the default tempo and meter during
1454 TempoMap construction.
1456 now see if we can find better candidates.
1459 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1461 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1462 BBT_Time section_start (mw->bbt());
1464 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1476 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1478 Glib::Threads::RWLock::ReaderLock lm (lock);
1484 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1487 bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, frame));
1491 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1493 Glib::Threads::RWLock::ReaderLock lm (lock);
1494 return bbt_to_beats_locked (_metrics, bbt);
1498 TempoMap::bbt_to_beats_locked (Metrics& metrics, Timecode::BBT_Time bbt)
1500 /* CALLER HOLDS READ LOCK */
1502 double accumulated_beats = 0.0;
1503 double accumulated_bars = 0.0;
1504 double bars_offset = 0.0;
1505 MeterSection* prev_ms = 0;
1507 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1509 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1510 double bars_to_m = 0.0;
1512 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1514 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1518 if (m->position_lock_style() == AudioTime) {
1519 accumulated_beats = 0.0;
1520 accumulated_bars = 0;
1521 bars_offset += bars_to_m;
1523 accumulated_beats += m->beat() - prev_ms->beat();
1524 accumulated_bars += bars_to_m;
1531 double const remaining_bars = ((bbt.bars - bars_offset) - 1) - accumulated_bars;
1532 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1533 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1539 TempoMap::beats_to_bbt (double beats)
1541 Glib::Threads::RWLock::ReaderLock lm (lock);
1542 return beats_to_bbt_locked (_metrics, beats);
1546 TempoMap::beats_to_bbt_locked (Metrics& metrics, double beats)
1548 /* CALLER HOLDS READ LOCK */
1550 MeterSection* prev_ms = 0;
1551 uint32_t accumulated_bars = 0;
1553 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1554 MeterSection* m = 0;
1556 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1559 if (m->beat() > beats) {
1560 /* this is the meter after the one our beat is on*/
1563 if(m->position_lock_style() == AudioTime) {
1564 accumulated_bars = 0;
1566 /* we need a whole number of bars. */
1567 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1575 double const beats_in_ms = beats - prev_ms->beat();
1576 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1577 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1578 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1579 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1583 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1584 ret.beats = (uint32_t) floor (remaining_beats);
1585 ret.bars = total_bars;
1587 /* 0 0 0 to 1 1 0 - based mapping*/
1591 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1593 ret.ticks -= BBT_Time::ticks_per_beat;
1596 if (ret.beats > prev_ms->divisions_per_bar()) {
1605 TempoMap::tick_at_frame_locked (const Metrics& metrics, framecnt_t frame) const
1607 /* HOLD (at least) THE READER LOCK */
1609 TempoSection* prev_ts = 0;
1610 double accumulated_ticks = 0.0;
1612 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1614 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1615 if ((prev_ts) && frame < t->frame()) {
1616 /*the previous ts is the one containing the frame */
1617 return prev_ts->tick_at_frame (frame, _frame_rate);
1620 accumulated_ticks = t->tick();
1625 /* treated as constant for this ts */
1626 double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1628 return ticks_in_section + accumulated_ticks;
1633 TempoMap::frame_at_tick_locked (const Metrics& metrics, double tick) const
1635 /* HOLD THE READER LOCK */
1637 const TempoSection* prev_ts = 0;
1638 double accumulated_ticks = 0.0;
1640 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1642 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1643 if (prev_ts && tick < t->tick()) {
1644 /* prev_ts is the one affecting us. */
1645 return prev_ts->frame_at_tick (tick, _frame_rate);
1648 accumulated_ticks = t->tick();
1652 /* must be treated as constant, irrespective of _type */
1653 double const ticks_in_section = tick - accumulated_ticks;
1654 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1656 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1662 TempoMap::beat_at_frame (framecnt_t frame) const
1664 Glib::Threads::RWLock::ReaderLock lm (lock);
1665 return tick_at_frame_locked (_metrics, frame) / BBT_Time::ticks_per_beat;
1669 TempoMap::beat_at_frame_locked (Metrics& metrics, framecnt_t frame) const
1671 return tick_at_frame_locked (metrics, frame) / BBT_Time::ticks_per_beat;
1675 TempoMap::frame_at_beat (double beat) const
1677 Glib::Threads::RWLock::ReaderLock lm (lock);
1678 return frame_at_tick_locked (_metrics, beat * BBT_Time::ticks_per_beat);
1682 TempoMap::frame_at_beat_locked (Metrics& metrics, double beat) const
1685 return frame_at_tick_locked (metrics, beat * BBT_Time::ticks_per_beat);
1689 TempoMap::frame_time (const BBT_Time& bbt)
1692 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1696 if (bbt.beats < 1) {
1697 throw std::logic_error ("beats are counted from one");
1699 Glib::Threads::RWLock::ReaderLock lm (lock);
1701 return frame_time_locked (_metrics, bbt);;
1705 TempoMap::frame_time_locked (Metrics& metrics, const BBT_Time& bbt)
1707 /* HOLD THE READER LOCK */
1709 framepos_t const ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1715 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1717 TempoSection* prev_ts = 0;
1719 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1721 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1723 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->beat() < prev_ts->beat())) {
1726 if (by_frame && t->frame() != prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate)) {
1729 if (!by_frame && fabs (t->beat() - prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate)) > 0.0000005) {
1730 std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
1731 " |error :" << t->beat() - prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << std::endl <<
1732 "|frame at beat :" << prev_ts->frame_at_beat (t->beat(), _frame_rate) << std::endl <<
1733 " |frame at tempo : " << prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
1745 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1747 TempoSection* prev_ts = 0;
1748 MetricSectionFrameSorter fcmp;
1749 MetricSectionSorter cmp;
1751 section->set_frame (frame);
1753 imaginary.sort (fcmp);
1754 if (section->position_lock_style() == MusicTime) {
1755 /* we're setting the frame */
1756 section->set_position_lock_style (AudioTime);
1757 recompute_map (imaginary);
1758 section->set_position_lock_style (MusicTime);
1760 recompute_map (imaginary);
1762 if (check_solved (imaginary, true)) {
1766 imaginary.sort (cmp);
1767 if (section->position_lock_style() == MusicTime) {
1768 /* we're setting the frame */
1769 section->set_position_lock_style (AudioTime);
1770 recompute_map (imaginary);
1771 section->set_position_lock_style (MusicTime);
1773 recompute_map (imaginary);
1775 if (check_solved (imaginary, true)) {
1781 std::cerr << "dumping frame order ------" << std::endl;;
1782 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1785 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1788 std::cerr << "current : " << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
1789 std::cerr << "previous : " << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
1790 std::cerr << "calculated : " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
1791 std::cerr << "------" << std::endl;
1797 std::cerr << "end dump ------" << std::endl;
1804 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& beat)
1806 MetricSectionSorter cmp;
1807 MetricSectionFrameSorter fcmp;
1809 section->set_beat (beat);
1811 imaginary.sort (cmp);
1812 if (section->position_lock_style() == AudioTime) {
1813 /* we're setting the beat */
1814 section->set_position_lock_style (MusicTime);
1815 recompute_map (imaginary);
1816 section->set_position_lock_style (AudioTime);
1818 recompute_map (imaginary);
1820 if (check_solved (imaginary, false)) {
1824 imaginary.sort (fcmp);
1825 if (section->position_lock_style() == AudioTime) {
1826 /* we're setting the beat */
1827 section->set_position_lock_style (MusicTime);
1828 recompute_map (imaginary);
1829 section->set_position_lock_style (AudioTime);
1831 recompute_map (imaginary);
1833 if (check_solved (imaginary, false)) {
1837 TempoSection* prev_ts = 0;
1838 std::cerr << "dumping beat order ------" << std::endl;
1839 std::cerr << "section : " << section->beats_per_minute() << " | " << section->beat() << " | " << section->frame() << std::endl;
1840 std::cerr << "------------------------------------" << std::endl;
1842 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1844 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1847 std::cerr << "current : " << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
1848 std::cerr << "prev : " << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
1849 std::cerr << "calculated : " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
1850 std::cerr << " ------" << std::endl;;
1856 std::cerr << "end dump ------";
1863 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& beat)
1865 MeterSection* prev_ms = 0;
1867 pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1868 section->set_beat (b_bbt);
1869 MetricSectionSorter cmp;
1870 imaginary.sort (cmp);
1872 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1874 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1876 if (m->beat() > beat){
1877 section->set_frame (frame_at_beat_locked (imaginary, beat));
1881 if (m->position_lock_style() == MusicTime) {
1882 m->set_frame (frame_at_beat_locked (imaginary, m->beat()));
1884 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (imaginary, m->frame()), BBT_Time (1, 1, 0));
1885 m->set_beat (b_bbt);
1892 MetricSectionFrameSorter fcmp;
1893 //imaginary.sort (fcmp);
1895 if (section->position_lock_style() == AudioTime) {
1896 /* we're setting the beat */
1897 section->set_position_lock_style (MusicTime);
1898 recompute_meters (imaginary);
1899 section->set_position_lock_style (AudioTime);
1901 recompute_meters (imaginary);
1907 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
1909 MeterSection* prev_ms = 0;
1911 section->set_frame (frame);
1912 MetricSectionFrameSorter fcmp;
1913 imaginary.sort (fcmp);
1915 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1917 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1919 if (m->frame() > frame){
1920 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (imaginary, frame), BBT_Time (1, 1, 0));
1921 section->set_beat (b_bbt);
1925 if (m->position_lock_style() == MusicTime) {
1926 m->set_frame (frame_at_beat_locked (imaginary, m->beat()));
1928 pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (imaginary, m->frame()), BBT_Time (1, 1, 0));
1929 m->set_beat (b_bbt);
1936 MetricSectionSorter cmp;
1937 imaginary.sort (cmp);
1939 if (section->position_lock_style() == MusicTime) {
1940 /* we're setting the frame */
1941 section->set_position_lock_style (AudioTime);
1942 recompute_map (imaginary);
1943 section->set_position_lock_style (MusicTime);
1945 recompute_map (imaginary);
1950 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1952 Glib::Threads::RWLock::ReaderLock lm (lock);
1954 Metrics::const_iterator i;
1955 TempoSection* first = 0;
1956 TempoSection* second = 0;
1958 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1961 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1963 if ((*i)->frame() > pos) {
1971 if (first && second) {
1972 double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
1973 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1974 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1976 return time_at_bbt - pos;
1978 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1980 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1984 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1986 return round_to_type (fr, dir, Bar);
1990 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1992 return round_to_type (fr, dir, Beat);
1996 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1998 Glib::Threads::RWLock::ReaderLock lm (lock);
2000 uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (_metrics, fr) + 0.5);
2001 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2002 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
2004 ticks -= beats * BBT_Time::ticks_per_beat;
2007 /* round to next (or same iff dir == RoundUpMaybe) */
2009 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2011 if (mod == 0 && dir == RoundUpMaybe) {
2012 /* right on the subdivision, which is fine, so do nothing */
2014 } else if (mod == 0) {
2015 /* right on the subdivision, so the difference is just the subdivision ticks */
2016 ticks += ticks_one_subdivisions_worth;
2019 /* not on subdivision, compute distance to next subdivision */
2021 ticks += ticks_one_subdivisions_worth - mod;
2024 if (ticks >= BBT_Time::ticks_per_beat) {
2025 ticks -= BBT_Time::ticks_per_beat;
2027 } else if (dir < 0) {
2029 /* round to previous (or same iff dir == RoundDownMaybe) */
2031 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2033 if (difference == 0 && dir == RoundDownAlways) {
2034 /* right on the subdivision, but force-rounding down,
2035 so the difference is just the subdivision ticks */
2036 difference = ticks_one_subdivisions_worth;
2039 if (ticks < difference) {
2040 ticks = BBT_Time::ticks_per_beat - ticks;
2042 ticks -= difference;
2046 /* round to nearest */
2049 /* compute the distance to the previous and next subdivision */
2051 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2053 /* closer to the next subdivision, so shift forward */
2055 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2057 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2059 if (ticks > BBT_Time::ticks_per_beat) {
2061 ticks -= BBT_Time::ticks_per_beat;
2062 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2065 } else if (rem > 0) {
2067 /* closer to previous subdivision, so shift backward */
2071 /* can't go backwards past zero, so ... */
2074 /* step back to previous beat */
2076 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2077 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2079 ticks = lrint (ticks - rem);
2080 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2083 /* on the subdivision, do nothing */
2086 return frame_at_tick_locked (_metrics, (beats * BBT_Time::ticks_per_beat) + ticks);
2090 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2092 Glib::Threads::RWLock::ReaderLock lm (lock);
2094 double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2096 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2101 /* find bar previous to 'frame' */
2104 return frame_time (bbt);
2106 } else if (dir > 0) {
2107 /* find bar following 'frame' */
2111 return frame_time (bbt);
2113 /* true rounding: find nearest bar */
2114 framepos_t raw_ft = frame_time (bbt);
2117 framepos_t prev_ft = frame_time (bbt);
2119 framepos_t next_ft = frame_time (bbt);
2121 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2132 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2133 } else if (dir > 0) {
2134 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2136 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2145 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2146 framepos_t lower, framepos_t upper)
2148 Glib::Threads::RWLock::ReaderLock lm (lock);
2149 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame_locked (_metrics, upper));
2150 uint32_t cnt = (uint32_t) ceil (beat_at_frame_locked (_metrics, lower));
2152 while (cnt <= upper_beat) {
2153 framecnt_t const pos = frame_at_beat_locked (_metrics, cnt);
2154 MeterSection const meter = meter_section_at (pos);
2156 Tempo const tempo = tempo_at (pos);
2157 BBT_Time const bbt = beats_to_bbt_locked (_metrics, (double) cnt);
2159 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
2165 TempoMap::tempo_section_at (framepos_t frame) const
2167 Glib::Threads::RWLock::ReaderLock lm (lock);
2169 Metrics::const_iterator i;
2170 TempoSection* prev = 0;
2172 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2175 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2177 if ((*i)->frame() > frame) {
2187 abort(); /*NOTREACHED*/
2193 /* don't use this to calculate length (the tempo is only correct for this frame).
2194 do that stuff based on the beat_at_frame and frame_at_beat api
2197 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2199 Glib::Threads::RWLock::ReaderLock lm (lock);
2201 const TempoSection* ts_at = &tempo_section_at (frame);
2202 const TempoSection* ts_after = 0;
2203 Metrics::const_iterator i;
2205 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2208 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2210 if ((*i)->frame() > frame) {
2218 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2220 /* must be treated as constant tempo */
2221 return ts_at->frames_per_beat (_frame_rate);
2225 TempoMap::tempo_at (framepos_t frame) const
2227 Glib::Threads::RWLock::ReaderLock lm (lock);
2229 TempoMetric m (metric_at (frame));
2230 TempoSection* prev_ts = 0;
2232 Metrics::const_iterator i;
2234 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2236 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2237 if ((prev_ts) && t->frame() > frame) {
2238 /* this is the one past frame */
2239 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
2240 Tempo const ret_tempo (ret, m.tempo().note_type ());
2251 TempoMap::meter_section_at (framepos_t frame) const
2253 Glib::Threads::RWLock::ReaderLock lm (lock);
2255 Metrics::const_iterator i;
2256 MeterSection* prev = 0;
2258 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2261 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2263 if ((*i)->frame() > frame) {
2273 abort(); /*NOTREACHED*/
2280 TempoMap::meter_section_at (double beat) const
2282 Glib::Threads::RWLock::ReaderLock lm (lock);
2284 Metrics::const_iterator i;
2285 MeterSection* prev = 0;
2287 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2290 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2292 if ((*i)->beat() > beat) {
2302 abort(); /*NOTREACHED*/
2309 TempoMap::meter_at (framepos_t frame) const
2311 TempoMetric m (metric_at (frame));
2316 TempoMap::get_state ()
2318 Metrics::const_iterator i;
2319 XMLNode *root = new XMLNode ("TempoMap");
2322 Glib::Threads::RWLock::ReaderLock lm (lock);
2323 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2324 root->add_child_nocopy ((*i)->get_state());
2332 TempoMap::set_state (const XMLNode& node, int /*version*/)
2335 Glib::Threads::RWLock::WriterLock lm (lock);
2338 XMLNodeConstIterator niter;
2339 Metrics old_metrics (_metrics);
2340 MeterSection* last_meter = 0;
2343 nlist = node.children();
2345 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2346 XMLNode* child = *niter;
2348 if (child->name() == TempoSection::xml_state_node_name) {
2351 TempoSection* ts = new TempoSection (*child);
2352 _metrics.push_back (ts);
2354 if (ts->bar_offset() < 0.0) {
2356 //ts->update_bar_offset_from_bbt (*last_meter);
2361 catch (failed_constructor& err){
2362 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2363 _metrics = old_metrics;
2367 } else if (child->name() == MeterSection::xml_state_node_name) {
2370 MeterSection* ms = new MeterSection (*child);
2371 _metrics.push_back (ms);
2375 catch (failed_constructor& err) {
2376 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2377 _metrics = old_metrics;
2383 if (niter == nlist.end()) {
2384 MetricSectionSorter cmp;
2385 _metrics.sort (cmp);
2387 /* check for legacy sessions where bbt was the base musical unit for tempo */
2388 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2389 MeterSection* prev_ms;
2390 TempoSection* prev_ts;
2391 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2392 if (prev_ms->beat() < 0.0) {
2393 /*XX we cannot possibly make this work??. */
2394 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());
2395 prev_ms->set_beat (start);
2397 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2398 if (prev_ts->beat() < 0.0) {
2399 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);
2400 prev_ts->set_beat (start);
2405 /* check for multiple tempo/meters at the same location, which
2406 ardour2 somehow allowed.
2409 Metrics::iterator prev = _metrics.end();
2410 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2411 if (prev != _metrics.end()) {
2413 MeterSection* prev_ms;
2415 TempoSection* prev_ts;
2416 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2417 if (prev_ms->beat() == ms->beat()) {
2418 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2419 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2422 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2423 if (prev_ts->beat() == ts->beat()) {
2424 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2425 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2433 recompute_map (_metrics);
2436 PropertyChanged (PropertyChange ());
2442 TempoMap::dump (std::ostream& o) const
2444 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2445 const MeterSection* m;
2446 const TempoSection* t;
2448 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2450 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2451 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? "
2452 << t->movable() << ')' << endl;
2453 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2454 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2455 << " (movable? " << m->movable() << ')' << endl;
2461 TempoMap::n_tempos() const
2463 Glib::Threads::RWLock::ReaderLock lm (lock);
2466 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2467 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2476 TempoMap::n_meters() const
2478 Glib::Threads::RWLock::ReaderLock lm (lock);
2481 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2482 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2491 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2494 Glib::Threads::RWLock::WriterLock lm (lock);
2495 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2496 if ((*i)->frame() >= where && (*i)->movable ()) {
2497 (*i)->set_frame ((*i)->frame() + amount);
2501 /* now reset the BBT time of all metrics, based on their new
2502 * audio time. This is the only place where we do this reverse
2506 Metrics::iterator i;
2507 const MeterSection* meter;
2508 const TempoSection* tempo;
2512 meter = &first_meter ();
2513 tempo = &first_tempo ();
2518 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2521 MetricSection* prev = 0;
2523 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2526 //TempoMetric metric (*meter, *tempo);
2527 MeterSection* ms = const_cast<MeterSection*>(meter);
2528 TempoSection* ts = const_cast<TempoSection*>(tempo);
2531 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2532 ts->set_beat (t->beat());
2534 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2535 ts->set_beat (m->beat());
2537 ts->set_frame (prev->frame());
2541 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2542 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2543 ms->set_beat (start);
2545 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2546 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (_metrics, t->beat()));
2547 ms->set_beat (start);
2549 ms->set_frame (prev->frame());
2553 // metric will be at frames=0 bbt=1|1|0 by default
2554 // which is correct for our purpose
2557 // cerr << bbt << endl;
2559 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2560 t->set_beat (beat_at_frame_locked (_metrics, m->frame()));
2562 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2563 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2564 bbt_time (m->frame(), bbt);
2566 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2572 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2573 /* round up to next beat */
2579 if (bbt.beats != 1) {
2580 /* round up to next bar */
2585 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
2586 m->set_beat (start);
2588 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2590 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2591 abort(); /*NOTREACHED*/
2597 recompute_map (_metrics);
2601 PropertyChanged (PropertyChange ());
2604 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2608 std::list<MetricSection*> metric_kill_list;
2610 TempoSection* last_tempo = NULL;
2611 MeterSection* last_meter = NULL;
2612 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2613 bool meter_after = false; // is there a meter marker likewise?
2615 Glib::Threads::RWLock::WriterLock lm (lock);
2616 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2617 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2618 metric_kill_list.push_back(*i);
2619 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2622 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2626 else if ((*i)->frame() >= where) {
2627 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2628 (*i)->set_frame ((*i)->frame() - amount);
2629 if ((*i)->frame() == where) {
2630 // marker was immediately after end of range
2631 tempo_after = dynamic_cast<TempoSection*> (*i);
2632 meter_after = dynamic_cast<MeterSection*> (*i);
2638 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2639 if (last_tempo && !tempo_after) {
2640 metric_kill_list.remove(last_tempo);
2641 last_tempo->set_frame(where);
2644 if (last_meter && !meter_after) {
2645 metric_kill_list.remove(last_meter);
2646 last_meter->set_frame(where);
2650 //remove all the remaining metrics
2651 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2652 _metrics.remove(*i);
2657 recompute_map (_metrics);
2660 PropertyChanged (PropertyChange ());
2664 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2665 * pos can be -ve, if required.
2668 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2670 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2673 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2675 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2677 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2680 /** Add the BBT interval op to pos and return the result */
2682 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2684 cerr << "framepos_plus_bbt - untested" << endl;
2685 Glib::Threads::RWLock::ReaderLock lm (lock);
2687 Metrics::const_iterator i;
2688 const MeterSection* meter;
2689 const MeterSection* m;
2690 const TempoSection* tempo;
2691 const TempoSection* next_tempo = 0;
2692 const TempoSection* t;
2693 double frames_per_beat;
2694 framepos_t effective_pos = max (pos, (framepos_t) 0);
2696 meter = &first_meter ();
2697 tempo = &first_tempo ();
2702 /* find the starting metrics for tempo & meter */
2704 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2706 if ((*i)->frame() > effective_pos) {
2710 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2712 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2717 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2718 if ((*i)->frame() > effective_pos) {
2719 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2728 meter -> the Meter for "pos"
2729 tempo -> the Tempo for "pos"
2730 next_tempo -> the Tempo after "pos" or 0
2731 i -> for first new metric after "pos", possibly metrics.end()
2734 /* now comes the complicated part. we have to add one beat a time,
2735 checking for a new metric on every beat.
2739 /* fpb is used for constant tempo */
2740 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2747 /* check if we need to use a new metric section: has adding frames moved us
2748 to or after the start of the next metric section? in which case, use it.
2751 if (i != _metrics.end()) {
2752 if ((*i)->frame() <= pos) {
2754 /* about to change tempo or meter, so add the
2755 * number of frames for the bars we've just
2756 * traversed before we change the
2757 * frames_per_beat value.
2760 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2765 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2767 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2772 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2774 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2778 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2785 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2787 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2794 /* given the current meter, have we gone past the end of the bar ? */
2799 /* check if we need to use a new metric section: has adding frames moved us
2800 to or after the start of the next metric section? in which case, use it.
2803 if (i != _metrics.end()) {
2804 if ((*i)->frame() <= pos) {
2806 /* about to change tempo or meter, so add the
2807 * number of frames for the beats we've just
2808 * traversed before we change the
2809 * frames_per_beat value.
2812 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2817 pos += tempo->frame_at_beat (beats, _frame_rate);
2819 pos += llrint (beats * frames_per_beat);
2824 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2826 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2830 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2836 pos += tempo->frame_at_beat (beats, _frame_rate);
2838 pos += llrint (beats * frames_per_beat);
2842 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2849 /** Count the number of beats that are equivalent to distance when going forward,
2853 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2855 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2859 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2865 operator<< (std::ostream& o, const Meter& m) {
2866 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2870 operator<< (std::ostream& o, const Tempo& t) {
2871 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2875 operator<< (std::ostream& o, const MetricSection& section) {
2877 o << "MetricSection @ " << section.frame() << ' ';
2879 const TempoSection* ts;
2880 const MeterSection* ms;
2882 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2883 o << *((const Tempo*) ts);
2884 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2885 //o << *((const Meter*) ms);