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;
84 if ((prop = node.property ("start")) != 0) {
85 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
89 /* legacy session - start used to be in bbt*/
95 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
99 if ((prop = node.property ("beat")) != 0) {
100 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
101 error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
106 error << _("TempoSection XML node has no \"beat\" property") << endmsg;
109 if ((prop = node.property ("beats-per-minute")) == 0) {
110 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
111 throw failed_constructor();
114 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
115 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
116 throw failed_constructor();
119 if ((prop = node.property ("note-type")) == 0) {
120 /* older session, make note type be quarter by default */
123 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
124 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
125 throw failed_constructor();
129 if ((prop = node.property ("movable")) == 0) {
130 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
131 throw failed_constructor();
134 set_movable (string_is_affirmative (prop->value()));
136 if ((prop = node.property ("bar-offset")) == 0) {
139 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
140 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
141 throw failed_constructor();
145 if ((prop = node.property ("tempo-type")) == 0) {
148 _type = Type (string_2_enum (prop->value(), _type));
153 TempoSection::get_state() const
155 XMLNode *root = new XMLNode (xml_state_node_name);
159 snprintf (buf, sizeof (buf), "%f", beat());
160 root->add_property ("beat", buf);
161 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
162 root->add_property ("beats-per-minute", buf);
163 snprintf (buf, sizeof (buf), "%f", _note_type);
164 root->add_property ("note-type", buf);
165 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
166 // root->add_property ("bar-offset", buf);
167 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
168 root->add_property ("movable", buf);
169 root->add_property ("tempo-type", enum_2_string (_type));
176 TempoSection::update_bar_offset_from_bbt (const Meter& m)
178 _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
179 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
181 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
185 TempoSection::set_type (Type type)
190 /** returns the tempo at the zero-based (relative to tempo section) frame.
193 TempoSection::tempo_at_frame (framepos_t frame, framecnt_t frame_rate) const
196 if (_type == Constant) {
197 return beats_per_minute();
200 return tick_tempo_at_time (frame_to_minute (frame, frame_rate)) / BBT_Time::ticks_per_beat;
203 /** returns the zero-based frame (relative to tempo section)
204 where the tempo occurs.
207 TempoSection::frame_at_tempo (double bpm, framecnt_t frame_rate) const
209 if (_type == Constant) {
213 return minute_to_frame (time_at_tick_tempo (bpm * BBT_Time::ticks_per_beat), frame_rate);
216 /** returns the zero-based tick (relative to tempo section)
217 where the zero-based frame (relative to tempo section)
221 TempoSection::tick_at_frame (framepos_t frame, framecnt_t frame_rate) const
223 if (_type == Constant) {
224 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
227 return tick_at_time (frame_to_minute (frame, frame_rate));
230 /** returns the zero-based frame (relative to tempo section)
231 where the zero-based tick (relative to tempo section)
235 TempoSection::frame_at_tick (double tick, framecnt_t frame_rate) const
237 if (_type == Constant) {
238 return (framepos_t) floor ((tick / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
241 return minute_to_frame (time_at_tick (tick), frame_rate);
244 /** returns the zero-based beat (relative to tempo section)
245 where the zero-based frame (relative to tempo section)
249 TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
251 return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
254 /** returns the zero-based frame (relative to tempo section start frame)
255 where the zero-based beat (relative to tempo section start)
260 TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
262 return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
270 Tt----|-----------------*|
271 Ta----|--------------|* |
277 _______________|___|____
278 time a t (next tempo)
281 Duration in beats at time a is the integral of some Tempo function.
282 In our case, the Tempo function (Tempo at time t) is
285 where c is the function constant
290 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
291 b(t) = T0(e^(ct) - 1) / c
293 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:
294 t(b) = log((cb / T0) + 1) / c
296 The time t at which Tempo T occurs is a as above:
297 t(T) = log(T / T0) / c
299 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
300 Our problem is that we usually don't know t.
301 We usually do know the duration in beats between this and the next tempo section.
302 Where t = a (i.e. when a is equal to the time of the next tempo section), we can solve t in terms of
303 beat duration and our two tempos.
304 A bit of scribbling with the beat function gives us:
305 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
307 By substituting our expanded t as a in the c function above, we see that our problem is reduced to:
308 c = T0 (e^(log (Ta / T0)) - 1) / b
310 We can now evaluate and store c for use in beat, time and tempo calculations until the following tempo section
311 (the one that defines c in conjunction with this one) is changed or moved.
313 Most of this stuff is taken from this paper:
316 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
319 Zurich University of Arts
320 Institute for Computer Music and Sound Technology
322 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
326 /* set this ramp's function constant using the end tempo and duration in beats of some later tempo section*/
328 TempoSection::set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
330 double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
331 _c_func = ticks_per_minute() * (exp (log_tempo_ratio) - 1) / (end_beat * BBT_Time::ticks_per_beat);
334 /* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from this tempo section */
336 TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
338 return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
342 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
344 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
348 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
350 return (frame / (double) frame_rate) / 60.0;
353 /* position function */
355 TempoSection::a_func (double end_tpm, double c_func) const
357 return log (end_tpm / ticks_per_minute()) / c_func;
360 /*function constant*/
362 TempoSection::c_func (double end_tpm, double end_time) const
364 return log (end_tpm / ticks_per_minute()) / end_time;
367 /* tempo in tpm at time in minutes */
369 TempoSection::tick_tempo_at_time (double time) const
371 return exp (_c_func * time) * ticks_per_minute();
374 /* time in minutes at tempo in tpm */
376 TempoSection::time_at_tick_tempo (double tick_tempo) const
378 return log (tick_tempo / ticks_per_minute()) / _c_func;
381 /* tick at time in minutes */
383 TempoSection::tick_at_time (double time) const
385 return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
388 /* time in minutes at tick */
390 TempoSection::time_at_tick (double tick) const
392 return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
395 /* beat at time in minutes */
397 TempoSection::beat_at_time (double time) const
399 return tick_at_time (time) / BBT_Time::ticks_per_beat;
402 /* time in munutes at beat */
404 TempoSection::time_at_beat (double beat) const
406 return time_at_tick (beat * BBT_Time::ticks_per_beat);
410 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
414 if (_bar_offset < 0.0) {
421 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
422 new_beat = ticks / BBT_Time::ticks_per_beat;
424 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
425 _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
430 /***********************************************************************/
432 const string MeterSection::xml_state_node_name = "Meter";
434 MeterSection::MeterSection (const XMLNode& node)
435 : MetricSection (0.0), Meter (TempoMap::default_meter())
437 XMLProperty const * prop;
440 const XMLProperty *prop;
443 pair<double, BBT_Time> start;
445 if ((prop = node.property ("start")) != 0) {
446 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
450 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
452 /* legacy session - start used to be in bbt*/
456 error << _("MeterSection XML node has no \"start\" property") << endmsg;
459 if ((prop = node.property ("beat")) != 0) {
460 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
461 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
464 error << _("MeterSection XML node has no \"beat\" property") << endmsg;
469 if ((prop = node.property ("bbt")) == 0) {
470 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
471 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
475 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
476 throw failed_constructor();
483 /* beats-per-bar is old; divisions-per-bar is new */
485 if ((prop = node.property ("divisions-per-bar")) == 0) {
486 if ((prop = node.property ("beats-per-bar")) == 0) {
487 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
488 throw failed_constructor();
492 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
493 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
494 throw failed_constructor();
497 if ((prop = node.property ("note-type")) == 0) {
498 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
499 throw failed_constructor();
502 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
503 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
504 throw failed_constructor();
507 if ((prop = node.property ("movable")) == 0) {
508 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
509 throw failed_constructor();
512 set_movable (string_is_affirmative (prop->value()));
516 MeterSection::get_state() const
518 XMLNode *root = new XMLNode (xml_state_node_name);
522 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
526 root->add_property ("bbt", buf);
527 snprintf (buf, sizeof (buf), "%lf", beat());
528 root->add_property ("beat", buf);
529 snprintf (buf, sizeof (buf), "%f", _note_type);
530 root->add_property ("note-type", buf);
531 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
532 root->add_property ("divisions-per-bar", buf);
533 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
534 root->add_property ("movable", buf);
539 /***********************************************************************/
541 struct MetricSectionSorter {
542 bool operator() (const MetricSection* a, const MetricSection* b) {
543 return a->beat() < b->beat();
547 struct MetricSectionFrameSorter {
548 bool operator() (const MetricSection* a, const MetricSection* b) {
549 return a->frame() < b->frame();
553 TempoMap::TempoMap (framecnt_t fr)
562 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
563 MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
565 t->set_movable (false);
566 m->set_movable (false);
568 /* note: frame time is correct (zero) for both of these */
570 metrics.push_back (t);
571 metrics.push_back (m);
575 TempoMap::~TempoMap ()
580 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
582 bool removed = false;
585 Glib::Threads::RWLock::WriterLock lm (lock);
586 if ((removed = remove_tempo_locked (tempo))) {
587 if (complete_operation) {
588 recompute_map (true);
593 if (removed && complete_operation) {
594 PropertyChanged (PropertyChange ());
599 TempoMap::remove_tempo_locked (const TempoSection& tempo)
603 for (i = metrics.begin(); i != metrics.end(); ++i) {
604 if (dynamic_cast<TempoSection*> (*i) != 0) {
605 if (tempo.frame() == (*i)->frame()) {
606 if ((*i)->movable()) {
618 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
620 bool removed = false;
623 Glib::Threads::RWLock::WriterLock lm (lock);
624 if ((removed = remove_meter_locked (tempo))) {
625 if (complete_operation) {
626 recompute_map (true);
631 if (removed && complete_operation) {
632 PropertyChanged (PropertyChange ());
637 TempoMap::remove_meter_locked (const MeterSection& tempo)
641 for (i = metrics.begin(); i != metrics.end(); ++i) {
642 if (dynamic_cast<MeterSection*> (*i) != 0) {
643 if (tempo.frame() == (*i)->frame()) {
644 if ((*i)->movable()) {
656 TempoMap::do_insert (MetricSection* section)
658 bool need_add = true;
660 /* we only allow new meters to be inserted on beat 1 of an existing
664 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
665 assert (m->bbt().ticks == 0);
667 /* we need to (potentially) update the BBT times of tempo
668 sections based on this new meter.
671 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
673 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
674 corrected.second.beats = 1;
675 corrected.second.ticks = 0;
676 corrected.first = bbt_to_beats_locked (corrected.second);
677 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
678 m->bbt(), corrected.second) << endmsg;
679 m->set_beat (corrected);
685 /* Look for any existing MetricSection that is of the same type and
686 in the same bar as the new one, and remove it before adding
687 the new one. Note that this means that if we find a matching,
688 existing section, we can break out of the loop since we're
689 guaranteed that there is only one such match.
692 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
694 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
695 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
697 if (tempo && insert_tempo) {
701 if (tempo->beat() == insert_tempo->beat()) {
703 if (!tempo->movable()) {
705 /* can't (re)move this section, so overwrite
706 * its data content (but not its properties as
710 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
718 } else if (!tempo && !insert_tempo) {
721 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
722 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
723 if (meter->beat() == insert_meter->beat()) {
725 if (!meter->movable()) {
727 /* can't (re)move this section, so overwrite
728 * its data content (but not its properties as
732 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
742 /* non-matching types, so we don't care */
746 /* Add the given MetricSection, if we didn't just reset an existing
751 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
752 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
756 for (i = metrics.begin(); i != metrics.end(); ++i) {
757 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
759 if (meter && meter->beat() > insert_meter->beat()) {
763 } else if (insert_tempo) {
764 for (i = metrics.begin(); i != metrics.end(); ++i) {
765 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
768 if (tempo->beat() > insert_tempo->beat()) {
775 metrics.insert (i, section);
780 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
783 Glib::Threads::RWLock::WriterLock lm (lock);
784 TempoSection& first (first_tempo());
785 if (ts.beat() != first.beat()) {
786 remove_tempo_locked (ts);
787 add_tempo_locked (tempo, where, true, type);
789 first.set_type (type);
791 /* cannot move the first tempo section */
792 *static_cast<Tempo*>(&first) = tempo;
793 recompute_map (false);
798 PropertyChanged (PropertyChange ());
802 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double beat_where)
805 Glib::Threads::RWLock::WriterLock lm (lock);
807 /* currently this is always done in audio time */
808 //if (ts.position_lock_style() == MusicTime) {
811 ts.set_beat (beat_where);
812 MetricSectionSorter cmp;
816 ts.set_frame (frame);
817 MetricSectionFrameSorter fcmp;
820 Metrics::const_iterator i;
821 TempoSection* prev_ts = 0;
822 TempoSection* next_ts = 0;
824 for (i = metrics.begin(); i != metrics.end(); ++i) {
826 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
828 if (t->frame() >= frame) {
836 for (i = metrics.begin(); i != metrics.end(); ++i) {
838 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
840 if (t->frame() > frame) {
848 /* set the start beat - we need to reset the function constant before beat calculations make sense*/
849 prev_ts->set_c_func (prev_ts->compute_c_func (ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate));
851 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), _frame_rate);
852 double beats = beats_to_ts + prev_ts->beat();
855 if (next_ts->beat() < beats) {
856 /* with frame-based editing, it is possible to get in a
857 situation where if the tempo was placed at the mouse pointer frame,
858 the following music-based tempo would jump to an earlier frame,
859 changing the beat beat of the moved tempo.
860 in this case, we have to do some beat-based comparison TODO
862 } else if (prev_ts->beat() > beats) {
863 ts.set_beat (prev_ts->beat());
872 MetricSectionSorter cmp;
877 recompute_map (false);
880 MetricPositionChanged (); // Emit Signal
884 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
887 Glib::Threads::RWLock::WriterLock lm (lock);
888 add_tempo_locked (tempo, where, true, type);
892 PropertyChanged (PropertyChange ());
896 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
898 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
903 recompute_map (false);
908 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
911 Glib::Threads::RWLock::WriterLock lm (lock);
912 MeterSection& first (first_meter());
913 if (ms.beat() != first.beat()) {
914 remove_meter_locked (ms);
915 add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
917 /* cannot move the first meter section */
918 *static_cast<Meter*>(&first) = meter;
919 recompute_map (true);
923 PropertyChanged (PropertyChange ());
927 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
930 Glib::Threads::RWLock::WriterLock lm (lock);
931 add_meter_locked (meter, beat, where, true);
936 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
941 PropertyChanged (PropertyChange ());
945 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
947 /* a new meter always starts a new bar on the first beat. so
948 round the start time appropriately. remember that
949 `where' is based on the existing tempo map, not
950 the result after we insert the new meter.
954 if (where.beats != 1) {
959 /* new meters *always* start on a beat. */
962 do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
965 recompute_map (true);
971 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
973 Tempo newtempo (beats_per_minute, note_type);
976 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
977 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
979 Glib::Threads::RWLock::WriterLock lm (lock);
980 *((Tempo*) t) = newtempo;
981 recompute_map (false);
983 PropertyChanged (PropertyChange ());
990 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
992 Tempo newtempo (beats_per_minute, note_type);
998 /* find the TempoSection immediately preceding "where"
1001 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
1003 if ((*i)->frame() > where) {
1009 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1019 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1029 Glib::Threads::RWLock::WriterLock lm (lock);
1030 /* cannot move the first tempo section */
1031 *((Tempo*)prev) = newtempo;
1032 recompute_map (false);
1035 PropertyChanged (PropertyChange ());
1039 TempoMap::first_meter () const
1041 const MeterSection *m = 0;
1043 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1044 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1049 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1050 abort(); /*NOTREACHED*/
1055 TempoMap::first_meter ()
1057 MeterSection *m = 0;
1059 /* CALLER MUST HOLD LOCK */
1061 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1062 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1067 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1068 abort(); /*NOTREACHED*/
1073 TempoMap::first_tempo () const
1075 const TempoSection *t = 0;
1077 /* CALLER MUST HOLD LOCK */
1079 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1080 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1085 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1086 abort(); /*NOTREACHED*/
1091 TempoMap::first_tempo ()
1093 TempoSection *t = 0;
1095 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1096 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1101 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1102 abort(); /*NOTREACHED*/
1107 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1109 /* CALLER MUST HOLD WRITE LOCK */
1113 /* we will actually stop once we hit
1120 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1123 /* silly call from Session::process() during startup
1128 Metrics::const_iterator i;
1130 TempoSection* prev_ts = 0;
1132 for (i = metrics.begin(); i != metrics.end(); ++i) {
1135 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1138 if (prev_ts->type() == TempoSection::Ramp) {
1139 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat() - prev_ts->beat(), _frame_rate);
1140 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), _frame_rate) + prev_ts->frame());
1142 double const ticks_relative_to_prev = (t->beat() - prev_ts->beat()) * BBT_Time::ticks_per_beat;
1143 framecnt_t const duration = (framecnt_t) floor (ticks_relative_to_prev * prev_ts->frames_per_beat (_frame_rate)
1144 * BBT_Time::ticks_per_beat);
1145 prev_ts->set_c_func (0.0);
1146 t->set_frame (duration + prev_ts->frame());
1153 Metrics::const_iterator mi;
1154 MeterSection* meter = 0;
1156 for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1157 /* We now have the tempo map set. use it to set meter positions.*/
1158 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1159 meter->set_frame (frame_at_tick (meter->beat() * BBT_Time::ticks_per_beat));
1166 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1168 Glib::Threads::RWLock::ReaderLock lm (lock);
1169 TempoMetric m (first_meter(), first_tempo());
1171 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1172 at something, because we insert the default tempo and meter during
1173 TempoMap construction.
1175 now see if we can find better candidates.
1178 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1180 if ((*i)->frame() > frame) {
1193 /* XX meters only */
1195 TempoMap::metric_at (BBT_Time bbt) const
1197 Glib::Threads::RWLock::ReaderLock lm (lock);
1198 TempoMetric m (first_meter(), first_tempo());
1200 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1201 at something, because we insert the default tempo and meter during
1202 TempoMap construction.
1204 now see if we can find better candidates.
1207 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1209 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1210 BBT_Time section_start (mw->bbt());
1212 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1224 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1226 Glib::Threads::RWLock::ReaderLock lm (lock);
1232 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1235 bbt = beats_to_bbt_locked (beat_at_frame (frame));
1239 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1241 Glib::Threads::RWLock::ReaderLock lm (lock);
1242 return bbt_to_beats_locked (bbt);
1246 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1248 /* CALLER HOLDS READ LOCK */
1250 double accumulated_beats = 0.0;
1251 double accumulated_bars = 0.0;
1252 MeterSection* prev_ms = 0;
1254 Metrics::const_iterator i;
1256 for (i = metrics.begin(); i != metrics.end(); ++i) {
1258 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1259 double bars_to_m = 0.0;
1261 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1263 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1267 accumulated_beats += m->beat() - prev_ms->beat();
1268 accumulated_bars += bars_to_m;
1274 double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1275 double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1276 double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1281 TempoMap::beats_to_bbt (double beats)
1283 Glib::Threads::RWLock::ReaderLock lm (lock);
1284 return beats_to_bbt_locked (beats);
1288 TempoMap::beats_to_bbt_locked (double beats)
1290 /* CALLER HOLDS READ LOCK */
1292 MeterSection* prev_ms = 0;
1293 uint32_t accumulated_bars = 0;
1295 Metrics::const_iterator i;
1297 for (i = metrics.begin(); i != metrics.end(); ++i) {
1298 MeterSection* m = 0;
1300 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1302 if (beats < m->beat()) {
1303 /* this is the meter after the one our beat is on*/
1308 /* we need a whole number of bars. */
1309 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1316 double const beats_in_ms = beats - prev_ms->beat();
1317 uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1318 uint32_t const total_bars = bars_in_ms + accumulated_bars;
1319 double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1320 double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1324 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1325 ret.beats = (uint32_t) floor (remaining_beats);
1326 ret.bars = total_bars;
1328 /* 0 0 0 to 1 1 0 - based mapping*/
1332 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1334 ret.ticks -= BBT_Time::ticks_per_beat;
1337 if (ret.beats > prev_ms->divisions_per_bar()) {
1346 TempoMap::tick_at_frame (framecnt_t frame) const
1348 /* HOLD (at least) THE READER LOCK */
1350 Metrics::const_iterator i;
1351 TempoSection* prev_ts = 0;
1352 double accumulated_ticks = 0.0;
1354 for (i = metrics.begin(); i != metrics.end(); ++i) {
1357 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1359 if ((prev_ts) && frame < t->frame()) {
1360 /*the previous ts is the one containing the frame */
1362 framepos_t const time = frame - prev_ts->frame();
1364 return prev_ts->tick_at_frame (time, _frame_rate) + accumulated_ticks;
1367 if (prev_ts && t->frame() > prev_ts->frame()) {
1368 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1375 /* treated as constant for this ts */
1376 framecnt_t const frames_in_section = frame - prev_ts->frame();
1377 double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1379 return ticks_in_section + accumulated_ticks;
1384 TempoMap::frame_at_tick (double tick) const
1386 /* HOLD THE READER LOCK */
1388 double accumulated_ticks = 0.0;
1389 double accumulated_ticks_to_prev = 0.0;
1390 const TempoSection* prev_ts = 0;
1392 Metrics::const_iterator i;
1394 for (i = metrics.begin(); i != metrics.end(); ++i) {
1396 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1398 if (prev_ts && t->frame() > prev_ts->frame()) {
1399 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1402 if (prev_ts && tick < accumulated_ticks) {
1403 /* prev_ts is the one affecting us. */
1405 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1407 return prev_ts->frame_at_tick (ticks_in_section, _frame_rate) + prev_ts->frame();
1409 accumulated_ticks_to_prev = accumulated_ticks;
1413 /* must be treated as constant, irrespective of _type */
1414 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1415 double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1417 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1423 TempoMap::beat_at_frame (framecnt_t frame) const
1425 Glib::Threads::RWLock::ReaderLock lm (lock);
1427 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1431 TempoMap::frame_at_beat (double beat) const
1433 Glib::Threads::RWLock::ReaderLock lm (lock);
1435 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1439 TempoMap::frame_time (const BBT_Time& bbt)
1442 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1446 if (bbt.beats < 1) {
1447 throw std::logic_error ("beats are counted from one");
1449 Glib::Threads::RWLock::ReaderLock lm (lock);
1451 framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
1458 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1461 Glib::Threads::RWLock::ReaderLock lm (lock);
1463 Metrics::const_iterator i;
1464 TempoSection* first = 0;
1465 TempoSection* second = 0;
1467 for (i = metrics.begin(); i != metrics.end(); ++i) {
1470 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1472 if ((*i)->frame() > pos) {
1480 if (first && second) {
1481 framepos_t const time = pos - first->frame();
1482 double const tick_at_time = first->tick_at_frame (time, _frame_rate);
1483 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1484 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1486 return time_at_bbt - time;
1489 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1490 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1494 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1496 return round_to_type (fr, dir, Bar);
1500 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1502 return round_to_type (fr, dir, Beat);
1506 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1508 Glib::Threads::RWLock::ReaderLock lm (lock);
1510 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1511 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1512 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1514 ticks -= beats * BBT_Time::ticks_per_beat;
1517 /* round to next (or same iff dir == RoundUpMaybe) */
1519 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1521 if (mod == 0 && dir == RoundUpMaybe) {
1522 /* right on the subdivision, which is fine, so do nothing */
1524 } else if (mod == 0) {
1525 /* right on the subdivision, so the difference is just the subdivision ticks */
1526 ticks += ticks_one_subdivisions_worth;
1529 /* not on subdivision, compute distance to next subdivision */
1531 ticks += ticks_one_subdivisions_worth - mod;
1534 if (ticks >= BBT_Time::ticks_per_beat) {
1535 ticks -= BBT_Time::ticks_per_beat;
1537 } else if (dir < 0) {
1539 /* round to previous (or same iff dir == RoundDownMaybe) */
1541 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1543 if (difference == 0 && dir == RoundDownAlways) {
1544 /* right on the subdivision, but force-rounding down,
1545 so the difference is just the subdivision ticks */
1546 difference = ticks_one_subdivisions_worth;
1549 if (ticks < difference) {
1550 ticks = BBT_Time::ticks_per_beat - ticks;
1552 ticks -= difference;
1556 /* round to nearest */
1559 /* compute the distance to the previous and next subdivision */
1561 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1563 /* closer to the next subdivision, so shift forward */
1565 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1567 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1569 if (ticks > BBT_Time::ticks_per_beat) {
1571 ticks -= BBT_Time::ticks_per_beat;
1572 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1575 } else if (rem > 0) {
1577 /* closer to previous subdivision, so shift backward */
1581 /* can't go backwards past zero, so ... */
1584 /* step back to previous beat */
1586 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1587 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1589 ticks = lrint (ticks - rem);
1590 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1593 /* on the subdivision, do nothing */
1596 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1600 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1602 Glib::Threads::RWLock::ReaderLock lm (lock);
1604 double const beat_at_framepos = beat_at_frame (frame);
1606 BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1611 /* find bar previous to 'frame' */
1614 return frame_time (bbt);
1616 } else if (dir > 0) {
1617 /* find bar following 'frame' */
1621 return frame_time (bbt);
1623 /* true rounding: find nearest bar */
1624 framepos_t raw_ft = frame_time (bbt);
1627 framepos_t prev_ft = frame_time (bbt);
1629 framepos_t next_ft = frame_time (bbt);
1631 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1642 return frame_at_beat (floor (beat_at_framepos));
1643 } else if (dir > 0) {
1644 return frame_at_beat (ceil (beat_at_framepos));
1646 return frame_at_beat (floor (beat_at_framepos + 0.5));
1655 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1656 framepos_t lower, framepos_t upper)
1658 Glib::Threads::RWLock::ReaderLock lm (lock);
1659 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1660 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1662 while (cnt <= upper_beat) {
1663 framecnt_t const pos = frame_at_beat (cnt);
1664 MeterSection const meter = meter_section_at (pos);
1665 Tempo const tempo = tempo_at (pos);
1666 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
1668 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1674 TempoMap::tempo_section_at (framepos_t frame) const
1676 Glib::Threads::RWLock::ReaderLock lm (lock);
1678 Metrics::const_iterator i;
1679 TempoSection* prev = 0;
1681 for (i = metrics.begin(); i != metrics.end(); ++i) {
1684 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1686 if ((*i)->frame() > frame) {
1696 abort(); /*NOTREACHED*/
1702 /* don't use this to calculate length (the tempo is only correct for this frame).
1703 do that stuff based on the beat_at_frame and frame_at_beat api
1706 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1708 Glib::Threads::RWLock::ReaderLock lm (lock);
1710 const TempoSection* ts_at = &tempo_section_at (frame);
1711 const TempoSection* ts_after = 0;
1712 Metrics::const_iterator i;
1714 for (i = metrics.begin(); i != metrics.end(); ++i) {
1717 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1719 if ((*i)->frame() > frame) {
1727 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), _frame_rate));
1729 /* must be treated as constant tempo */
1730 return ts_at->frames_per_beat (_frame_rate);
1734 TempoMap::tempo_at (framepos_t frame) const
1736 Glib::Threads::RWLock::ReaderLock lm (lock);
1738 TempoMetric m (metric_at (frame));
1739 TempoSection* prev_ts = 0;
1741 Metrics::const_iterator i;
1743 for (i = metrics.begin(); i != metrics.end(); ++i) {
1745 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1746 if ((prev_ts) && t->frame() > frame) {
1747 /* this is the one past frame */
1748 framepos_t const time = frame - prev_ts->frame();
1749 double const ret = prev_ts->tempo_at_frame (time, _frame_rate);
1750 Tempo const ret_tempo (ret, m.tempo().note_type ());
1762 TempoMap::meter_section_at (framepos_t frame) const
1764 Glib::Threads::RWLock::ReaderLock lm (lock);
1765 Metrics::const_iterator i;
1766 MeterSection* prev = 0;
1768 for (i = metrics.begin(); i != metrics.end(); ++i) {
1771 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1773 if ((*i)->frame() > frame) {
1783 abort(); /*NOTREACHED*/
1790 TempoMap::meter_at (framepos_t frame) const
1792 TempoMetric m (metric_at (frame));
1797 TempoMap::get_state ()
1799 Metrics::const_iterator i;
1800 XMLNode *root = new XMLNode ("TempoMap");
1803 Glib::Threads::RWLock::ReaderLock lm (lock);
1804 for (i = metrics.begin(); i != metrics.end(); ++i) {
1805 root->add_child_nocopy ((*i)->get_state());
1813 TempoMap::set_state (const XMLNode& node, int /*version*/)
1816 Glib::Threads::RWLock::WriterLock lm (lock);
1819 XMLNodeConstIterator niter;
1820 Metrics old_metrics (metrics);
1821 MeterSection* last_meter = 0;
1824 nlist = node.children();
1826 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1827 XMLNode* child = *niter;
1829 if (child->name() == TempoSection::xml_state_node_name) {
1832 TempoSection* ts = new TempoSection (*child);
1833 metrics.push_back (ts);
1835 if (ts->bar_offset() < 0.0) {
1837 //ts->update_bar_offset_from_bbt (*last_meter);
1842 catch (failed_constructor& err){
1843 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1844 metrics = old_metrics;
1848 } else if (child->name() == MeterSection::xml_state_node_name) {
1851 MeterSection* ms = new MeterSection (*child);
1852 metrics.push_back (ms);
1856 catch (failed_constructor& err) {
1857 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1858 metrics = old_metrics;
1864 if (niter == nlist.end()) {
1865 MetricSectionSorter cmp;
1868 /* check for legacy sessions where bbt was the base musical unit for tempo */
1869 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1870 MeterSection* prev_ms;
1871 TempoSection* prev_ts;
1872 if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1873 if (prev_ms->beat() < 0.0) {
1874 /*XX we cannot possibly make this work??. */
1875 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());
1876 prev_ms->set_beat (start);
1878 } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1879 if (prev_ts->beat() < 0.0) {
1880 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);
1881 prev_ts->set_beat (start);
1886 /* check for multiple tempo/meters at the same location, which
1887 ardour2 somehow allowed.
1890 Metrics::iterator prev = metrics.end();
1891 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1892 if (prev != metrics.end()) {
1894 MeterSection* prev_ms;
1896 TempoSection* prev_ts;
1897 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1898 if (prev_ms->beat() == ms->beat()) {
1899 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
1900 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
1903 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1904 if (prev_ts->beat() == ts->beat()) {
1905 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
1906 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
1914 recompute_map (true, -1);
1917 PropertyChanged (PropertyChange ());
1923 TempoMap::dump (std::ostream& o) const
1925 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1926 const MeterSection* m;
1927 const TempoSection* t;
1929 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1931 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1932 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? "
1933 << t->movable() << ')' << endl;
1934 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1935 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
1936 << " (movable? " << m->movable() << ')' << endl;
1942 TempoMap::n_tempos() const
1944 Glib::Threads::RWLock::ReaderLock lm (lock);
1947 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1948 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1957 TempoMap::n_meters() const
1959 Glib::Threads::RWLock::ReaderLock lm (lock);
1962 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1963 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1972 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1975 Glib::Threads::RWLock::WriterLock lm (lock);
1976 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1977 if ((*i)->frame() >= where && (*i)->movable ()) {
1978 (*i)->set_frame ((*i)->frame() + amount);
1982 /* now reset the BBT time of all metrics, based on their new
1983 * audio time. This is the only place where we do this reverse
1987 Metrics::iterator i;
1988 const MeterSection* meter;
1989 const TempoSection* tempo;
1993 meter = &first_meter ();
1994 tempo = &first_tempo ();
1999 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2002 MetricSection* prev = 0;
2004 for (i = metrics.begin(); i != metrics.end(); ++i) {
2007 //TempoMetric metric (*meter, *tempo);
2008 MeterSection* ms = const_cast<MeterSection*>(meter);
2009 TempoSection* ts = const_cast<TempoSection*>(tempo);
2012 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2013 ts->set_beat (t->beat());
2015 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2016 ts->set_beat (m->beat());
2018 ts->set_frame (prev->frame());
2022 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2023 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2024 ms->set_beat (start);
2026 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2027 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2028 ms->set_beat (start);
2030 ms->set_frame (prev->frame());
2034 // metric will be at frames=0 bbt=1|1|0 by default
2035 // which is correct for our purpose
2038 // cerr << bbt << endl;
2040 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2041 t->set_beat (beat_at_frame (m->frame()));
2043 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2044 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2045 bbt_time (m->frame(), bbt);
2047 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2053 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2054 /* round up to next beat */
2060 if (bbt.beats != 1) {
2061 /* round up to next bar */
2066 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
2067 m->set_beat (start);
2069 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2071 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2072 abort(); /*NOTREACHED*/
2078 recompute_map (true);
2082 PropertyChanged (PropertyChange ());
2085 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2089 std::list<MetricSection*> metric_kill_list;
2091 TempoSection* last_tempo = NULL;
2092 MeterSection* last_meter = NULL;
2093 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2094 bool meter_after = false; // is there a meter marker likewise?
2096 Glib::Threads::RWLock::WriterLock lm (lock);
2097 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2098 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2099 metric_kill_list.push_back(*i);
2100 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2103 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2107 else if ((*i)->frame() >= where) {
2108 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2109 (*i)->set_frame ((*i)->frame() - amount);
2110 if ((*i)->frame() == where) {
2111 // marker was immediately after end of range
2112 tempo_after = dynamic_cast<TempoSection*> (*i);
2113 meter_after = dynamic_cast<MeterSection*> (*i);
2119 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2120 if (last_tempo && !tempo_after) {
2121 metric_kill_list.remove(last_tempo);
2122 last_tempo->set_frame(where);
2125 if (last_meter && !meter_after) {
2126 metric_kill_list.remove(last_meter);
2127 last_meter->set_frame(where);
2131 //remove all the remaining metrics
2132 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2138 recompute_map (true);
2141 PropertyChanged (PropertyChange ());
2145 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2146 * pos can be -ve, if required.
2149 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2151 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2154 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2156 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2158 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2161 /** Add the BBT interval op to pos and return the result */
2163 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2165 cerr << "framepos_plus_bbt - untested" << endl;
2166 Glib::Threads::RWLock::ReaderLock lm (lock);
2168 Metrics::const_iterator i;
2169 const MeterSection* meter;
2170 const MeterSection* m;
2171 const TempoSection* tempo;
2172 const TempoSection* next_tempo = 0;
2173 const TempoSection* t;
2174 double frames_per_beat;
2175 framepos_t effective_pos = max (pos, (framepos_t) 0);
2177 meter = &first_meter ();
2178 tempo = &first_tempo ();
2183 /* find the starting metrics for tempo & meter */
2185 for (i = metrics.begin(); i != metrics.end(); ++i) {
2187 if ((*i)->frame() > effective_pos) {
2191 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2193 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2198 for (i = metrics.begin(); i != metrics.end(); ++i) {
2199 if ((*i)->frame() > effective_pos) {
2200 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2209 meter -> the Meter for "pos"
2210 tempo -> the Tempo for "pos"
2211 next_tempo -> the Tempo after "pos" or 0
2212 i -> for first new metric after "pos", possibly metrics.end()
2215 /* now comes the complicated part. we have to add one beat a time,
2216 checking for a new metric on every beat.
2220 /* fpb is used for constant tempo */
2221 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2228 /* check if we need to use a new metric section: has adding frames moved us
2229 to or after the start of the next metric section? in which case, use it.
2232 if (i != metrics.end()) {
2233 if ((*i)->frame() <= pos) {
2235 /* about to change tempo or meter, so add the
2236 * number of frames for the bars we've just
2237 * traversed before we change the
2238 * frames_per_beat value.
2241 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2246 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2248 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2253 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2255 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2259 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2266 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2268 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2275 /* given the current meter, have we gone past the end of the bar ? */
2280 /* check if we need to use a new metric section: has adding frames moved us
2281 to or after the start of the next metric section? in which case, use it.
2284 if (i != metrics.end()) {
2285 if ((*i)->frame() <= pos) {
2287 /* about to change tempo or meter, so add the
2288 * number of frames for the beats we've just
2289 * traversed before we change the
2290 * frames_per_beat value.
2293 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2298 pos += tempo->frame_at_beat (beats, _frame_rate);
2300 pos += llrint (beats * frames_per_beat);
2305 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2307 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2311 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2317 pos += tempo->frame_at_beat (beats, _frame_rate);
2319 pos += llrint (beats * frames_per_beat);
2323 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2330 /** Count the number of beats that are equivalent to distance when going forward,
2334 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2336 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2340 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2346 operator<< (std::ostream& o, const Meter& m) {
2347 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2351 operator<< (std::ostream& o, const Tempo& t) {
2352 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2356 operator<< (std::ostream& o, const MetricSection& section) {
2358 o << "MetricSection @ " << section.frame() << ' ';
2360 const TempoSection* ts;
2361 const MeterSection* ms;
2363 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2364 o << *((const Tempo*) ts);
2365 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2366 //o << *((const Meter*) ms);