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);
929 /*****************************
930 XXX just keeping this for reference
932 TempoMap::BBTPointList::iterator i;
933 TempoMap::BBTPointList *more_zoomed_bbt_points;
934 nframes_t frame_one_beats_worth;
936 nframes_t next_pos = 0 ;
938 double frames_one_subdivisions_worth;
939 bool fr_has_changed = false;
943 frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
945 Glib::RWLock::ReaderLock lm (lock);
946 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
947 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
949 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
953 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
954 if ((*i).frame <= fr) {
956 tempo = (*i).tempo->beats_per_minute();
960 next_pos = (*i).frame;
964 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
966 for (n = sub_num; n > 0; n--) {
967 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
968 fr = (nframes_t) round(pos + (n * frames_one_subdivisions_worth));
970 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
972 fr_has_changed = true;
977 if (!fr_has_changed) {
981 delete more_zoomed_bbt_points;
984 ******************************/
991 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
993 Metric metric = metric_at (frame);
996 bbt_time_with_metric (frame, bbt, metric);
1003 } else if (dir > 0) {
1004 if (bbt.beats > 0) {
1008 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1020 } else if (dir > 0) {
1021 if (bbt.ticks > 0) {
1025 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1029 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1038 return metric.frame() + count_frames_between (metric.start(), bbt);
1041 TempoMap::BBTPointList *
1042 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1045 Metrics::const_iterator i;
1046 BBTPointList *points;
1048 const MeterSection* meter;
1049 const MeterSection* m;
1050 const TempoSection* tempo;
1051 const TempoSection* t;
1054 double beats_per_bar;
1057 double frames_per_bar;
1063 meter = &first_meter ();
1064 tempo = &first_tempo ();
1066 /* find the starting point */
1068 for (i = metrics->begin(); i != metrics->end(); ++i) {
1070 if ((*i)->frame() > lower) {
1074 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1076 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1083 meter -> the Meter for "lower"
1084 tempo -> the Tempo for "lower"
1085 i -> for first new metric after "lower", possibly metrics->end()
1087 Now start generating points.
1090 beats_per_bar = meter->beats_per_bar ();
1091 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1092 beat_frames = tempo->frames_per_beat (_frame_rate);
1094 if (meter->frame() > tempo->frame()) {
1095 bar = meter->start().bars;
1096 beat = meter->start().beats;
1097 current = meter->frame();
1099 bar = tempo->start().bars;
1100 beat = tempo->start().beats;
1101 current = tempo->frame();
1104 /* initialize current to point to the bar/beat just prior to the
1105 lower frame bound passed in. assumes that current is initialized
1106 above to be on a beat.
1109 delta_bars = (lower-current) / frames_per_bar;
1110 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1111 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1113 // adjust bars and beats too
1114 bar += (uint32_t) (floor(delta_bars));
1115 beat += (uint32_t) (floor(delta_beats));
1117 points = new BBTPointList;
1121 if (i == metrics->end()) {
1124 limit = (*i)->frame();
1127 limit = min (limit, upper);
1129 while (current < limit) {
1131 /* if we're at the start of a bar, add bar point */
1134 if (current >= lower) {
1135 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1140 /* add some beats if we can */
1142 beat_frame = current;
1144 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1145 if (beat_frame >= lower) {
1146 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1148 beat_frame += beat_frames;
1149 current+= beat_frames;
1154 if (beat > ceil(beats_per_bar) ) {
1156 /* we walked an entire bar. its
1157 important to move `current' forward
1158 by the actual frames_per_bar, not move it to
1159 an integral beat_frame, so that metrics with
1160 non-integral beats-per-bar have
1161 their bar positions set
1162 correctly. consider a metric with
1163 9-1/2 beats-per-bar. the bar we
1164 just filled had 10 beat marks,
1165 but the bar end is 1/2 beat before
1167 And it is also possible that a tempo
1168 change occured in the middle of a bar,
1169 so we subtract the possible extra fraction from the current
1172 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1180 /* if we're done, then we're done */
1182 if (current >= upper) {
1186 /* i is an iterator that refers to the next metric (or none).
1187 if there is a next metric, move to it, and continue.
1190 if (i != metrics->end()) {
1192 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1194 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1196 /* new MeterSection, beat always returns to 1 */
1200 beats_per_bar = meter->beats_per_bar ();
1201 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1202 beat_frames = tempo->frames_per_beat (_frame_rate);
1213 TempoMap::tempo_at (nframes_t frame)
1215 Metric m (metric_at (frame));
1221 TempoMap::meter_at (nframes_t frame)
1223 Metric m (metric_at (frame));
1228 TempoMap::get_state ()
1230 Metrics::const_iterator i;
1231 XMLNode *root = new XMLNode ("TempoMap");
1234 Glib::RWLock::ReaderLock lm (lock);
1235 for (i = metrics->begin(); i != metrics->end(); ++i) {
1236 root->add_child_nocopy ((*i)->get_state());
1244 TempoMap::set_state (const XMLNode& node)
1247 Glib::RWLock::WriterLock lm (lock);
1250 XMLNodeConstIterator niter;
1251 Metrics old_metrics (*metrics);
1255 nlist = node.children();
1257 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1258 XMLNode* child = *niter;
1260 if (child->name() == TempoSection::xml_state_node_name) {
1263 metrics->push_back (new TempoSection (*child));
1266 catch (failed_constructor& err){
1267 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1268 *metrics = old_metrics;
1272 } else if (child->name() == MeterSection::xml_state_node_name) {
1275 metrics->push_back (new MeterSection (*child));
1278 catch (failed_constructor& err) {
1279 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1280 *metrics = old_metrics;
1286 if (niter == nlist.end()) {
1288 MetricSectionSorter cmp;
1289 metrics->sort (cmp);
1290 timestamp_metrics ();
1294 StateChanged (Change (0));
1300 TempoMap::dump (std::ostream& o) const
1302 const MeterSection* m;
1303 const TempoSection* t;
1305 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1307 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1308 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1309 << t->movable() << ')' << endl;
1310 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1311 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1312 << " (move? " << m->movable() << ')' << endl;