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 (prop->value() == "yes");
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 (prop->value() == "yes");
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);
1103 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
1107 uint32_t ticks_one_half_subdivisions_worth;
1108 uint32_t ticks_one_subdivisions_worth;
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;
1115 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1116 uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1117 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1119 the_beat.ticks += difference;
1120 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1122 the_beat.ticks += difference;
1125 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1128 return frame_time (the_beat);
1132 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1134 Metric metric = metric_at (frame);
1137 bbt_time_with_metric (frame, bbt, metric);
1143 } else if (dir > 0) {
1144 if (bbt.beats > 0) {
1146 } else if (metric.frame() < frame) {
1150 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1161 } else if (dir > 0) {
1162 if (bbt.ticks > 0) {
1164 } else if (metric.frame() < frame) {
1168 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1172 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1182 cerr << "for " << frame << " round to " << bbt << " using "
1186 return metric.frame() + count_frames_between (metric.start(), bbt);
1189 TempoMap::BBTPointList *
1190 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1193 Metrics::const_iterator i;
1194 BBTPointList *points;
1196 const MeterSection* meter;
1197 const MeterSection* m;
1198 const TempoSection* tempo;
1199 const TempoSection* t;
1202 double beats_per_bar;
1205 double frames_per_bar;
1211 meter = &first_meter ();
1212 tempo = &first_tempo ();
1214 /* find the starting point */
1216 for (i = metrics->begin(); i != metrics->end(); ++i) {
1218 if ((*i)->frame() > lower) {
1222 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1224 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1231 meter -> the Meter for "lower"
1232 tempo -> the Tempo for "lower"
1233 i -> for first new metric after "lower", possibly metrics->end()
1235 Now start generating points.
1238 beats_per_bar = meter->beats_per_bar ();
1239 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1240 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1242 if (meter->frame() > tempo->frame()) {
1243 bar = meter->start().bars;
1244 beat = meter->start().beats;
1245 current = meter->frame();
1247 bar = tempo->start().bars;
1248 beat = tempo->start().beats;
1249 current = tempo->frame();
1252 /* initialize current to point to the bar/beat just prior to the
1253 lower frame bound passed in. assumes that current is initialized
1254 above to be on a beat.
1257 delta_bars = (lower-current) / frames_per_bar;
1258 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1259 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1261 // adjust bars and beats too
1262 bar += (uint32_t) (floor(delta_bars));
1263 beat += (uint32_t) (floor(delta_beats));
1265 points = new BBTPointList;
1269 if (i == metrics->end()) {
1271 // cerr << "== limit set to end of request @ " << limit << endl;
1273 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1274 limit = (*i)->frame();
1277 limit = min (limit, upper);
1279 while (current < limit) {
1281 /* if we're at the start of a bar, add bar point */
1284 if (current >= lower) {
1285 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1286 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1291 /* add some beats if we can */
1293 beat_frame = current;
1295 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1296 if (beat_frame >= lower) {
1297 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1298 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1300 beat_frame += beat_frames;
1301 current+= beat_frames;
1306 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1307 // << (beat > ceil(beats_per_bar))
1310 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1312 /* we walked an entire bar. its
1313 important to move `current' forward
1314 by the actual frames_per_bar, not move it to
1315 an integral beat_frame, so that metrics with
1316 non-integral beats-per-bar have
1317 their bar positions set
1318 correctly. consider a metric with
1319 9-1/2 beats-per-bar. the bar we
1320 just filled had 10 beat marks,
1321 but the bar end is 1/2 beat before
1323 And it is also possible that a tempo
1324 change occured in the middle of a bar,
1325 so we subtract the possible extra fraction from the current
1328 if (beat > ceil (beats_per_bar)) {
1329 /* next bar goes where the numbers suggest */
1330 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1331 // cerr << "++ next bar from numbers\n";
1333 /* next bar goes where the next metric is */
1335 // cerr << "++ next bar at next metric\n";
1343 /* if we're done, then we're done */
1345 if (current >= upper) {
1349 /* i is an iterator that refers to the next metric (or none).
1350 if there is a next metric, move to it, and continue.
1353 if (i != metrics->end()) {
1355 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1357 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1359 /* new MeterSection, beat always returns to 1 */
1363 current = (*i)->frame ();
1364 // cerr << "loop around with current @ " << current << endl;
1366 beats_per_bar = meter->beats_per_bar ();
1367 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1368 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1379 TempoMap::tempo_section_at (nframes_t frame)
1381 Glib::RWLock::ReaderLock lm (lock);
1382 Metrics::iterator i;
1383 TempoSection* prev = 0;
1385 for (i = metrics->begin(); i != metrics->end(); ++i) {
1388 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1390 if ((*i)->frame() > frame) {
1406 TempoMap::tempo_at (nframes_t frame)
1408 Metric m (metric_at (frame));
1414 TempoMap::meter_at (nframes_t frame)
1416 Metric m (metric_at (frame));
1421 TempoMap::get_state ()
1423 Metrics::const_iterator i;
1424 XMLNode *root = new XMLNode ("TempoMap");
1427 Glib::RWLock::ReaderLock lm (lock);
1428 for (i = metrics->begin(); i != metrics->end(); ++i) {
1429 root->add_child_nocopy ((*i)->get_state());
1437 TempoMap::set_state (const XMLNode& node)
1440 Glib::RWLock::WriterLock lm (lock);
1443 XMLNodeConstIterator niter;
1444 Metrics old_metrics (*metrics);
1448 nlist = node.children();
1450 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1451 XMLNode* child = *niter;
1453 if (child->name() == TempoSection::xml_state_node_name) {
1456 metrics->push_back (new TempoSection (*child));
1459 catch (failed_constructor& err){
1460 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1461 *metrics = old_metrics;
1465 } else if (child->name() == MeterSection::xml_state_node_name) {
1468 metrics->push_back (new MeterSection (*child));
1471 catch (failed_constructor& err) {
1472 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1473 *metrics = old_metrics;
1479 if (niter == nlist.end()) {
1481 MetricSectionSorter cmp;
1482 metrics->sort (cmp);
1483 timestamp_metrics (true);
1487 StateChanged (Change (0));
1493 TempoMap::dump (std::ostream& o) const
1495 const MeterSection* m;
1496 const TempoSection* t;
1498 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1500 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1501 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1502 << t->movable() << ')' << endl;
1503 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1504 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1505 << " (move? " << m->movable() << ')' << endl;
1511 TempoMap::n_tempos() const
1513 Glib::RWLock::ReaderLock lm (lock);
1516 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1517 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1526 TempoMap::n_meters() const
1528 Glib::RWLock::ReaderLock lm (lock);
1531 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1532 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1541 TempoMap::insert_time (nframes_t where, nframes_t amount)
1543 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1544 if ((*i)->frame() >= where) {
1545 (*i)->set_frame ((*i)->frame() + amount);
1549 timestamp_metrics (false);
1551 StateChanged (Change (0));