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/tempo.h"
30 #include "ardour/utils.h"
36 using namespace ARDOUR;
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 /***********************************************************************/
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;
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,
79 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
80 throw failed_constructor();
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 */
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 (string_is_affirmative (prop->value()));
114 TempoSection::get_state() const
116 XMLNode *root = new XMLNode (xml_state_node_name);
118 LocaleGuard lg (X_("POSIX"));
120 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
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);
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;
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,
155 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
156 throw failed_constructor();
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 (string_is_affirmative (prop->value()));
190 MeterSection::get_state() const
192 XMLNode *root = new XMLNode (xml_state_node_name);
194 LocaleGuard lg (X_("POSIX"));
196 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
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);
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;
223 last_bbt_valid = false;
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()) {
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)) {
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;
285 /* positioned at bar start already, so just put it there */
287 section.set_start (when);
289 timestamp_metrics (true);
297 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
299 if (move_metric_section (tempo, when) == 0) {
300 StateChanged (Change (0));
305 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
307 if (move_metric_section (meter, when) == 0) {
308 StateChanged (Change (0));
313 TempoMap::remove_tempo (const TempoSection& tempo)
315 bool removed = false;
318 Glib::RWLock::WriterLock lm (lock);
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()) {
335 StateChanged (Change (0));
340 TempoMap::remove_meter (const MeterSection& tempo)
342 bool removed = false;
345 Glib::RWLock::WriterLock lm (lock);
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()) {
362 StateChanged (Change (0));
367 TempoMap::do_insert (MetricSection* section, bool with_bbt)
371 for (i = metrics->begin(); i != metrics->end(); ++i) {
374 if ((*i)->start() < section->start()) {
378 if ((*i)->frame() < section->frame()) {
383 metrics->insert (i, section);
387 if (i == metrics->end()) {
388 metrics->insert (metrics->end(), section);
391 timestamp_metrics (with_bbt);
395 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
398 Glib::RWLock::WriterLock lm (lock);
400 /* new tempos always start on a beat */
404 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
407 StateChanged (Change (0));
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));
422 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
424 bool replaced = false;
427 Glib::RWLock::WriterLock lm (lock);
430 for (i = metrics->begin(); i != metrics->end(); ++i) {
433 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
435 *((Tempo *) ts) = replacement;
438 timestamp_metrics (true);
446 StateChanged (Change (0));
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) {
468 /* new meters *always* start on a beat. */
472 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
475 StateChanged (Change (0));
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));
490 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
492 bool replaced = false;
495 Glib::RWLock::WriterLock lm (lock);
498 for (i = metrics->begin(); i != metrics->end(); ++i) {
500 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
502 *((Meter*) ms) = replacement;
505 timestamp_metrics (true);
512 StateChanged (Change (0));
517 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
519 Tempo newtempo (beats_per_minute, note_type);
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));
532 TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type)
534 Tempo newtempo (beats_per_minute, note_type);
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) {
551 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
561 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
570 *((Tempo*)prev) = newtempo;
571 StateChanged (Change (0));
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) {
585 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
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) {
601 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
607 TempoMap::timestamp_metrics (bool use_bbt)
615 meter = &first_meter ();
616 tempo = &first_tempo ();
620 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
622 nframes_t current = 0;
623 nframes_t section_frames;
627 for (i = metrics->begin(); i != metrics->end(); ++i) {
631 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
633 current += section_frames;
637 (*i)->set_frame (current);
639 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
641 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
644 fatal << _("programming error: unhandled MetricSection type") << endmsg;
651 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
654 MetricSection* prev = 0;
656 for (i = metrics->begin(); i != metrics->end(); ++i) {
659 Metric metric (*meter, *tempo);
662 metric.set_start (prev->start());
663 metric.set_frame (prev->frame());
665 // metric will be at frames=0 bbt=1|1|0 by default
666 // which is correct for our purpose
669 bbt_time_with_metric ((*i)->frame(), bbt, metric);
671 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
678 if (bbt.ticks > Meter::ticks_per_beat/2) {
679 /* round up to next beat */
685 if (bbt.beats != 1) {
686 /* round up to next bar */
692 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
694 (*i)->set_start (bbt);
696 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
698 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
699 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
701 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
703 fatal << _("programming error: unhandled MetricSection type") << endmsg;
712 // cerr << "###############################################\n\n\n" << endl;
717 TempoMap::metric_at (nframes_t frame) const
719 Metric m (first_meter(), first_tempo());
723 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
724 at something, because we insert the default tempo and meter during
725 TempoMap construction.
727 now see if we can find better candidates.
730 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
732 if ((*i)->frame() > frame) {
736 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
737 m.set_tempo (*tempo);
738 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
739 m.set_meter (*meter);
742 m.set_frame ((*i)->frame ());
743 m.set_start ((*i)->start ());
750 TempoMap::metric_at (BBT_Time bbt) const
752 Metric m (first_meter(), first_tempo());
756 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
757 at something, because we insert the default tempo and meter during
758 TempoMap construction.
760 now see if we can find better candidates.
763 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
765 BBT_Time section_start ((*i)->start());
767 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
771 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
772 m.set_tempo (*tempo);
773 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
774 m.set_meter (*meter);
777 m.set_frame ((*i)->frame ());
778 m.set_start (section_start);
785 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
788 Glib::RWLock::ReaderLock lm (lock);
789 bbt_time_unlocked (frame, bbt);
794 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
796 bbt_time_with_metric (frame, bbt, metric_at (frame));
800 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
802 nframes_t frame_diff;
804 // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
806 const double beats_per_bar = metric.meter().beats_per_bar();
807 const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat;
809 /* now compute how far beyond that point we actually are. */
811 frame_diff = frame - metric.frame();
813 bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
814 uint32_t xtra_beats = bbt.ticks / (uint32_t)Meter::ticks_per_beat;
815 bbt.ticks %= (uint32_t)Meter::ticks_per_beat;
817 bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
818 bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
819 bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
821 /* if we have a fractional number of beats per bar, we see if
822 we're in the last beat (the fractional one). if so, we
823 round ticks appropriately and bump to the next bar. */
824 double beat_fraction = beats_per_bar - floor(beats_per_bar);
825 /* XXX one problem here is that I'm not sure how to handle
826 fractional beats that don't evenly divide ticks_per_beat.
827 If they aren't handled consistently, I would guess we'll
828 continue to have strange discrepancies occuring. Perhaps
829 this will also behave badly in the case of meters like
830 0.1/4, but I can't be bothered to test that.
832 uint32_t ticks_on_last_beat = (uint32_t)floor(Meter::ticks_per_beat * beat_fraction);
833 if(bbt.beats > (uint32_t)floor(beats_per_bar) &&
834 bbt.ticks >= ticks_on_last_beat) {
835 bbt.ticks -= ticks_on_last_beat;
840 bbt.beats++; // correction for 1-based counting, see above for matching operation.
842 // cerr << "-----\t RETURN " << bbt << endl;
846 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
848 /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
849 that means that the beats and ticks should be inside a bar
852 nframes_t frames = 0;
853 nframes_t start_frame = 0;
854 nframes_t end_frame = 0;
856 Metric m = metric_at (start);
858 uint32_t bar_offset = start.bars - m.start().bars;
860 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
861 + start.ticks/Meter::ticks_per_beat;
864 start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
868 bar_offset = end.bars - m.start().bars;
870 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
871 + end.ticks/Meter::ticks_per_beat;
873 end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
875 frames = end_frame - start_frame;
882 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
884 /* this is used in timestamping the metrics by actually counting the beats */
886 nframes_t frames = 0;
887 uint32_t bar = start.bars;
888 double beat = (double) start.beats;
889 double beats_counted = 0;
890 double beats_per_bar = 0;
891 double beat_frames = 0;
893 beats_per_bar = meter.beats_per_bar();
894 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
898 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
900 if (beat >= beats_per_bar) {
905 if (beat > beats_per_bar) {
907 /* this is a fractional beat at the end of a fractional bar
908 so it should only count for the fraction
911 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
920 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
921 // << " bpb were " << beats_per_bar
922 // << " fpb was " << beat_frames
925 frames = (nframes_t) floor (beats_counted * beat_frames);
932 TempoMap::frame_time (const BBT_Time& bbt) const
934 BBT_Time start ; /* 1|1|0 */
936 return count_frames_between ( start, bbt);
940 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
942 nframes_t frames = 0;
948 Glib::RWLock::ReaderLock lm (lock);
949 frames = bbt_duration_at_unlocked (when, bbt,dir);
956 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
959 nframes_t frames = 0;
961 double beats_per_bar;
964 result.bars = max(1U, when.bars + dir * bbt.bars) ;
968 Metric metric = metric_at(result);
969 beats_per_bar = metric.meter().beats_per_bar();
973 /*reduce things to legal bbt values
974 we have to handle possible fractional=shorter beats at the end of measures
975 and things like 0|11|9000 as a duration in a 4.5/4 measure
976 the musical decision is that the fractional beat is also a beat , although a shorter one
981 result.beats = when.beats + bbt.beats;
982 result.ticks = when.ticks + bbt.ticks;
984 while (result.beats >= (beats_per_bar + 1)) {
986 result.beats -= (uint32_t) ceil(beats_per_bar);
987 metric = metric_at(result); // maybe there is a meter change
988 beats_per_bar = metric.meter().beats_per_bar();
991 /*we now counted the beats and landed in the target measure, now deal with ticks
992 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
993 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
996 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
999 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1000 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
1001 : Meter::ticks_per_beat );
1003 while (result.ticks >= ticks_at_beat) {
1005 result.ticks -= ticks_at_beat;
1006 if (result.beats >= (beats_per_bar + 1)) {
1009 metric = metric_at(result); // maybe there is a meter change
1010 beats_per_bar = metric.meter().beats_per_bar();
1012 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1013 (1 - (ceil(beats_per_bar) - beats_per_bar) ) * Meter::ticks_per_beat
1014 : Meter::ticks_per_beat);
1020 uint32_t b = bbt.beats;
1023 while( b > when.beats ) {
1025 result.bars = max(1U,result.bars-- ) ;
1026 metric = metric_at(result); // maybe there is a meter change
1027 beats_per_bar = metric.meter().beats_per_bar();
1028 if (b >= ceil(beats_per_bar)) {
1030 b -= (uint32_t) ceil(beats_per_bar);
1032 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1035 result.beats = when.beats - b;
1039 if (bbt.ticks <= when.ticks) {
1040 result.ticks = when.ticks - bbt.ticks;
1043 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1044 uint32_t t = bbt.ticks - when.ticks;
1048 if (result.beats == 1) {
1049 result.bars = max(1U, result.bars-- ) ;
1050 metric = metric_at(result); // maybe there is a meter change
1051 beats_per_bar = metric.meter().beats_per_bar();
1052 result.beats = (uint32_t) ceil(beats_per_bar);
1053 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * Meter::ticks_per_beat) ;
1056 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1059 if (t <= ticks_at_beat) {
1060 result.ticks = ticks_at_beat - t;
1064 } while (t > ticks_at_beat);
1072 frames = count_frames_between( result,when);
1074 frames = count_frames_between(when,result);
1083 TempoMap::round_to_bar (nframes_t fr, int dir)
1086 Glib::RWLock::ReaderLock lm (lock);
1087 return round_to_type (fr, dir, Bar);
1093 TempoMap::round_to_beat (nframes_t fr, int dir)
1096 Glib::RWLock::ReaderLock lm (lock);
1097 return round_to_type (fr, dir, Beat);
1102 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num, int dir)
1106 uint32_t ticks_one_half_subdivisions_worth;
1107 uint32_t ticks_one_subdivisions_worth;
1108 uint32_t difference;
1110 bbt_time(fr, the_beat);
1112 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1113 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1119 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1122 /* right on the subdivision, so the difference is just the subdivision ticks */
1123 difference = ticks_one_subdivisions_worth;
1126 /* not on subdivision, compute distance to next subdivision */
1128 difference = ticks_one_subdivisions_worth - mod;
1131 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1133 the_beat.ticks += difference;
1134 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1136 the_beat.ticks += difference;
1139 } else if (dir < 0) {
1141 /* round to previous */
1143 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1146 /* right on the subdivision, so the difference is just the subdivision ticks */
1147 difference = ticks_one_subdivisions_worth;
1148 cerr << "On the sub, move by 1 sub = " << difference << endl;
1150 /* not on subdivision, compute distance to previous subdivision, which
1151 is just the modulus.
1155 cerr << "off the sub, move by 1 sub = " << difference << endl;
1159 cerr << "ticks = " << the_beat.ticks << endl;
1161 if (the_beat.ticks < difference) {
1162 cerr << "backup beats, set ticks to "
1163 << (uint32_t)Meter::ticks_per_beat - difference << endl;
1165 the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference;
1167 cerr << " reduce ticks\n";
1168 the_beat.ticks -= difference;
1172 /* round to nearest */
1174 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1175 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1176 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1178 the_beat.ticks += difference;
1179 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1181 the_beat.ticks += difference;
1184 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1185 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1189 return frame_time (the_beat);
1193 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1195 Metric metric = metric_at (frame);
1198 bbt_time_with_metric (frame, bbt, metric);
1206 } else if (dir > 0) {
1207 if (bbt.beats > 0) {
1209 } else if (metric.frame() < frame) {
1213 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1223 if (bbt.beats > 1) {
1226 } else if (dir > 0) {
1227 if (bbt.ticks > 0) {
1229 } else if (metric.frame() < frame) {
1233 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1237 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1247 cerr << "for " << frame << " round to " << bbt << " using "
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)
1474 Metric m (metric_at (frame));
1480 TempoMap::meter_at (nframes_t frame)
1482 Metric 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));