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.
25 #include <sigc++/bind.h>
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "ardour/debug.h"
30 #include "ardour/tempo.h"
31 #include "ardour/utils.h"
37 using namespace ARDOUR;
40 /* _default tempo is 4/4 qtr=120 */
42 Meter TempoMap::_default_meter (4.0, 4.0);
43 Tempo TempoMap::_default_tempo (120.0);
45 const double Meter::ticks_per_beat = 1920.0;
47 double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
49 return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
52 /***********************************************************************/
55 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
57 return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
60 /***********************************************************************/
62 const string TempoSection::xml_state_node_name = "Tempo";
64 TempoSection::TempoSection (const XMLNode& node)
65 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
67 const XMLProperty *prop;
69 LocaleGuard lg (X_("POSIX"));
71 if ((prop = node.property ("start")) == 0) {
72 error << _("TempoSection XML node has no \"start\" property") << endmsg;
73 throw failed_constructor();
76 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
80 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
81 throw failed_constructor();
86 if ((prop = node.property ("beats-per-minute")) == 0) {
87 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
88 throw failed_constructor();
91 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
92 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
93 throw failed_constructor();
96 if ((prop = node.property ("note-type")) == 0) {
97 /* older session, make note type be quarter by default */
100 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
101 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
102 throw failed_constructor();
106 if ((prop = node.property ("movable")) == 0) {
107 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
108 throw failed_constructor();
111 set_movable (string_is_affirmative (prop->value()));
115 TempoSection::get_state() const
117 XMLNode *root = new XMLNode (xml_state_node_name);
119 LocaleGuard lg (X_("POSIX"));
121 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
125 root->add_property ("start", buf);
126 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
127 root->add_property ("beats-per-minute", buf);
128 snprintf (buf, sizeof (buf), "%f", _note_type);
129 root->add_property ("note-type", buf);
130 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
131 root->add_property ("movable", buf);
136 /***********************************************************************/
138 const string MeterSection::xml_state_node_name = "Meter";
140 MeterSection::MeterSection (const XMLNode& node)
141 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
143 const XMLProperty *prop;
145 LocaleGuard lg (X_("POSIX"));
147 if ((prop = node.property ("start")) == 0) {
148 error << _("MeterSection XML node has no \"start\" property") << endmsg;
149 throw failed_constructor();
152 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
156 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
157 throw failed_constructor();
162 if ((prop = node.property ("beats-per-bar")) == 0) {
163 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
164 throw failed_constructor();
167 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
168 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
169 throw failed_constructor();
172 if ((prop = node.property ("note-type")) == 0) {
173 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
174 throw failed_constructor();
177 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
178 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
179 throw failed_constructor();
182 if ((prop = node.property ("movable")) == 0) {
183 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
184 throw failed_constructor();
187 set_movable (string_is_affirmative (prop->value()));
191 MeterSection::get_state() const
193 XMLNode *root = new XMLNode (xml_state_node_name);
195 LocaleGuard lg (X_("POSIX"));
197 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
201 root->add_property ("start", buf);
202 snprintf (buf, sizeof (buf), "%f", _note_type);
203 root->add_property ("note-type", buf);
204 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
205 root->add_property ("beats-per-bar", buf);
206 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
207 root->add_property ("movable", buf);
212 /***********************************************************************/
214 struct MetricSectionSorter {
215 bool operator() (const MetricSection* a, const MetricSection* b) {
216 return a->start() < b->start();
220 TempoMap::TempoMap (nframes_t fr)
222 metrics = new Metrics;
224 last_bbt_valid = false;
231 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
232 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
234 t->set_movable (false);
235 m->set_movable (false);
237 /* note: frame time is correct (zero) for both of these */
239 metrics->push_back (t);
240 metrics->push_back (m);
243 TempoMap::~TempoMap ()
248 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
250 if (when == section.start() || !section.movable()) {
254 Glib::RWLock::WriterLock lm (lock);
255 MetricSectionSorter cmp;
257 if (when.beats != 1) {
259 /* position by audio frame, then recompute BBT timestamps from the audio ones */
261 nframes_t frame = frame_time (when);
262 // cerr << "nominal frame time = " << frame << endl;
264 nframes_t prev_frame = round_to_type (frame, -1, Beat);
265 nframes_t next_frame = round_to_type (frame, 1, Beat);
267 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
269 /* use the closest beat */
271 if ((frame - prev_frame) < (next_frame - frame)) {
277 // cerr << "actual frame time = " << frame << endl;
278 section.set_frame (frame);
279 // cerr << "frame time = " << section.frame() << endl;
280 timestamp_metrics (false);
281 // cerr << "new BBT time = " << section.start() << endl;
286 /* positioned at bar start already, so just put it there */
288 section.set_start (when);
290 timestamp_metrics (true);
298 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
300 if (move_metric_section (tempo, when) == 0) {
301 StateChanged (Change (0));
306 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
308 if (move_metric_section (meter, when) == 0) {
309 StateChanged (Change (0));
314 TempoMap::remove_tempo (const TempoSection& tempo)
316 bool removed = false;
319 Glib::RWLock::WriterLock lm (lock);
322 for (i = metrics->begin(); i != metrics->end(); ++i) {
323 if (dynamic_cast<TempoSection*> (*i) != 0) {
324 if (tempo.frame() == (*i)->frame()) {
325 if ((*i)->movable()) {
336 StateChanged (Change (0));
341 TempoMap::remove_meter (const MeterSection& tempo)
343 bool removed = false;
346 Glib::RWLock::WriterLock lm (lock);
349 for (i = metrics->begin(); i != metrics->end(); ++i) {
350 if (dynamic_cast<MeterSection*> (*i) != 0) {
351 if (tempo.frame() == (*i)->frame()) {
352 if ((*i)->movable()) {
363 StateChanged (Change (0));
368 TempoMap::do_insert (MetricSection* section, bool with_bbt)
372 for (i = metrics->begin(); i != metrics->end(); ++i) {
375 if ((*i)->start() < section->start()) {
379 if ((*i)->frame() < section->frame()) {
384 metrics->insert (i, section);
388 if (i == metrics->end()) {
389 metrics->insert (metrics->end(), section);
392 timestamp_metrics (with_bbt);
396 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
399 Glib::RWLock::WriterLock lm (lock);
401 /* new tempos always start on a beat */
405 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
408 StateChanged (Change (0));
412 TempoMap::add_tempo (const Tempo& tempo, nframes_t where)
415 Glib::RWLock::WriterLock lm (lock);
416 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
419 StateChanged (Change (0));
423 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
425 bool replaced = false;
428 Glib::RWLock::WriterLock lm (lock);
431 for (i = metrics->begin(); i != metrics->end(); ++i) {
434 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
436 *((Tempo *) ts) = replacement;
439 timestamp_metrics (true);
447 StateChanged (Change (0));
452 TempoMap::add_meter (const Meter& meter, BBT_Time where)
455 Glib::RWLock::WriterLock lm (lock);
457 /* a new meter always starts a new bar on the first beat. so
458 round the start time appropriately. remember that
459 `where' is based on the existing tempo map, not
460 the result after we insert the new meter.
464 if (where.beats != 1) {
469 /* new meters *always* start on a beat. */
473 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
476 StateChanged (Change (0));
480 TempoMap::add_meter (const Meter& meter, nframes_t where)
483 Glib::RWLock::WriterLock lm (lock);
484 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
487 StateChanged (Change (0));
491 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
493 bool replaced = false;
496 Glib::RWLock::WriterLock lm (lock);
499 for (i = metrics->begin(); i != metrics->end(); ++i) {
501 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
503 *((Meter*) ms) = replacement;
506 timestamp_metrics (true);
513 StateChanged (Change (0));
518 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
520 Tempo newtempo (beats_per_minute, note_type);
523 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
524 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
525 *((Tempo*) t) = newtempo;
526 StateChanged (Change (0));
533 TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type)
535 Tempo newtempo (beats_per_minute, note_type);
541 /* find the TempoSection immediately preceding "where"
544 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
546 if ((*i)->frame() > where) {
552 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
562 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
571 *((Tempo*)prev) = newtempo;
572 StateChanged (Change (0));
576 TempoMap::first_meter () const
578 const MeterSection *m = 0;
580 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
581 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
586 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
592 TempoMap::first_tempo () const
594 const TempoSection *t = 0;
596 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
597 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
602 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
608 TempoMap::timestamp_metrics (bool use_bbt)
616 meter = &first_meter ();
617 tempo = &first_tempo ();
621 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
623 nframes_t current = 0;
624 nframes_t section_frames;
628 for (i = metrics->begin(); i != metrics->end(); ++i) {
632 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
634 current += section_frames;
638 (*i)->set_frame (current);
640 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
642 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
645 fatal << _("programming error: unhandled MetricSection type") << endmsg;
652 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
655 MetricSection* prev = 0;
657 for (i = metrics->begin(); i != metrics->end(); ++i) {
660 TempoMetric metric (*meter, *tempo);
663 metric.set_start (prev->start());
664 metric.set_frame (prev->frame());
666 // metric will be at frames=0 bbt=1|1|0 by default
667 // which is correct for our purpose
670 bbt_time_with_metric ((*i)->frame(), bbt, metric);
672 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
679 if (bbt.ticks > Meter::ticks_per_beat/2) {
680 /* round up to next beat */
686 if (bbt.beats != 1) {
687 /* round up to next bar */
693 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
695 (*i)->set_start (bbt);
697 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
699 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
700 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
702 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
704 fatal << _("programming error: unhandled MetricSection type") << endmsg;
713 // cerr << "###############################################\n\n\n" << endl;
718 TempoMap::metric_at (nframes_t frame) const
720 TempoMetric m (first_meter(), first_tempo());
724 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
725 at something, because we insert the default tempo and meter during
726 TempoMap construction.
728 now see if we can find better candidates.
731 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
733 if ((*i)->frame() > frame) {
737 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
738 m.set_tempo (*tempo);
739 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
740 m.set_meter (*meter);
743 m.set_frame ((*i)->frame ());
744 m.set_start ((*i)->start ());
751 TempoMap::metric_at (BBT_Time bbt) const
753 TempoMetric m (first_meter(), first_tempo());
757 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
758 at something, because we insert the default tempo and meter during
759 TempoMap construction.
761 now see if we can find better candidates.
764 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
766 BBT_Time section_start ((*i)->start());
768 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
772 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
773 m.set_tempo (*tempo);
774 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
775 m.set_meter (*meter);
778 m.set_frame ((*i)->frame ());
779 m.set_start (section_start);
786 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
789 Glib::RWLock::ReaderLock lm (lock);
790 bbt_time_unlocked (frame, bbt);
795 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
797 bbt_time_with_metric (frame, bbt, metric_at (frame));
801 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const TempoMetric& metric) const
803 nframes_t frame_diff;
805 // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
807 const double beats_per_bar = metric.meter().beats_per_bar();
808 const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat;
810 /* now compute how far beyond that point we actually are. */
812 frame_diff = frame - metric.frame();
814 bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
815 uint32_t xtra_beats = bbt.ticks / (uint32_t)Meter::ticks_per_beat;
816 bbt.ticks %= (uint32_t)Meter::ticks_per_beat;
818 bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
819 bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
820 bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
822 /* if we have a fractional number of beats per bar, we see if
823 we're in the last beat (the fractional one). if so, we
824 round ticks appropriately and bump to the next bar. */
825 double beat_fraction = beats_per_bar - floor(beats_per_bar);
826 /* XXX one problem here is that I'm not sure how to handle
827 fractional beats that don't evenly divide ticks_per_beat.
828 If they aren't handled consistently, I would guess we'll
829 continue to have strange discrepancies occuring. Perhaps
830 this will also behave badly in the case of meters like
831 0.1/4, but I can't be bothered to test that.
833 uint32_t ticks_on_last_beat = (uint32_t)floor(Meter::ticks_per_beat * beat_fraction);
834 if(bbt.beats > (uint32_t)floor(beats_per_bar) &&
835 bbt.ticks >= ticks_on_last_beat) {
836 bbt.ticks -= ticks_on_last_beat;
841 bbt.beats++; // correction for 1-based counting, see above for matching operation.
843 // cerr << "-----\t RETURN " << bbt << endl;
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 TempoMetric 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()));
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;
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);
899 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
901 if (beat >= beats_per_bar) {
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);
921 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
922 // << " bpb were " << beats_per_bar
923 // << " fpb was " << beat_frames
926 frames = (nframes_t) floor (beats_counted * beat_frames);
933 TempoMap::frame_time (const BBT_Time& bbt) const
935 BBT_Time start ; /* 1|1|0 */
937 return count_frames_between ( start, bbt);
941 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
943 nframes_t frames = 0;
949 Glib::RWLock::ReaderLock lm (lock);
950 frames = bbt_duration_at_unlocked (when, bbt,dir);
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;
965 result.bars = max(1U, when.bars + dir * bbt.bars) ;
969 TempoMetric 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
982 result.beats = when.beats + bbt.beats;
983 result.ticks = when.ticks + bbt.ticks;
985 while (result.beats >= (beats_per_bar + 1)) {
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) {
1006 result.ticks -= ticks_at_beat;
1007 if (result.beats >= (beats_per_bar + 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);
1021 uint32_t b = bbt.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);
1033 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1036 result.beats = when.beats - b;
1040 if (bbt.ticks <= when.ticks) {
1041 result.ticks = when.ticks - bbt.ticks;
1044 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1045 uint32_t t = bbt.ticks - when.ticks;
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) ;
1057 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1060 if (t <= ticks_at_beat) {
1061 result.ticks = ticks_at_beat - t;
1065 } while (t > ticks_at_beat);
1073 frames = count_frames_between( result,when);
1075 frames = count_frames_between(when,result);
1084 TempoMap::round_to_bar (nframes_t fr, int dir)
1087 Glib::RWLock::ReaderLock lm (lock);
1088 return round_to_type (fr, dir, Bar);
1094 TempoMap::round_to_beat (nframes_t fr, int dir)
1097 Glib::RWLock::ReaderLock lm (lock);
1098 return round_to_type (fr, dir, Beat);
1103 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num, int dir)
1107 uint32_t ticks_one_half_subdivisions_worth;
1108 uint32_t ticks_one_subdivisions_worth;
1109 uint32_t difference;
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;
1120 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1123 /* right on the subdivision, so the difference is just the subdivision ticks */
1124 difference = ticks_one_subdivisions_worth;
1127 /* not on subdivision, compute distance to next subdivision */
1129 difference = ticks_one_subdivisions_worth - mod;
1132 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1134 the_beat.ticks += difference;
1135 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1137 the_beat.ticks += difference;
1140 } else if (dir < 0) {
1142 /* round to previous */
1144 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1147 /* right on the subdivision, so the difference is just the subdivision ticks */
1148 difference = ticks_one_subdivisions_worth;
1149 cerr << "On the sub, move by 1 sub = " << difference << endl;
1151 /* not on subdivision, compute distance to previous subdivision, which
1152 is just the modulus.
1156 cerr << "off the sub, move by 1 sub = " << difference << endl;
1160 cerr << "ticks = " << the_beat.ticks << endl;
1162 if (the_beat.ticks < difference) {
1163 cerr << "backup beats, set ticks to "
1164 << (uint32_t)Meter::ticks_per_beat - difference << endl;
1166 the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference;
1168 cerr << " reduce ticks\n";
1169 the_beat.ticks -= difference;
1173 /* round to nearest */
1175 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1176 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1177 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1179 the_beat.ticks += difference;
1180 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1182 the_beat.ticks += difference;
1185 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1186 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1190 return frame_time (the_beat);
1194 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1196 TempoMetric metric = metric_at (frame);
1199 bbt_time_with_metric (frame, bbt, metric);
1204 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1209 } else if (dir > 0) {
1210 if (bbt.beats > 0) {
1212 } else if (metric.frame() < frame) {
1216 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1225 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1227 if (bbt.beats > 1) {
1231 } else if (dir > 0) {
1232 if (bbt.ticks > 0) {
1234 } else if (metric.frame() < frame) {
1238 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1242 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1251 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)));
1252 return metric.frame() + count_frames_between (metric.start(), bbt);
1255 TempoMap::BBTPointList *
1256 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1259 Metrics::const_iterator i;
1260 BBTPointList *points;
1262 const MeterSection* meter;
1263 const MeterSection* m;
1264 const TempoSection* tempo;
1265 const TempoSection* t;
1268 double beats_per_bar;
1271 double frames_per_bar;
1277 meter = &first_meter ();
1278 tempo = &first_tempo ();
1280 /* find the starting point */
1282 for (i = metrics->begin(); i != metrics->end(); ++i) {
1284 if ((*i)->frame() > lower) {
1288 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1290 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1297 meter -> the Meter for "lower"
1298 tempo -> the Tempo for "lower"
1299 i -> for first new metric after "lower", possibly metrics->end()
1301 Now start generating points.
1304 beats_per_bar = meter->beats_per_bar ();
1305 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1306 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1308 if (meter->frame() > tempo->frame()) {
1309 bar = meter->start().bars;
1310 beat = meter->start().beats;
1311 current = meter->frame();
1313 bar = tempo->start().bars;
1314 beat = tempo->start().beats;
1315 current = tempo->frame();
1318 /* initialize current to point to the bar/beat just prior to the
1319 lower frame bound passed in. assumes that current is initialized
1320 above to be on a beat.
1323 delta_bars = (lower-current) / frames_per_bar;
1324 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1325 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1327 // adjust bars and beats too
1328 bar += (uint32_t) (floor(delta_bars));
1329 beat += (uint32_t) (floor(delta_beats));
1331 points = new BBTPointList;
1335 if (i == metrics->end()) {
1337 // cerr << "== limit set to end of request @ " << limit << endl;
1339 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1340 limit = (*i)->frame();
1343 limit = min (limit, upper);
1345 while (current < limit) {
1347 /* if we're at the start of a bar, add bar point */
1350 if (current >= lower) {
1351 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1352 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1357 /* add some beats if we can */
1359 beat_frame = current;
1361 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1362 if (beat_frame >= lower) {
1363 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1364 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1366 beat_frame += beat_frames;
1367 current+= beat_frames;
1372 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1373 // << (beat > ceil(beats_per_bar))
1376 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1378 /* we walked an entire bar. its
1379 important to move `current' forward
1380 by the actual frames_per_bar, not move it to
1381 an integral beat_frame, so that metrics with
1382 non-integral beats-per-bar have
1383 their bar positions set
1384 correctly. consider a metric with
1385 9-1/2 beats-per-bar. the bar we
1386 just filled had 10 beat marks,
1387 but the bar end is 1/2 beat before
1389 And it is also possible that a tempo
1390 change occured in the middle of a bar,
1391 so we subtract the possible extra fraction from the current
1394 if (beat > ceil (beats_per_bar)) {
1395 /* next bar goes where the numbers suggest */
1396 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1397 // cerr << "++ next bar from numbers\n";
1399 /* next bar goes where the next metric is */
1401 // cerr << "++ next bar at next metric\n";
1409 /* if we're done, then we're done */
1411 if (current >= upper) {
1415 /* i is an iterator that refers to the next metric (or none).
1416 if there is a next metric, move to it, and continue.
1419 if (i != metrics->end()) {
1421 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1423 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1425 /* new MeterSection, beat always returns to 1 */
1429 current = (*i)->frame ();
1430 // cerr << "loop around with current @ " << current << endl;
1432 beats_per_bar = meter->beats_per_bar ();
1433 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1434 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1445 TempoMap::tempo_section_at (nframes_t frame)
1447 Glib::RWLock::ReaderLock lm (lock);
1448 Metrics::iterator i;
1449 TempoSection* prev = 0;
1451 for (i = metrics->begin(); i != metrics->end(); ++i) {
1454 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1456 if ((*i)->frame() > frame) {
1472 TempoMap::tempo_at (nframes_t frame) const
1474 TempoMetric m (metric_at (frame));
1480 TempoMap::meter_at (nframes_t frame) const
1482 TempoMetric m (metric_at (frame));
1487 TempoMap::get_state ()
1489 Metrics::const_iterator i;
1490 XMLNode *root = new XMLNode ("TempoMap");
1493 Glib::RWLock::ReaderLock lm (lock);
1494 for (i = metrics->begin(); i != metrics->end(); ++i) {
1495 root->add_child_nocopy ((*i)->get_state());
1503 TempoMap::set_state (const XMLNode& node, int /*version*/)
1506 Glib::RWLock::WriterLock lm (lock);
1509 XMLNodeConstIterator niter;
1510 Metrics old_metrics (*metrics);
1514 nlist = node.children();
1516 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1517 XMLNode* child = *niter;
1519 if (child->name() == TempoSection::xml_state_node_name) {
1522 metrics->push_back (new TempoSection (*child));
1525 catch (failed_constructor& err){
1526 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1527 *metrics = old_metrics;
1531 } else if (child->name() == MeterSection::xml_state_node_name) {
1534 metrics->push_back (new MeterSection (*child));
1537 catch (failed_constructor& err) {
1538 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1539 *metrics = old_metrics;
1545 if (niter == nlist.end()) {
1547 MetricSectionSorter cmp;
1548 metrics->sort (cmp);
1549 timestamp_metrics (true);
1553 StateChanged (Change (0));
1559 TempoMap::dump (std::ostream& o) const
1561 const MeterSection* m;
1562 const TempoSection* t;
1564 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1566 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1567 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1568 << t->movable() << ')' << endl;
1569 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1570 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1571 << " (move? " << m->movable() << ')' << endl;
1577 TempoMap::n_tempos() const
1579 Glib::RWLock::ReaderLock lm (lock);
1582 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1583 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1592 TempoMap::n_meters() const
1594 Glib::RWLock::ReaderLock lm (lock);
1597 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1598 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1607 TempoMap::insert_time (nframes_t where, nframes_t amount)
1609 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1610 if ((*i)->frame() >= where) {
1611 (*i)->set_frame ((*i)->frame() + amount);
1615 timestamp_metrics (false);
1617 StateChanged (Change (0));
1621 TempoMap::bbt_add (const BBT_Time& a, const BBT_Time& b, const TempoMetric& /*metric*/)
1623 // FIXME: Obviously not correct!
1624 return BBT_Time(a.bars + b.bars, a.beats + b.beats, a.ticks + b.ticks);
1628 TempoMap::bbt_add (const BBT_Time& a, const BBT_Time& b)
1630 // FIXME: Obviously not correct!
1631 return BBT_Time(a.bars + b.bars, a.beats + b.beats, a.ticks + b.ticks);
1635 TempoMap::bbt_subtract (const BBT_Time& a, const BBT_Time& b)
1637 // FIXME: Obviously not correct!
1638 return BBT_Time(a.bars - b.bars, a.beats - b.beats, a.ticks - b.ticks);