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.
28 #include <glibmm/thread.h>
29 #include "pbd/xml++.h"
30 #include "ardour/debug.h"
31 #include "ardour/tempo.h"
32 #include "ardour/utils.h"
38 using namespace ARDOUR;
41 /* _default tempo is 4/4 qtr=120 */
43 Meter TempoMap::_default_meter (4.0, 4.0);
44 Tempo TempoMap::_default_tempo (120.0);
46 const double Meter::ticks_per_beat = 1920.0;
48 double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
50 return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
53 /***********************************************************************/
56 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
58 return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
61 /***********************************************************************/
63 const string TempoSection::xml_state_node_name = "Tempo";
65 TempoSection::TempoSection (const XMLNode& node)
66 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
68 const XMLProperty *prop;
70 LocaleGuard lg (X_("POSIX"));
72 if ((prop = node.property ("start")) == 0) {
73 error << _("TempoSection XML node has no \"start\" property") << endmsg;
74 throw failed_constructor();
77 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
81 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
82 throw failed_constructor();
87 if ((prop = node.property ("beats-per-minute")) == 0) {
88 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
89 throw failed_constructor();
92 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
93 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
94 throw failed_constructor();
97 if ((prop = node.property ("note-type")) == 0) {
98 /* older session, make note type be quarter by default */
101 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
102 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
103 throw failed_constructor();
107 if ((prop = node.property ("movable")) == 0) {
108 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
109 throw failed_constructor();
112 set_movable (string_is_affirmative (prop->value()));
116 TempoSection::get_state() const
118 XMLNode *root = new XMLNode (xml_state_node_name);
120 LocaleGuard lg (X_("POSIX"));
122 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
126 root->add_property ("start", buf);
127 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
128 root->add_property ("beats-per-minute", buf);
129 snprintf (buf, sizeof (buf), "%f", _note_type);
130 root->add_property ("note-type", buf);
131 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
132 root->add_property ("movable", buf);
137 /***********************************************************************/
139 const string MeterSection::xml_state_node_name = "Meter";
141 MeterSection::MeterSection (const XMLNode& node)
142 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
144 const XMLProperty *prop;
146 LocaleGuard lg (X_("POSIX"));
148 if ((prop = node.property ("start")) == 0) {
149 error << _("MeterSection XML node has no \"start\" property") << endmsg;
150 throw failed_constructor();
153 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
157 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
158 throw failed_constructor();
163 if ((prop = node.property ("beats-per-bar")) == 0) {
164 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
165 throw failed_constructor();
168 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
169 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
170 throw failed_constructor();
173 if ((prop = node.property ("note-type")) == 0) {
174 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
175 throw failed_constructor();
178 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
179 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
180 throw failed_constructor();
183 if ((prop = node.property ("movable")) == 0) {
184 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
185 throw failed_constructor();
188 set_movable (string_is_affirmative (prop->value()));
192 MeterSection::get_state() const
194 XMLNode *root = new XMLNode (xml_state_node_name);
196 LocaleGuard lg (X_("POSIX"));
198 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
202 root->add_property ("start", buf);
203 snprintf (buf, sizeof (buf), "%f", _note_type);
204 root->add_property ("note-type", buf);
205 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
206 root->add_property ("beats-per-bar", buf);
207 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
208 root->add_property ("movable", buf);
213 /***********************************************************************/
215 struct MetricSectionSorter {
216 bool operator() (const MetricSection* a, const MetricSection* b) {
217 return a->start() < b->start();
221 TempoMap::TempoMap (nframes64_t fr)
223 metrics = new Metrics;
225 last_bbt_valid = false;
232 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
233 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
235 t->set_movable (false);
236 m->set_movable (false);
238 /* note: frame time is correct (zero) for both of these */
240 metrics->push_back (t);
241 metrics->push_back (m);
244 TempoMap::~TempoMap ()
249 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
251 if (when == section.start() || !section.movable()) {
255 Glib::RWLock::WriterLock lm (lock);
256 MetricSectionSorter cmp;
258 if (when.beats != 1) {
260 /* position by audio frame, then recompute BBT timestamps from the audio ones */
262 nframes64_t frame = frame_time (when);
263 // cerr << "nominal frame time = " << frame << endl;
265 nframes64_t prev_frame = round_to_type (frame, -1, Beat);
266 nframes64_t next_frame = round_to_type (frame, 1, Beat);
268 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
270 /* use the closest beat */
272 if ((frame - prev_frame) < (next_frame - frame)) {
278 // cerr << "actual frame time = " << frame << endl;
279 section.set_frame (frame);
280 // cerr << "frame time = " << section.frame() << endl;
281 timestamp_metrics (false);
282 // cerr << "new BBT time = " << section.start() << endl;
287 /* positioned at bar start already, so just put it there */
289 section.set_start (when);
291 timestamp_metrics (true);
299 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
301 if (move_metric_section (tempo, when) == 0) {
302 StateChanged (PropertyChange (0));
307 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
309 if (move_metric_section (meter, when) == 0) {
310 StateChanged (PropertyChange (0));
315 TempoMap::remove_tempo (const TempoSection& tempo)
317 bool removed = false;
320 Glib::RWLock::WriterLock lm (lock);
323 for (i = metrics->begin(); i != metrics->end(); ++i) {
324 if (dynamic_cast<TempoSection*> (*i) != 0) {
325 if (tempo.frame() == (*i)->frame()) {
326 if ((*i)->movable()) {
337 StateChanged (PropertyChange (0));
342 TempoMap::remove_meter (const MeterSection& tempo)
344 bool removed = false;
347 Glib::RWLock::WriterLock lm (lock);
350 for (i = metrics->begin(); i != metrics->end(); ++i) {
351 if (dynamic_cast<MeterSection*> (*i) != 0) {
352 if (tempo.frame() == (*i)->frame()) {
353 if ((*i)->movable()) {
364 StateChanged (PropertyChange (0));
369 TempoMap::do_insert (MetricSection* section, bool with_bbt)
373 for (i = metrics->begin(); i != metrics->end(); ++i) {
376 if ((*i)->start() < section->start()) {
380 if ((*i)->frame() < section->frame()) {
385 metrics->insert (i, section);
389 if (i == metrics->end()) {
390 metrics->insert (metrics->end(), section);
393 timestamp_metrics (with_bbt);
397 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
400 Glib::RWLock::WriterLock lm (lock);
402 /* new tempos always start on a beat */
406 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
409 StateChanged (PropertyChange (0));
413 TempoMap::add_tempo (const Tempo& tempo, nframes64_t where)
416 Glib::RWLock::WriterLock lm (lock);
417 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
420 StateChanged (PropertyChange (0));
424 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
426 bool replaced = false;
429 Glib::RWLock::WriterLock lm (lock);
432 for (i = metrics->begin(); i != metrics->end(); ++i) {
435 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
437 *((Tempo *) ts) = replacement;
440 timestamp_metrics (true);
448 StateChanged (PropertyChange (0));
453 TempoMap::add_meter (const Meter& meter, BBT_Time where)
456 Glib::RWLock::WriterLock lm (lock);
458 /* a new meter always starts a new bar on the first beat. so
459 round the start time appropriately. remember that
460 `where' is based on the existing tempo map, not
461 the result after we insert the new meter.
465 if (where.beats != 1) {
470 /* new meters *always* start on a beat. */
474 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
477 StateChanged (PropertyChange (0));
481 TempoMap::add_meter (const Meter& meter, nframes64_t where)
484 Glib::RWLock::WriterLock lm (lock);
485 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
488 StateChanged (PropertyChange (0));
492 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
494 bool replaced = false;
497 Glib::RWLock::WriterLock lm (lock);
500 for (i = metrics->begin(); i != metrics->end(); ++i) {
502 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
504 *((Meter*) ms) = replacement;
507 timestamp_metrics (true);
514 StateChanged (PropertyChange (0));
519 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
521 Tempo newtempo (beats_per_minute, note_type);
524 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
525 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
526 *((Tempo*) t) = newtempo;
527 StateChanged (PropertyChange (0));
534 TempoMap::change_existing_tempo_at (nframes64_t where, double beats_per_minute, double note_type)
536 Tempo newtempo (beats_per_minute, note_type);
542 /* find the TempoSection immediately preceding "where"
545 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
547 if ((*i)->frame() > where) {
553 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
563 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
572 *((Tempo*)prev) = newtempo;
573 StateChanged (PropertyChange (0));
577 TempoMap::first_meter () const
579 const MeterSection *m = 0;
581 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
582 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
587 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
593 TempoMap::first_tempo () const
595 const TempoSection *t = 0;
597 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
598 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
603 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
609 TempoMap::timestamp_metrics (bool use_bbt)
617 meter = &first_meter ();
618 tempo = &first_tempo ();
622 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
624 nframes64_t current = 0;
625 nframes64_t section_frames;
629 for (i = metrics->begin(); i != metrics->end(); ++i) {
633 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
635 current += section_frames;
639 (*i)->set_frame (current);
641 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
643 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
646 fatal << _("programming error: unhandled MetricSection type") << endmsg;
653 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
656 MetricSection* prev = 0;
658 for (i = metrics->begin(); i != metrics->end(); ++i) {
661 TempoMetric metric (*meter, *tempo);
664 metric.set_start (prev->start());
665 metric.set_frame (prev->frame());
667 // metric will be at frames=0 bbt=1|1|0 by default
668 // which is correct for our purpose
671 bbt_time_with_metric ((*i)->frame(), bbt, metric);
673 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
680 if (bbt.ticks > Meter::ticks_per_beat/2) {
681 /* round up to next beat */
687 if (bbt.beats != 1) {
688 /* round up to next bar */
694 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
696 (*i)->set_start (bbt);
698 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
700 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
701 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
703 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
705 fatal << _("programming error: unhandled MetricSection type") << endmsg;
714 // cerr << "###############################################\n\n\n" << endl;
719 TempoMap::metric_at (nframes64_t frame) const
721 TempoMetric m (first_meter(), first_tempo());
725 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
726 at something, because we insert the default tempo and meter during
727 TempoMap construction.
729 now see if we can find better candidates.
732 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
734 if ((*i)->frame() > frame) {
738 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
739 m.set_tempo (*tempo);
740 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
741 m.set_meter (*meter);
744 m.set_frame ((*i)->frame ());
745 m.set_start ((*i)->start ());
752 TempoMap::metric_at (BBT_Time bbt) const
754 TempoMetric m (first_meter(), first_tempo());
758 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
759 at something, because we insert the default tempo and meter during
760 TempoMap construction.
762 now see if we can find better candidates.
765 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
767 BBT_Time section_start ((*i)->start());
769 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
773 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
774 m.set_tempo (*tempo);
775 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
776 m.set_meter (*meter);
779 m.set_frame ((*i)->frame ());
780 m.set_start (section_start);
787 TempoMap::bbt_time (nframes64_t frame, BBT_Time& bbt) const
790 Glib::RWLock::ReaderLock lm (lock);
791 bbt_time_unlocked (frame, bbt);
796 TempoMap::bbt_time_unlocked (nframes64_t frame, BBT_Time& bbt) const
798 bbt_time_with_metric (frame, bbt, metric_at (frame));
802 TempoMap::bbt_time_with_metric (nframes64_t frame, BBT_Time& bbt, const TempoMetric& metric) const
804 nframes64_t frame_diff;
806 // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
808 const double beats_per_bar = metric.meter().beats_per_bar();
809 const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat;
811 /* now compute how far beyond that point we actually are. */
813 frame_diff = frame - metric.frame();
815 bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
816 uint32_t xtra_beats = bbt.ticks / (uint32_t)Meter::ticks_per_beat;
817 bbt.ticks %= (uint32_t)Meter::ticks_per_beat;
819 bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
820 bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
821 bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
823 /* if we have a fractional number of beats per bar, we see if
824 we're in the last beat (the fractional one). if so, we
825 round ticks appropriately and bump to the next bar. */
826 double beat_fraction = beats_per_bar - floor(beats_per_bar);
827 /* XXX one problem here is that I'm not sure how to handle
828 fractional beats that don't evenly divide ticks_per_beat.
829 If they aren't handled consistently, I would guess we'll
830 continue to have strange discrepancies occuring. Perhaps
831 this will also behave badly in the case of meters like
832 0.1/4, but I can't be bothered to test that.
834 uint32_t ticks_on_last_beat = (uint32_t)floor(Meter::ticks_per_beat * beat_fraction);
836 if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) {
837 bbt.ticks -= ticks_on_last_beat;
842 bbt.beats++; // correction for 1-based counting, see above for matching operation.
844 // cerr << "-----\t RETURN " << bbt << endl;
848 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
850 /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
851 that means that the beats and ticks should be inside a bar
854 nframes64_t frames = 0;
855 nframes64_t start_frame = 0;
856 nframes64_t end_frame = 0;
858 TempoMetric m = metric_at (start);
860 uint32_t bar_offset = start.bars - m.start().bars;
862 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
863 + start.ticks/Meter::ticks_per_beat;
866 start_frame = m.frame() + (nframes64_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
870 bar_offset = end.bars - m.start().bars;
872 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
873 + end.ticks/Meter::ticks_per_beat;
875 end_frame = m.frame() + (nframes64_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
877 frames = end_frame - start_frame;
884 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
886 /* this is used in timestamping the metrics by actually counting the beats */
888 nframes64_t frames = 0;
889 uint32_t bar = start.bars;
890 double beat = (double) start.beats;
891 double beats_counted = 0;
892 double beats_per_bar = 0;
893 double beat_frames = 0;
895 beats_per_bar = meter.beats_per_bar();
896 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
900 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
902 if (beat >= beats_per_bar) {
907 if (beat > beats_per_bar) {
909 /* this is a fractional beat at the end of a fractional bar
910 so it should only count for the fraction
913 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
922 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
923 // << " bpb were " << beats_per_bar
924 // << " fpb was " << beat_frames
927 frames = (nframes64_t) floor (beats_counted * beat_frames);
934 TempoMap::frame_time (const BBT_Time& bbt) const
936 BBT_Time start ; /* 1|1|0 */
938 return count_frames_between ( start, bbt);
942 TempoMap::bbt_duration_at (nframes64_t pos, const BBT_Time& bbt, int dir) const
944 nframes64_t frames = 0;
950 Glib::RWLock::ReaderLock lm (lock);
951 frames = bbt_duration_at_unlocked (when, bbt,dir);
958 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
961 nframes64_t frames = 0;
963 double beats_per_bar;
966 result.bars = max(1U, when.bars + dir * bbt.bars) ;
970 TempoMetric metric = metric_at(result);
971 beats_per_bar = metric.meter().beats_per_bar();
975 /*reduce things to legal bbt values
976 we have to handle possible fractional=shorter beats at the end of measures
977 and things like 0|11|9000 as a duration in a 4.5/4 measure
978 the musical decision is that the fractional beat is also a beat , although a shorter one
983 result.beats = when.beats + bbt.beats;
984 result.ticks = when.ticks + bbt.ticks;
986 while (result.beats >= (beats_per_bar + 1)) {
988 result.beats -= (uint32_t) ceil(beats_per_bar);
989 metric = metric_at(result); // maybe there is a meter change
990 beats_per_bar = metric.meter().beats_per_bar();
993 /*we now counted the beats and landed in the target measure, now deal with ticks
994 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
995 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
998 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1001 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1002 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
1003 : Meter::ticks_per_beat );
1005 while (result.ticks >= ticks_at_beat) {
1007 result.ticks -= ticks_at_beat;
1008 if (result.beats >= (beats_per_bar + 1)) {
1011 metric = metric_at(result); // maybe there is a meter change
1012 beats_per_bar = metric.meter().beats_per_bar();
1014 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1015 (1 - (ceil(beats_per_bar) - beats_per_bar) ) * Meter::ticks_per_beat
1016 : Meter::ticks_per_beat);
1022 uint32_t b = bbt.beats;
1025 while( b > when.beats ) {
1027 result.bars = max(1U,result.bars-- ) ;
1028 metric = metric_at(result); // maybe there is a meter change
1029 beats_per_bar = metric.meter().beats_per_bar();
1030 if (b >= ceil(beats_per_bar)) {
1032 b -= (uint32_t) ceil(beats_per_bar);
1034 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1037 result.beats = when.beats - b;
1041 if (bbt.ticks <= when.ticks) {
1042 result.ticks = when.ticks - bbt.ticks;
1045 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1046 uint32_t t = bbt.ticks - when.ticks;
1050 if (result.beats == 1) {
1051 result.bars = max(1U, result.bars-- ) ;
1052 metric = metric_at(result); // maybe there is a meter change
1053 beats_per_bar = metric.meter().beats_per_bar();
1054 result.beats = (uint32_t) ceil(beats_per_bar);
1055 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * Meter::ticks_per_beat) ;
1058 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1061 if (t <= ticks_at_beat) {
1062 result.ticks = ticks_at_beat - t;
1066 } while (t > ticks_at_beat);
1074 frames = count_frames_between( result,when);
1076 frames = count_frames_between(when,result);
1085 TempoMap::round_to_bar (nframes64_t fr, int dir)
1088 Glib::RWLock::ReaderLock lm (lock);
1089 return round_to_type (fr, dir, Bar);
1095 TempoMap::round_to_beat (nframes64_t fr, int dir)
1098 Glib::RWLock::ReaderLock lm (lock);
1099 return round_to_type (fr, dir, Beat);
1104 TempoMap::round_to_beat_subdivision (nframes64_t fr, int sub_num, int dir)
1108 uint32_t ticks_one_half_subdivisions_worth;
1109 uint32_t ticks_one_subdivisions_worth;
1110 uint32_t difference;
1112 bbt_time(fr, the_beat);
1114 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1115 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1121 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1124 /* right on the subdivision, so the difference is just the subdivision ticks */
1125 difference = ticks_one_subdivisions_worth;
1128 /* not on subdivision, compute distance to next subdivision */
1130 difference = ticks_one_subdivisions_worth - mod;
1133 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1135 the_beat.ticks += difference;
1136 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1138 the_beat.ticks += difference;
1141 } else if (dir < 0) {
1143 /* round to previous */
1145 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1148 /* right on the subdivision, so the difference is just the subdivision ticks */
1149 difference = ticks_one_subdivisions_worth;
1150 cerr << "On the sub, move by 1 sub = " << difference << endl;
1152 /* not on subdivision, compute distance to previous subdivision, which
1153 is just the modulus.
1157 cerr << "off the sub, move by 1 sub = " << difference << endl;
1161 cerr << "ticks = " << the_beat.ticks << endl;
1163 if (the_beat.ticks < difference) {
1164 cerr << "backup beats, set ticks to "
1165 << (uint32_t)Meter::ticks_per_beat - difference << endl;
1167 the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference;
1169 cerr << " reduce ticks\n";
1170 the_beat.ticks -= difference;
1174 /* round to nearest */
1176 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1177 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1178 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1180 the_beat.ticks += difference;
1181 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1183 the_beat.ticks += difference;
1186 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1187 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1191 return frame_time (the_beat);
1195 TempoMap::round_to_type (nframes64_t frame, int dir, BBTPointType type)
1197 TempoMetric metric = metric_at (frame);
1200 BBT_Time one_bar (1,0,0);
1201 BBT_Time one_beat (0,1,0);
1203 bbt_time_with_metric (frame, bbt, metric);
1207 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1211 /* find bar position preceding frame */
1214 bbt = bbt_subtract (bbt, one_bar);
1222 } else if (dir > 0) {
1224 /* find bar position following frame */
1227 bbt = bbt_add (bbt, one_bar, metric);
1235 /* "true" rounding */
1240 midbar_beats = metric.meter().beats_per_bar() / 2;
1241 midbar_ticks = Meter::ticks_per_beat * fmod (midbar_beats, 1.0f);
1242 midbar_beats = floor (midbar_beats);
1244 BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1257 /* force beats & ticks to their values at the start of a bar */
1263 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1267 /* find beat position preceding frame */
1270 bbt = bbt_subtract (bbt, one_beat);
1278 } else if (dir > 0) {
1280 /* find beat position following frame */
1283 bbt = bbt_add (bbt, one_beat, metric);
1291 /* "true" rounding */
1293 /* round to nearest beat */
1294 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1297 bbt = bbt_add (bbt, one_beat, metric);
1304 /* force ticks to the value at the start of a beat */
1310 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("\tat %1 count frames from %2 to %3 = %4\n", metric.frame(), metric.start(), bbt, count_frames_between (metric.start(), bbt)));
1311 return metric.frame() + count_frames_between (metric.start(), bbt);
1314 TempoMap::BBTPointList *
1315 TempoMap::get_points (nframes64_t lower, nframes64_t upper) const
1318 Metrics::const_iterator i;
1319 BBTPointList *points;
1321 const MeterSection* meter;
1322 const MeterSection* m;
1323 const TempoSection* tempo;
1324 const TempoSection* t;
1327 double beats_per_bar;
1330 double frames_per_bar;
1336 meter = &first_meter ();
1337 tempo = &first_tempo ();
1339 /* find the starting point */
1341 for (i = metrics->begin(); i != metrics->end(); ++i) {
1343 if ((*i)->frame() > lower) {
1347 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1349 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1356 meter -> the Meter for "lower"
1357 tempo -> the Tempo for "lower"
1358 i -> for first new metric after "lower", possibly metrics->end()
1360 Now start generating points.
1363 beats_per_bar = meter->beats_per_bar ();
1364 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1365 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1367 if (meter->frame() > tempo->frame()) {
1368 bar = meter->start().bars;
1369 beat = meter->start().beats;
1370 current = meter->frame();
1372 bar = tempo->start().bars;
1373 beat = tempo->start().beats;
1374 current = tempo->frame();
1377 /* initialize current to point to the bar/beat just prior to the
1378 lower frame bound passed in. assumes that current is initialized
1379 above to be on a beat.
1382 delta_bars = (lower-current) / frames_per_bar;
1383 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1384 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1386 // adjust bars and beats too
1387 bar += (uint32_t) (floor(delta_bars));
1388 beat += (uint32_t) (floor(delta_beats));
1390 points = new BBTPointList;
1394 if (i == metrics->end()) {
1396 // cerr << "== limit set to end of request @ " << limit << endl;
1398 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1399 limit = (*i)->frame();
1402 limit = min (limit, upper);
1404 while (current < limit) {
1406 /* if we're at the start of a bar, add bar point */
1409 if (current >= lower) {
1410 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1411 points->push_back (BBTPoint (*meter, *tempo,(nframes64_t)rint(current), Bar, bar, 1));
1416 /* add some beats if we can */
1418 beat_frame = current;
1420 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1421 if (beat_frame >= lower) {
1422 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1423 points->push_back (BBTPoint (*meter, *tempo, (nframes64_t) rint(beat_frame), Beat, bar, beat));
1425 beat_frame += beat_frames;
1426 current+= beat_frames;
1431 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1432 // << (beat > ceil(beats_per_bar))
1435 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1437 /* we walked an entire bar. its
1438 important to move `current' forward
1439 by the actual frames_per_bar, not move it to
1440 an integral beat_frame, so that metrics with
1441 non-integral beats-per-bar have
1442 their bar positions set
1443 correctly. consider a metric with
1444 9-1/2 beats-per-bar. the bar we
1445 just filled had 10 beat marks,
1446 but the bar end is 1/2 beat before
1448 And it is also possible that a tempo
1449 change occured in the middle of a bar,
1450 so we subtract the possible extra fraction from the current
1453 if (beat > ceil (beats_per_bar)) {
1454 /* next bar goes where the numbers suggest */
1455 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1456 // cerr << "++ next bar from numbers\n";
1458 /* next bar goes where the next metric is */
1460 // cerr << "++ next bar at next metric\n";
1468 /* if we're done, then we're done */
1470 if (current >= upper) {
1474 /* i is an iterator that refers to the next metric (or none).
1475 if there is a next metric, move to it, and continue.
1478 if (i != metrics->end()) {
1480 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1482 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1484 /* new MeterSection, beat always returns to 1 */
1488 current = (*i)->frame ();
1489 // cerr << "loop around with current @ " << current << endl;
1491 beats_per_bar = meter->beats_per_bar ();
1492 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1493 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1504 TempoMap::tempo_section_at (nframes64_t frame)
1506 Glib::RWLock::ReaderLock lm (lock);
1507 Metrics::iterator i;
1508 TempoSection* prev = 0;
1510 for (i = metrics->begin(); i != metrics->end(); ++i) {
1513 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1515 if ((*i)->frame() > frame) {
1531 TempoMap::tempo_at (nframes64_t frame) const
1533 TempoMetric m (metric_at (frame));
1539 TempoMap::meter_at (nframes64_t frame) const
1541 TempoMetric m (metric_at (frame));
1546 TempoMap::get_state ()
1548 Metrics::const_iterator i;
1549 XMLNode *root = new XMLNode ("TempoMap");
1552 Glib::RWLock::ReaderLock lm (lock);
1553 for (i = metrics->begin(); i != metrics->end(); ++i) {
1554 root->add_child_nocopy ((*i)->get_state());
1562 TempoMap::set_state (const XMLNode& node, int /*version*/)
1565 Glib::RWLock::WriterLock lm (lock);
1568 XMLNodeConstIterator niter;
1569 Metrics old_metrics (*metrics);
1573 nlist = node.children();
1575 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1576 XMLNode* child = *niter;
1578 if (child->name() == TempoSection::xml_state_node_name) {
1581 metrics->push_back (new TempoSection (*child));
1584 catch (failed_constructor& err){
1585 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1586 *metrics = old_metrics;
1590 } else if (child->name() == MeterSection::xml_state_node_name) {
1593 metrics->push_back (new MeterSection (*child));
1596 catch (failed_constructor& err) {
1597 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1598 *metrics = old_metrics;
1604 if (niter == nlist.end()) {
1606 MetricSectionSorter cmp;
1607 metrics->sort (cmp);
1608 timestamp_metrics (true);
1612 StateChanged (PropertyChange (0));
1618 TempoMap::dump (std::ostream& o) const
1620 const MeterSection* m;
1621 const TempoSection* t;
1623 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1625 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1626 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1627 << t->movable() << ')' << endl;
1628 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1629 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1630 << " (move? " << m->movable() << ')' << endl;
1636 TempoMap::n_tempos() const
1638 Glib::RWLock::ReaderLock lm (lock);
1641 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1642 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1651 TempoMap::n_meters() const
1653 Glib::RWLock::ReaderLock lm (lock);
1656 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1657 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1666 TempoMap::insert_time (nframes64_t where, nframes64_t amount)
1668 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1669 if ((*i)->frame() >= where) {
1670 (*i)->set_frame ((*i)->frame() + amount);
1674 timestamp_metrics (false);
1676 StateChanged (PropertyChange (0));
1680 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1682 TempoMetric metric = metric_at (start);
1683 return bbt_add (start, other, metric);
1687 * add the BBT interval @param increment to @param start and return the result
1690 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
1692 BBT_Time result = start;
1693 BBT_Time op = increment; /* argument is const, but we need to modify it */
1694 uint32_t ticks = result.ticks + op.ticks;
1696 if (ticks >= Meter::ticks_per_beat) {
1698 result.ticks = ticks % (uint32_t) Meter::ticks_per_beat;
1701 /* now comes the complicated part. we have to add one beat a time,
1702 checking for a new metric on every beat.
1705 /* grab all meter sections */
1707 list<const MeterSection*> meter_sections;
1709 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1710 const MeterSection* ms;
1711 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1712 meter_sections.push_back (ms);
1716 assert (!meter_sections.empty());
1718 list<const MeterSection*>::const_iterator next_meter;
1719 const Meter* meter = 0;
1721 /* go forwards through the meter sections till we get to the one
1722 covering the current value of result. this positions i to point to
1723 the next meter section too, or the end.
1726 for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1728 if (result < (*next_meter)->start()) {
1729 /* this metric is past the result time. stop looking, we have what we need */
1733 if (result == (*next_meter)->start()) {
1734 /* this meter section starts at result, push i beyond it so that it points
1735 to the NEXT section, opwise we will get stuck later, and use this meter section.
1737 meter = *next_meter;
1742 meter = *next_meter;
1745 assert (meter != 0);
1747 /* OK, now have the meter for the bar start we are on, and i is an iterator
1748 that points to the metric after the one we are currently dealing with
1749 (or to metrics->end(), of course)
1754 /* given the current meter, have we gone past the end of the bar ? */
1756 if (result.beats >= meter->beats_per_bar()) {
1757 /* move to next bar, first beat */
1768 /* check if we need to use a new meter section: has adding beats to result taken us
1769 to or after the start of the next meter section? in which case, use it.
1772 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1773 meter = *next_meter;
1778 /* finally, add bars */
1780 result.bars += op.bars++;
1786 * subtract the BBT interval @param decrement from @param start and return the result
1789 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1791 BBT_Time result = start;
1792 BBT_Time op = decrement; /* argument is const, but we need to modify it */
1794 if (op.ticks > result.ticks) {
1795 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1797 result.ticks = Meter::ticks_per_beat - (op.ticks - result.ticks);
1799 result.ticks -= op.ticks;
1802 /* now comes the complicated part. we have to subtract one beat a time,
1803 checking for a new metric on every beat.
1806 /* grab all meter sections */
1808 list<const MeterSection*> meter_sections;
1810 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1811 const MeterSection* ms;
1812 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1813 meter_sections.push_back (ms);
1817 assert (!meter_sections.empty());
1819 /* go backwards through the meter sections till we get to the one
1820 covering the current value of result. this positions i to point to
1821 the next (previous) meter section too, or the end.
1824 const MeterSection* meter = 0;
1825 list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't
1826 // support const_reverse_iterator::operator!=()
1828 for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1830 /* when we find the first meter section that is before or at result, use it,
1831 and set next_meter to the previous one
1834 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1835 meter = *next_meter;
1841 assert (meter != 0);
1843 /* OK, now have the meter for the bar start we are on, and i is an iterator
1844 that points to the metric after the one we are currently dealing with
1845 (or to metrics->end(), of course)
1850 /* have we reached the start of the bar? if so, move to the last beat of the previous
1851 bar. opwise, just step back 1 beat.
1854 if (result.beats == 1) {
1856 /* move to previous bar, last beat */
1858 if (result.bars <= 1) {
1859 /* i'm sorry dave, i can't do that */
1860 throw std::out_of_range ("illegal BBT subtraction");
1864 result.beats = meter->beats_per_bar();
1875 /* check if we need to use a new meter section: has subtracting beats to result taken us
1876 to before the start of the current meter section? in which case, use the prior one.
1879 if (result < meter->start() && next_meter != meter_sections.rend()) {
1880 meter = *next_meter;
1885 /* finally, subtract bars */
1887 if (op.bars >= result.bars) {
1888 /* i'm sorry dave, i can't do that */
1889 throw std::out_of_range ("illegal BBT subtraction");
1892 result.bars -= op.bars;