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>
27 #include "pbd/xml++.h"
28 #include "evoral/Beats.hpp"
29 #include "ardour/debug.h"
30 #include "ardour/lmath.h"
31 #include "ardour/tempo.h"
37 using namespace ARDOUR;
40 using Timecode::BBT_Time;
42 /* _default tempo is 4/4 qtr=120 */
44 Meter TempoMap::_default_meter (4.0, 4.0);
45 Tempo TempoMap::_default_tempo (120.0);
47 /***********************************************************************/
50 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
52 /* This is tempo- and meter-sensitive. The number it returns
53 is based on the interval between any two lines in the
54 grid that is constructed from tempo and meter sections.
56 The return value IS NOT interpretable in terms of "beats".
59 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
65 return frames_per_grid (tempo, sr) * _divisions_per_bar;
69 /***********************************************************************/
71 const string TempoSection::xml_state_node_name = "Tempo";
73 TempoSection::TempoSection (const XMLNode& node)
74 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
76 const XMLProperty *prop;
80 if ((prop = node.property ("start")) == 0) {
81 error << _("TempoSection XML node has no \"start\" property") << endmsg;
82 throw failed_constructor();
85 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
89 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
90 throw failed_constructor();
95 if ((prop = node.property ("beats-per-minute")) == 0) {
96 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
97 throw failed_constructor();
100 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
101 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
102 throw failed_constructor();
105 if ((prop = node.property ("note-type")) == 0) {
106 /* older session, make note type be quarter by default */
109 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
110 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
111 throw failed_constructor();
115 if ((prop = node.property ("movable")) == 0) {
116 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
117 throw failed_constructor();
120 set_movable (string_is_affirmative (prop->value()));
122 if ((prop = node.property ("bar-offset")) == 0) {
125 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
126 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
127 throw failed_constructor();
131 if ((prop = node.property ("tempo-type")) == 0) {
132 _type = Type::Constant;
134 if (strstr(prop->value().c_str(),"Constant")) {
135 _type = Type::Constant;
143 TempoSection::get_state() const
145 XMLNode *root = new XMLNode (xml_state_node_name);
149 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
153 root->add_property ("start", buf);
154 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
155 root->add_property ("beats-per-minute", buf);
156 snprintf (buf, sizeof (buf), "%f", _note_type);
157 root->add_property ("note-type", buf);
158 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
159 // root->add_property ("bar-offset", buf);
160 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
161 root->add_property ("movable", buf);
163 snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
164 root->add_property ("tempo-type", buf);
171 TempoSection::update_bar_offset_from_bbt (const Meter& m)
173 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
174 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
176 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
180 TempoSection::set_type (Type type)
186 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
189 if (_type == Constant) {
190 return beats_per_minute();
193 return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat;
197 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
199 if (_type == Constant) {
203 return minute_to_frame (time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
207 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
209 if (_type == Constant) {
210 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
213 return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
217 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
219 if (_type == Constant) {
220 return (framepos_t) floor ((tick / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
223 return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
226 double TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
228 return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
231 framepos_t TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
233 return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
237 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
239 return time * 60.0 * frame_rate;
243 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
245 return (frame / (double) frame_rate) / 60.0;
248 /* constant for exp */
250 TempoSection::a_func (double begin_tpm, double end_tpm, double end_time) const
252 return log (end_tpm / ticks_per_minute()) / c_func (end_tpm, end_time);
255 TempoSection::c_func (double end_tpm, double end_time) const
257 return log (end_tpm / ticks_per_minute()) / end_time;
260 /* tempo in tpm at time in minutes */
262 TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
264 return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
267 /* time in minutes at tempo in tpm */
269 TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
271 return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
274 /* tempo in bpm at time in minutes */
276 TempoSection::tempo_at_time (double time, double end_bpm, double end_time) const
278 return tick_tempo_at_time (time, end_bpm * BBT_Time::ticks_per_beat, end_time) / BBT_Time::ticks_per_beat;
281 /* time in minutes at tempo in bpm */
283 TempoSection::time_at_tempo (double tempo, double end_bpm, double end_time) const
285 return time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, end_time);
288 /* tick at time in minutes */
290 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
292 return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
295 /* time in minutes at tick */
297 TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
299 return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
302 /* beat at time in minutes */
304 TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
306 return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
309 /* time in munutes at beat */
311 TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
313 return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
317 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
321 if (_bar_offset < 0.0) {
326 new_start.bars = start().bars;
328 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
329 new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
330 //new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
331 new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat);
333 /* remember the 1-based counting properties of beats */
334 new_start.beats += 1;
335 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
336 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
338 set_start (new_start);
341 /***********************************************************************/
343 const string MeterSection::xml_state_node_name = "Meter";
345 MeterSection::MeterSection (const XMLNode& node)
346 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
348 XMLProperty const * prop;
352 if ((prop = node.property ("start")) == 0) {
353 error << _("MeterSection XML node has no \"start\" property") << endmsg;
354 throw failed_constructor();
357 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
361 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
362 throw failed_constructor();
367 /* beats-per-bar is old; divisions-per-bar is new */
369 if ((prop = node.property ("divisions-per-bar")) == 0) {
370 if ((prop = node.property ("beats-per-bar")) == 0) {
371 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
372 throw failed_constructor();
376 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
377 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
378 throw failed_constructor();
381 if ((prop = node.property ("note-type")) == 0) {
382 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
383 throw failed_constructor();
386 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
387 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
388 throw failed_constructor();
391 if ((prop = node.property ("movable")) == 0) {
392 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
393 throw failed_constructor();
396 set_movable (string_is_affirmative (prop->value()));
400 MeterSection::get_state() const
402 XMLNode *root = new XMLNode (xml_state_node_name);
406 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
410 root->add_property ("start", buf);
411 snprintf (buf, sizeof (buf), "%f", _note_type);
412 root->add_property ("note-type", buf);
413 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
414 root->add_property ("divisions-per-bar", buf);
415 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
416 root->add_property ("movable", buf);
421 /***********************************************************************/
423 struct MetricSectionSorter {
424 bool operator() (const MetricSection* a, const MetricSection* b) {
425 return a->start() < b->start();
429 TempoMap::TempoMap (framecnt_t fr)
438 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Ramp);
439 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
441 t->set_movable (false);
442 m->set_movable (false);
444 /* note: frame time is correct (zero) for both of these */
446 metrics.push_back (t);
447 metrics.push_back (m);
451 TempoMap::~TempoMap ()
456 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
458 bool removed = false;
461 Glib::Threads::RWLock::WriterLock lm (lock);
462 if ((removed = remove_tempo_locked (tempo))) {
463 if (complete_operation) {
464 recompute_map (true);
469 if (removed && complete_operation) {
470 PropertyChanged (PropertyChange ());
475 TempoMap::remove_tempo_locked (const TempoSection& tempo)
479 for (i = metrics.begin(); i != metrics.end(); ++i) {
480 if (dynamic_cast<TempoSection*> (*i) != 0) {
481 if (tempo.frame() == (*i)->frame()) {
482 if ((*i)->movable()) {
494 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
496 bool removed = false;
499 Glib::Threads::RWLock::WriterLock lm (lock);
500 if ((removed = remove_meter_locked (tempo))) {
501 if (complete_operation) {
502 recompute_map (true);
507 if (removed && complete_operation) {
508 PropertyChanged (PropertyChange ());
513 TempoMap::remove_meter_locked (const MeterSection& tempo)
517 for (i = metrics.begin(); i != metrics.end(); ++i) {
518 if (dynamic_cast<MeterSection*> (*i) != 0) {
519 if (tempo.frame() == (*i)->frame()) {
520 if ((*i)->movable()) {
532 TempoMap::do_insert (MetricSection* section)
534 bool need_add = true;
536 assert (section->start().ticks == 0);
538 /* we only allow new meters to be inserted on beat 1 of an existing
542 if (dynamic_cast<MeterSection*>(section)) {
544 /* we need to (potentially) update the BBT times of tempo
545 sections based on this new meter.
548 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
550 BBT_Time corrected = section->start();
554 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
555 section->start(), corrected) << endmsg;
557 section->set_start (corrected);
563 /* Look for any existing MetricSection that is of the same type and
564 in the same bar as the new one, and remove it before adding
565 the new one. Note that this means that if we find a matching,
566 existing section, we can break out of the loop since we're
567 guaranteed that there is only one such match.
570 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
572 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
573 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
575 if (iter_is_tempo && insert_is_tempo) {
579 if ((*i)->start().bars == section->start().bars &&
580 (*i)->start().beats == section->start().beats) {
582 if (!(*i)->movable()) {
584 /* can't (re)move this section, so overwrite
585 * its data content (but not its properties as
589 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
597 } else if (!iter_is_tempo && !insert_is_tempo) {
601 if ((*i)->start().bars == section->start().bars) {
603 if (!(*i)->movable()) {
605 /* can't (re)move this section, so overwrite
606 * its data content (but not its properties as
610 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
620 /* non-matching types, so we don't care */
624 /* Add the given MetricSection, if we didn't just reset an existing
632 for (i = metrics.begin(); i != metrics.end(); ++i) {
633 if ((*i)->start() > section->start()) {
638 metrics.insert (i, section);
643 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where, TempoSection::Type type)
646 Glib::Threads::RWLock::WriterLock lm (lock);
647 TempoSection& first (first_tempo());
649 if (ts.start() != first.start()) {
650 remove_tempo_locked (ts);
651 add_tempo_locked (tempo, where, true, type);
653 first.set_type (type);
655 /* cannot move the first tempo section */
656 *static_cast<Tempo*>(&first) = tempo;
657 recompute_map (false);
662 PropertyChanged (PropertyChange ());
666 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame)
669 TempoSection& first (first_tempo());
671 if (ts.start() != first.start()) {
673 bbt_time (frame, bbt);
675 Glib::Threads::RWLock::WriterLock lm (lock);
676 ts.set_frame (frame);
679 recompute_map (false);
684 MetricPositionChanged (); // Emit Signal
688 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::Type type)
691 Glib::Threads::RWLock::WriterLock lm (lock);
692 add_tempo_locked (tempo, where, true, type);
696 PropertyChanged (PropertyChange ());
700 TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, ARDOUR::TempoSection::Type type)
702 /* new tempos always start on a beat */
704 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
706 /* find the meter to use to set the bar offset of this
710 const Meter* meter = &first_meter();
712 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
713 at something, because we insert the default tempo and meter during
714 TempoMap construction.
716 now see if we can find better candidates.
719 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
721 const MeterSection* m;
723 if (where < (*i)->start()) {
727 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
732 ts->update_bar_offset_from_bbt (*meter);
739 recompute_map (false);
744 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
747 Glib::Threads::RWLock::WriterLock lm (lock);
748 MeterSection& first (first_meter());
750 if (ms.start() != first.start()) {
751 remove_meter_locked (ms);
752 add_meter_locked (meter, where, true);
754 /* cannot move the first meter section */
755 *static_cast<Meter*>(&first) = meter;
756 recompute_map (true);
760 PropertyChanged (PropertyChange ());
764 TempoMap::add_meter (const Meter& meter, BBT_Time where)
767 Glib::Threads::RWLock::WriterLock lm (lock);
768 add_meter_locked (meter, where, true);
773 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
778 PropertyChanged (PropertyChange ());
782 TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
784 /* a new meter always starts a new bar on the first beat. so
785 round the start time appropriately. remember that
786 `where' is based on the existing tempo map, not
787 the result after we insert the new meter.
791 if (where.beats != 1) {
796 /* new meters *always* start on a beat. */
799 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
802 recompute_map (true);
808 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
810 Tempo newtempo (beats_per_minute, note_type);
813 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
814 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
816 Glib::Threads::RWLock::WriterLock lm (lock);
817 *((Tempo*) t) = newtempo;
818 recompute_map (false);
820 PropertyChanged (PropertyChange ());
827 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
829 Tempo newtempo (beats_per_minute, note_type);
835 /* find the TempoSection immediately preceding "where"
838 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
840 if ((*i)->frame() > where) {
846 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
856 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
866 Glib::Threads::RWLock::WriterLock lm (lock);
867 /* cannot move the first tempo section */
868 *((Tempo*)prev) = newtempo;
869 recompute_map (false);
872 PropertyChanged (PropertyChange ());
876 TempoMap::first_meter () const
878 const MeterSection *m = 0;
880 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
881 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
886 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
887 abort(); /*NOTREACHED*/
892 TempoMap::first_meter ()
896 /* CALLER MUST HOLD LOCK */
898 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
899 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
904 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
905 abort(); /*NOTREACHED*/
910 TempoMap::first_tempo () const
912 const TempoSection *t = 0;
914 /* CALLER MUST HOLD LOCK */
916 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
917 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
922 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
923 abort(); /*NOTREACHED*/
928 TempoMap::first_tempo ()
932 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
933 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
938 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
939 abort(); /*NOTREACHED*/
944 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
946 /* CALLER MUST HOLD WRITE LOCK */
948 MeterSection* meter = 0;
949 TempoSection* tempo = 0;
950 double current_frame;
952 Metrics::iterator next_metric;
956 /* we will actually stop once we hit
963 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
965 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
968 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
976 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
979 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
987 /* assumes that the first meter & tempo are at frame zero */
989 meter->set_frame (0);
990 tempo->set_frame (0);
992 /* assumes that the first meter & tempo are at 1|1|0 */
996 if (reassign_tempo_bbt) {
998 MeterSection* rmeter = meter;
1000 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
1002 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1007 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1009 /* reassign the BBT time of this tempo section
1010 * based on its bar offset position.
1013 ts->update_bbt_time_from_bar_offset (*rmeter);
1015 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1018 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1019 abort(); /*NOTREACHED*/
1024 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
1026 next_metric = metrics.begin();
1027 ++next_metric; // skip meter (or tempo)
1028 ++next_metric; // skip tempo (or meter)
1030 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
1033 /* silly call from Session::process() during startup
1038 _extend_map (tempo, meter, next_metric, current, current_frame, end);
1042 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
1043 Metrics::iterator next_metric,
1044 BBT_Time current, framepos_t current_frame, framepos_t end)
1046 /* CALLER MUST HOLD WRITE LOCK */
1048 uint32_t first_tick_in_new_meter = 0;
1049 Metrics::const_iterator i;
1050 Metrics::const_iterator mi;
1052 TempoSection* prev_ts = tempo;
1054 for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1055 MeterSection* m = 0;
1057 if ((m = dynamic_cast<MeterSection*> (*mi)) != 0) {
1059 if (m->start() >= prev_ts->start()) {
1060 first_tick_in_new_meter = ((((m->start().bars - 1) * meter->divisions_per_bar()) + (m->start().beats - 1)) * BBT_Time::ticks_per_beat) + m->start().ticks; // expressed in ticks from the previous meter
1061 for (i = metrics.begin(); i != metrics.end(); ++i) {
1064 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1066 if (t->start() >= m->start() && t->start() > prev_ts->start()) {
1067 //cerr << "new ts start bars = " << t->start().bars << " beats = " << t->start().beats << " ticks = " << t->start().ticks << endl;
1068 //cerr << "prev ts start bars = " << prev_ts->start().bars << " beats = " << prev_ts->start().beats << " ticks = " << prev_ts->start().ticks << endl;
1070 /*tempo section (t) lies in the previous meter */
1071 double ticks_at_ts = ((((t->start().bars - 1 ) * meter->divisions_per_bar()) + (t->start().beats - 1) ) * BBT_Time::ticks_per_beat) + t->start().ticks;
1072 double ticks_at_prev_ts = ((((prev_ts->start().bars - 1) * meter->divisions_per_bar()) + (prev_ts->start().beats - 1)) * BBT_Time::ticks_per_beat) + prev_ts->start().ticks;
1073 double ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts;
1074 /* assume (falsely) that the target tempo is constant */
1075 double length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
1076 if (prev_ts->type() == TempoSection::Type::Constant) {
1077 length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1079 cerr<< "initial length extimate = " << length_estimate << " ticks_relative_to_prev_ts " << ticks_relative_to_prev_ts << endl;
1080 double system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute());
1081 cerr << " system_precision_at_target_tempo = " << system_precision_at_target_tempo << endl;
1082 double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
1084 while (fabs (tick_error) >= system_precision_at_target_tempo) {
1086 double actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate);
1087 tick_error = ticks_relative_to_prev_ts - actual_ticks;
1088 length_estimate += (tick_error / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
1089 //cerr << "actual ticks = " << actual_ticks << endl;
1091 //cerr << "tick error = " << tick_error << endl;
1093 cerr << "setting t frame to " << length_estimate + prev_ts->frame() << "tick error = " << tick_error << endl;
1094 t->set_frame (length_estimate + prev_ts->frame());
1096 if (m->start() < t->start() && m->start() == prev_ts->start()) {
1097 m->set_frame (prev_ts->frame());
1098 } else if (m->start() < t->start() && m->start() > prev_ts->start()) {
1099 cerr << "setting m frame to " << prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate) << " ticks = " << first_tick_in_new_meter - ticks_at_prev_ts << endl;
1101 m->set_frame (prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate));
1115 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1117 Glib::Threads::RWLock::ReaderLock lm (lock);
1118 TempoMetric m (first_meter(), first_tempo());
1120 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1121 at something, because we insert the default tempo and meter during
1122 TempoMap construction.
1124 now see if we can find better candidates.
1127 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1129 if ((*i)->frame() > frame) {
1144 TempoMap::metric_at (BBT_Time bbt) const
1146 Glib::Threads::RWLock::ReaderLock lm (lock);
1147 TempoMetric m (first_meter(), first_tempo());
1149 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1150 at something, because we insert the default tempo and meter during
1151 TempoMap construction.
1153 now see if we can find better candidates.
1156 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1158 BBT_Time section_start ((*i)->start());
1160 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1171 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1173 Glib::Threads::RWLock::ReaderLock lm (lock);
1179 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1182 bbt = beats_to_bbt (beat_at_frame (frame));
1186 TempoMap::bars_in_meter_section (MeterSection* ms) const
1188 /* YOU MUST HAVE THE READ LOCK */
1189 Metrics::const_iterator i;
1191 MeterSection* next_ms = 0;
1192 const MeterSection* prev_ms = &first_meter();
1194 for (i = metrics.begin(); i != metrics.end(); ++i) {
1196 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1197 if (ms->frame() < m->frame()) {
1205 double ticks_at_next = tick_at_frame (next_ms->frame());
1206 double ticks_at_prev = tick_at_frame (prev_ms->frame());
1207 double ticks_in_meter = ticks_at_next - ticks_at_prev;
1209 return (int32_t) floor ((ticks_in_meter / BBT_Time::ticks_per_beat) / prev_ms->note_divisor());
1215 TempoMap::beats_to_bbt (double beats)
1217 /* CALLER HOLDS READ LOCK */
1219 MeterSection* prev_ms = &first_meter();
1221 framecnt_t frame = frame_at_beat (beats);
1224 if (n_meters() < 2) {
1225 uint32_t bars = (uint32_t) floor (beats / prev_ms->note_divisor());
1226 double remaining_beats = beats - (bars * prev_ms->note_divisor());
1227 double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1229 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1230 ret.beats = (uint32_t) floor (remaining_beats);
1233 /* 0 0 0 to 1 1 0 - based mapping*/
1237 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1239 ret.ticks -= BBT_Time::ticks_per_beat;
1242 if (ret.beats > prev_ms->note_divisor()) {
1250 uint32_t first_beat_in_meter = 0;
1251 uint32_t accumulated_bars = 0;
1252 Metrics::const_iterator i;
1254 for (i = metrics.begin(); i != metrics.end(); ++i) {
1255 MeterSection* m = 0;
1257 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1258 first_beat_in_meter = beat_at_frame (m->frame());
1260 if (beats < first_beat_in_meter) {
1261 /* this is the meter after the one our beat is on*/
1264 int32_t const bars_in_ms = bars_in_meter_section (m);
1266 if (bars_in_ms > 0) {
1267 accumulated_bars += bars_in_ms;
1274 //cerr << "beats to bbr with beats = " << beats << " first_beat_in_meter = " << first_beat_in_meter << " accumulated_bars = " << accumulated_bars << endl;
1276 if (beats > first_beat_in_meter) {
1277 /* prev_ms is the relevant one here */
1279 /* now get the ticks at frame */
1280 double ticks_at_frame = tick_at_frame (frame);
1282 /* find the number of ticks at the beginning of the meter section (bar 1)*/
1283 double ticks_at_ms = tick_at_frame (prev_ms->frame());
1285 double beats_used_by_ms = (ticks_at_frame - ticks_at_ms) / BBT_Time::ticks_per_beat;
1287 uint32_t bars = (uint32_t) floor (beats_used_by_ms / prev_ms->note_divisor());
1288 double remaining_beats = beats_used_by_ms - (bars * prev_ms->note_divisor());
1289 double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1291 ret.bars = bars + accumulated_bars;
1292 ret.beats = (uint32_t) floor (remaining_beats);
1293 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1295 /* now ensure we srtart at 1 1 0 */
1298 //cerr << "part 1 ret bars = " << ret.bars << " ret beats = " << ret.beats << " ret ticks = " << ret.ticks << endl;
1299 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1301 ret.ticks -= BBT_Time::ticks_per_beat;
1304 if (ret.beats > prev_ms->note_divisor()) {
1312 /* find the number of ticks at the beginning of the meter section (bar 1)*/
1313 double ticks_at_ms = tick_at_frame (prev_ms->frame());
1315 /* now get the ticks at frame */
1316 double ticks_at_frame = tick_at_frame (frame);
1318 double ticks_within_ms = ticks_at_frame - ticks_at_ms;
1320 ret.bars = (uint32_t) floor (((ticks_within_ms / BBT_Time::ticks_per_beat) / prev_ms->note_divisor())) + accumulated_bars;
1321 uint32_t remaining_ticks = ticks_within_ms - (ret.bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat);
1322 ret.beats = (uint32_t) floor (remaining_ticks);
1323 remaining_ticks -= ret.beats * BBT_Time::ticks_per_beat;
1325 /* only round ticks */
1326 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1328 /* now ensure we srtart at 1 1 0 */
1331 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1333 ret.ticks -= BBT_Time::ticks_per_beat;
1336 if (ret.beats > prev_ms->note_divisor()) {
1345 TempoMap::tick_at_frame (framecnt_t frame) const
1347 Glib::Threads::RWLock::ReaderLock lm (lock);
1349 Metrics::const_iterator i;
1350 const TempoSection* prev_ts = &first_tempo();
1351 double accumulated_ticks = 0.0;
1354 for (i = metrics.begin(); i != metrics.end(); ++i) {
1357 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1359 if (frame < t->frame()) {
1360 /*the previous ts is the one containing the frame */
1362 framepos_t time = frame - prev_ts->frame();
1363 framepos_t last_frame = t->frame() - prev_ts->frame();
1364 double last_beats_per_minute = t->beats_per_minute();
1366 return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
1369 if (cnt > 0 && t->frame() > prev_ts->frame()) {
1370 framepos_t time = t->frame() - prev_ts->frame();
1371 framepos_t last_frame = t->frame() - prev_ts->frame();
1372 double last_beats_per_minute = t->beats_per_minute();
1373 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate);
1381 /* treated s linear for this ts */
1382 framecnt_t frames_in_section = frame - prev_ts->frame();
1383 double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1385 return ticks_in_section + accumulated_ticks;
1390 TempoMap::frame_at_tick (double tick) const
1392 Glib::Threads::RWLock::ReaderLock lm (lock);
1394 double accumulated_ticks = 0.0;
1395 double accumulated_ticks_to_prev = 0.0;
1397 const TempoSection* prev_ts = &first_tempo();
1400 Metrics::const_iterator i;
1402 for (i = metrics.begin(); i != metrics.end(); ++i) {
1404 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1406 if (cnt > 0 && t->frame() > prev_ts->frame()) {
1407 framepos_t time = t->frame() - prev_ts->frame();
1408 framepos_t last_time = t->frame() - prev_ts->frame();
1409 double last_beats_per_minute = t->beats_per_minute();
1410 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1413 if (tick < accumulated_ticks) {
1414 /* prev_ts is the one affecting us. */
1416 double ticks_in_section = tick - accumulated_ticks_to_prev;
1417 framepos_t section_start = prev_ts->frame();
1418 framepos_t last_time = t->frame() - prev_ts->frame();
1419 double last_beats_per_minute = t->beats_per_minute();
1420 return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start;
1422 accumulated_ticks_to_prev = accumulated_ticks;
1428 double ticks_in_section = tick - tick_at_frame (prev_ts->frame());
1429 double dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
1430 framecnt_t ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1436 TempoMap::beat_at_frame (framecnt_t frame) const
1438 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1442 TempoMap::frame_at_beat (double beat) const
1444 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1448 TempoMap::frame_time (const BBT_Time& bbt)
1451 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1455 if (bbt.beats < 1) {
1456 throw std::logic_error ("beats are counted from one");
1459 Glib::Threads::RWLock::ReaderLock lm (lock);
1461 Metrics::const_iterator i;
1462 uint32_t accumulated_bars = 0;
1464 MeterSection* prev_ms = &first_meter();
1466 for (i = metrics.begin(); i != metrics.end(); ++i) {
1468 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1469 int32_t const bims = bars_in_meter_section (m);
1471 if (bims < 0 || bbt.bars <= (accumulated_bars + bims)) {
1475 accumulated_bars += bims;
1481 uint32_t remaining_bars = bbt.bars - accumulated_bars - 1; // back to zero - based bars
1482 double const ticks_within_prev_taken_by_remaining_bars = remaining_bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat;
1483 double const ticks_after_space_used_by_bars = ((bbt.beats - 1) * BBT_Time::ticks_per_beat) + bbt.ticks; // back to zero - based beats
1484 double const ticks_target = ticks_within_prev_taken_by_remaining_bars + ticks_after_space_used_by_bars;
1486 TempoSection* prev_ts = &first_tempo();
1487 double accumulated_ticks = 0.0;
1490 for (i = metrics.begin(); i != metrics.end(); ++i) {
1492 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1493 if (t->frame() < prev_ms->frame()) {
1497 if (cnt > 0 && t->frame() > prev_ts->frame()) {
1498 /*find the number of ticke in this section */
1499 framepos_t const time = t->frame() - prev_ts->frame();
1500 framepos_t const last_time = t->frame() - prev_ts->frame();
1501 double const last_beats_per_minute = t->beats_per_minute();
1502 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1505 if (ticks_target < accumulated_ticks) {
1506 double const ticks_in_section = ticks_target - tick_at_frame (prev_ts->frame());
1507 framepos_t const section_start_time = prev_ts->frame();
1508 framepos_t const last_time = t->frame() - prev_ts->frame();
1509 double const last_beats_per_minute = t->beats_per_minute();
1510 framepos_t const ret = prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start_time;
1519 /*treat this ts as constant tempo */
1520 double const ticks_in_this_ts = ticks_target - tick_at_frame (prev_ts->frame());
1521 double const dtime = (ticks_in_this_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
1522 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1528 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1531 Glib::Threads::RWLock::ReaderLock lm (lock);
1533 Metrics::const_iterator i;
1534 TempoSection* first = 0;
1535 TempoSection* second = 0;
1537 for (i = metrics.begin(); i != metrics.end(); ++i) {
1540 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1542 if ((*i)->frame() > pos) {
1550 if (first && second) {
1551 framepos_t const last_time = second->frame() - first->frame();
1552 double const last_beats_per_minute = second->beats_per_minute();
1554 framepos_t const time = pos - first->frame();
1555 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1556 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1558 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1560 return time_at_bbt - time;
1563 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1564 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1568 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1570 return round_to_type (fr, dir, Bar);
1574 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1576 return round_to_type (fr, dir, Beat);
1580 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1582 Glib::Threads::RWLock::ReaderLock lm (lock);
1584 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1585 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1586 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1588 ticks -= beats * BBT_Time::ticks_per_beat;
1591 /* round to next (or same iff dir == RoundUpMaybe) */
1593 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1595 if (mod == 0 && dir == RoundUpMaybe) {
1596 /* right on the subdivision, which is fine, so do nothing */
1598 } else if (mod == 0) {
1599 /* right on the subdivision, so the difference is just the subdivision ticks */
1600 ticks += ticks_one_subdivisions_worth;
1603 /* not on subdivision, compute distance to next subdivision */
1605 ticks += ticks_one_subdivisions_worth - mod;
1608 if (ticks >= BBT_Time::ticks_per_beat) {
1609 ticks -= BBT_Time::ticks_per_beat;
1611 } else if (dir < 0) {
1613 /* round to previous (or same iff dir == RoundDownMaybe) */
1615 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1617 if (difference == 0 && dir == RoundDownAlways) {
1618 /* right on the subdivision, but force-rounding down,
1619 so the difference is just the subdivision ticks */
1620 difference = ticks_one_subdivisions_worth;
1623 if (ticks < difference) {
1624 ticks = BBT_Time::ticks_per_beat - ticks;
1626 ticks -= difference;
1630 /* round to nearest */
1633 /* compute the distance to the previous and next subdivision */
1635 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1637 /* closer to the next subdivision, so shift forward */
1639 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1641 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1643 if (ticks > BBT_Time::ticks_per_beat) {
1645 ticks -= BBT_Time::ticks_per_beat;
1646 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1649 } else if (rem > 0) {
1651 /* closer to previous subdivision, so shift backward */
1655 /* can't go backwards past zero, so ... */
1658 /* step back to previous beat */
1660 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1661 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1663 ticks = lrint (ticks - rem);
1664 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1667 /* on the subdivision, do nothing */
1670 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1674 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1676 Glib::Threads::RWLock::ReaderLock lm (lock);
1678 double const beat_at_framepos = beat_at_frame (frame);
1680 BBT_Time bbt (beats_to_bbt (beat_at_framepos));
1685 /* find bar previous to 'frame' */
1688 return frame_time (bbt);
1690 } else if (dir > 0) {
1691 /* find bar following 'frame' */
1695 return frame_time (bbt);
1697 /* true rounding: find nearest bar */
1698 framepos_t raw_ft = frame_time (bbt);
1701 framepos_t prev_ft = frame_time (bbt);
1703 framepos_t next_ft = frame_time (bbt);
1705 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1716 return frame_at_beat (floor (beat_at_framepos));
1717 } else if (dir > 0) {
1718 return frame_at_beat (ceil (beat_at_framepos));
1720 return frame_at_beat (floor (beat_at_framepos + 0.5));
1729 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1730 framepos_t lower, framepos_t upper)
1732 Glib::Threads::RWLock::ReaderLock lm (lock);
1733 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1734 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1736 while (cnt <= upper_beat) {
1737 framecnt_t const pos = frame_at_beat (cnt);
1738 MeterSection const meter = meter_section_at (pos);
1739 Tempo const tempo = tempo_at (pos);
1740 BBT_Time const bbt = beats_to_bbt ((double) cnt);
1742 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1748 TempoMap::tempo_section_after (framepos_t frame) const
1750 Glib::Threads::RWLock::ReaderLock lm (lock);
1752 Metrics::const_iterator i;
1753 TempoSection* next = 0;
1755 for (i = metrics.begin(); i != metrics.end(); ++i) {
1758 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1760 if ((*i)->frame() > frame) {
1772 TempoMap::tempo_section_at (framepos_t frame) const
1774 Glib::Threads::RWLock::ReaderLock lm (lock);
1776 Metrics::const_iterator i;
1777 TempoSection* prev = 0;
1779 for (i = metrics.begin(); i != metrics.end(); ++i) {
1782 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1784 if ((*i)->frame() > frame) {
1794 abort(); /*NOTREACHED*/
1800 /* don't use this to calculate length (the tempo is only correct for this frame).
1801 do that stuff based on the beat_at_frame and frame_at_beat api
1804 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1806 Glib::Threads::RWLock::ReaderLock lm (lock);
1808 const TempoSection* ts_at = &tempo_section_at (frame);
1809 const TempoSection* ts_after = tempo_section_after (frame);
1812 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
1814 /* must be treated as constant tempo */
1815 return ts_at->frames_per_beat (_frame_rate);
1819 TempoMap::tempo_at (framepos_t frame) const
1821 Glib::Threads::RWLock::ReaderLock lm (lock);
1823 TempoMetric m (metric_at (frame));
1824 TempoSection* prev_ts = 0;
1826 Metrics::const_iterator i;
1828 for (i = metrics.begin(); i != metrics.end(); ++i) {
1830 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1831 if ((prev_ts) && t->frame() > frame) {
1832 /* this is the one past frame */
1833 framepos_t const time = frame - prev_ts->frame();
1834 framepos_t const last_time = t->frame() - prev_ts->frame();
1835 double const last_beats_per_minute = t->beats_per_minute();
1836 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1837 Tempo const ret_tempo (ret, m.tempo().note_type ());
1849 TempoMap::meter_section_at (framepos_t frame) const
1851 Glib::Threads::RWLock::ReaderLock lm (lock);
1852 Metrics::const_iterator i;
1853 MeterSection* prev = 0;
1855 for (i = metrics.begin(); i != metrics.end(); ++i) {
1858 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1860 if ((*i)->frame() > frame) {
1870 abort(); /*NOTREACHED*/
1877 TempoMap::meter_at (framepos_t frame) const
1879 TempoMetric m (metric_at (frame));
1884 TempoMap::get_state ()
1886 Metrics::const_iterator i;
1887 XMLNode *root = new XMLNode ("TempoMap");
1890 Glib::Threads::RWLock::ReaderLock lm (lock);
1891 for (i = metrics.begin(); i != metrics.end(); ++i) {
1892 root->add_child_nocopy ((*i)->get_state());
1900 TempoMap::set_state (const XMLNode& node, int /*version*/)
1903 Glib::Threads::RWLock::WriterLock lm (lock);
1906 XMLNodeConstIterator niter;
1907 Metrics old_metrics (metrics);
1908 MeterSection* last_meter = 0;
1911 nlist = node.children();
1913 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1914 XMLNode* child = *niter;
1916 if (child->name() == TempoSection::xml_state_node_name) {
1919 TempoSection* ts = new TempoSection (*child);
1920 metrics.push_back (ts);
1922 if (ts->bar_offset() < 0.0) {
1924 ts->update_bar_offset_from_bbt (*last_meter);
1929 catch (failed_constructor& err){
1930 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1931 metrics = old_metrics;
1935 } else if (child->name() == MeterSection::xml_state_node_name) {
1938 MeterSection* ms = new MeterSection (*child);
1939 metrics.push_back (ms);
1943 catch (failed_constructor& err) {
1944 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1945 metrics = old_metrics;
1951 if (niter == nlist.end()) {
1952 MetricSectionSorter cmp;
1956 /* check for multiple tempo/meters at the same location, which
1957 ardour2 somehow allowed.
1960 Metrics::iterator prev = metrics.end();
1961 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1962 if (prev != metrics.end()) {
1963 if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
1964 if ((*prev)->start() == (*i)->start()) {
1965 cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1966 error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1969 } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
1970 if ((*prev)->start() == (*i)->start()) {
1971 cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1972 error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1980 recompute_map (true, -1);
1983 PropertyChanged (PropertyChange ());
1989 TempoMap::dump (std::ostream& o) const
1991 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1992 const MeterSection* m;
1993 const TempoSection* t;
1995 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1997 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1998 o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
1999 << t->movable() << ')' << endl;
2000 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2001 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
2002 << " (movable? " << m->movable() << ')' << endl;
2008 TempoMap::n_tempos() const
2010 Glib::Threads::RWLock::ReaderLock lm (lock);
2013 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2014 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2023 TempoMap::n_meters() const
2025 Glib::Threads::RWLock::ReaderLock lm (lock);
2028 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2029 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2038 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2041 Glib::Threads::RWLock::WriterLock lm (lock);
2042 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2043 if ((*i)->frame() >= where && (*i)->movable ()) {
2044 (*i)->set_frame ((*i)->frame() + amount);
2048 /* now reset the BBT time of all metrics, based on their new
2049 * audio time. This is the only place where we do this reverse
2053 Metrics::iterator i;
2054 const MeterSection* meter;
2055 const TempoSection* tempo;
2059 meter = &first_meter ();
2060 tempo = &first_tempo ();
2065 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2068 MetricSection* prev = 0;
2070 for (i = metrics.begin(); i != metrics.end(); ++i) {
2073 TempoMetric metric (*meter, *tempo);
2076 metric.set_start (prev->start());
2077 metric.set_frame (prev->frame());
2079 // metric will be at frames=0 bbt=1|1|0 by default
2080 // which is correct for our purpose
2083 bbt_time ((*i)->frame(), bbt);
2085 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2091 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2092 /* round up to next beat */
2098 if (bbt.beats != 1) {
2099 /* round up to next bar */
2105 // cerr << bbt << endl;
2107 (*i)->set_start (bbt);
2109 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2111 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2112 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2114 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2116 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2117 abort(); /*NOTREACHED*/
2123 recompute_map (true);
2127 PropertyChanged (PropertyChange ());
2130 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2134 std::list<MetricSection*> metric_kill_list;
2136 TempoSection* last_tempo = NULL;
2137 MeterSection* last_meter = NULL;
2138 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2139 bool meter_after = false; // is there a meter marker likewise?
2141 Glib::Threads::RWLock::WriterLock lm (lock);
2142 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2143 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2144 metric_kill_list.push_back(*i);
2145 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2148 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2152 else if ((*i)->frame() >= where) {
2153 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2154 (*i)->set_frame ((*i)->frame() - amount);
2155 if ((*i)->frame() == where) {
2156 // marker was immediately after end of range
2157 tempo_after = dynamic_cast<TempoSection*> (*i);
2158 meter_after = dynamic_cast<MeterSection*> (*i);
2164 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2165 if (last_tempo && !tempo_after) {
2166 metric_kill_list.remove(last_tempo);
2167 last_tempo->set_frame(where);
2170 if (last_meter && !meter_after) {
2171 metric_kill_list.remove(last_meter);
2172 last_meter->set_frame(where);
2176 //remove all the remaining metrics
2177 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2183 recompute_map (true);
2186 PropertyChanged (PropertyChange ());
2190 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2191 * pos can be -ve, if required.
2194 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2196 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2199 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2201 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2203 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2206 /** Add the BBT interval op to pos and return the result */
2208 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2210 cerr << "framepos_plus_bbt - untested" << endl;
2211 Glib::Threads::RWLock::ReaderLock lm (lock);
2213 Metrics::const_iterator i;
2214 const MeterSection* meter;
2215 const MeterSection* m;
2216 const TempoSection* tempo;
2217 const TempoSection* next_tempo = 0;
2218 const TempoSection* t;
2219 double frames_per_beat;
2220 framepos_t effective_pos = max (pos, (framepos_t) 0);
2222 meter = &first_meter ();
2223 tempo = &first_tempo ();
2228 /* find the starting metrics for tempo & meter */
2230 for (i = metrics.begin(); i != metrics.end(); ++i) {
2232 if ((*i)->frame() > effective_pos) {
2236 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2238 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2243 for (i = metrics.begin(); i != metrics.end(); ++i) {
2244 if ((*i)->frame() > effective_pos) {
2245 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2254 meter -> the Meter for "pos"
2255 tempo -> the Tempo for "pos"
2256 next_tempo -> the Tempo after "pos" or 0
2257 i -> for first new metric after "pos", possibly metrics.end()
2260 /* now comes the complicated part. we have to add one beat a time,
2261 checking for a new metric on every beat.
2265 /* fpb is used for constant tempo */
2266 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2273 /* check if we need to use a new metric section: has adding frames moved us
2274 to or after the start of the next metric section? in which case, use it.
2277 if (i != metrics.end()) {
2278 if ((*i)->frame() <= pos) {
2280 /* about to change tempo or meter, so add the
2281 * number of frames for the bars we've just
2282 * traversed before we change the
2283 * frames_per_beat value.
2286 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2291 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2293 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2298 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2300 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2304 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2311 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2313 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2320 /* given the current meter, have we gone past the end of the bar ? */
2325 /* check if we need to use a new metric section: has adding frames moved us
2326 to or after the start of the next metric section? in which case, use it.
2329 if (i != metrics.end()) {
2330 if ((*i)->frame() <= pos) {
2332 /* about to change tempo or meter, so add the
2333 * number of frames for the beats we've just
2334 * traversed before we change the
2335 * frames_per_beat value.
2338 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2343 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2345 pos += llrint (beats * frames_per_beat);
2350 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2352 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2356 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2362 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2364 pos += llrint (beats * frames_per_beat);
2368 pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2375 /** Count the number of beats that are equivalent to distance when going forward,
2379 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2381 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2385 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2391 operator<< (std::ostream& o, const Meter& m) {
2392 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2396 operator<< (std::ostream& o, const Tempo& t) {
2397 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2401 operator<< (std::ostream& o, const MetricSection& section) {
2403 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2405 const TempoSection* ts;
2406 const MeterSection* ms;
2408 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2409 o << *((const Tempo*) ts);
2410 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2411 o << *((const Meter*) ms);