merge from thirdparty rubberband 1.3 @ 4901
[ardour2.git] / libs / ardour / tempo.cc
blobab7b7c096eeb619a0f7bc5fbf117c0b035a72e03
1 /*
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.
20 #include <algorithm>
21 #include <unistd.h>
23 #include <cmath>
25 #include <sigc++/bind.h>
27 #include <glibmm/thread.h>
28 #include <pbd/xml++.h>
29 #include <ardour/tempo.h>
30 #include <ardour/utils.h>
32 #include "i18n.h"
33 #include <locale.h>
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace PBD;
39 /* _default tempo is 4/4 qtr=120 */
41 Meter TempoMap::_default_meter (4.0, 4.0);
42 Tempo TempoMap::_default_tempo (120.0);
44 const double Meter::ticks_per_beat = 1920.0;
46 double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
48 return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
51 /***********************************************************************/
53 double
54 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
56 return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
59 /***********************************************************************/
61 const string TempoSection::xml_state_node_name = "Tempo";
63 TempoSection::TempoSection (const XMLNode& node)
64 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
66 const XMLProperty *prop;
67 BBT_Time start;
68 LocaleGuard lg (X_("POSIX"));
70 if ((prop = node.property ("start")) == 0) {
71 error << _("TempoSection XML node has no \"start\" property") << endmsg;
72 throw failed_constructor();
75 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
76 &start.bars,
77 &start.beats,
78 &start.ticks) < 3) {
79 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
80 throw failed_constructor();
83 set_start (start);
85 if ((prop = node.property ("beats-per-minute")) == 0) {
86 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
87 throw failed_constructor();
90 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
91 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
92 throw failed_constructor();
95 if ((prop = node.property ("note-type")) == 0) {
96 /* older session, make note type be quarter by default */
97 _note_type = 4.0;
98 } else {
99 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
100 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
101 throw failed_constructor();
105 if ((prop = node.property ("movable")) == 0) {
106 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
107 throw failed_constructor();
110 set_movable (prop->value() == "yes");
113 XMLNode&
114 TempoSection::get_state() const
116 XMLNode *root = new XMLNode (xml_state_node_name);
117 char buf[256];
118 LocaleGuard lg (X_("POSIX"));
120 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
121 start().bars,
122 start().beats,
123 start().ticks);
124 root->add_property ("start", buf);
125 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
126 root->add_property ("beats-per-minute", buf);
127 snprintf (buf, sizeof (buf), "%f", _note_type);
128 root->add_property ("note-type", buf);
129 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
130 root->add_property ("movable", buf);
132 return *root;
135 /***********************************************************************/
137 const string MeterSection::xml_state_node_name = "Meter";
139 MeterSection::MeterSection (const XMLNode& node)
140 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
142 const XMLProperty *prop;
143 BBT_Time start;
144 LocaleGuard lg (X_("POSIX"));
146 if ((prop = node.property ("start")) == 0) {
147 error << _("MeterSection XML node has no \"start\" property") << endmsg;
148 throw failed_constructor();
151 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
152 &start.bars,
153 &start.beats,
154 &start.ticks) < 3) {
155 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
156 throw failed_constructor();
159 set_start (start);
161 if ((prop = node.property ("beats-per-bar")) == 0) {
162 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
163 throw failed_constructor();
166 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
167 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
168 throw failed_constructor();
171 if ((prop = node.property ("note-type")) == 0) {
172 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
173 throw failed_constructor();
176 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
177 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
178 throw failed_constructor();
181 if ((prop = node.property ("movable")) == 0) {
182 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
183 throw failed_constructor();
186 set_movable (prop->value() == "yes");
189 XMLNode&
190 MeterSection::get_state() const
192 XMLNode *root = new XMLNode (xml_state_node_name);
193 char buf[256];
194 LocaleGuard lg (X_("POSIX"));
196 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
197 start().bars,
198 start().beats,
199 start().ticks);
200 root->add_property ("start", buf);
201 snprintf (buf, sizeof (buf), "%f", _note_type);
202 root->add_property ("note-type", buf);
203 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
204 root->add_property ("beats-per-bar", buf);
205 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
206 root->add_property ("movable", buf);
208 return *root;
211 /***********************************************************************/
213 struct MetricSectionSorter {
214 bool operator() (const MetricSection* a, const MetricSection* b) {
215 return a->start() < b->start();
219 TempoMap::TempoMap (nframes_t fr)
221 metrics = new Metrics;
222 _frame_rate = fr;
223 last_bbt_valid = false;
224 BBT_Time start;
226 start.bars = 1;
227 start.beats = 1;
228 start.ticks = 0;
230 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
231 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
233 t->set_movable (false);
234 m->set_movable (false);
236 /* note: frame time is correct (zero) for both of these */
238 metrics->push_back (t);
239 metrics->push_back (m);
242 TempoMap::~TempoMap ()
247 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
249 if (when == section.start() || !section.movable()) {
250 return -1;
253 Glib::RWLock::WriterLock lm (lock);
254 MetricSectionSorter cmp;
256 if (when.beats != 1) {
258 /* position by audio frame, then recompute BBT timestamps from the audio ones */
260 nframes_t frame = frame_time (when);
261 // cerr << "nominal frame time = " << frame << endl;
263 nframes_t prev_frame = round_to_type (frame, -1, Beat);
264 nframes_t next_frame = round_to_type (frame, 1, Beat);
266 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
268 /* use the closest beat */
270 if ((frame - prev_frame) < (next_frame - frame)) {
271 frame = prev_frame;
272 } else {
273 frame = next_frame;
276 // cerr << "actual frame time = " << frame << endl;
277 section.set_frame (frame);
278 // cerr << "frame time = " << section.frame() << endl;
279 timestamp_metrics (false);
280 // cerr << "new BBT time = " << section.start() << endl;
281 metrics->sort (cmp);
283 } else {
285 /* positioned at bar start already, so just put it there */
287 section.set_start (when);
288 metrics->sort (cmp);
289 timestamp_metrics (true);
293 return 0;
296 void
297 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
299 if (move_metric_section (tempo, when) == 0) {
300 StateChanged (Change (0));
304 void
305 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
307 if (move_metric_section (meter, when) == 0) {
308 StateChanged (Change (0));
312 void
313 TempoMap::remove_tempo (const TempoSection& tempo)
315 bool removed = false;
318 Glib::RWLock::WriterLock lm (lock);
319 Metrics::iterator i;
321 for (i = metrics->begin(); i != metrics->end(); ++i) {
322 if (dynamic_cast<TempoSection*> (*i) != 0) {
323 if (tempo.frame() == (*i)->frame()) {
324 if ((*i)->movable()) {
325 metrics->erase (i);
326 removed = true;
327 break;
334 if (removed) {
335 StateChanged (Change (0));
339 void
340 TempoMap::remove_meter (const MeterSection& tempo)
342 bool removed = false;
345 Glib::RWLock::WriterLock lm (lock);
346 Metrics::iterator i;
348 for (i = metrics->begin(); i != metrics->end(); ++i) {
349 if (dynamic_cast<MeterSection*> (*i) != 0) {
350 if (tempo.frame() == (*i)->frame()) {
351 if ((*i)->movable()) {
352 metrics->erase (i);
353 removed = true;
354 break;
361 if (removed) {
362 StateChanged (Change (0));
366 void
367 TempoMap::do_insert (MetricSection* section, bool with_bbt)
369 Metrics::iterator i;
371 for (i = metrics->begin(); i != metrics->end(); ++i) {
373 if (with_bbt) {
374 if ((*i)->start() < section->start()) {
375 continue;
377 } else {
378 if ((*i)->frame() < section->frame()) {
379 continue;
383 metrics->insert (i, section);
384 break;
387 if (i == metrics->end()) {
388 metrics->insert (metrics->end(), section);
391 timestamp_metrics (with_bbt);
394 void
395 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
398 Glib::RWLock::WriterLock lm (lock);
400 /* new tempos always start on a beat */
402 where.ticks = 0;
404 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
407 StateChanged (Change (0));
410 void
411 TempoMap::add_tempo (const Tempo& tempo, nframes_t where)
414 Glib::RWLock::WriterLock lm (lock);
415 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
418 StateChanged (Change (0));
421 void
422 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
424 bool replaced = false;
427 Glib::RWLock::WriterLock lm (lock);
428 Metrics::iterator i;
430 for (i = metrics->begin(); i != metrics->end(); ++i) {
431 TempoSection *ts;
433 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
435 *((Tempo *) ts) = replacement;
437 replaced = true;
438 timestamp_metrics (true);
440 break;
445 if (replaced) {
446 StateChanged (Change (0));
450 void
451 TempoMap::add_meter (const Meter& meter, BBT_Time where)
454 Glib::RWLock::WriterLock lm (lock);
456 /* a new meter always starts a new bar on the first beat. so
457 round the start time appropriately. remember that
458 `where' is based on the existing tempo map, not
459 the result after we insert the new meter.
463 if (where.beats != 1) {
464 where.beats = 1;
465 where.bars++;
468 /* new meters *always* start on a beat. */
470 where.ticks = 0;
472 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
475 StateChanged (Change (0));
478 void
479 TempoMap::add_meter (const Meter& meter, nframes_t where)
482 Glib::RWLock::WriterLock lm (lock);
483 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
486 StateChanged (Change (0));
489 void
490 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
492 bool replaced = false;
495 Glib::RWLock::WriterLock lm (lock);
496 Metrics::iterator i;
498 for (i = metrics->begin(); i != metrics->end(); ++i) {
499 MeterSection *ms;
500 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
502 *((Meter*) ms) = replacement;
504 replaced = true;
505 timestamp_metrics (true);
506 break;
511 if (replaced) {
512 StateChanged (Change (0));
516 void
517 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
519 Tempo newtempo (beats_per_minute, note_type);
520 TempoSection* t;
522 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
523 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
524 *((Tempo*) t) = newtempo;
525 StateChanged (Change (0));
526 break;
531 void
532 TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type)
534 Tempo newtempo (beats_per_minute, note_type);
536 TempoSection* prev;
537 TempoSection* first;
538 Metrics::iterator i;
540 /* find the TempoSection immediately preceding "where"
543 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
545 if ((*i)->frame() > where) {
546 break;
549 TempoSection* t;
551 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
552 if (!first) {
553 first = t;
555 prev = t;
559 if (!prev) {
560 if (!first) {
561 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
562 return;
565 prev = first;
568 /* reset */
570 *((Tempo*)prev) = newtempo;
571 StateChanged (Change (0));
574 const MeterSection&
575 TempoMap::first_meter () const
577 const MeterSection *m = 0;
579 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
580 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
581 return *m;
585 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
586 /*NOTREACHED*/
587 return *m;
590 const TempoSection&
591 TempoMap::first_tempo () const
593 const TempoSection *t = 0;
595 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
596 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
597 return *t;
601 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
602 /*NOTREACHED*/
603 return *t;
606 void
607 TempoMap::timestamp_metrics (bool use_bbt)
609 Metrics::iterator i;
610 const Meter* meter;
611 const Tempo* tempo;
612 Meter *m;
613 Tempo *t;
615 meter = &first_meter ();
616 tempo = &first_tempo ();
618 if (use_bbt) {
620 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
622 nframes_t current = 0;
623 nframes_t section_frames;
624 BBT_Time start;
625 BBT_Time end;
627 for (i = metrics->begin(); i != metrics->end(); ++i) {
629 end = (*i)->start();
631 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
633 current += section_frames;
635 start = end;
637 (*i)->set_frame (current);
639 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
640 tempo = t;
641 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
642 meter = m;
643 } else {
644 fatal << _("programming error: unhandled MetricSection type") << endmsg;
645 /*NOTREACHED*/
649 } else {
651 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
653 bool first = true;
654 MetricSection* prev = 0;
656 for (i = metrics->begin(); i != metrics->end(); ++i) {
658 BBT_Time bbt;
659 Metric metric (*meter, *tempo);
661 if (prev) {
662 metric.set_start (prev->start());
663 } else {
664 // metric will be at frames=0 bbt=1|1|0 by default
665 // which is correct for our purpose
668 bbt_time_with_metric ((*i)->frame(), bbt, metric);
670 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
673 if (first) {
674 first = false;
675 } else {
677 if (bbt.ticks > Meter::ticks_per_beat/2) {
678 /* round up to next beat */
679 bbt.beats += 1;
682 bbt.ticks = 0;
684 if (bbt.beats != 1) {
685 /* round up to next bar */
686 bbt.bars += 1;
687 bbt.beats = 1;
691 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
693 (*i)->set_start (bbt);
695 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
696 tempo = t;
697 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
698 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
699 meter = m;
700 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
701 } else {
702 fatal << _("programming error: unhandled MetricSection type") << endmsg;
703 /*NOTREACHED*/
706 prev = (*i);
710 // dump (cerr);
711 // cerr << "###############################################\n\n\n" << endl;
715 TempoMap::Metric
716 TempoMap::metric_at (nframes_t frame) const
718 Metric m (first_meter(), first_tempo());
719 const Meter* meter;
720 const Tempo* tempo;
722 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
723 at something, because we insert the default tempo and meter during
724 TempoMap construction.
726 now see if we can find better candidates.
729 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
731 if ((*i)->frame() > frame) {
732 break;
735 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
736 m.set_tempo (*tempo);
737 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
738 m.set_meter (*meter);
741 m.set_frame ((*i)->frame ());
742 m.set_start ((*i)->start ());
745 return m;
748 TempoMap::Metric
749 TempoMap::metric_at (BBT_Time bbt) const
751 Metric m (first_meter(), first_tempo());
752 const Meter* meter;
753 const Tempo* tempo;
755 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
756 at something, because we insert the default tempo and meter during
757 TempoMap construction.
759 now see if we can find better candidates.
762 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
764 BBT_Time section_start ((*i)->start());
766 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
767 break;
770 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
771 m.set_tempo (*tempo);
772 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
773 m.set_meter (*meter);
776 m.set_frame ((*i)->frame ());
777 m.set_start (section_start);
780 return m;
783 void
784 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
787 Glib::RWLock::ReaderLock lm (lock);
788 bbt_time_unlocked (frame, bbt);
792 void
793 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
795 bbt_time_with_metric (frame, bbt, metric_at (frame));
798 void
799 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
801 nframes_t frame_diff;
803 uint32_t xtra_bars = 0;
804 double xtra_beats = 0;
805 double beats = 0;
807 // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
809 const double beats_per_bar = metric.meter().beats_per_bar();
810 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
811 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
813 /* now compute how far beyond that point we actually are. */
815 frame_diff = frame - metric.frame();
817 // cerr << "----\tdelta = " << frame_diff << endl;
819 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
820 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
821 xtra_beats = (double) frame_diff / beat_frames;
823 // cerr << "---\tmeaning " << xtra_bars << " xtra bars and " << xtra_beats << " xtra beats\n";
825 /* and set the returned value */
827 /* and correct beat/bar shifts to match the meter.
828 remember: beat and bar counting is 1-based,
829 not zero-based
830 also the meter may contain a fraction
833 bbt.bars = metric.start().bars + xtra_bars;
835 beats = (double) metric.start().beats + xtra_beats;
837 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
839 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
840 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
841 bbt.beats = (uint32_t) floor(beats);
843 // cerr << "-----\t RETURN " << bbt << endl;
846 nframes_t
847 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
849 /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
850 that means that the beats and ticks should be inside a bar
853 nframes_t frames = 0;
854 nframes_t start_frame = 0;
855 nframes_t end_frame = 0;
857 Metric m = metric_at (start);
859 uint32_t bar_offset = start.bars - m.start().bars;
861 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
862 + start.ticks/Meter::ticks_per_beat;
865 start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
867 m = metric_at(end);
869 bar_offset = end.bars - m.start().bars;
871 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
872 + end.ticks/Meter::ticks_per_beat;
874 end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
876 frames = end_frame - start_frame;
878 return frames;
882 nframes_t
883 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
885 /* this is used in timestamping the metrics by actually counting the beats */
887 nframes_t frames = 0;
888 uint32_t bar = start.bars;
889 double beat = (double) start.beats;
890 double beats_counted = 0;
891 double beats_per_bar = 0;
892 double beat_frames = 0;
894 beats_per_bar = meter.beats_per_bar();
895 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
897 frames = 0;
899 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
901 if (beat >= beats_per_bar) {
902 beat = 1;
903 ++bar;
904 ++beats_counted;
906 if (beat > beats_per_bar) {
908 /* this is a fractional beat at the end of a fractional bar
909 so it should only count for the fraction
912 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
915 } else {
916 ++beat;
917 ++beats_counted;
921 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
922 // << " bpb were " << beats_per_bar
923 // << " fpb was " << beat_frames
924 // << endl;
926 frames = (nframes_t) floor (beats_counted * beat_frames);
928 return frames;
932 nframes_t
933 TempoMap::frame_time (const BBT_Time& bbt) const
935 BBT_Time start ; /* 1|1|0 */
937 return count_frames_between ( start, bbt);
940 nframes_t
941 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
943 nframes_t frames = 0;
945 BBT_Time when;
946 bbt_time(pos,when);
949 Glib::RWLock::ReaderLock lm (lock);
950 frames = bbt_duration_at_unlocked (when, bbt,dir);
953 return frames;
956 nframes_t
957 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
960 nframes_t frames = 0;
962 double beats_per_bar;
963 BBT_Time result;
965 result.bars = max(1U,when.bars + dir * bbt.bars) ;
966 result.beats = 1;
967 result.ticks = 0;
969 Metric metric = metric_at(result);
970 beats_per_bar = metric.meter().beats_per_bar();
974 /*reduce things to legal bbt values
975 we have to handle possible fractional=shorter beats at the end of measures
976 and things like 0|11|9000 as a duration in a 4.5/4 measure
977 the musical decision is that the fractional beat is also a beat , although a shorter one
981 if (dir >= 0) {
982 result.beats = when.beats + bbt.beats;
983 result.ticks = when.ticks + bbt.ticks;
985 while (result.beats >= (beats_per_bar+1)) {
986 result.bars++;
987 result.beats -= (uint32_t) ceil(beats_per_bar);
988 metric = metric_at(result); // maybe there is a meter change
989 beats_per_bar = metric.meter().beats_per_bar();
992 /*we now counted the beats and landed in the target measure, now deal with ticks
993 this seems complicated, but we want to deal with the corner case of a sequence of time signatures like 0.2/4-0.7/4
994 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
997 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1000 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1001 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
1002 : Meter::ticks_per_beat );
1004 while (result.ticks >= ticks_at_beat) {
1005 result.beats++;
1006 result.ticks -= ticks_at_beat;
1007 if (result.beats >= (beats_per_bar+1)) {
1008 result.bars++;
1009 result.beats = 1;
1010 metric = metric_at(result); // maybe there is a meter change
1011 beats_per_bar = metric.meter().beats_per_bar();
1013 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1014 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
1015 : Meter::ticks_per_beat);
1020 } else {
1021 uint32_t b = bbt.beats;
1023 /* count beats */
1024 while( b > when.beats ) {
1026 result.bars = max(1U,result.bars-- ) ;
1027 metric = metric_at(result); // maybe there is a meter change
1028 beats_per_bar = metric.meter().beats_per_bar();
1029 if (b >= ceil(beats_per_bar)) {
1031 b -= (uint32_t) ceil(beats_per_bar);
1032 } else {
1033 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
1036 result.beats = when.beats - b;
1038 /*count ticks */
1040 if (bbt.ticks <= when.ticks) {
1041 result.ticks = when.ticks - bbt.ticks;
1042 } else {
1044 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1045 uint32_t t = bbt.ticks - when.ticks;
1047 do {
1049 if (result.beats == 1) {
1050 result.bars = max(1U,result.bars-- ) ;
1051 metric = metric_at(result); // maybe there is a meter change
1052 beats_per_bar = metric.meter().beats_per_bar();
1053 result.beats = (uint32_t) ceil(beats_per_bar);
1054 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
1055 } else {
1056 result.beats --;
1057 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1060 if (t <= ticks_at_beat) {
1061 result.ticks = ticks_at_beat - t;
1062 } else {
1063 t-= ticks_at_beat;
1065 } while (t > ticks_at_beat);
1072 if (dir < 0 ) {
1073 frames = count_frames_between( result,when);
1074 } else {
1075 frames = count_frames_between(when,result);
1078 return frames;
1083 nframes_t
1084 TempoMap::round_to_bar (nframes_t fr, int dir)
1087 Glib::RWLock::ReaderLock lm (lock);
1088 return round_to_type (fr, dir, Bar);
1093 nframes_t
1094 TempoMap::round_to_beat (nframes_t fr, int dir)
1097 Glib::RWLock::ReaderLock lm (lock);
1098 return round_to_type (fr, dir, Beat);
1102 nframes_t
1104 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
1107 BBT_Time the_beat;
1108 uint32_t ticks_one_half_subdivisions_worth;
1109 uint32_t ticks_one_subdivisions_worth;
1111 bbt_time(fr, the_beat);
1113 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1114 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1116 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1117 uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1118 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1119 the_beat.beats++;
1120 the_beat.ticks += difference;
1121 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1122 } else {
1123 the_beat.ticks += difference;
1125 } else {
1126 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1129 return frame_time (the_beat);
1132 nframes_t
1133 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1135 Metric metric = metric_at (frame);
1136 BBT_Time bbt;
1137 BBT_Time start;
1138 bbt_time_with_metric (frame, bbt, metric);
1140 switch (type) {
1141 case Bar:
1142 if (dir < 0) {
1143 /* relax */
1144 } else if (dir > 0) {
1145 if (bbt.beats > 0) {
1146 bbt.bars++;
1147 } else if (metric.frame() < frame) {
1148 bbt.bars++;
1150 } else {
1151 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1152 bbt.bars++;
1155 bbt.beats = 1;
1156 bbt.ticks = 0;
1157 break;
1159 case Beat:
1160 if (dir < 0) {
1161 /* relax */
1162 } else if (dir > 0) {
1163 if (bbt.ticks > 0) {
1164 bbt.beats++;
1165 } else if (metric.frame() < frame) {
1166 bbt.beats++;
1168 } else {
1169 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1170 bbt.beats++;
1173 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1174 bbt.beats = 1;
1175 bbt.bars++;
1177 bbt.ticks = 0;
1178 break;
1183 cerr << "for " << frame << " round to " << bbt << " using "
1184 << metric.start()
1185 << endl;
1187 return metric.frame() + count_frames_between (metric.start(), bbt);
1190 TempoMap::BBTPointList *
1191 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1194 Metrics::const_iterator i;
1195 BBTPointList *points;
1196 double current;
1197 const MeterSection* meter;
1198 const MeterSection* m;
1199 const TempoSection* tempo;
1200 const TempoSection* t;
1201 uint32_t bar;
1202 uint32_t beat;
1203 double beats_per_bar;
1204 double beat_frame;
1205 double beat_frames;
1206 double frames_per_bar;
1207 double delta_bars;
1208 double delta_beats;
1209 double dummy;
1210 nframes_t limit;
1212 meter = &first_meter ();
1213 tempo = &first_tempo ();
1215 /* find the starting point */
1217 for (i = metrics->begin(); i != metrics->end(); ++i) {
1219 if ((*i)->frame() > lower) {
1220 break;
1223 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1224 tempo = t;
1225 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1226 meter = m;
1230 /* We now have:
1232 meter -> the Meter for "lower"
1233 tempo -> the Tempo for "lower"
1234 i -> for first new metric after "lower", possibly metrics->end()
1236 Now start generating points.
1239 beats_per_bar = meter->beats_per_bar ();
1240 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1241 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1243 if (meter->frame() > tempo->frame()) {
1244 bar = meter->start().bars;
1245 beat = meter->start().beats;
1246 current = meter->frame();
1247 } else {
1248 bar = tempo->start().bars;
1249 beat = tempo->start().beats;
1250 current = tempo->frame();
1253 /* initialize current to point to the bar/beat just prior to the
1254 lower frame bound passed in. assumes that current is initialized
1255 above to be on a beat.
1258 delta_bars = (lower-current) / frames_per_bar;
1259 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1260 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1262 // adjust bars and beats too
1263 bar += (uint32_t) (floor(delta_bars));
1264 beat += (uint32_t) (floor(delta_beats));
1266 points = new BBTPointList;
1268 do {
1270 if (i == metrics->end()) {
1271 limit = upper;
1272 // cerr << "== limit set to end of request @ " << limit << endl;
1273 } else {
1274 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1275 limit = (*i)->frame();
1278 limit = min (limit, upper);
1280 while (current < limit) {
1282 /* if we're at the start of a bar, add bar point */
1284 if (beat == 1) {
1285 if (current >= lower) {
1286 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1287 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1292 /* add some beats if we can */
1294 beat_frame = current;
1296 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1297 if (beat_frame >= lower) {
1298 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1299 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1301 beat_frame += beat_frames;
1302 current+= beat_frames;
1304 beat++;
1307 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1308 // << (beat > ceil(beats_per_bar))
1309 // << endl;
1311 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1313 /* we walked an entire bar. its
1314 important to move `current' forward
1315 by the actual frames_per_bar, not move it to
1316 an integral beat_frame, so that metrics with
1317 non-integral beats-per-bar have
1318 their bar positions set
1319 correctly. consider a metric with
1320 9-1/2 beats-per-bar. the bar we
1321 just filled had 10 beat marks,
1322 but the bar end is 1/2 beat before
1323 the last beat mark.
1324 And it is also possible that a tempo
1325 change occured in the middle of a bar,
1326 so we subtract the possible extra fraction from the current
1329 if (beat > ceil (beats_per_bar)) {
1330 /* next bar goes where the numbers suggest */
1331 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1332 // cerr << "++ next bar from numbers\n";
1333 } else {
1334 /* next bar goes where the next metric is */
1335 current = limit;
1336 // cerr << "++ next bar at next metric\n";
1338 bar++;
1339 beat = 1;
1344 /* if we're done, then we're done */
1346 if (current >= upper) {
1347 break;
1350 /* i is an iterator that refers to the next metric (or none).
1351 if there is a next metric, move to it, and continue.
1354 if (i != metrics->end()) {
1356 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1357 tempo = t;
1358 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1359 meter = m;
1360 /* new MeterSection, beat always returns to 1 */
1361 beat = 1;
1364 current = (*i)->frame ();
1365 // cerr << "loop around with current @ " << current << endl;
1367 beats_per_bar = meter->beats_per_bar ();
1368 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1369 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1371 ++i;
1374 } while (1);
1376 return points;
1379 const TempoSection&
1380 TempoMap::tempo_section_at (nframes_t frame)
1382 Glib::RWLock::ReaderLock lm (lock);
1383 Metrics::iterator i;
1384 TempoSection* prev = 0;
1386 for (i = metrics->begin(); i != metrics->end(); ++i) {
1387 TempoSection* t;
1389 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1391 if ((*i)->frame() > frame) {
1392 break;
1395 prev = t;
1399 if (prev == 0) {
1400 fatal << endmsg;
1403 return *prev;
1406 const Tempo&
1407 TempoMap::tempo_at (nframes_t frame)
1409 Metric m (metric_at (frame));
1410 return m.tempo();
1414 const Meter&
1415 TempoMap::meter_at (nframes_t frame)
1417 Metric m (metric_at (frame));
1418 return m.meter();
1421 XMLNode&
1422 TempoMap::get_state ()
1424 Metrics::const_iterator i;
1425 XMLNode *root = new XMLNode ("TempoMap");
1428 Glib::RWLock::ReaderLock lm (lock);
1429 for (i = metrics->begin(); i != metrics->end(); ++i) {
1430 root->add_child_nocopy ((*i)->get_state());
1434 return *root;
1438 TempoMap::set_state (const XMLNode& node)
1441 Glib::RWLock::WriterLock lm (lock);
1443 XMLNodeList nlist;
1444 XMLNodeConstIterator niter;
1445 Metrics old_metrics (*metrics);
1447 metrics->clear();
1449 nlist = node.children();
1451 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1452 XMLNode* child = *niter;
1454 if (child->name() == TempoSection::xml_state_node_name) {
1456 try {
1457 metrics->push_back (new TempoSection (*child));
1460 catch (failed_constructor& err){
1461 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1462 *metrics = old_metrics;
1463 break;
1466 } else if (child->name() == MeterSection::xml_state_node_name) {
1468 try {
1469 metrics->push_back (new MeterSection (*child));
1472 catch (failed_constructor& err) {
1473 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1474 *metrics = old_metrics;
1475 break;
1480 if (niter == nlist.end()) {
1482 MetricSectionSorter cmp;
1483 metrics->sort (cmp);
1484 timestamp_metrics (true);
1488 StateChanged (Change (0));
1490 return 0;
1493 void
1494 TempoMap::dump (std::ostream& o) const
1496 const MeterSection* m;
1497 const TempoSection* t;
1499 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1501 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1502 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1503 << t->movable() << ')' << endl;
1504 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1505 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1506 << " (move? " << m->movable() << ')' << endl;
1512 TempoMap::n_tempos() const
1514 Glib::RWLock::ReaderLock lm (lock);
1515 int cnt = 0;
1517 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1518 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1519 cnt++;
1523 return cnt;
1527 TempoMap::n_meters() const
1529 Glib::RWLock::ReaderLock lm (lock);
1530 int cnt = 0;
1532 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1533 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1534 cnt++;
1538 return cnt;