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.
27 #include <sigc++/bind.h>
29 #include <glibmm/thread.h>
30 #include "pbd/xml++.h"
31 #include "ardour/debug.h"
32 #include "ardour/tempo.h"
33 #include "ardour/utils.h"
39 using namespace ARDOUR;
42 /* _default tempo is 4/4 qtr=120 */
44 Meter TempoMap::_default_meter (4.0, 4.0);
45 Tempo TempoMap::_default_tempo (120.0);
47 const double Meter::ticks_per_beat = 1920.0;
49 double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
51 return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
54 /***********************************************************************/
57 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
59 return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
62 /***********************************************************************/
64 const string TempoSection::xml_state_node_name = "Tempo";
66 TempoSection::TempoSection (const XMLNode& node)
67 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
69 const XMLProperty *prop;
71 LocaleGuard lg (X_("POSIX"));
73 if ((prop = node.property ("start")) == 0) {
74 error << _("TempoSection XML node has no \"start\" property") << endmsg;
75 throw failed_constructor();
78 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
82 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
83 throw failed_constructor();
88 if ((prop = node.property ("beats-per-minute")) == 0) {
89 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
90 throw failed_constructor();
93 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
94 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
95 throw failed_constructor();
98 if ((prop = node.property ("note-type")) == 0) {
99 /* older session, make note type be quarter by default */
102 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
103 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
104 throw failed_constructor();
108 if ((prop = node.property ("movable")) == 0) {
109 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
110 throw failed_constructor();
113 set_movable (string_is_affirmative (prop->value()));
117 TempoSection::get_state() const
119 XMLNode *root = new XMLNode (xml_state_node_name);
121 LocaleGuard lg (X_("POSIX"));
123 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
127 root->add_property ("start", buf);
128 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
129 root->add_property ("beats-per-minute", buf);
130 snprintf (buf, sizeof (buf), "%f", _note_type);
131 root->add_property ("note-type", buf);
132 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
133 root->add_property ("movable", buf);
138 /***********************************************************************/
140 const string MeterSection::xml_state_node_name = "Meter";
142 MeterSection::MeterSection (const XMLNode& node)
143 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
145 const XMLProperty *prop;
147 LocaleGuard lg (X_("POSIX"));
149 if ((prop = node.property ("start")) == 0) {
150 error << _("MeterSection XML node has no \"start\" property") << endmsg;
151 throw failed_constructor();
154 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
158 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
159 throw failed_constructor();
164 if ((prop = node.property ("beats-per-bar")) == 0) {
165 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
166 throw failed_constructor();
169 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
170 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
171 throw failed_constructor();
174 if ((prop = node.property ("note-type")) == 0) {
175 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
176 throw failed_constructor();
179 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
180 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
181 throw failed_constructor();
184 if ((prop = node.property ("movable")) == 0) {
185 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
186 throw failed_constructor();
189 set_movable (string_is_affirmative (prop->value()));
193 MeterSection::get_state() const
195 XMLNode *root = new XMLNode (xml_state_node_name);
197 LocaleGuard lg (X_("POSIX"));
199 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
203 root->add_property ("start", buf);
204 snprintf (buf, sizeof (buf), "%f", _note_type);
205 root->add_property ("note-type", buf);
206 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
207 root->add_property ("beats-per-bar", buf);
208 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
209 root->add_property ("movable", buf);
214 /***********************************************************************/
216 struct MetricSectionSorter {
217 bool operator() (const MetricSection* a, const MetricSection* b) {
218 return a->start() < b->start();
222 TempoMap::TempoMap (nframes64_t fr)
224 metrics = new Metrics;
226 last_bbt_valid = false;
233 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
234 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
236 t->set_movable (false);
237 m->set_movable (false);
239 /* note: frame time is correct (zero) for both of these */
241 metrics->push_back (t);
242 metrics->push_back (m);
245 TempoMap::~TempoMap ()
250 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
252 if (when == section.start() || !section.movable()) {
256 Glib::RWLock::WriterLock lm (lock);
257 MetricSectionSorter cmp;
259 if (when.beats != 1) {
261 /* position by audio frame, then recompute BBT timestamps from the audio ones */
263 nframes64_t frame = frame_time (when);
264 // cerr << "nominal frame time = " << frame << endl;
266 nframes64_t prev_frame = round_to_type (frame, -1, Beat);
267 nframes64_t next_frame = round_to_type (frame, 1, Beat);
269 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
271 /* use the closest beat */
273 if ((frame - prev_frame) < (next_frame - frame)) {
279 // cerr << "actual frame time = " << frame << endl;
280 section.set_frame (frame);
281 // cerr << "frame time = " << section.frame() << endl;
282 timestamp_metrics (false);
283 // cerr << "new BBT time = " << section.start() << endl;
288 /* positioned at bar start already, so just put it there */
290 section.set_start (when);
292 timestamp_metrics (true);
300 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
302 if (move_metric_section (tempo, when) == 0) {
303 StateChanged (Change (0));
308 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
310 if (move_metric_section (meter, when) == 0) {
311 StateChanged (Change (0));
316 TempoMap::remove_tempo (const TempoSection& tempo)
318 bool removed = false;
321 Glib::RWLock::WriterLock lm (lock);
324 for (i = metrics->begin(); i != metrics->end(); ++i) {
325 if (dynamic_cast<TempoSection*> (*i) != 0) {
326 if (tempo.frame() == (*i)->frame()) {
327 if ((*i)->movable()) {
338 StateChanged (Change (0));
343 TempoMap::remove_meter (const MeterSection& tempo)
345 bool removed = false;
348 Glib::RWLock::WriterLock lm (lock);
351 for (i = metrics->begin(); i != metrics->end(); ++i) {
352 if (dynamic_cast<MeterSection*> (*i) != 0) {
353 if (tempo.frame() == (*i)->frame()) {
354 if ((*i)->movable()) {
365 StateChanged (Change (0));
370 TempoMap::do_insert (MetricSection* section, bool with_bbt)
374 for (i = metrics->begin(); i != metrics->end(); ++i) {
377 if ((*i)->start() < section->start()) {
381 if ((*i)->frame() < section->frame()) {
386 metrics->insert (i, section);
390 if (i == metrics->end()) {
391 metrics->insert (metrics->end(), section);
394 timestamp_metrics (with_bbt);
398 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
401 Glib::RWLock::WriterLock lm (lock);
403 /* new tempos always start on a beat */
407 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
410 StateChanged (Change (0));
414 TempoMap::add_tempo (const Tempo& tempo, nframes64_t where)
417 Glib::RWLock::WriterLock lm (lock);
418 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
421 StateChanged (Change (0));
425 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
427 bool replaced = false;
430 Glib::RWLock::WriterLock lm (lock);
433 for (i = metrics->begin(); i != metrics->end(); ++i) {
436 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
438 *((Tempo *) ts) = replacement;
441 timestamp_metrics (true);
449 StateChanged (Change (0));
454 TempoMap::add_meter (const Meter& meter, BBT_Time where)
457 Glib::RWLock::WriterLock lm (lock);
459 /* a new meter always starts a new bar on the first beat. so
460 round the start time appropriately. remember that
461 `where' is based on the existing tempo map, not
462 the result after we insert the new meter.
466 if (where.beats != 1) {
471 /* new meters *always* start on a beat. */
475 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
478 StateChanged (Change (0));
482 TempoMap::add_meter (const Meter& meter, nframes64_t where)
485 Glib::RWLock::WriterLock lm (lock);
486 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
489 StateChanged (Change (0));
493 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
495 bool replaced = false;
498 Glib::RWLock::WriterLock lm (lock);
501 for (i = metrics->begin(); i != metrics->end(); ++i) {
503 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
505 *((Meter*) ms) = replacement;
508 timestamp_metrics (true);
515 StateChanged (Change (0));
520 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
522 Tempo newtempo (beats_per_minute, note_type);
525 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
526 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
527 *((Tempo*) t) = newtempo;
528 StateChanged (Change (0));
535 TempoMap::change_existing_tempo_at (nframes64_t where, double beats_per_minute, double note_type)
537 Tempo newtempo (beats_per_minute, note_type);
543 /* find the TempoSection immediately preceding "where"
546 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
548 if ((*i)->frame() > where) {
554 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
564 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
573 *((Tempo*)prev) = newtempo;
574 StateChanged (Change (0));
578 TempoMap::first_meter () const
580 const MeterSection *m = 0;
582 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
583 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
588 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
594 TempoMap::first_tempo () const
596 const TempoSection *t = 0;
598 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
599 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
604 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
610 TempoMap::timestamp_metrics (bool use_bbt)
618 meter = &first_meter ();
619 tempo = &first_tempo ();
623 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
625 nframes64_t current = 0;
626 nframes64_t section_frames;
630 for (i = metrics->begin(); i != metrics->end(); ++i) {
634 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
636 current += section_frames;
640 (*i)->set_frame (current);
642 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
644 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
647 fatal << _("programming error: unhandled MetricSection type") << endmsg;
654 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
657 MetricSection* prev = 0;
659 for (i = metrics->begin(); i != metrics->end(); ++i) {
662 TempoMetric metric (*meter, *tempo);
665 metric.set_start (prev->start());
666 metric.set_frame (prev->frame());
668 // metric will be at frames=0 bbt=1|1|0 by default
669 // which is correct for our purpose
672 bbt_time_with_metric ((*i)->frame(), bbt, metric);
674 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
681 if (bbt.ticks > Meter::ticks_per_beat/2) {
682 /* round up to next beat */
688 if (bbt.beats != 1) {
689 /* round up to next bar */
695 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
697 (*i)->set_start (bbt);
699 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
701 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
702 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
704 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
706 fatal << _("programming error: unhandled MetricSection type") << endmsg;
715 // cerr << "###############################################\n\n\n" << endl;
720 TempoMap::metric_at (nframes64_t frame) const
722 TempoMetric m (first_meter(), first_tempo());
726 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
727 at something, because we insert the default tempo and meter during
728 TempoMap construction.
730 now see if we can find better candidates.
733 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
735 if ((*i)->frame() > frame) {
739 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
740 m.set_tempo (*tempo);
741 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
742 m.set_meter (*meter);
745 m.set_frame ((*i)->frame ());
746 m.set_start ((*i)->start ());
753 TempoMap::metric_at (BBT_Time bbt) const
755 TempoMetric m (first_meter(), first_tempo());
759 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
760 at something, because we insert the default tempo and meter during
761 TempoMap construction.
763 now see if we can find better candidates.
766 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
768 BBT_Time section_start ((*i)->start());
770 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
774 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
775 m.set_tempo (*tempo);
776 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
777 m.set_meter (*meter);
780 m.set_frame ((*i)->frame ());
781 m.set_start (section_start);
788 TempoMap::bbt_time (nframes64_t frame, BBT_Time& bbt) const
791 Glib::RWLock::ReaderLock lm (lock);
792 bbt_time_unlocked (frame, bbt);
797 TempoMap::bbt_time_unlocked (nframes64_t frame, BBT_Time& bbt) const
799 bbt_time_with_metric (frame, bbt, metric_at (frame));
803 TempoMap::bbt_time_with_metric (nframes64_t frame, BBT_Time& bbt, const TempoMetric& metric) const
805 nframes64_t frame_diff;
807 // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
809 const double beats_per_bar = metric.meter().beats_per_bar();
810 const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat;
812 /* now compute how far beyond that point we actually are. */
814 frame_diff = frame - metric.frame();
816 bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
817 uint32_t xtra_beats = bbt.ticks / (uint32_t)Meter::ticks_per_beat;
818 bbt.ticks %= (uint32_t)Meter::ticks_per_beat;
820 bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
821 bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
822 bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
824 /* if we have a fractional number of beats per bar, we see if
825 we're in the last beat (the fractional one). if so, we
826 round ticks appropriately and bump to the next bar. */
827 double beat_fraction = beats_per_bar - floor(beats_per_bar);
828 /* XXX one problem here is that I'm not sure how to handle
829 fractional beats that don't evenly divide ticks_per_beat.
830 If they aren't handled consistently, I would guess we'll
831 continue to have strange discrepancies occuring. Perhaps
832 this will also behave badly in the case of meters like
833 0.1/4, but I can't be bothered to test that.
835 uint32_t ticks_on_last_beat = (uint32_t)floor(Meter::ticks_per_beat * beat_fraction);
837 if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) {
838 bbt.ticks -= ticks_on_last_beat;
843 bbt.beats++; // correction for 1-based counting, see above for matching operation.
845 // cerr << "-----\t RETURN " << bbt << endl;
849 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
851 /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
852 that means that the beats and ticks should be inside a bar
855 nframes64_t frames = 0;
856 nframes64_t start_frame = 0;
857 nframes64_t end_frame = 0;
859 TempoMetric m = metric_at (start);
861 uint32_t bar_offset = start.bars - m.start().bars;
863 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
864 + start.ticks/Meter::ticks_per_beat;
867 start_frame = m.frame() + (nframes64_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
871 bar_offset = end.bars - m.start().bars;
873 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
874 + end.ticks/Meter::ticks_per_beat;
876 end_frame = m.frame() + (nframes64_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
878 frames = end_frame - start_frame;
885 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
887 /* this is used in timestamping the metrics by actually counting the beats */
889 nframes64_t frames = 0;
890 uint32_t bar = start.bars;
891 double beat = (double) start.beats;
892 double beats_counted = 0;
893 double beats_per_bar = 0;
894 double beat_frames = 0;
896 beats_per_bar = meter.beats_per_bar();
897 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
901 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
903 if (beat >= beats_per_bar) {
908 if (beat > beats_per_bar) {
910 /* this is a fractional beat at the end of a fractional bar
911 so it should only count for the fraction
914 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
923 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
924 // << " bpb were " << beats_per_bar
925 // << " fpb was " << beat_frames
928 frames = (nframes64_t) floor (beats_counted * beat_frames);
935 TempoMap::frame_time (const BBT_Time& bbt) const
937 BBT_Time start ; /* 1|1|0 */
939 return count_frames_between ( start, bbt);
943 TempoMap::bbt_duration_at (nframes64_t pos, const BBT_Time& bbt, int dir) const
945 nframes64_t frames = 0;
951 Glib::RWLock::ReaderLock lm (lock);
952 frames = bbt_duration_at_unlocked (when, bbt,dir);
959 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
962 nframes64_t frames = 0;
964 double beats_per_bar;
967 result.bars = max(1U, when.bars + dir * bbt.bars) ;
971 TempoMetric metric = metric_at(result);
972 beats_per_bar = metric.meter().beats_per_bar();
976 /*reduce things to legal bbt values
977 we have to handle possible fractional=shorter beats at the end of measures
978 and things like 0|11|9000 as a duration in a 4.5/4 measure
979 the musical decision is that the fractional beat is also a beat , although a shorter one
984 result.beats = when.beats + bbt.beats;
985 result.ticks = when.ticks + bbt.ticks;
987 while (result.beats >= (beats_per_bar + 1)) {
989 result.beats -= (uint32_t) ceil(beats_per_bar);
990 metric = metric_at(result); // maybe there is a meter change
991 beats_per_bar = metric.meter().beats_per_bar();
994 /*we now counted the beats and landed in the target measure, now deal with ticks
995 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
996 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
999 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1002 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1003 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
1004 : Meter::ticks_per_beat );
1006 while (result.ticks >= ticks_at_beat) {
1008 result.ticks -= ticks_at_beat;
1009 if (result.beats >= (beats_per_bar + 1)) {
1012 metric = metric_at(result); // maybe there is a meter change
1013 beats_per_bar = metric.meter().beats_per_bar();
1015 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1016 (1 - (ceil(beats_per_bar) - beats_per_bar) ) * Meter::ticks_per_beat
1017 : Meter::ticks_per_beat);
1023 uint32_t b = bbt.beats;
1026 while( b > when.beats ) {
1028 result.bars = max(1U,result.bars-- ) ;
1029 metric = metric_at(result); // maybe there is a meter change
1030 beats_per_bar = metric.meter().beats_per_bar();
1031 if (b >= ceil(beats_per_bar)) {
1033 b -= (uint32_t) ceil(beats_per_bar);
1035 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1038 result.beats = when.beats - b;
1042 if (bbt.ticks <= when.ticks) {
1043 result.ticks = when.ticks - bbt.ticks;
1046 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1047 uint32_t t = bbt.ticks - when.ticks;
1051 if (result.beats == 1) {
1052 result.bars = max(1U, result.bars-- ) ;
1053 metric = metric_at(result); // maybe there is a meter change
1054 beats_per_bar = metric.meter().beats_per_bar();
1055 result.beats = (uint32_t) ceil(beats_per_bar);
1056 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * Meter::ticks_per_beat) ;
1059 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1062 if (t <= ticks_at_beat) {
1063 result.ticks = ticks_at_beat - t;
1067 } while (t > ticks_at_beat);
1075 frames = count_frames_between( result,when);
1077 frames = count_frames_between(when,result);
1086 TempoMap::round_to_bar (nframes64_t fr, int dir)
1089 Glib::RWLock::ReaderLock lm (lock);
1090 return round_to_type (fr, dir, Bar);
1096 TempoMap::round_to_beat (nframes64_t fr, int dir)
1099 Glib::RWLock::ReaderLock lm (lock);
1100 return round_to_type (fr, dir, Beat);
1105 TempoMap::round_to_beat_subdivision (nframes64_t fr, int sub_num, int dir)
1109 uint32_t ticks_one_half_subdivisions_worth;
1110 uint32_t ticks_one_subdivisions_worth;
1111 uint32_t difference;
1113 bbt_time(fr, the_beat);
1115 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1116 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1122 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1125 /* right on the subdivision, so the difference is just the subdivision ticks */
1126 difference = ticks_one_subdivisions_worth;
1129 /* not on subdivision, compute distance to next subdivision */
1131 difference = ticks_one_subdivisions_worth - mod;
1134 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1136 the_beat.ticks += difference;
1137 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1139 the_beat.ticks += difference;
1142 } else if (dir < 0) {
1144 /* round to previous */
1146 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1149 /* right on the subdivision, so the difference is just the subdivision ticks */
1150 difference = ticks_one_subdivisions_worth;
1151 cerr << "On the sub, move by 1 sub = " << difference << endl;
1153 /* not on subdivision, compute distance to previous subdivision, which
1154 is just the modulus.
1158 cerr << "off the sub, move by 1 sub = " << difference << endl;
1162 cerr << "ticks = " << the_beat.ticks << endl;
1164 if (the_beat.ticks < difference) {
1165 cerr << "backup beats, set ticks to "
1166 << (uint32_t)Meter::ticks_per_beat - difference << endl;
1168 the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference;
1170 cerr << " reduce ticks\n";
1171 the_beat.ticks -= difference;
1175 /* round to nearest */
1177 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1178 difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1179 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1181 the_beat.ticks += difference;
1182 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1184 the_beat.ticks += difference;
1187 // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1188 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1192 return frame_time (the_beat);
1196 TempoMap::round_to_type (nframes64_t frame, int dir, BBTPointType type)
1198 TempoMetric metric = metric_at (frame);
1201 BBT_Time one_bar (1,0,0);
1202 BBT_Time one_beat (0,1,0);
1204 bbt_time_with_metric (frame, bbt, metric);
1208 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1212 /* find bar position preceding frame */
1215 bbt = bbt_subtract (bbt, one_bar);
1223 } else if (dir > 0) {
1225 /* find bar position following frame */
1228 bbt = bbt_add (bbt, one_bar, metric);
1236 /* "true" rounding */
1241 midbar_beats = metric.meter().beats_per_bar() / 2;
1242 midbar_ticks = Meter::ticks_per_beat * fmod (midbar_beats, 1.0f);
1243 midbar_beats = floor (midbar_beats);
1245 BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1258 /* force beats & ticks to their values at the start of a bar */
1264 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1268 /* find beat position preceding frame */
1271 bbt = bbt_subtract (bbt, one_beat);
1279 } else if (dir > 0) {
1281 /* find beat position following frame */
1284 bbt = bbt_add (bbt, one_beat, metric);
1292 /* "true" rounding */
1294 /* round to nearest beat */
1295 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1298 bbt = bbt_add (bbt, one_beat, metric);
1305 /* force ticks to the value at the start of a beat */
1311 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)));
1312 return metric.frame() + count_frames_between (metric.start(), bbt);
1315 TempoMap::BBTPointList *
1316 TempoMap::get_points (nframes64_t lower, nframes64_t upper) const
1319 Metrics::const_iterator i;
1320 BBTPointList *points;
1322 const MeterSection* meter;
1323 const MeterSection* m;
1324 const TempoSection* tempo;
1325 const TempoSection* t;
1328 double beats_per_bar;
1331 double frames_per_bar;
1337 meter = &first_meter ();
1338 tempo = &first_tempo ();
1340 /* find the starting point */
1342 for (i = metrics->begin(); i != metrics->end(); ++i) {
1344 if ((*i)->frame() > lower) {
1348 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1350 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1357 meter -> the Meter for "lower"
1358 tempo -> the Tempo for "lower"
1359 i -> for first new metric after "lower", possibly metrics->end()
1361 Now start generating points.
1364 beats_per_bar = meter->beats_per_bar ();
1365 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1366 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1368 if (meter->frame() > tempo->frame()) {
1369 bar = meter->start().bars;
1370 beat = meter->start().beats;
1371 current = meter->frame();
1373 bar = tempo->start().bars;
1374 beat = tempo->start().beats;
1375 current = tempo->frame();
1378 /* initialize current to point to the bar/beat just prior to the
1379 lower frame bound passed in. assumes that current is initialized
1380 above to be on a beat.
1383 delta_bars = (lower-current) / frames_per_bar;
1384 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1385 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1387 // adjust bars and beats too
1388 bar += (uint32_t) (floor(delta_bars));
1389 beat += (uint32_t) (floor(delta_beats));
1391 points = new BBTPointList;
1395 if (i == metrics->end()) {
1397 // cerr << "== limit set to end of request @ " << limit << endl;
1399 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1400 limit = (*i)->frame();
1403 limit = min (limit, upper);
1405 while (current < limit) {
1407 /* if we're at the start of a bar, add bar point */
1410 if (current >= lower) {
1411 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1412 points->push_back (BBTPoint (*meter, *tempo,(nframes64_t)rint(current), Bar, bar, 1));
1417 /* add some beats if we can */
1419 beat_frame = current;
1421 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1422 if (beat_frame >= lower) {
1423 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1424 points->push_back (BBTPoint (*meter, *tempo, (nframes64_t) rint(beat_frame), Beat, bar, beat));
1426 beat_frame += beat_frames;
1427 current+= beat_frames;
1432 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1433 // << (beat > ceil(beats_per_bar))
1436 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1438 /* we walked an entire bar. its
1439 important to move `current' forward
1440 by the actual frames_per_bar, not move it to
1441 an integral beat_frame, so that metrics with
1442 non-integral beats-per-bar have
1443 their bar positions set
1444 correctly. consider a metric with
1445 9-1/2 beats-per-bar. the bar we
1446 just filled had 10 beat marks,
1447 but the bar end is 1/2 beat before
1449 And it is also possible that a tempo
1450 change occured in the middle of a bar,
1451 so we subtract the possible extra fraction from the current
1454 if (beat > ceil (beats_per_bar)) {
1455 /* next bar goes where the numbers suggest */
1456 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1457 // cerr << "++ next bar from numbers\n";
1459 /* next bar goes where the next metric is */
1461 // cerr << "++ next bar at next metric\n";
1469 /* if we're done, then we're done */
1471 if (current >= upper) {
1475 /* i is an iterator that refers to the next metric (or none).
1476 if there is a next metric, move to it, and continue.
1479 if (i != metrics->end()) {
1481 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1483 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1485 /* new MeterSection, beat always returns to 1 */
1489 current = (*i)->frame ();
1490 // cerr << "loop around with current @ " << current << endl;
1492 beats_per_bar = meter->beats_per_bar ();
1493 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1494 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1505 TempoMap::tempo_section_at (nframes64_t frame)
1507 Glib::RWLock::ReaderLock lm (lock);
1508 Metrics::iterator i;
1509 TempoSection* prev = 0;
1511 for (i = metrics->begin(); i != metrics->end(); ++i) {
1514 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1516 if ((*i)->frame() > frame) {
1532 TempoMap::tempo_at (nframes64_t frame) const
1534 TempoMetric m (metric_at (frame));
1540 TempoMap::meter_at (nframes64_t frame) const
1542 TempoMetric m (metric_at (frame));
1547 TempoMap::get_state ()
1549 Metrics::const_iterator i;
1550 XMLNode *root = new XMLNode ("TempoMap");
1553 Glib::RWLock::ReaderLock lm (lock);
1554 for (i = metrics->begin(); i != metrics->end(); ++i) {
1555 root->add_child_nocopy ((*i)->get_state());
1563 TempoMap::set_state (const XMLNode& node, int /*version*/)
1566 Glib::RWLock::WriterLock lm (lock);
1569 XMLNodeConstIterator niter;
1570 Metrics old_metrics (*metrics);
1574 nlist = node.children();
1576 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1577 XMLNode* child = *niter;
1579 if (child->name() == TempoSection::xml_state_node_name) {
1582 metrics->push_back (new TempoSection (*child));
1585 catch (failed_constructor& err){
1586 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1587 *metrics = old_metrics;
1591 } else if (child->name() == MeterSection::xml_state_node_name) {
1594 metrics->push_back (new MeterSection (*child));
1597 catch (failed_constructor& err) {
1598 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1599 *metrics = old_metrics;
1605 if (niter == nlist.end()) {
1607 MetricSectionSorter cmp;
1608 metrics->sort (cmp);
1609 timestamp_metrics (true);
1613 StateChanged (Change (0));
1619 TempoMap::dump (std::ostream& o) const
1621 const MeterSection* m;
1622 const TempoSection* t;
1624 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1626 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1627 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1628 << t->movable() << ')' << endl;
1629 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1630 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1631 << " (move? " << m->movable() << ')' << endl;
1637 TempoMap::n_tempos() const
1639 Glib::RWLock::ReaderLock lm (lock);
1642 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1643 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1652 TempoMap::n_meters() const
1654 Glib::RWLock::ReaderLock lm (lock);
1657 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1658 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1667 TempoMap::insert_time (nframes64_t where, nframes64_t amount)
1669 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1670 if ((*i)->frame() >= where) {
1671 (*i)->set_frame ((*i)->frame() + amount);
1675 timestamp_metrics (false);
1677 StateChanged (Change (0));
1681 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1683 TempoMetric metric = metric_at (start);
1684 return bbt_add (start, other, metric);
1688 * add the BBT interval @param increment to @param start and return the result
1691 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& metric) const
1693 BBT_Time result = start;
1694 BBT_Time op = increment; /* argument is const, but we need to modify it */
1695 uint32_t ticks = result.ticks + op.ticks;
1697 if (ticks >= Meter::ticks_per_beat) {
1699 result.ticks = ticks % (uint32_t) Meter::ticks_per_beat;
1702 /* now comes the complicated part. we have to add one beat a time,
1703 checking for a new metric on every beat.
1706 /* grab all meter sections */
1708 list<const MeterSection*> meter_sections;
1710 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1711 const MeterSection* ms;
1712 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1713 meter_sections.push_back (ms);
1717 assert (!meter_sections.empty());
1719 list<const MeterSection*>::const_iterator next_meter;
1720 const Meter* meter = 0;
1722 /* go forwards through the meter sections till we get to the one
1723 covering the current value of result. this positions i to point to
1724 the next meter section too, or the end.
1727 for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1729 if (result < (*next_meter)->start()) {
1730 /* this metric is past the result time. stop looking, we have what we need */
1734 if (result == (*next_meter)->start()) {
1735 /* this meter section starts at result, push i beyond it so that it points
1736 to the NEXT section, opwise we will get stuck later, and use this meter section.
1738 meter = *next_meter;
1743 meter = *next_meter;
1746 assert (meter != 0);
1748 /* OK, now have the meter for the bar start we are on, and i is an iterator
1749 that points to the metric after the one we are currently dealing with
1750 (or to metrics->end(), of course)
1755 /* given the current meter, have we gone past the end of the bar ? */
1757 if (result.beats >= meter->beats_per_bar()) {
1758 /* move to next bar, first beat */
1769 /* check if we need to use a new meter section: has adding beats to result taken us
1770 to or after the start of the next meter section? in which case, use it.
1773 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1774 meter = *next_meter;
1779 /* finally, add bars */
1781 result.bars += op.bars++;
1787 * subtract the BBT interval @param decrement from @param start and return the result
1790 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1792 BBT_Time result = start;
1793 BBT_Time op = decrement; /* argument is const, but we need to modify it */
1795 if (op.ticks > result.ticks) {
1796 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1798 result.ticks = Meter::ticks_per_beat - (op.ticks - result.ticks);
1800 result.ticks -= op.ticks;
1803 /* now comes the complicated part. we have to subtract one beat a time,
1804 checking for a new metric on every beat.
1807 /* grab all meter sections */
1809 list<const MeterSection*> meter_sections;
1811 for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1812 const MeterSection* ms;
1813 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1814 meter_sections.push_back (ms);
1818 assert (!meter_sections.empty());
1820 /* go backwards through the meter sections till we get to the one
1821 covering the current value of result. this positions i to point to
1822 the next (previous) meter section too, or the end.
1825 const MeterSection* meter = 0;
1826 list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't
1827 // support const_reverse_iterator::operator!=()
1829 for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1831 /* when we find the first meter section that is before or at result, use it,
1832 and set next_meter to the previous one
1835 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1836 meter = *next_meter;
1842 assert (meter != 0);
1844 /* OK, now have the meter for the bar start we are on, and i is an iterator
1845 that points to the metric after the one we are currently dealing with
1846 (or to metrics->end(), of course)
1851 /* have we reached the start of the bar? if so, move to the last beat of the previous
1852 bar. opwise, just step back 1 beat.
1855 if (result.beats == 1) {
1857 /* move to previous bar, last beat */
1859 if (result.bars <= 1) {
1860 /* i'm sorry dave, i can't do that */
1861 throw std::out_of_range ("illegal BBT subtraction");
1865 result.beats = meter->beats_per_bar();
1876 /* check if we need to use a new meter section: has subtracting beats to result taken us
1877 to before the start of the current meter section? in which case, use the prior one.
1880 if (result < meter->start() && next_meter != meter_sections.rend()) {
1881 meter = *next_meter;
1886 /* finally, subtract bars */
1888 if (op.bars >= result.bars) {
1889 /* i'm sorry dave, i can't do that */
1890 throw std::out_of_range ("illegal BBT subtraction");
1893 result.bars -= op.bars;