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;
68 /***********************************************************************/
70 const string TempoSection::xml_state_node_name = "Tempo";
72 TempoSection::TempoSection (const XMLNode& node)
73 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
75 XMLProperty const * prop;
79 if ((prop = node.property ("start")) == 0) {
80 error << _("TempoSection XML node has no \"start\" property") << endmsg;
81 throw failed_constructor();
84 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
88 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
89 throw failed_constructor();
94 if ((prop = node.property ("beats-per-minute")) == 0) {
95 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
96 throw failed_constructor();
99 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
100 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
101 throw failed_constructor();
104 if ((prop = node.property ("note-type")) == 0) {
105 /* older session, make note type be quarter by default */
108 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
109 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
110 throw failed_constructor();
114 if ((prop = node.property ("movable")) == 0) {
115 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
116 throw failed_constructor();
119 set_movable (string_is_affirmative (prop->value()));
121 if ((prop = node.property ("bar-offset")) == 0) {
124 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
125 error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
126 throw failed_constructor();
130 if ((prop = node.property ("tempo-type")) == 0) {
131 _type = TempoSectionType::Ramp;
133 if (strstr(prop->value().c_str(),"Constant")) {
134 _type = TempoSectionType::Constant;
136 _type = TempoSectionType::Ramp;
142 TempoSection::get_state() const
144 XMLNode *root = new XMLNode (xml_state_node_name);
148 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
152 root->add_property ("start", buf);
153 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
154 root->add_property ("beats-per-minute", buf);
155 snprintf (buf, sizeof (buf), "%f", _note_type);
156 root->add_property ("note-type", buf);
157 // snprintf (buf, sizeof (buf), "%f", _bar_offset);
158 // root->add_property ("bar-offset", buf);
159 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
160 root->add_property ("movable", buf);
162 snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
163 root->add_property ("tempo-type", buf);
170 TempoSection::update_bar_offset_from_bbt (const Meter& m)
172 _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
173 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
175 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
179 TempoSection::set_type (TempoSectionType type)
185 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
188 if (_type == Constant) {
189 return beats_per_minute();
192 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;
196 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
198 if (_type == Constant) {
202 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);
206 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
208 if (_type == Constant) {
209 return frame / frames_per_beat (frame_rate);
212 return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
216 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
218 if (_type == Constant) {
219 return (framepos_t) floor (tick * frames_per_beat(frame_rate));
222 return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
225 double TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
227 return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
230 framepos_t TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
232 return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
236 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
238 return time * 60.0 * frame_rate;
242 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
244 return (frame / (double) frame_rate) / 60.0;
247 /* constant for exp */
249 TempoSection::a_func (double begin_tpm, double end_tpm, double end_time) const
251 return log (end_tpm / ticks_per_minute()) / c_func (end_tpm, end_time);
254 TempoSection::c_func (double end_tpm, double end_time) const
256 return log (end_tpm / ticks_per_minute()) / end_time;
259 /* tempo in tpm at time in minutes */
261 TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
263 return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
266 /* time in minutes at tempo in tpm */
268 TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
270 return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
273 /* tempo in bpm at time in minutes */
275 TempoSection::tempo_at_time (double time, double end_bpm, double end_time) const
277 return tick_tempo_at_time (time, end_bpm * BBT_Time::ticks_per_beat, end_time) / BBT_Time::ticks_per_beat;
280 /* time in minutes at tempo in bpm */
282 TempoSection::time_at_tempo (double tempo, double end_bpm, double end_time) const
284 return time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, end_time);
287 /* tick at time in minutes */
289 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
291 return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
294 /* time in minutes at tick */
296 TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
298 return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
301 /* beat at time in minutes */
303 TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
305 return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
308 /* time in munutes at beat */
310 TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
312 return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
316 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
320 if (_bar_offset < 0.0) {
325 new_start.bars = start().bars;
327 double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
328 new_start.beats = (uint32_t) floor (ticks/BBT_Time::ticks_per_beat);
329 new_start.ticks = 0; /* (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat); */
331 /* remember the 1-based counting properties of beats */
332 new_start.beats += 1;
333 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
334 _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
336 set_start (new_start);
339 /***********************************************************************/
341 const string MeterSection::xml_state_node_name = "Meter";
343 MeterSection::MeterSection (const XMLNode& node)
344 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
346 XMLProperty const * prop;
350 if ((prop = node.property ("start")) == 0) {
351 error << _("MeterSection XML node has no \"start\" property") << endmsg;
352 throw failed_constructor();
355 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
359 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
360 throw failed_constructor();
365 /* beats-per-bar is old; divisions-per-bar is new */
367 if ((prop = node.property ("divisions-per-bar")) == 0) {
368 if ((prop = node.property ("beats-per-bar")) == 0) {
369 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
370 throw failed_constructor();
374 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
375 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
376 throw failed_constructor();
379 if ((prop = node.property ("note-type")) == 0) {
380 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
381 throw failed_constructor();
384 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
385 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
386 throw failed_constructor();
389 if ((prop = node.property ("movable")) == 0) {
390 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
391 throw failed_constructor();
394 set_movable (string_is_affirmative (prop->value()));
398 MeterSection::get_state() const
400 XMLNode *root = new XMLNode (xml_state_node_name);
404 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
408 root->add_property ("start", buf);
409 snprintf (buf, sizeof (buf), "%f", _note_type);
410 root->add_property ("note-type", buf);
411 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
412 root->add_property ("divisions-per-bar", buf);
413 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
414 root->add_property ("movable", buf);
419 /***********************************************************************/
421 struct MetricSectionSorter {
422 bool operator() (const MetricSection* a, const MetricSection* b) {
423 return a->start() < b->start();
427 TempoMap::TempoMap (framecnt_t fr)
436 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::TempoSectionType::Ramp);
437 MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
439 t->set_movable (false);
440 m->set_movable (false);
442 /* note: frame time is correct (zero) for both of these */
444 metrics.push_back (t);
445 metrics.push_back (m);
448 TempoMap::~TempoMap ()
453 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
455 bool removed = false;
458 Glib::Threads::RWLock::WriterLock lm (lock);
459 if ((removed = remove_tempo_locked (tempo))) {
460 if (complete_operation) {
461 recompute_map (true);
466 if (removed && complete_operation) {
467 PropertyChanged (PropertyChange ());
472 TempoMap::remove_tempo_locked (const TempoSection& tempo)
476 for (i = metrics.begin(); i != metrics.end(); ++i) {
477 if (dynamic_cast<TempoSection*> (*i) != 0) {
478 if (tempo.frame() == (*i)->frame()) {
479 if ((*i)->movable()) {
491 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
493 bool removed = false;
496 Glib::Threads::RWLock::WriterLock lm (lock);
497 if ((removed = remove_meter_locked (tempo))) {
498 if (complete_operation) {
499 recompute_map (true);
504 if (removed && complete_operation) {
505 PropertyChanged (PropertyChange ());
510 TempoMap::remove_meter_locked (const MeterSection& tempo)
514 for (i = metrics.begin(); i != metrics.end(); ++i) {
515 if (dynamic_cast<MeterSection*> (*i) != 0) {
516 if (tempo.frame() == (*i)->frame()) {
517 if ((*i)->movable()) {
529 TempoMap::do_insert (MetricSection* section)
531 bool need_add = true;
533 assert (section->start().ticks == 0);
535 /* we only allow new meters to be inserted on beat 1 of an existing
539 if (dynamic_cast<MeterSection*>(section)) {
541 /* we need to (potentially) update the BBT times of tempo
542 sections based on this new meter.
545 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
547 BBT_Time corrected = section->start();
551 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
552 section->start(), corrected) << endmsg;
554 section->set_start (corrected);
560 /* Look for any existing MetricSection that is of the same type and
561 in the same bar as the new one, and remove it before adding
562 the new one. Note that this means that if we find a matching,
563 existing section, we can break out of the loop since we're
564 guaranteed that there is only one such match.
567 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
569 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
570 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
572 if (iter_is_tempo && insert_is_tempo) {
576 if ((*i)->start().bars == section->start().bars &&
577 (*i)->start().beats == section->start().beats) {
579 if (!(*i)->movable()) {
581 /* can't (re)move this section, so overwrite
582 * its data content (but not its properties as
586 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
594 } else if (!iter_is_tempo && !insert_is_tempo) {
598 if ((*i)->start().bars == section->start().bars) {
600 if (!(*i)->movable()) {
602 /* can't (re)move this section, so overwrite
603 * its data content (but not its properties as
607 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
617 /* non-matching types, so we don't care */
621 /* Add the given MetricSection, if we didn't just reset an existing
629 for (i = metrics.begin(); i != metrics.end(); ++i) {
630 if ((*i)->start() > section->start()) {
635 metrics.insert (i, section);
640 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where, TempoSection::TempoSectionType type)
643 Glib::Threads::RWLock::WriterLock lm (lock);
644 TempoSection& first (first_tempo());
645 TempoSection::TempoSectionType tt = first.type();
647 if (ts.start() != first.start()) {
648 remove_tempo_locked (ts);
649 add_tempo_locked (tempo, where, true, tt);
652 /* cannot move the first tempo section */
653 *static_cast<Tempo*>(&first) = tempo;
654 recompute_map (false);
659 PropertyChanged (PropertyChange ());
663 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::TempoSectionType type)
666 Glib::Threads::RWLock::WriterLock lm (lock);
667 add_tempo_locked (tempo, where, true, type);
671 PropertyChanged (PropertyChange ());
675 TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, ARDOUR::TempoSection::TempoSectionType type)
677 /* new tempos always start on a beat */
679 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
681 /* find the meter to use to set the bar offset of this
685 const Meter* meter = &first_meter();
687 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
688 at something, because we insert the default tempo and meter during
689 TempoMap construction.
691 now see if we can find better candidates.
694 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
696 const MeterSection* m;
698 if (where < (*i)->start()) {
702 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
707 ts->update_bar_offset_from_bbt (*meter);
714 recompute_map (false);
719 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
722 Glib::Threads::RWLock::WriterLock lm (lock);
723 MeterSection& first (first_meter());
725 if (ms.start() != first.start()) {
726 remove_meter_locked (ms);
727 add_meter_locked (meter, where, true);
729 /* cannot move the first meter section */
730 *static_cast<Meter*>(&first) = meter;
731 recompute_map (true);
735 PropertyChanged (PropertyChange ());
739 TempoMap::add_meter (const Meter& meter, BBT_Time where)
742 Glib::Threads::RWLock::WriterLock lm (lock);
743 add_meter_locked (meter, where, true);
748 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
753 PropertyChanged (PropertyChange ());
757 TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
759 /* a new meter always starts a new bar on the first beat. so
760 round the start time appropriately. remember that
761 `where' is based on the existing tempo map, not
762 the result after we insert the new meter.
766 if (where.beats != 1) {
771 /* new meters *always* start on a beat. */
774 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
777 recompute_map (true);
783 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
785 Tempo newtempo (beats_per_minute, note_type);
788 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
789 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
791 Glib::Threads::RWLock::WriterLock lm (lock);
792 *((Tempo*) t) = newtempo;
793 recompute_map (false);
795 PropertyChanged (PropertyChange ());
802 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
804 Tempo newtempo (beats_per_minute, note_type);
810 /* find the TempoSection immediately preceding "where"
813 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
815 if ((*i)->frame() > where) {
821 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
831 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
841 Glib::Threads::RWLock::WriterLock lm (lock);
842 /* cannot move the first tempo section */
843 *((Tempo*)prev) = newtempo;
844 recompute_map (false);
847 PropertyChanged (PropertyChange ());
851 TempoMap::first_meter () const
853 const MeterSection *m = 0;
855 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
856 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
861 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
862 abort(); /*NOTREACHED*/
867 TempoMap::first_meter ()
871 /* CALLER MUST HOLD LOCK */
873 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
874 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
879 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
880 abort(); /*NOTREACHED*/
885 TempoMap::first_tempo () const
887 const TempoSection *t = 0;
889 /* CALLER MUST HOLD LOCK */
891 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
892 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
897 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
898 abort(); /*NOTREACHED*/
903 TempoMap::first_tempo ()
907 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
908 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
913 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
914 abort(); /*NOTREACHED*/
919 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
921 /* CALLER MUST HOLD WRITE LOCK */
923 MeterSection* meter = 0;
924 TempoSection* tempo = 0;
925 double current_frame;
927 Metrics::iterator next_metric;
931 /* we will actually stop once we hit
938 if (!_map.empty ()) {
939 /* never allow the map to be shortened /
940 end = max (end, _map.back().frame);
945 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
947 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
950 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
958 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
961 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
969 /* assumes that the first meter & tempo are at frame zero */
971 meter->set_frame (0);
972 tempo->set_frame (0);
974 /* assumes that the first meter & tempo are at 1|1|0 */
978 if (reassign_tempo_bbt) {
980 MeterSection* rmeter = meter;
982 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
984 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
989 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
991 /* reassign the BBT time of this tempo section
992 * based on its bar offset position.
995 ts->update_bbt_time_from_bar_offset (*rmeter);
997 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1000 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1001 abort(); /*NOTREACHED*/
1006 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
1008 next_metric = metrics.begin();
1009 ++next_metric; // skip meter (or tempo)
1010 ++next_metric; // skip tempo (or meter)
1012 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
1015 /* silly call from Session::process() during startup
1020 _extend_map (tempo, meter, next_metric, current, current_frame, end);
1024 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
1025 Metrics::iterator next_metric,
1026 BBT_Time current, framepos_t current_frame, framepos_t end)
1028 /* CALLER MUST HOLD WRITE LOCK */
1030 uint32_t first_tick_in_new_meter = 0;
1031 Metrics::const_iterator i;
1032 TempoSection* prev_ts = tempo;
1034 for (i = metrics.begin(); i != metrics.end(); ++i) {
1035 MeterSection* m = 0;
1037 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1039 if (m->start() >= prev_ts->start()) {
1040 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
1041 for (i = metrics.begin(); i != metrics.end(); ++i) {
1044 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1046 if (t->start() >= m->start() && t->start() > prev_ts->start()) {
1047 //cerr << "new ts start bars = " << t->start().bars << " beats = " << t->start().beats << " ticks = " << t->start().ticks << endl;
1048 //cerr << "prev ts start bars = " << prev_ts->start().bars << " beats = " << prev_ts->start().beats << " ticks = " << prev_ts->start().ticks << endl;
1050 /*tempo section (t) lies in the previous meter */
1051 double ticks_at_ts = ((((t->start().bars - 1 ) * meter->divisions_per_bar()) + (t->start().beats - 1) ) * BBT_Time::ticks_per_beat) + t->start().ticks;
1054 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;
1056 double ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts;
1057 /* assume (falsely) that the target tempo is constant */
1058 double length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
1059 double system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute());
1060 cerr << " system_precision_at_target_tempo = " << system_precision_at_target_tempo << endl;
1061 double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
1063 while (fabs (tick_error) >= system_precision_at_target_tempo) {
1065 double actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate);
1066 tick_error = ticks_relative_to_prev_ts - actual_ticks;
1067 length_estimate += (tick_error / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate);
1068 cerr << "actual ticks = " << actual_ticks << endl;
1070 cerr << "tick error = " << tick_error << endl;
1072 t->set_frame (length_estimate + prev_ts->frame());
1074 if (m->start() < t->start() && m->start() == prev_ts->start()) {
1075 m->set_frame (prev_ts->frame());
1076 } else if (m->start() < t->start() && m->start() > prev_ts->start()) {
1077 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));
1091 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1093 Glib::Threads::RWLock::ReaderLock lm (lock);
1094 TempoMetric m (first_meter(), first_tempo());
1096 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1097 at something, because we insert the default tempo and meter during
1098 TempoMap construction.
1100 now see if we can find better candidates.
1103 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1105 if ((*i)->frame() > frame) {
1120 TempoMap::metric_at (BBT_Time bbt) const
1122 Glib::Threads::RWLock::ReaderLock lm (lock);
1123 TempoMetric m (first_meter(), first_tempo());
1125 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1126 at something, because we insert the default tempo and meter during
1127 TempoMap construction.
1129 now see if we can find better candidates.
1132 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1134 BBT_Time section_start ((*i)->start());
1136 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1147 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1149 Glib::Threads::RWLock::ReaderLock lm (lock);
1155 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1158 bbt = beats_to_bbt (beat_at_frame (frame));
1162 TempoMap::bars_in_meter_section (MeterSection* ms) const
1164 /* YOU MUST HAVE THE READ LOCK */
1165 Metrics::const_iterator i;
1167 MeterSection* next_ms = 0;
1168 const MeterSection* prev_ms = &first_meter();
1170 for (i = metrics.begin(); i != metrics.end(); ++i) {
1172 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1173 if (ms->frame() < m->frame()) {
1181 double ticks_at_next = tick_at_frame (next_ms->frame());
1182 double ticks_at_prev = tick_at_frame (prev_ms->frame());
1183 double ticks_in_meter = ticks_at_next - ticks_at_prev;
1185 return (int32_t) floor ((ticks_in_meter / BBT_Time::ticks_per_beat) / prev_ms->note_divisor());
1191 TempoMap::beats_to_bbt (double beats)
1193 /* CALLER HOLDS READ LOCK */
1195 MeterSection* prev_ms = &first_meter();
1197 framecnt_t frame = frame_at_beat (beats);
1200 if (n_meters() < 2) {
1201 uint32_t bars = (uint32_t) floor (beats / prev_ms->note_divisor());
1202 double remaining_beats = beats - (bars * prev_ms->note_divisor());
1203 double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1205 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1206 ret.beats = (uint32_t) floor (remaining_beats);
1209 /* 0 0 0 to 1 1 0 - based mapping*/
1213 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1215 ret.ticks -= BBT_Time::ticks_per_beat;
1218 if (ret.beats > prev_ms->note_divisor()) {
1226 uint32_t first_beat_in_meter = 0;
1227 uint32_t accumulated_bars = 0;
1228 Metrics::const_iterator i;
1230 for (i = metrics.begin(); i != metrics.end(); ++i) {
1231 MeterSection* m = 0;
1233 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1234 first_beat_in_meter = beat_at_frame (m->frame());
1236 if (beats < first_beat_in_meter) {
1237 /* this is the meter after the one our beat is on*/
1240 int32_t const bars_in_ms = bars_in_meter_section (m);
1242 if (bars_in_ms > 0) {
1243 accumulated_bars += bars_in_ms;
1250 //cerr << "beats to bbr with beats = " << beats << " first_beat_in_meter = " << first_beat_in_meter << " accumulated_bars = " << accumulated_bars << endl;
1252 if (beats > first_beat_in_meter) {
1253 /* prev_ms is the relevant one here */
1255 /* now get the ticks at frame */
1256 double ticks_at_frame = tick_at_frame (frame);
1258 /* find the number of ticks at the beginning of the meter section (bar 1)*/
1259 double ticks_at_ms = tick_at_frame (prev_ms->frame());
1261 double beats_used_by_ms = (ticks_at_frame - ticks_at_ms) / BBT_Time::ticks_per_beat;
1263 uint32_t bars = (uint32_t) floor (beats_used_by_ms / prev_ms->note_divisor());
1264 double remaining_beats = beats_used_by_ms - (bars * prev_ms->note_divisor());
1265 double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1267 ret.bars = bars + accumulated_bars;
1268 ret.beats = (uint32_t) floor (remaining_beats);
1269 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1271 /* now ensure we srtart at 1 1 0 */
1274 //cerr << "part 1 ret bars = " << ret.bars << " ret beats = " << ret.beats << " ret ticks = " << ret.ticks << endl;
1275 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1277 ret.ticks -= BBT_Time::ticks_per_beat;
1280 if (ret.beats > prev_ms->note_divisor()) {
1288 /* find the number of ticks at the beginning of the meter section (bar 1)*/
1289 double ticks_at_ms = tick_at_frame (prev_ms->frame());
1291 /* now get the ticks at frame */
1292 double ticks_at_frame = tick_at_frame (frame);
1294 double ticks_within_ms = ticks_at_frame - ticks_at_ms;
1296 ret.bars = (uint32_t) floor (((ticks_within_ms / BBT_Time::ticks_per_beat) / prev_ms->note_divisor())) + accumulated_bars;
1297 uint32_t remaining_ticks = ticks_within_ms - (ret.bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat);
1298 ret.beats = (uint32_t) floor (remaining_ticks);
1299 remaining_ticks -= ret.beats * BBT_Time::ticks_per_beat;
1301 /* only round ticks */
1302 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1304 /* now ensure we srtart at 1 1 0 */
1307 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1309 ret.ticks -= BBT_Time::ticks_per_beat;
1312 if (ret.beats > prev_ms->note_divisor()) {
1321 TempoMap::tick_at_frame (framecnt_t frame) const
1323 Metrics::const_iterator i;
1324 const TempoSection* prev_ts = &first_tempo();
1325 double accumulated_ticks = 0.0;
1328 for (i = metrics.begin(); i != metrics.end(); ++i) {
1331 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1333 if (frame < t->frame()) {
1334 /*the previous ts is the one containing the frame */
1336 framepos_t time = frame - prev_ts->frame();
1337 framepos_t last_frame = t->frame() - prev_ts->frame();
1338 double last_beats_per_minute = t->beats_per_minute();
1340 return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
1343 if (cnt > 0 && t->frame() > prev_ts->frame()) {
1344 framepos_t time = t->frame() - prev_ts->frame();
1345 framepos_t last_frame = t->frame() - prev_ts->frame();
1346 double last_beats_per_minute = t->beats_per_minute();
1347 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate);
1355 /* treated s linear for this ts */
1356 framecnt_t frames_in_section = frame - prev_ts->frame();
1357 double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1359 return ticks_in_section + accumulated_ticks;
1364 TempoMap::frame_at_tick (double tick) const
1366 double accumulated_ticks = 0.0;
1367 const TempoSection* prev_ts = &first_tempo();
1370 Metrics::const_iterator i;
1372 for (i = metrics.begin(); i != metrics.end(); ++i) {
1374 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1376 if (cnt > 0 && t->frame() > prev_ts->frame()) {
1377 framepos_t time = t->frame() - prev_ts->frame();
1378 framepos_t last_time = t->frame() - prev_ts->frame();
1379 double last_beats_per_minute = t->beats_per_minute();
1380 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1383 if (tick < accumulated_ticks) {
1384 /* prev_ts is the one affecting us. */
1386 double ticks_in_section = tick - tick_at_frame (prev_ts->frame());
1387 framepos_t section_start = prev_ts->frame();
1388 framepos_t last_time = t->frame() - prev_ts->frame();
1389 double last_beats_per_minute = t->beats_per_minute();
1390 return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start;
1397 double ticks_in_section = tick - tick_at_frame (prev_ts->frame());
1398 double dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
1399 framecnt_t ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1405 TempoMap::beat_at_frame (framecnt_t frame) const
1407 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1411 TempoMap::frame_at_beat (double beat) const
1413 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1417 TempoMap::frame_time (const BBT_Time& bbt)
1420 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1424 if (bbt.beats < 1) {
1425 throw std::logic_error ("beats are counted from one");
1428 Glib::Threads::RWLock::ReaderLock lm (lock);
1430 Metrics::const_iterator i;
1431 uint32_t accumulated_bars = 0;
1433 MeterSection* prev_ms = &first_meter();
1435 for (i = metrics.begin(); i != metrics.end(); ++i) {
1437 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1438 int32_t const bims = bars_in_meter_section (m);
1440 if (bims < 0 || bbt.bars <= (accumulated_bars + bims)) {
1444 accumulated_bars += bims;
1450 uint32_t remaining_bars = bbt.bars - accumulated_bars - 1; // back to zero - based bars
1451 double const ticks_within_prev_taken_by_remaining_bars = remaining_bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat;
1452 double const ticks_after_space_used_by_bars = ((bbt.beats - 1) * BBT_Time::ticks_per_beat) + bbt.ticks; // back to zero - based beats
1453 double const ticks_target = ticks_within_prev_taken_by_remaining_bars + ticks_after_space_used_by_bars;
1455 TempoSection* prev_ts = &first_tempo();
1456 double accumulated_ticks = 0.0;
1459 for (i = metrics.begin(); i != metrics.end(); ++i) {
1461 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1462 if (t->frame() < prev_ms->frame()) {
1466 if (cnt > 0 && t->frame() > prev_ts->frame()) {
1467 /*find the number of ticke in this section */
1468 framepos_t const time = t->frame() - prev_ts->frame();
1469 framepos_t const last_time = t->frame() - prev_ts->frame();
1470 double const last_beats_per_minute = t->beats_per_minute();
1471 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1474 if (ticks_target < accumulated_ticks) {
1475 double const ticks_in_section = ticks_target - tick_at_frame (prev_ts->frame());
1476 framepos_t const section_start_time = prev_ts->frame();
1477 framepos_t const last_time = t->frame() - prev_ts->frame();
1478 double const last_beats_per_minute = t->beats_per_minute();
1479 framepos_t const ret = prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start_time;
1488 /*treat this ts as constant tempo */
1489 double const ticks_in_this_ts = ticks_target - tick_at_frame (prev_ts->frame());
1490 double const dtime = (ticks_in_this_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
1491 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1497 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1500 Glib::Threads::RWLock::ReaderLock lm (lock);
1502 Metrics::const_iterator i;
1503 TempoSection* first = 0;
1504 TempoSection* second = 0;
1506 for (i = metrics.begin(); i != metrics.end(); ++i) {
1509 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1511 if ((*i)->frame() > pos) {
1519 if (first && second) {
1520 framepos_t const last_time = second->frame() - first->frame();
1521 double const last_beats_per_minute = second->beats_per_minute();
1523 framepos_t const time = pos - first->frame();
1524 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1525 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1527 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1529 return time_at_bbt - time;
1532 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1533 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1537 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1539 return round_to_type (fr, dir, Bar);
1543 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1545 return round_to_type (fr, dir, Beat);
1549 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1551 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1552 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1553 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1555 ticks -= beats * BBT_Time::ticks_per_beat;
1558 /* round to next (or same iff dir == RoundUpMaybe) */
1560 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1562 if (mod == 0 && dir == RoundUpMaybe) {
1563 /* right on the subdivision, which is fine, so do nothing */
1565 } else if (mod == 0) {
1566 /* right on the subdivision, so the difference is just the subdivision ticks */
1567 ticks += ticks_one_subdivisions_worth;
1570 /* not on subdivision, compute distance to next subdivision */
1572 ticks += ticks_one_subdivisions_worth - mod;
1575 if (ticks >= BBT_Time::ticks_per_beat) {
1576 ticks -= BBT_Time::ticks_per_beat;
1578 } else if (dir < 0) {
1580 /* round to previous (or same iff dir == RoundDownMaybe) */
1582 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1584 if (difference == 0 && dir == RoundDownAlways) {
1585 /* right on the subdivision, but force-rounding down,
1586 so the difference is just the subdivision ticks */
1587 difference = ticks_one_subdivisions_worth;
1590 if (ticks < difference) {
1591 ticks = BBT_Time::ticks_per_beat - ticks;
1593 ticks -= difference;
1597 /* round to nearest */
1600 /* compute the distance to the previous and next subdivision */
1602 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1604 /* closer to the next subdivision, so shift forward */
1606 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1608 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1610 if (ticks > BBT_Time::ticks_per_beat) {
1612 ticks -= BBT_Time::ticks_per_beat;
1613 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1616 } else if (rem > 0) {
1618 /* closer to previous subdivision, so shift backward */
1622 /* can't go backwards past zero, so ... */
1625 /* step back to previous beat */
1627 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1628 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1630 ticks = lrint (ticks - rem);
1631 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1634 /* on the subdivision, do nothing */
1637 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1641 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1643 Glib::Threads::RWLock::ReaderLock lm (lock);
1645 double const beat_at_framepos = beat_at_frame (frame);
1647 BBT_Time bbt (beats_to_bbt (beat_at_framepos));
1652 /* find bar previous to 'frame' */
1655 return frame_time (bbt);
1657 } else if (dir > 0) {
1658 /* find bar following 'frame' */
1662 return frame_time (bbt);
1664 /* true rounding: find nearest bar */
1666 framepos_t raw_ft = frame_time (bbt);
1669 framepos_t prev_ft = frame_time (bbt);
1671 framepos_t next_ft = frame_time (bbt);
1673 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1684 return frame_at_beat (floor (beat_at_framepos));
1685 } else if (dir > 0) {
1686 return frame_at_beat (ceil (beat_at_framepos));
1688 return frame_at_beat (floor (beat_at_framepos + 0.5));
1697 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1698 framepos_t lower, framepos_t upper)
1700 Glib::Threads::RWLock::ReaderLock lm (lock);
1701 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1702 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1704 while (cnt <= upper_beat) {
1705 framecnt_t const pos = frame_at_beat (cnt);
1706 MeterSection const meter = meter_section_at (pos);
1707 Tempo const tempo = tempo_at (pos);
1708 BBT_Time const bbt = beats_to_bbt ((double) cnt);
1710 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1716 TempoMap::tempo_section_at (framepos_t frame) const
1718 Glib::Threads::RWLock::ReaderLock lm (lock);
1720 Metrics::const_iterator i;
1721 TempoSection* prev = 0;
1723 for (i = metrics.begin(); i != metrics.end(); ++i) {
1726 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1728 if ((*i)->frame() > frame) {
1738 abort(); /*NOTREACHED*/
1745 TempoMap::tempo_at (framepos_t frame) const
1747 Glib::Threads::RWLock::ReaderLock lm (lock);
1749 TempoMetric m (metric_at (frame));
1750 TempoSection* prev_ts = 0;
1752 Metrics::const_iterator i;
1754 for (i = metrics.begin(); i != metrics.end(); ++i) {
1756 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1757 if ((prev_ts) && t->frame() > frame) {
1758 /* this is the one past frame */
1759 framepos_t const time = frame - prev_ts->frame();
1760 framepos_t const last_time = t->frame() - prev_ts->frame();
1761 double const last_beats_per_minute = t->beats_per_minute();
1762 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1763 Tempo const ret_tempo (ret, m.tempo().note_type ());
1775 TempoMap::meter_section_at (framepos_t frame) const
1777 Glib::Threads::RWLock::ReaderLock lm (lock);
1778 Metrics::const_iterator i;
1779 MeterSection* prev = 0;
1781 for (i = metrics.begin(); i != metrics.end(); ++i) {
1784 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1786 if ((*i)->frame() > frame) {
1796 abort(); /*NOTREACHED*/
1803 TempoMap::meter_at (framepos_t frame) const
1805 TempoMetric m (metric_at (frame));
1810 TempoMap::get_state ()
1812 Metrics::const_iterator i;
1813 XMLNode *root = new XMLNode ("TempoMap");
1816 Glib::Threads::RWLock::ReaderLock lm (lock);
1817 for (i = metrics.begin(); i != metrics.end(); ++i) {
1818 root->add_child_nocopy ((*i)->get_state());
1826 TempoMap::set_state (const XMLNode& node, int /*version*/)
1829 Glib::Threads::RWLock::WriterLock lm (lock);
1832 XMLNodeConstIterator niter;
1833 Metrics old_metrics (metrics);
1834 MeterSection* last_meter = 0;
1837 nlist = node.children();
1839 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1840 XMLNode* child = *niter;
1842 if (child->name() == TempoSection::xml_state_node_name) {
1845 TempoSection* ts = new TempoSection (*child);
1846 metrics.push_back (ts);
1848 if (ts->bar_offset() < 0.0) {
1850 ts->update_bar_offset_from_bbt (*last_meter);
1855 catch (failed_constructor& err){
1856 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1857 metrics = old_metrics;
1861 } else if (child->name() == MeterSection::xml_state_node_name) {
1864 MeterSection* ms = new MeterSection (*child);
1865 metrics.push_back (ms);
1869 catch (failed_constructor& err) {
1870 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1871 metrics = old_metrics;
1877 if (niter == nlist.end()) {
1878 MetricSectionSorter cmp;
1882 /* check for multiple tempo/meters at the same location, which
1883 ardour2 somehow allowed.
1886 Metrics::iterator prev = metrics.end();
1887 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1888 if (prev != metrics.end()) {
1889 if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
1890 if ((*prev)->start() == (*i)->start()) {
1891 cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1892 error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1895 } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
1896 if ((*prev)->start() == (*i)->start()) {
1897 cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1898 error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1906 recompute_map (true, -1);
1909 PropertyChanged (PropertyChange ());
1915 TempoMap::dump (std::ostream& o) const
1917 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1918 const MeterSection* m;
1919 const TempoSection* t;
1921 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1923 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1924 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? "
1925 << t->movable() << ')' << endl;
1926 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1927 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1928 << " (movable? " << m->movable() << ')' << endl;
1934 TempoMap::n_tempos() const
1936 Glib::Threads::RWLock::ReaderLock lm (lock);
1939 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1940 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1949 TempoMap::n_meters() const
1951 Glib::Threads::RWLock::ReaderLock lm (lock);
1954 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1955 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1964 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1967 Glib::Threads::RWLock::WriterLock lm (lock);
1968 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1969 if ((*i)->frame() >= where && (*i)->movable ()) {
1970 (*i)->set_frame ((*i)->frame() + amount);
1974 /* now reset the BBT time of all metrics, based on their new
1975 * audio time. This is the only place where we do this reverse
1979 Metrics::iterator i;
1980 const MeterSection* meter;
1981 const TempoSection* tempo;
1985 meter = &first_meter ();
1986 tempo = &first_tempo ();
1991 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1994 MetricSection* prev = 0;
1996 for (i = metrics.begin(); i != metrics.end(); ++i) {
1999 TempoMetric metric (*meter, *tempo);
2002 metric.set_start (prev->start());
2003 metric.set_frame (prev->frame());
2005 // metric will be at frames=0 bbt=1|1|0 by default
2006 // which is correct for our purpose
2009 bbt_time ((*i)->frame(), bbt);
2011 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2017 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2018 /* round up to next beat */
2024 if (bbt.beats != 1) {
2025 /* round up to next bar */
2031 // cerr << bbt << endl;
2033 (*i)->set_start (bbt);
2035 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2037 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2038 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2040 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2042 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2043 abort(); /*NOTREACHED*/
2049 recompute_map (true);
2053 PropertyChanged (PropertyChange ());
2056 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2060 std::list<MetricSection*> metric_kill_list;
2062 TempoSection* last_tempo = NULL;
2063 MeterSection* last_meter = NULL;
2064 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2065 bool meter_after = false; // is there a meter marker likewise?
2067 Glib::Threads::RWLock::WriterLock lm (lock);
2068 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2069 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2070 metric_kill_list.push_back(*i);
2071 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2074 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2078 else if ((*i)->frame() >= where) {
2079 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2080 (*i)->set_frame ((*i)->frame() - amount);
2081 if ((*i)->frame() == where) {
2082 // marker was immediately after end of range
2083 tempo_after = dynamic_cast<TempoSection*> (*i);
2084 meter_after = dynamic_cast<MeterSection*> (*i);
2090 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2091 if (last_tempo && !tempo_after) {
2092 metric_kill_list.remove(last_tempo);
2093 last_tempo->set_frame(where);
2096 if (last_meter && !meter_after) {
2097 metric_kill_list.remove(last_meter);
2098 last_meter->set_frame(where);
2102 //remove all the remaining metrics
2103 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2109 recompute_map (true);
2112 PropertyChanged (PropertyChange ());
2116 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2117 * pos can be -ve, if required.
2120 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2122 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2125 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2127 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2129 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2132 /** Add the BBT interval op to pos and return the result */
2134 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2136 Glib::Threads::RWLock::ReaderLock lm (lock);
2137 Metrics::const_iterator i;
2138 const MeterSection* meter;
2139 const MeterSection* m;
2140 const TempoSection* tempo;
2141 const TempoSection* t;
2142 double frames_per_beat;
2143 framepos_t effective_pos = max (pos, (framepos_t) 0);
2145 meter = &first_meter ();
2146 tempo = &first_tempo ();
2151 /* find the starting metrics for tempo & meter */
2153 for (i = metrics.begin(); i != metrics.end(); ++i) {
2155 if ((*i)->frame() > effective_pos) {
2159 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2161 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2168 meter -> the Meter for "pos"
2169 tempo -> the Tempo for "pos"
2170 i -> for first new metric after "pos", possibly metrics.end()
2173 /* now comes the complicated part. we have to add one beat a time,
2174 checking for a new metric on every beat.
2177 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2186 /* check if we need to use a new metric section: has adding frames moved us
2187 to or after the start of the next metric section? in which case, use it.
2190 if (i != metrics.end()) {
2191 if ((*i)->frame() <= pos) {
2193 /* about to change tempo or meter, so add the
2194 * number of frames for the bars we've just
2195 * traversed before we change the
2196 * frames_per_beat value.
2199 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2202 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2204 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2208 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2215 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2221 /* given the current meter, have we gone past the end of the bar ? */
2226 /* check if we need to use a new metric section: has adding frames moved us
2227 to or after the start of the next metric section? in which case, use it.
2230 if (i != metrics.end()) {
2231 if ((*i)->frame() <= pos) {
2233 /* about to change tempo or meter, so add the
2234 * number of frames for the beats we've just
2235 * traversed before we change the
2236 * frames_per_beat value.
2239 pos += llrint (beats * frames_per_beat);
2242 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2244 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2248 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2253 pos += llrint (beats * frames_per_beat);
2256 if (op.ticks >= BBT_Time::ticks_per_beat) {
2257 pos += llrint (frames_per_beat + /* extra beat */
2258 (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
2259 (double) BBT_Time::ticks_per_beat)));
2261 pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2269 /** Count the number of beats that are equivalent to distance when going forward,
2273 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2275 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2279 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2285 operator<< (std::ostream& o, const Meter& m) {
2286 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2290 operator<< (std::ostream& o, const Tempo& t) {
2291 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2295 operator<< (std::ostream& o, const MetricSection& section) {
2297 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2299 const TempoSection* ts;
2300 const MeterSection* ms;
2302 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2303 o << *((const Tempo*) ts);
2304 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2305 o << *((const Meter*) ms);