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 <pbd/lockmonitor.h>
29 #include <pbd/xml++.h>
30 #include <ardour/tempo.h>
31 #include <ardour/utils.h>
37 using namespace ARDOUR;
39 /* _default tempo is 4/4 qtr=120 */
41 Meter TempoMap::_default_meter (4.0, 4.0);
42 Tempo TempoMap::_default_tempo (120.0);
44 const double Meter::ticks_per_beat = 1920.0;
46 /***********************************************************************/
49 Meter::frames_per_bar (const Tempo& tempo, jack_nframes_t sr) const
51 return ((60.0 * sr * _beats_per_bar) / tempo.beats_per_minute());
54 /***********************************************************************/
56 const string TempoSection::xml_state_node_name = "Tempo";
58 TempoSection::TempoSection (const XMLNode& node)
59 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
61 const XMLProperty *prop;
63 LocaleGuard lg (X_("POSIX"));
65 if ((prop = node.property ("start")) == 0) {
66 error << _("TempoSection XML node has no \"start\" property") << endmsg;
67 throw failed_constructor();
70 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
74 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
75 throw failed_constructor();
80 if ((prop = node.property ("beats-per-minute")) == 0) {
81 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
82 throw failed_constructor();
85 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
86 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
87 throw failed_constructor();
90 if ((prop = node.property ("movable")) == 0) {
91 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
92 throw failed_constructor();
95 set_movable (prop->value() == "yes");
99 TempoSection::get_state() const
101 XMLNode *root = new XMLNode (xml_state_node_name);
103 LocaleGuard lg (X_("POSIX"));
105 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
109 root->add_property ("start", buf);
110 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
111 root->add_property ("beats-per-minute", buf);
112 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
113 root->add_property ("movable", buf);
118 /***********************************************************************/
120 const string MeterSection::xml_state_node_name = "Meter";
122 MeterSection::MeterSection (const XMLNode& node)
123 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
125 const XMLProperty *prop;
127 LocaleGuard lg (X_("POSIX"));
129 if ((prop = node.property ("start")) == 0) {
130 error << _("MeterSection XML node has no \"start\" property") << endmsg;
131 throw failed_constructor();
134 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
138 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
139 throw failed_constructor();
144 if ((prop = node.property ("beats-per-bar")) == 0) {
145 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
146 throw failed_constructor();
149 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
150 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
151 throw failed_constructor();
154 if ((prop = node.property ("note-type")) == 0) {
155 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
156 throw failed_constructor();
159 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
160 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
161 throw failed_constructor();
164 if ((prop = node.property ("movable")) == 0) {
165 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
166 throw failed_constructor();
169 set_movable (prop->value() == "yes");
173 MeterSection::get_state() const
175 XMLNode *root = new XMLNode (xml_state_node_name);
177 LocaleGuard lg (X_("POSIX"));
179 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
183 root->add_property ("start", buf);
184 snprintf (buf, sizeof (buf), "%f", _note_type);
185 root->add_property ("note-type", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
187 root->add_property ("beats-per-bar", buf);
188 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
189 root->add_property ("movable", buf);
194 /***********************************************************************/
196 struct MetricSectionSorter {
197 bool operator() (const MetricSection* a, const MetricSection* b) {
198 return a->start() < b->start();
202 TempoMap::TempoMap (jack_nframes_t fr)
204 metrics = new Metrics;
206 last_bbt_valid = false;
208 in_set_state = 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);
225 save_state (_("initial"));
228 TempoMap::~TempoMap ()
233 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
235 if (when == section.start()) {
239 if (!section.movable()) {
243 LockMonitor lm (lock, __LINE__, __FILE__);
244 MetricSectionSorter cmp;
245 BBT_Time corrected (when);
247 if (dynamic_cast<MeterSection*>(§ion) != 0) {
248 if (corrected.beats > 1) {
255 section.set_start (corrected);
257 timestamp_metrics ();
258 save_state (_("move metric"));
264 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
266 if (move_metric_section (tempo, when) == 0) {
267 send_state_changed (Change (0));
272 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
274 if (move_metric_section (meter, when) == 0) {
275 send_state_changed (Change (0));
281 TempoMap::remove_tempo (const TempoSection& tempo)
283 bool removed = false;
286 LockMonitor lm (lock, __LINE__, __FILE__);
289 for (i = metrics->begin(); i != metrics->end(); ++i) {
290 if (dynamic_cast<TempoSection*> (*i) != 0) {
291 if (tempo.frame() == (*i)->frame()) {
292 if ((*i)->movable()) {
303 send_state_changed (Change (0));
308 TempoMap::remove_meter (const MeterSection& tempo)
310 bool removed = false;
313 LockMonitor lm (lock, __LINE__, __FILE__);
316 for (i = metrics->begin(); i != metrics->end(); ++i) {
317 if (dynamic_cast<MeterSection*> (*i) != 0) {
318 if (tempo.frame() == (*i)->frame()) {
319 if ((*i)->movable()) {
329 save_state (_("metric removed"));
334 send_state_changed (Change (0));
339 TempoMap::do_insert (MetricSection* section)
343 for (i = metrics->begin(); i != metrics->end(); ++i) {
345 if ((*i)->start() < section->start()) {
349 metrics->insert (i, section);
353 if (i == metrics->end()) {
354 metrics->insert (metrics->end(), section);
357 timestamp_metrics ();
361 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
364 LockMonitor lm (lock, __LINE__, __FILE__);
366 /* new tempos always start on a beat */
370 do_insert (new TempoSection (where, tempo.beats_per_minute()));
372 save_state (_("add tempo"));
375 send_state_changed (Change (0));
379 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
381 bool replaced = false;
384 LockMonitor lm (lock, __LINE__, __FILE__);
387 for (i = metrics->begin(); i != metrics->end(); ++i) {
390 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
392 *((Tempo *) ts) = replacement;
395 timestamp_metrics ();
401 save_state (_("replace tempo"));
406 send_state_changed (Change (0));
411 TempoMap::add_meter (const Meter& meter, BBT_Time where)
414 LockMonitor lm (lock, __LINE__, __FILE__);
416 /* a new meter always starts a new bar on the first beat. so
417 round the start time appropriately. remember that
418 `where' is based on the existing tempo map, not
419 the result after we insert the new meter.
423 if (where.beats != 1) {
428 /* new meters *always* start on a beat. */
432 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()));
434 save_state (_("add meter"));
437 send_state_changed (Change (0));
441 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
443 bool replaced = false;
446 LockMonitor lm (lock, __LINE__, __FILE__);
449 for (i = metrics->begin(); i != metrics->end(); ++i) {
451 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
453 *((Meter*) ms) = replacement;
456 timestamp_metrics ();
462 save_state (_("replaced meter"));
467 send_state_changed (Change (0));
472 TempoMap::first_meter () const
474 const MeterSection *m = 0;
476 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
477 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
482 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
488 TempoMap::first_tempo () const
490 const TempoSection *t = 0;
492 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
493 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
498 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
504 TempoMap::timestamp_metrics ()
511 jack_nframes_t current;
512 jack_nframes_t section_frames;
516 meter = &first_meter ();
517 tempo = &first_tempo ();
520 for (i = metrics->begin(); i != metrics->end(); ++i) {
524 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
526 current += section_frames;
530 (*i)->set_frame (current);
532 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
534 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
537 fatal << _("programming error: unhandled MetricSection type") << endmsg;
544 TempoMap::metric_at (jack_nframes_t frame) const
546 Metric m (first_meter(), first_tempo());
550 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
551 at something, because we insert the default tempo and meter during
552 TempoMap construction.
554 now see if we can find better candidates.
557 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
559 if ((*i)->frame() > frame) {
563 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
564 m.set_tempo (*tempo);
565 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
566 m.set_meter (*meter);
569 m.set_frame ((*i)->frame ());
570 m.set_start ((*i)->start ());
577 TempoMap::metric_at (BBT_Time bbt) const
579 Metric m (first_meter(), first_tempo());
583 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
584 at something, because we insert the default tempo and meter during
585 TempoMap construction.
587 now see if we can find better candidates.
590 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
592 BBT_Time section_start ((*i)->start());
594 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
598 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
599 m.set_tempo (*tempo);
600 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
601 m.set_meter (*meter);
604 m.set_frame ((*i)->frame ());
605 m.set_start (section_start);
612 TempoMap::bbt_time (jack_nframes_t frame, BBT_Time& bbt) const
614 LockMonitor lm (lock, __LINE__, __FILE__);
615 bbt_time_unlocked (frame, bbt);
619 TempoMap::bbt_time_unlocked (jack_nframes_t frame, BBT_Time& bbt) const
621 bbt_time_with_metric (frame, bbt, metric_at (frame));
625 TempoMap::bbt_time_with_metric (jack_nframes_t frame, BBT_Time& bbt, const Metric& metric) const
627 jack_nframes_t frame_diff;
629 uint32_t xtra_bars = 0;
630 double xtra_beats = 0;
633 const double beats_per_bar = metric.meter().beats_per_bar();
634 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
635 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
637 /* now compute how far beyond that point we actually are. */
639 frame_diff = frame - metric.frame();
641 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
642 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
643 xtra_beats = (double) frame_diff / beat_frames;
646 /* and set the returned value */
648 /* and correct beat/bar shifts to match the meter.
649 remember: beat and bar counting is 1-based,
651 also the meter may contain a fraction
654 bbt.bars = metric.start().bars + xtra_bars;
656 beats = (double) metric.start().beats + xtra_beats;
658 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
660 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
661 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
662 bbt.beats = (uint32_t) floor(beats);
668 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
671 /* for this to work with fractional measure types, start and end have to "legal" BBT types,
672 that means that the beats and ticks should be inside a bar
676 jack_nframes_t frames = 0;
677 jack_nframes_t start_frame = 0;
678 jack_nframes_t end_frame = 0;
680 Metric m = metric_at(start);
682 uint32_t bar_offset = start.bars - m.start().bars;
684 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
685 + start.ticks/Meter::ticks_per_beat;
688 start_frame = m.frame() + (jack_nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
692 bar_offset = end.bars - m.start().bars;
694 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
695 + end.ticks/Meter::ticks_per_beat;
697 end_frame = m.frame() + (jack_nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
699 frames = end_frame - start_frame;
706 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
708 /*this is used in timestamping the metrics by actually counting the beats */
710 jack_nframes_t frames = 0;
711 uint32_t bar = start.bars;
712 double beat = (double) start.beats;
713 double beats_counted = 0;
714 double beats_per_bar = 0;
715 double beat_frames = 0;
717 beats_per_bar = meter.beats_per_bar();
718 beat_frames = tempo.frames_per_beat (_frame_rate);
722 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
724 if (beat >= beats_per_bar) {
731 if (beat > beats_per_bar) {
732 /* this is a fractional beat at the end of a fractional bar
733 so it should only count for the fraction */
734 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
739 frames = (jack_nframes_t) floor (beats_counted * beat_frames);
746 TempoMap::frame_time (const BBT_Time& bbt) const
748 BBT_Time start ; /* 1|1|0 */
750 return count_frames_between ( start, bbt);
754 TempoMap::bbt_duration_at (jack_nframes_t pos, const BBT_Time& bbt, int dir) const
756 jack_nframes_t frames = 0;
762 LockMonitor lm (lock, __LINE__, __FILE__);
763 frames = bbt_duration_at_unlocked (when, bbt,dir);
770 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
773 jack_nframes_t frames = 0;
775 double beats_per_bar;
778 result.bars = max(1U,when.bars + dir * bbt.bars) ;
782 Metric metric = metric_at(result);
783 beats_per_bar = metric.meter().beats_per_bar();
787 /*reduce things to legal bbt values
788 we have to handle possible fractional=shorter beats at the end of measures
789 and things like 0|11|9000 as a duration in a 4.5/4 measure
790 the musical decision is that the fractional beat is also a beat , although a shorter one
795 result.beats = when.beats + bbt.beats;
796 result.ticks = when.ticks + bbt.ticks;
798 while (result.beats >= (beats_per_bar+1)) {
800 result.beats -= (uint32_t) ceil(beats_per_bar);
801 metric = metric_at(result); // maybe there is a meter change
802 beats_per_bar = metric.meter().beats_per_bar();
805 /*we now counted the beats and landed in the target measure, now deal with ticks
806 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
807 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
810 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
813 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
814 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
815 : Meter::ticks_per_beat );
817 while (result.ticks >= ticks_at_beat) {
819 result.ticks -= ticks_at_beat;
820 if (result.beats >= (beats_per_bar+1)) {
823 metric = metric_at(result); // maybe there is a meter change
824 beats_per_bar = metric.meter().beats_per_bar();
826 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
827 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
828 : Meter::ticks_per_beat);
834 uint32_t b = bbt.beats;
837 while( b > when.beats ) {
839 result.bars = max(1U,result.bars-- ) ;
840 metric = metric_at(result); // maybe there is a meter change
841 beats_per_bar = metric.meter().beats_per_bar();
842 if (b >= ceil(beats_per_bar)) {
844 b -= (uint32_t) ceil(beats_per_bar);
846 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
849 result.beats = when.beats - b;
853 if (bbt.ticks <= when.ticks) {
854 result.ticks = when.ticks - bbt.ticks;
857 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
858 uint32_t t = bbt.ticks - when.ticks;
862 if (result.beats == 1) {
863 result.bars = max(1U,result.bars-- ) ;
864 metric = metric_at(result); // maybe there is a meter change
865 beats_per_bar = metric.meter().beats_per_bar();
866 result.beats = (uint32_t) ceil(beats_per_bar);
867 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
870 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
873 if (t <= ticks_at_beat) {
874 result.ticks = ticks_at_beat - t;
878 } while (t > ticks_at_beat);
886 frames = count_frames_between( result,when);
888 frames = count_frames_between(when,result);
898 TempoMap::round_to_bar (jack_nframes_t fr, int dir)
900 LockMonitor lm (lock, __LINE__, __FILE__);
901 return round_to_type (fr, dir, Bar);
907 TempoMap::round_to_beat (jack_nframes_t fr, int dir)
909 LockMonitor lm (lock, __LINE__, __FILE__);
910 return round_to_type (fr, dir, Beat);
915 TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num)
917 LockMonitor lm (lock, __LINE__, __FILE__);
918 TempoMap::BBTPointList::iterator i;
919 TempoMap::BBTPointList *more_zoomed_bbt_points;
920 jack_nframes_t frame_one_beats_worth;
921 jack_nframes_t pos = 0;
922 jack_nframes_t next_pos = 0 ;
924 double frames_one_subdivisions_worth;
925 bool fr_has_changed = false;
929 frame_one_beats_worth = (jack_nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
930 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
931 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
933 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
937 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
938 if ((*i).frame <= fr) {
940 tempo = (*i).tempo->beats_per_minute();
944 next_pos = (*i).frame;
948 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
950 for (n = sub_num; n > 0; n--) {
951 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
952 fr = (jack_nframes_t) round(pos + (n * frames_one_subdivisions_worth));
954 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
956 fr_has_changed = true;
961 if (!fr_has_changed) {
965 delete more_zoomed_bbt_points;
971 TempoMap::round_to_type (jack_nframes_t frame, int dir, BBTPointType type)
973 Metric metric = metric_at (frame);
976 bbt_time_with_metric (frame, bbt, metric);
983 } else if (dir > 0) {
988 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1000 } else if (dir > 0) {
1001 if (bbt.ticks > 0) {
1005 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1009 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1018 return metric.frame() + count_frames_between (metric.start(), bbt);
1021 TempoMap::BBTPointList *
1022 TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const
1025 Metrics::const_iterator i;
1026 BBTPointList *points;
1028 const MeterSection* meter;
1029 const MeterSection* m;
1030 const TempoSection* tempo;
1031 const TempoSection* t;
1035 meter = &first_meter ();
1036 tempo = &first_tempo ();
1038 /* find the starting point */
1040 for (i = metrics->begin(); i != metrics->end(); ++i) {
1042 if ((*i)->frame() > lower) {
1046 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1048 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1055 meter -> the Meter for "lower"
1056 tempo -> the Tempo for "lower"
1057 i -> for first new metric after "lower", possibly metrics->end()
1059 Now start generating points.
1062 if (meter->frame() > tempo->frame()) {
1063 bar = meter->start().bars;
1064 beat = meter->start().beats;
1065 current = meter->frame();
1067 bar = tempo->start().bars;
1068 beat = tempo->start().beats;
1069 current = tempo->frame();
1072 points = new BBTPointList;
1075 double beats_per_bar;
1078 double frames_per_bar;
1079 jack_nframes_t limit;
1081 beats_per_bar = meter->beats_per_bar ();
1082 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1083 beat_frames = tempo->frames_per_beat (_frame_rate);
1085 if (i == metrics->end()) {
1088 limit = (*i)->frame();
1091 limit = min (limit, upper);
1093 while (current < limit) {
1095 /* if we're at the start of a bar, add bar point */
1098 if (current >= lower) {
1099 points->push_back (BBTPoint (*meter, *tempo,(jack_nframes_t)rint(current), Bar, bar, 1));
1104 /* add some beats if we can */
1106 beat_frame = current;
1108 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1109 if (beat_frame >= lower) {
1110 points->push_back (BBTPoint (*meter, *tempo, (jack_nframes_t) rint(beat_frame), Beat, bar, beat));
1112 beat_frame += beat_frames;
1113 current+= beat_frames;
1118 if (beat > ceil(beats_per_bar) ) {
1120 /* we walked an entire bar. its
1121 important to move `current' forward
1122 by the actual frames_per_bar, not move it to
1123 an integral beat_frame, so that metrics with
1124 non-integral beats-per-bar have
1125 their bar positions set
1126 correctly. consider a metric with
1127 9-1/2 beats-per-bar. the bar we
1128 just filled had 10 beat marks,
1129 but the bar end is 1/2 beat before
1131 And it is also possible that a tempo
1132 change occured in the middle of a bar,
1133 so we subtract the possible extra fraction from the current
1136 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1144 /* if we're done, then we're done */
1146 if (current >= upper) {
1150 /* i is an iterator that refers to the next metric (or none).
1151 if there is a next metric, move to it, and continue.
1154 if (i != metrics->end()) {
1156 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1158 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1160 /* new MeterSection, beat always returns to 1 */
1173 TempoMap::tempo_at (jack_nframes_t frame)
1175 Metric m (metric_at (frame));
1181 TempoMap::meter_at (jack_nframes_t frame)
1183 Metric m (metric_at (frame));
1188 TempoMap::get_state ()
1190 LockMonitor lm (lock, __LINE__, __FILE__);
1191 Metrics::const_iterator i;
1192 XMLNode *root = new XMLNode ("TempoMap");
1194 for (i = metrics->begin(); i != metrics->end(); ++i) {
1195 root->add_child_nocopy ((*i)->get_state());
1202 TempoMap::set_state (const XMLNode& node)
1205 LockMonitor lm (lock, __LINE__, __FILE__);
1208 XMLNodeConstIterator niter;
1209 Metrics old_metrics (*metrics);
1211 in_set_state = true;
1215 nlist = node.children();
1217 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1218 XMLNode* child = *niter;
1220 if (child->name() == TempoSection::xml_state_node_name) {
1223 metrics->push_back (new TempoSection (*child));
1226 catch (failed_constructor& err){
1227 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1228 *metrics = old_metrics;
1232 } else if (child->name() == MeterSection::xml_state_node_name) {
1235 metrics->push_back (new MeterSection (*child));
1238 catch (failed_constructor& err) {
1239 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1240 *metrics = old_metrics;
1246 if (niter == nlist.end()) {
1248 MetricSectionSorter cmp;
1249 metrics->sort (cmp);
1250 timestamp_metrics ();
1253 in_set_state = false;
1256 send_state_changed (Change (0));
1262 TempoMap::dump (std::ostream& o) const
1264 const MeterSection* m;
1265 const TempoSection* t;
1267 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1269 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1270 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1271 << t->movable() << ')' << endl;
1272 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1273 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1274 << " (move? " << m->movable() << ')' << endl;
1280 TempoMap::get_memento () const
1282 return sigc::bind (mem_fun (*(const_cast<TempoMap *> (this)), &StateManager::use_state), _current_state_id);
1286 TempoMap::restore_state (StateManager::State& state)
1288 LockMonitor lm (lock, __LINE__, __FILE__);
1290 TempoMapState* tmstate = dynamic_cast<TempoMapState*> (&state);
1292 metrics = tmstate->metrics;
1293 last_bbt_valid = false;
1298 StateManager::State*
1299 TempoMap::state_factory (std::string why) const
1301 TempoMapState* state = new TempoMapState (why);
1303 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1307 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1308 state->metrics->push_back (new TempoSection (*ts));
1309 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1310 state->metrics->push_back (new MeterSection (*ms));
1318 TempoMap::save_state (std::string why)
1320 if (!in_set_state) {
1321 StateManager::save_state (why);