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 /* we only allow new meters to be inserted on beat 1 of an existing
540 if (dynamic_cast<MeterSection*>(section)) {
541 assert (section->start().ticks == 0);
543 /* we need to (potentially) update the BBT times of tempo
544 sections based on this new meter.
547 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
549 BBT_Time corrected = section->start();
553 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
554 section->start(), corrected) << endmsg;
556 section->set_start (corrected);
562 /* Look for any existing MetricSection that is of the same type and
563 in the same bar as the new one, and remove it before adding
564 the new one. Note that this means that if we find a matching,
565 existing section, we can break out of the loop since we're
566 guaranteed that there is only one such match.
569 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
571 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
572 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
574 if (iter_is_tempo && insert_is_tempo) {
578 if ((*i)->start().bars == section->start().bars &&
579 (*i)->start().beats == section->start().beats) {
581 if (!(*i)->movable()) {
583 /* can't (re)move this section, so overwrite
584 * its data content (but not its properties as
588 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
596 } else if (!iter_is_tempo && !insert_is_tempo) {
600 if ((*i)->start().bars == section->start().bars) {
602 if (!(*i)->movable()) {
604 /* can't (re)move this section, so overwrite
605 * its data content (but not its properties as
609 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
619 /* non-matching types, so we don't care */
623 /* Add the given MetricSection, if we didn't just reset an existing
631 for (i = metrics.begin(); i != metrics.end(); ++i) {
632 if ((*i)->start() > section->start()) {
637 metrics.insert (i, section);
642 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where, TempoSection::Type type)
645 Glib::Threads::RWLock::WriterLock lm (lock);
646 TempoSection& first (first_tempo());
648 if (ts.start() != first.start()) {
649 remove_tempo_locked (ts);
650 add_tempo_locked (tempo, where, true, type);
652 first.set_type (type);
654 /* cannot move the first tempo section */
655 *static_cast<Tempo*>(&first) = tempo;
656 recompute_map (false);
661 PropertyChanged (PropertyChange ());
665 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame)
668 TempoSection& first (first_tempo());
670 if (ts.start() != first.start()) {
672 bbt_time (frame, bbt);
674 Glib::Threads::RWLock::WriterLock lm (lock);
675 ts.set_frame (frame);
678 recompute_map (false);
683 MetricPositionChanged (); // Emit Signal
687 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::Type type)
690 Glib::Threads::RWLock::WriterLock lm (lock);
691 add_tempo_locked (tempo, where, true, type);
695 PropertyChanged (PropertyChange ());
699 TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, ARDOUR::TempoSection::Type type)
701 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
703 /* find the meter to use to set the bar offset of this
707 const Meter* meter = &first_meter();
709 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
710 at something, because we insert the default tempo and meter during
711 TempoMap construction.
713 now see if we can find better candidates.
716 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
718 const MeterSection* m;
720 if (where < (*i)->start()) {
724 if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
729 ts->update_bar_offset_from_bbt (*meter);
736 recompute_map (false);
741 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
744 Glib::Threads::RWLock::WriterLock lm (lock);
745 MeterSection& first (first_meter());
747 if (ms.start() != first.start()) {
748 remove_meter_locked (ms);
749 add_meter_locked (meter, where, true);
751 /* cannot move the first meter section */
752 *static_cast<Meter*>(&first) = meter;
753 recompute_map (true);
757 PropertyChanged (PropertyChange ());
761 TempoMap::add_meter (const Meter& meter, BBT_Time where)
764 Glib::Threads::RWLock::WriterLock lm (lock);
765 add_meter_locked (meter, where, true);
770 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
775 PropertyChanged (PropertyChange ());
779 TempoMap::add_meter_locked (const Meter& meter, BBT_Time where, bool recompute)
781 /* a new meter always starts a new bar on the first beat. so
782 round the start time appropriately. remember that
783 `where' is based on the existing tempo map, not
784 the result after we insert the new meter.
788 if (where.beats != 1) {
793 /* new meters *always* start on a beat. */
796 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
799 recompute_map (true);
805 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
807 Tempo newtempo (beats_per_minute, note_type);
810 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
811 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
813 Glib::Threads::RWLock::WriterLock lm (lock);
814 *((Tempo*) t) = newtempo;
815 recompute_map (false);
817 PropertyChanged (PropertyChange ());
824 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
826 Tempo newtempo (beats_per_minute, note_type);
832 /* find the TempoSection immediately preceding "where"
835 for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
837 if ((*i)->frame() > where) {
843 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
853 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
863 Glib::Threads::RWLock::WriterLock lm (lock);
864 /* cannot move the first tempo section */
865 *((Tempo*)prev) = newtempo;
866 recompute_map (false);
869 PropertyChanged (PropertyChange ());
873 TempoMap::first_meter () const
875 const MeterSection *m = 0;
877 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
878 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
883 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
884 abort(); /*NOTREACHED*/
889 TempoMap::first_meter ()
893 /* CALLER MUST HOLD LOCK */
895 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
896 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
901 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
902 abort(); /*NOTREACHED*/
907 TempoMap::first_tempo () const
909 const TempoSection *t = 0;
911 /* CALLER MUST HOLD LOCK */
913 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
914 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
919 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
920 abort(); /*NOTREACHED*/
925 TempoMap::first_tempo ()
929 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
930 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
935 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
936 abort(); /*NOTREACHED*/
941 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
943 /* CALLER MUST HOLD WRITE LOCK */
945 MeterSection* meter = 0;
946 TempoSection* tempo = 0;
947 double current_frame;
949 Metrics::iterator next_metric;
953 /* we will actually stop once we hit
960 MetricSectionSorter cmp;
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 const 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 const 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 const 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 const 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 const 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 << "recompute map - setting meter 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);
1223 /* XX most of this is utter crap */
1224 if (n_meters() == 1) {
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 /* HOLD THE READER 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 - accumulated_ticks_to_prev;
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 Glib::Threads::RWLock::ReaderLock lm (lock);
1440 return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1444 TempoMap::frame_at_beat (double beat) const
1446 Glib::Threads::RWLock::ReaderLock lm (lock);
1448 return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1452 TempoMap::frame_time (const BBT_Time& bbt)
1455 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1459 if (bbt.beats < 1) {
1460 throw std::logic_error ("beats are counted from one");
1463 Glib::Threads::RWLock::ReaderLock lm (lock);
1465 Metrics::const_iterator i;
1466 uint32_t accumulated_bars = 0;
1468 MeterSection* prev_ms = &first_meter();
1470 for (i = metrics.begin(); i != metrics.end(); ++i) {
1472 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1473 int32_t const bims = bars_in_meter_section (m);
1475 if (bims < 0 || bbt.bars <= (accumulated_bars + bims)) {
1479 accumulated_bars += bims;
1485 uint32_t remaining_bars = bbt.bars - accumulated_bars - 1; // back to zero - based bars
1486 double const ticks_within_prev_taken_by_remaining_bars = remaining_bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat;
1487 double const ticks_after_space_used_by_bars = ((bbt.beats - 1) * BBT_Time::ticks_per_beat) + bbt.ticks; // back to zero - based beats
1488 double const ticks_target = ticks_within_prev_taken_by_remaining_bars + ticks_after_space_used_by_bars;
1490 TempoSection* prev_ts = &first_tempo();
1491 double accumulated_ticks = 0.0;
1492 double accumulated_ticks_to_prev = 0.0;
1496 for (i = metrics.begin(); i != metrics.end(); ++i) {
1498 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1499 if (t->frame() < prev_ms->frame()) {
1503 if (cnt > 0 && t->frame() > prev_ts->frame()) {
1504 /*find the number of ticke in this section */
1505 framepos_t const time = t->frame() - prev_ts->frame();
1506 framepos_t const last_time = t->frame() - prev_ts->frame();
1507 double const last_beats_per_minute = t->beats_per_minute();
1508 accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1511 if (ticks_target < accumulated_ticks) {
1512 double const ticks_in_section = ticks_target - accumulated_ticks_to_prev;
1513 framepos_t const section_start_time = prev_ts->frame();
1514 framepos_t const last_time = t->frame() - prev_ts->frame();
1515 double const last_beats_per_minute = t->beats_per_minute();
1516 framepos_t const ret = prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start_time;
1519 accumulated_ticks_to_prev = accumulated_ticks;
1525 /*treat this ts as constant tempo */
1526 double const ticks_in_this_ts = ticks_target - accumulated_ticks_to_prev;
1527 double const dtime = (ticks_in_this_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate);
1528 framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1534 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1537 Glib::Threads::RWLock::ReaderLock lm (lock);
1539 Metrics::const_iterator i;
1540 TempoSection* first = 0;
1541 TempoSection* second = 0;
1543 for (i = metrics.begin(); i != metrics.end(); ++i) {
1546 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1548 if ((*i)->frame() > pos) {
1556 if (first && second) {
1557 framepos_t const last_time = second->frame() - first->frame();
1558 double const last_beats_per_minute = second->beats_per_minute();
1560 framepos_t const time = pos - first->frame();
1561 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1562 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1564 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1566 return time_at_bbt - time;
1569 double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1570 return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1574 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1576 return round_to_type (fr, dir, Bar);
1580 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1582 return round_to_type (fr, dir, Beat);
1586 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1588 Glib::Threads::RWLock::ReaderLock lm (lock);
1590 uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1591 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1592 uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1594 ticks -= beats * BBT_Time::ticks_per_beat;
1597 /* round to next (or same iff dir == RoundUpMaybe) */
1599 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1601 if (mod == 0 && dir == RoundUpMaybe) {
1602 /* right on the subdivision, which is fine, so do nothing */
1604 } else if (mod == 0) {
1605 /* right on the subdivision, so the difference is just the subdivision ticks */
1606 ticks += ticks_one_subdivisions_worth;
1609 /* not on subdivision, compute distance to next subdivision */
1611 ticks += ticks_one_subdivisions_worth - mod;
1614 if (ticks >= BBT_Time::ticks_per_beat) {
1615 ticks -= BBT_Time::ticks_per_beat;
1617 } else if (dir < 0) {
1619 /* round to previous (or same iff dir == RoundDownMaybe) */
1621 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1623 if (difference == 0 && dir == RoundDownAlways) {
1624 /* right on the subdivision, but force-rounding down,
1625 so the difference is just the subdivision ticks */
1626 difference = ticks_one_subdivisions_worth;
1629 if (ticks < difference) {
1630 ticks = BBT_Time::ticks_per_beat - ticks;
1632 ticks -= difference;
1636 /* round to nearest */
1639 /* compute the distance to the previous and next subdivision */
1641 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1643 /* closer to the next subdivision, so shift forward */
1645 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1647 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1649 if (ticks > BBT_Time::ticks_per_beat) {
1651 ticks -= BBT_Time::ticks_per_beat;
1652 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1655 } else if (rem > 0) {
1657 /* closer to previous subdivision, so shift backward */
1661 /* can't go backwards past zero, so ... */
1664 /* step back to previous beat */
1666 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1667 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1669 ticks = lrint (ticks - rem);
1670 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1673 /* on the subdivision, do nothing */
1676 return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1680 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1682 Glib::Threads::RWLock::ReaderLock lm (lock);
1684 double const beat_at_framepos = beat_at_frame (frame);
1686 BBT_Time bbt (beats_to_bbt (beat_at_framepos));
1691 /* find bar previous to 'frame' */
1694 return frame_time (bbt);
1696 } else if (dir > 0) {
1697 /* find bar following 'frame' */
1701 return frame_time (bbt);
1703 /* true rounding: find nearest bar */
1704 framepos_t raw_ft = frame_time (bbt);
1707 framepos_t prev_ft = frame_time (bbt);
1709 framepos_t next_ft = frame_time (bbt);
1711 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
1722 return frame_at_beat (floor (beat_at_framepos));
1723 } else if (dir > 0) {
1724 return frame_at_beat (ceil (beat_at_framepos));
1726 return frame_at_beat (floor (beat_at_framepos + 0.5));
1735 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1736 framepos_t lower, framepos_t upper)
1738 Glib::Threads::RWLock::ReaderLock lm (lock);
1739 uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1740 uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1742 while (cnt <= upper_beat) {
1743 framecnt_t const pos = frame_at_beat (cnt);
1744 MeterSection const meter = meter_section_at (pos);
1745 Tempo const tempo = tempo_at (pos);
1746 BBT_Time const bbt = beats_to_bbt ((double) cnt);
1748 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1754 TempoMap::tempo_section_after (framepos_t frame) const
1756 Glib::Threads::RWLock::ReaderLock lm (lock);
1758 Metrics::const_iterator i;
1759 TempoSection* next = 0;
1761 for (i = metrics.begin(); i != metrics.end(); ++i) {
1764 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1766 if ((*i)->frame() > frame) {
1778 TempoMap::tempo_section_at (framepos_t frame) const
1780 Glib::Threads::RWLock::ReaderLock lm (lock);
1782 Metrics::const_iterator i;
1783 TempoSection* prev = 0;
1785 for (i = metrics.begin(); i != metrics.end(); ++i) {
1788 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1790 if ((*i)->frame() > frame) {
1800 abort(); /*NOTREACHED*/
1806 /* don't use this to calculate length (the tempo is only correct for this frame).
1807 do that stuff based on the beat_at_frame and frame_at_beat api
1810 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1812 Glib::Threads::RWLock::ReaderLock lm (lock);
1814 const TempoSection* ts_at = &tempo_section_at (frame);
1815 const TempoSection* ts_after = tempo_section_after (frame);
1818 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
1820 /* must be treated as constant tempo */
1821 return ts_at->frames_per_beat (_frame_rate);
1825 TempoMap::tempo_at (framepos_t frame) const
1827 Glib::Threads::RWLock::ReaderLock lm (lock);
1829 TempoMetric m (metric_at (frame));
1830 TempoSection* prev_ts = 0;
1832 Metrics::const_iterator i;
1834 for (i = metrics.begin(); i != metrics.end(); ++i) {
1836 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1837 if ((prev_ts) && t->frame() > frame) {
1838 /* this is the one past frame */
1839 framepos_t const time = frame - prev_ts->frame();
1840 framepos_t const last_time = t->frame() - prev_ts->frame();
1841 double const last_beats_per_minute = t->beats_per_minute();
1842 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1843 Tempo const ret_tempo (ret, m.tempo().note_type ());
1855 TempoMap::meter_section_at (framepos_t frame) const
1857 Glib::Threads::RWLock::ReaderLock lm (lock);
1858 Metrics::const_iterator i;
1859 MeterSection* prev = 0;
1861 for (i = metrics.begin(); i != metrics.end(); ++i) {
1864 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1866 if ((*i)->frame() > frame) {
1876 abort(); /*NOTREACHED*/
1883 TempoMap::meter_at (framepos_t frame) const
1885 TempoMetric m (metric_at (frame));
1890 TempoMap::get_state ()
1892 Metrics::const_iterator i;
1893 XMLNode *root = new XMLNode ("TempoMap");
1896 Glib::Threads::RWLock::ReaderLock lm (lock);
1897 for (i = metrics.begin(); i != metrics.end(); ++i) {
1898 root->add_child_nocopy ((*i)->get_state());
1906 TempoMap::set_state (const XMLNode& node, int /*version*/)
1909 Glib::Threads::RWLock::WriterLock lm (lock);
1912 XMLNodeConstIterator niter;
1913 Metrics old_metrics (metrics);
1914 MeterSection* last_meter = 0;
1917 nlist = node.children();
1919 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1920 XMLNode* child = *niter;
1922 if (child->name() == TempoSection::xml_state_node_name) {
1925 TempoSection* ts = new TempoSection (*child);
1926 metrics.push_back (ts);
1928 if (ts->bar_offset() < 0.0) {
1930 ts->update_bar_offset_from_bbt (*last_meter);
1935 catch (failed_constructor& err){
1936 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1937 metrics = old_metrics;
1941 } else if (child->name() == MeterSection::xml_state_node_name) {
1944 MeterSection* ms = new MeterSection (*child);
1945 metrics.push_back (ms);
1949 catch (failed_constructor& err) {
1950 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1951 metrics = old_metrics;
1957 if (niter == nlist.end()) {
1958 MetricSectionSorter cmp;
1962 /* check for multiple tempo/meters at the same location, which
1963 ardour2 somehow allowed.
1966 Metrics::iterator prev = metrics.end();
1967 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1968 if (prev != metrics.end()) {
1969 if (dynamic_cast<MeterSection*>(*prev) && dynamic_cast<MeterSection*>(*i)) {
1970 if ((*prev)->start() == (*i)->start()) {
1971 cerr << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1972 error << string_compose (_("Multiple meter definitions found at %1"), (*prev)->start()) << endmsg;
1975 } else if (dynamic_cast<TempoSection*>(*prev) && dynamic_cast<TempoSection*>(*i)) {
1976 if ((*prev)->start() == (*i)->start()) {
1977 cerr << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1978 error << string_compose (_("Multiple tempo definitions found at %1"), (*prev)->start()) << endmsg;
1986 recompute_map (true, -1);
1989 PropertyChanged (PropertyChange ());
1995 TempoMap::dump (std::ostream& o) const
1997 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1998 const MeterSection* m;
1999 const TempoSection* t;
2001 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2003 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2004 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? "
2005 << t->movable() << ')' << endl;
2006 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2007 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
2008 << " (movable? " << m->movable() << ')' << endl;
2014 TempoMap::n_tempos() const
2016 Glib::Threads::RWLock::ReaderLock lm (lock);
2019 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2020 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2029 TempoMap::n_meters() const
2031 Glib::Threads::RWLock::ReaderLock lm (lock);
2034 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2035 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2044 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2047 Glib::Threads::RWLock::WriterLock lm (lock);
2048 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2049 if ((*i)->frame() >= where && (*i)->movable ()) {
2050 (*i)->set_frame ((*i)->frame() + amount);
2054 /* now reset the BBT time of all metrics, based on their new
2055 * audio time. This is the only place where we do this reverse
2059 Metrics::iterator i;
2060 const MeterSection* meter;
2061 const TempoSection* tempo;
2065 meter = &first_meter ();
2066 tempo = &first_tempo ();
2071 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2074 MetricSection* prev = 0;
2076 for (i = metrics.begin(); i != metrics.end(); ++i) {
2079 TempoMetric metric (*meter, *tempo);
2082 metric.set_start (prev->start());
2083 metric.set_frame (prev->frame());
2085 // metric will be at frames=0 bbt=1|1|0 by default
2086 // which is correct for our purpose
2089 bbt_time ((*i)->frame(), bbt);
2091 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2097 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2098 /* round up to next beat */
2104 if (bbt.beats != 1) {
2105 /* round up to next bar */
2111 // cerr << bbt << endl;
2113 (*i)->set_start (bbt);
2115 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2117 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2118 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2120 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
2122 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2123 abort(); /*NOTREACHED*/
2129 recompute_map (true);
2133 PropertyChanged (PropertyChange ());
2136 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2140 std::list<MetricSection*> metric_kill_list;
2142 TempoSection* last_tempo = NULL;
2143 MeterSection* last_meter = NULL;
2144 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2145 bool meter_after = false; // is there a meter marker likewise?
2147 Glib::Threads::RWLock::WriterLock lm (lock);
2148 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2149 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2150 metric_kill_list.push_back(*i);
2151 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2154 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2158 else if ((*i)->frame() >= where) {
2159 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2160 (*i)->set_frame ((*i)->frame() - amount);
2161 if ((*i)->frame() == where) {
2162 // marker was immediately after end of range
2163 tempo_after = dynamic_cast<TempoSection*> (*i);
2164 meter_after = dynamic_cast<MeterSection*> (*i);
2170 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2171 if (last_tempo && !tempo_after) {
2172 metric_kill_list.remove(last_tempo);
2173 last_tempo->set_frame(where);
2176 if (last_meter && !meter_after) {
2177 metric_kill_list.remove(last_meter);
2178 last_meter->set_frame(where);
2182 //remove all the remaining metrics
2183 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2189 recompute_map (true);
2192 PropertyChanged (PropertyChange ());
2196 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2197 * pos can be -ve, if required.
2200 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2202 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2205 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2207 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2209 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2212 /** Add the BBT interval op to pos and return the result */
2214 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2216 cerr << "framepos_plus_bbt - untested" << endl;
2217 Glib::Threads::RWLock::ReaderLock lm (lock);
2219 Metrics::const_iterator i;
2220 const MeterSection* meter;
2221 const MeterSection* m;
2222 const TempoSection* tempo;
2223 const TempoSection* next_tempo = 0;
2224 const TempoSection* t;
2225 double frames_per_beat;
2226 framepos_t effective_pos = max (pos, (framepos_t) 0);
2228 meter = &first_meter ();
2229 tempo = &first_tempo ();
2234 /* find the starting metrics for tempo & meter */
2236 for (i = metrics.begin(); i != metrics.end(); ++i) {
2238 if ((*i)->frame() > effective_pos) {
2242 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2244 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2249 for (i = metrics.begin(); i != metrics.end(); ++i) {
2250 if ((*i)->frame() > effective_pos) {
2251 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2260 meter -> the Meter for "pos"
2261 tempo -> the Tempo for "pos"
2262 next_tempo -> the Tempo after "pos" or 0
2263 i -> for first new metric after "pos", possibly metrics.end()
2266 /* now comes the complicated part. we have to add one beat a time,
2267 checking for a new metric on every beat.
2271 /* fpb is used for constant tempo */
2272 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2279 /* check if we need to use a new metric section: has adding frames moved us
2280 to or after the start of the next metric section? in which case, use it.
2283 if (i != metrics.end()) {
2284 if ((*i)->frame() <= pos) {
2286 /* about to change tempo or meter, so add the
2287 * number of frames for the bars we've just
2288 * traversed before we change the
2289 * frames_per_beat value.
2292 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2297 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2299 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2304 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2306 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2310 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2317 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2319 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2326 /* given the current meter, have we gone past the end of the bar ? */
2331 /* check if we need to use a new metric section: has adding frames moved us
2332 to or after the start of the next metric section? in which case, use it.
2335 if (i != metrics.end()) {
2336 if ((*i)->frame() <= pos) {
2338 /* about to change tempo or meter, so add the
2339 * number of frames for the beats we've just
2340 * traversed before we change the
2341 * frames_per_beat value.
2344 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2349 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2351 pos += llrint (beats * frames_per_beat);
2356 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2358 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2362 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2368 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2370 pos += llrint (beats * frames_per_beat);
2374 pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2381 /** Count the number of beats that are equivalent to distance when going forward,
2385 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2387 return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2391 bool operator() (const BBT_Time& a, const BBT_Time& b) {
2397 operator<< (std::ostream& o, const Meter& m) {
2398 return o << m.divisions_per_bar() << '/' << m.note_divisor();
2402 operator<< (std::ostream& o, const Tempo& t) {
2403 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2407 operator<< (std::ostream& o, const MetricSection& section) {
2409 o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2411 const TempoSection* ts;
2412 const MeterSection* ms;
2414 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
2415 o << *((const Tempo*) ts);
2416 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
2417 o << *((const Meter*) ms);