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.
26 #include <sigc++/bind.h>
28 #include <glibmm/thread.h>
29 #include <pbd/xml++.h>
30 #include <ardour/tempo.h>
31 #include <ardour/utils.h>
37 using namespace ARDOUR;
40 /* _default tempo is 4/4 qtr=120 */
42 Meter TempoMap::_default_meter (4.0, 4.0);
43 Tempo TempoMap::_default_tempo (120.0);
45 const double Meter::ticks_per_beat = 1920.0;
47 /***********************************************************************/
50 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
52 return ((60.0 * sr * _beats_per_bar) / tempo.beats_per_minute());
55 /***********************************************************************/
57 const string TempoSection::xml_state_node_name = "Tempo";
59 TempoSection::TempoSection (const XMLNode& node)
60 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
62 const XMLProperty *prop;
64 LocaleGuard lg (X_("POSIX"));
66 if ((prop = node.property ("start")) == 0) {
67 error << _("TempoSection XML node has no \"start\" property") << endmsg;
68 throw failed_constructor();
71 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
75 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
76 throw failed_constructor();
81 if ((prop = node.property ("beats-per-minute")) == 0) {
82 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
83 throw failed_constructor();
86 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
87 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
88 throw failed_constructor();
91 if ((prop = node.property ("movable")) == 0) {
92 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
93 throw failed_constructor();
96 set_movable (prop->value() == "yes");
100 TempoSection::get_state() const
102 XMLNode *root = new XMLNode (xml_state_node_name);
104 LocaleGuard lg (X_("POSIX"));
106 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
110 root->add_property ("start", buf);
111 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
112 root->add_property ("beats-per-minute", buf);
113 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
114 root->add_property ("movable", buf);
119 /***********************************************************************/
121 const string MeterSection::xml_state_node_name = "Meter";
123 MeterSection::MeterSection (const XMLNode& node)
124 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
126 const XMLProperty *prop;
128 LocaleGuard lg (X_("POSIX"));
130 if ((prop = node.property ("start")) == 0) {
131 error << _("MeterSection XML node has no \"start\" property") << endmsg;
132 throw failed_constructor();
135 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
139 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
140 throw failed_constructor();
145 if ((prop = node.property ("beats-per-bar")) == 0) {
146 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
147 throw failed_constructor();
150 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
151 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
152 throw failed_constructor();
155 if ((prop = node.property ("note-type")) == 0) {
156 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
157 throw failed_constructor();
160 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
161 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
162 throw failed_constructor();
165 if ((prop = node.property ("movable")) == 0) {
166 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
167 throw failed_constructor();
170 set_movable (prop->value() == "yes");
174 MeterSection::get_state() const
176 XMLNode *root = new XMLNode (xml_state_node_name);
178 LocaleGuard lg (X_("POSIX"));
180 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
184 root->add_property ("start", buf);
185 snprintf (buf, sizeof (buf), "%f", _note_type);
186 root->add_property ("note-type", buf);
187 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
188 root->add_property ("beats-per-bar", buf);
189 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
190 root->add_property ("movable", buf);
195 /***********************************************************************/
197 struct MetricSectionSorter {
198 bool operator() (const MetricSection* a, const MetricSection* b) {
199 return a->start() < b->start();
203 TempoMap::TempoMap (nframes_t fr)
205 metrics = new Metrics;
207 last_bbt_valid = false;
214 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute());
215 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
217 t->set_movable (false);
218 m->set_movable (false);
220 /* note: frame time is correct (zero) for both of these */
222 metrics->push_back (t);
223 metrics->push_back (m);
226 TempoMap::~TempoMap ()
231 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
233 if (when == section.start()) {
237 if (!section.movable()) {
241 Glib::RWLock::WriterLock lm (lock);
242 MetricSectionSorter cmp;
243 BBT_Time corrected (when);
245 if (dynamic_cast<MeterSection*>(§ion) != 0) {
246 if (corrected.beats > 1) {
253 section.set_start (corrected);
255 timestamp_metrics ();
261 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
263 if (move_metric_section (tempo, when) == 0) {
264 StateChanged (Change (0));
269 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
271 if (move_metric_section (meter, when) == 0) {
272 StateChanged (Change (0));
278 TempoMap::remove_tempo (const TempoSection& tempo)
280 bool removed = false;
283 Glib::RWLock::WriterLock lm (lock);
286 for (i = metrics->begin(); i != metrics->end(); ++i) {
287 if (dynamic_cast<TempoSection*> (*i) != 0) {
288 if (tempo.frame() == (*i)->frame()) {
289 if ((*i)->movable()) {
300 StateChanged (Change (0));
305 TempoMap::remove_meter (const MeterSection& tempo)
307 bool removed = false;
310 Glib::RWLock::WriterLock lm (lock);
313 for (i = metrics->begin(); i != metrics->end(); ++i) {
314 if (dynamic_cast<MeterSection*> (*i) != 0) {
315 if (tempo.frame() == (*i)->frame()) {
316 if ((*i)->movable()) {
327 StateChanged (Change (0));
332 TempoMap::do_insert (MetricSection* section)
336 for (i = metrics->begin(); i != metrics->end(); ++i) {
338 if ((*i)->start() < section->start()) {
342 metrics->insert (i, section);
346 if (i == metrics->end()) {
347 metrics->insert (metrics->end(), section);
350 timestamp_metrics ();
354 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
357 Glib::RWLock::WriterLock lm (lock);
359 /* new tempos always start on a beat */
363 do_insert (new TempoSection (where, tempo.beats_per_minute()));
366 StateChanged (Change (0));
370 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
372 bool replaced = false;
375 Glib::RWLock::WriterLock lm (lock);
378 for (i = metrics->begin(); i != metrics->end(); ++i) {
381 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
383 *((Tempo *) ts) = replacement;
386 timestamp_metrics ();
393 StateChanged (Change (0));
398 TempoMap::add_meter (const Meter& meter, BBT_Time where)
401 Glib::RWLock::WriterLock lm (lock);
403 /* a new meter always starts a new bar on the first beat. so
404 round the start time appropriately. remember that
405 `where' is based on the existing tempo map, not
406 the result after we insert the new meter.
410 if (where.beats != 1) {
415 /* new meters *always* start on a beat. */
419 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()));
422 StateChanged (Change (0));
426 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
428 bool replaced = false;
431 Glib::RWLock::WriterLock lm (lock);
434 for (i = metrics->begin(); i != metrics->end(); ++i) {
436 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
438 *((Meter*) ms) = replacement;
441 timestamp_metrics ();
448 StateChanged (Change (0));
453 TempoMap::first_meter () const
455 const MeterSection *m = 0;
457 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
458 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
463 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
469 TempoMap::first_tempo () const
471 const TempoSection *t = 0;
473 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
474 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
479 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
485 TempoMap::timestamp_metrics ()
493 nframes_t section_frames;
497 meter = &first_meter ();
498 tempo = &first_tempo ();
501 for (i = metrics->begin(); i != metrics->end(); ++i) {
505 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
507 current += section_frames;
511 (*i)->set_frame (current);
513 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
515 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
518 fatal << _("programming error: unhandled MetricSection type") << endmsg;
525 TempoMap::metric_at (nframes_t frame) const
527 Metric m (first_meter(), first_tempo());
531 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
532 at something, because we insert the default tempo and meter during
533 TempoMap construction.
535 now see if we can find better candidates.
538 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
540 if ((*i)->frame() > frame) {
544 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
545 m.set_tempo (*tempo);
546 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
547 m.set_meter (*meter);
550 m.set_frame ((*i)->frame ());
551 m.set_start ((*i)->start ());
558 TempoMap::metric_at (BBT_Time bbt) const
560 Metric m (first_meter(), first_tempo());
564 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
565 at something, because we insert the default tempo and meter during
566 TempoMap construction.
568 now see if we can find better candidates.
571 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
573 BBT_Time section_start ((*i)->start());
575 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
579 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
580 m.set_tempo (*tempo);
581 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
582 m.set_meter (*meter);
585 m.set_frame ((*i)->frame ());
586 m.set_start (section_start);
593 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
596 Glib::RWLock::ReaderLock lm (lock);
597 bbt_time_unlocked (frame, bbt);
602 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
604 bbt_time_with_metric (frame, bbt, metric_at (frame));
608 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
610 nframes_t frame_diff;
612 uint32_t xtra_bars = 0;
613 double xtra_beats = 0;
616 const double beats_per_bar = metric.meter().beats_per_bar();
617 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
618 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
620 /* now compute how far beyond that point we actually are. */
622 frame_diff = frame - metric.frame();
624 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
625 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
626 xtra_beats = (double) frame_diff / beat_frames;
629 /* and set the returned value */
631 /* and correct beat/bar shifts to match the meter.
632 remember: beat and bar counting is 1-based,
634 also the meter may contain a fraction
637 bbt.bars = metric.start().bars + xtra_bars;
639 beats = (double) metric.start().beats + xtra_beats;
641 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
643 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
644 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
645 bbt.beats = (uint32_t) floor(beats);
651 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
654 /* for this to work with fractional measure types, start and end have to "legal" BBT types,
655 that means that the beats and ticks should be inside a bar
659 nframes_t frames = 0;
660 nframes_t start_frame = 0;
661 nframes_t end_frame = 0;
663 Metric m = metric_at(start);
665 uint32_t bar_offset = start.bars - m.start().bars;
667 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
668 + start.ticks/Meter::ticks_per_beat;
671 start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
675 bar_offset = end.bars - m.start().bars;
677 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
678 + end.ticks/Meter::ticks_per_beat;
680 end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
682 frames = end_frame - start_frame;
689 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
691 /*this is used in timestamping the metrics by actually counting the beats */
693 nframes_t frames = 0;
694 uint32_t bar = start.bars;
695 double beat = (double) start.beats;
696 double beats_counted = 0;
697 double beats_per_bar = 0;
698 double beat_frames = 0;
700 beats_per_bar = meter.beats_per_bar();
701 beat_frames = tempo.frames_per_beat (_frame_rate);
705 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
707 if (beat >= beats_per_bar) {
714 if (beat > beats_per_bar) {
715 /* this is a fractional beat at the end of a fractional bar
716 so it should only count for the fraction */
717 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
722 frames = (nframes_t) floor (beats_counted * beat_frames);
729 TempoMap::frame_time (const BBT_Time& bbt) const
731 BBT_Time start ; /* 1|1|0 */
733 return count_frames_between ( start, bbt);
737 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
739 nframes_t frames = 0;
745 Glib::RWLock::ReaderLock lm (lock);
746 frames = bbt_duration_at_unlocked (when, bbt,dir);
753 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
756 nframes_t frames = 0;
758 double beats_per_bar;
761 result.bars = max(1U,when.bars + dir * bbt.bars) ;
765 Metric metric = metric_at(result);
766 beats_per_bar = metric.meter().beats_per_bar();
770 /*reduce things to legal bbt values
771 we have to handle possible fractional=shorter beats at the end of measures
772 and things like 0|11|9000 as a duration in a 4.5/4 measure
773 the musical decision is that the fractional beat is also a beat , although a shorter one
778 result.beats = when.beats + bbt.beats;
779 result.ticks = when.ticks + bbt.ticks;
781 while (result.beats >= (beats_per_bar+1)) {
783 result.beats -= (uint32_t) ceil(beats_per_bar);
784 metric = metric_at(result); // maybe there is a meter change
785 beats_per_bar = metric.meter().beats_per_bar();
788 /*we now counted the beats and landed in the target measure, now deal with ticks
789 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
790 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
793 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
796 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
797 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
798 : Meter::ticks_per_beat );
800 while (result.ticks >= ticks_at_beat) {
802 result.ticks -= ticks_at_beat;
803 if (result.beats >= (beats_per_bar+1)) {
806 metric = metric_at(result); // maybe there is a meter change
807 beats_per_bar = metric.meter().beats_per_bar();
809 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
810 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
811 : Meter::ticks_per_beat);
817 uint32_t b = bbt.beats;
820 while( b > when.beats ) {
822 result.bars = max(1U,result.bars-- ) ;
823 metric = metric_at(result); // maybe there is a meter change
824 beats_per_bar = metric.meter().beats_per_bar();
825 if (b >= ceil(beats_per_bar)) {
827 b -= (uint32_t) ceil(beats_per_bar);
829 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
832 result.beats = when.beats - b;
836 if (bbt.ticks <= when.ticks) {
837 result.ticks = when.ticks - bbt.ticks;
840 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
841 uint32_t t = bbt.ticks - when.ticks;
845 if (result.beats == 1) {
846 result.bars = max(1U,result.bars-- ) ;
847 metric = metric_at(result); // maybe there is a meter change
848 beats_per_bar = metric.meter().beats_per_bar();
849 result.beats = (uint32_t) ceil(beats_per_bar);
850 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
853 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
856 if (t <= ticks_at_beat) {
857 result.ticks = ticks_at_beat - t;
861 } while (t > ticks_at_beat);
869 frames = count_frames_between( result,when);
871 frames = count_frames_between(when,result);
880 TempoMap::round_to_bar (nframes_t fr, int dir)
883 Glib::RWLock::ReaderLock lm (lock);
884 return round_to_type (fr, dir, Bar);
890 TempoMap::round_to_beat (nframes_t fr, int dir)
893 Glib::RWLock::ReaderLock lm (lock);
894 return round_to_type (fr, dir, Beat);
900 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
904 uint32_t ticks_one_half_subdivisions_worth;
905 uint32_t ticks_one_subdivisions_worth;
907 bbt_time(fr, the_beat);
909 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
910 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
912 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
913 uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
914 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
916 the_beat.ticks += difference;
917 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
919 the_beat.ticks += difference;
922 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
925 return frame_time (the_beat);
927 /* XXX just keeping this for reference
929 TempoMap::BBTPointList::iterator i;
930 TempoMap::BBTPointList *more_zoomed_bbt_points;
931 nframes_t frame_one_beats_worth;
933 nframes_t next_pos = 0 ;
935 double frames_one_subdivisions_worth;
936 bool fr_has_changed = false;
940 frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
942 Glib::RWLock::ReaderLock lm (lock);
943 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
944 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
946 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
950 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
951 if ((*i).frame <= fr) {
953 tempo = (*i).tempo->beats_per_minute();
957 next_pos = (*i).frame;
961 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
963 for (n = sub_num; n > 0; n--) {
964 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
965 fr = (nframes_t) round(pos + (n * frames_one_subdivisions_worth));
967 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
969 fr_has_changed = true;
974 if (!fr_has_changed) {
978 delete more_zoomed_bbt_points;
987 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
989 Metric metric = metric_at (frame);
992 bbt_time_with_metric (frame, bbt, metric);
999 } else if (dir > 0) {
1000 if (bbt.beats > 0) {
1004 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1016 } else if (dir > 0) {
1017 if (bbt.ticks > 0) {
1021 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1025 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1034 return metric.frame() + count_frames_between (metric.start(), bbt);
1037 TempoMap::BBTPointList *
1038 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1041 Metrics::const_iterator i;
1042 BBTPointList *points;
1044 const MeterSection* meter;
1045 const MeterSection* m;
1046 const TempoSection* tempo;
1047 const TempoSection* t;
1050 double beats_per_bar;
1053 double frames_per_bar;
1059 meter = &first_meter ();
1060 tempo = &first_tempo ();
1062 /* find the starting point */
1064 for (i = metrics->begin(); i != metrics->end(); ++i) {
1066 if ((*i)->frame() > lower) {
1070 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1072 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1079 meter -> the Meter for "lower"
1080 tempo -> the Tempo for "lower"
1081 i -> for first new metric after "lower", possibly metrics->end()
1083 Now start generating points.
1086 beats_per_bar = meter->beats_per_bar ();
1087 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1088 beat_frames = tempo->frames_per_beat (_frame_rate);
1090 if (meter->frame() > tempo->frame()) {
1091 bar = meter->start().bars;
1092 beat = meter->start().beats;
1093 current = meter->frame();
1095 bar = tempo->start().bars;
1096 beat = tempo->start().beats;
1097 current = tempo->frame();
1100 /* initialize current to point to the bar/beat just prior to the
1101 lower frame bound passed in. assumes that current is initialized
1102 above to be on a beat.
1105 delta_bars = (lower-current) / frames_per_bar;
1106 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1107 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1109 // adjust bars and beats too
1110 bar += (uint32_t) (floor(delta_bars));
1111 beat += (uint32_t) (floor(delta_beats));
1113 points = new BBTPointList;
1117 if (i == metrics->end()) {
1120 limit = (*i)->frame();
1123 limit = min (limit, upper);
1125 while (current < limit) {
1127 /* if we're at the start of a bar, add bar point */
1130 if (current >= lower) {
1131 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1136 /* add some beats if we can */
1138 beat_frame = current;
1140 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1141 if (beat_frame >= lower) {
1142 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1144 beat_frame += beat_frames;
1145 current+= beat_frames;
1150 if (beat > ceil(beats_per_bar) ) {
1152 /* we walked an entire bar. its
1153 important to move `current' forward
1154 by the actual frames_per_bar, not move it to
1155 an integral beat_frame, so that metrics with
1156 non-integral beats-per-bar have
1157 their bar positions set
1158 correctly. consider a metric with
1159 9-1/2 beats-per-bar. the bar we
1160 just filled had 10 beat marks,
1161 but the bar end is 1/2 beat before
1163 And it is also possible that a tempo
1164 change occured in the middle of a bar,
1165 so we subtract the possible extra fraction from the current
1168 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1176 /* if we're done, then we're done */
1178 if (current >= upper) {
1182 /* i is an iterator that refers to the next metric (or none).
1183 if there is a next metric, move to it, and continue.
1186 if (i != metrics->end()) {
1188 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1190 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1192 /* new MeterSection, beat always returns to 1 */
1196 beats_per_bar = meter->beats_per_bar ();
1197 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1198 beat_frames = tempo->frames_per_beat (_frame_rate);
1209 TempoMap::tempo_at (nframes_t frame)
1211 Metric m (metric_at (frame));
1217 TempoMap::meter_at (nframes_t frame)
1219 Metric m (metric_at (frame));
1224 TempoMap::get_state ()
1226 Metrics::const_iterator i;
1227 XMLNode *root = new XMLNode ("TempoMap");
1230 Glib::RWLock::ReaderLock lm (lock);
1231 for (i = metrics->begin(); i != metrics->end(); ++i) {
1232 root->add_child_nocopy ((*i)->get_state());
1240 TempoMap::set_state (const XMLNode& node)
1243 Glib::RWLock::WriterLock lm (lock);
1246 XMLNodeConstIterator niter;
1247 Metrics old_metrics (*metrics);
1251 nlist = node.children();
1253 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1254 XMLNode* child = *niter;
1256 if (child->name() == TempoSection::xml_state_node_name) {
1259 metrics->push_back (new TempoSection (*child));
1262 catch (failed_constructor& err){
1263 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1264 *metrics = old_metrics;
1268 } else if (child->name() == MeterSection::xml_state_node_name) {
1271 metrics->push_back (new MeterSection (*child));
1274 catch (failed_constructor& err) {
1275 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1276 *metrics = old_metrics;
1282 if (niter == nlist.end()) {
1284 MetricSectionSorter cmp;
1285 metrics->sort (cmp);
1286 timestamp_metrics ();
1290 StateChanged (Change (0));
1296 TempoMap::dump (std::ostream& o) const
1298 const MeterSection* m;
1299 const TempoSection* t;
1301 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1303 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1304 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1305 << t->movable() << ')' << endl;
1306 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1307 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1308 << " (move? " << m->movable() << ')' << endl;