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);
897 TempoMap::round_to_bar (jack_nframes_t fr, int dir)
899 LockMonitor lm (lock, __LINE__, __FILE__);
900 return round_to_type (fr, dir, Bar);
905 TempoMap::round_to_beat (jack_nframes_t fr, int dir)
907 LockMonitor lm (lock, __LINE__, __FILE__);
908 return round_to_type (fr, dir, Beat);
913 TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num)
915 LockMonitor lm (lock, __LINE__, __FILE__);
916 TempoMap::BBTPointList::iterator i;
917 TempoMap::BBTPointList *more_zoomed_bbt_points;
918 jack_nframes_t frame_one_beats_worth;
919 jack_nframes_t pos = 0;
920 jack_nframes_t next_pos = 0 ;
922 double frames_one_subdivisions_worth;
923 bool fr_has_changed = false;
927 frame_one_beats_worth = (jack_nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
928 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
929 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
931 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
935 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
936 if ((*i).frame <= fr) {
938 tempo = (*i).tempo->beats_per_minute();
942 next_pos = (*i).frame;
946 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
948 for (n = sub_num; n > 0; n--) {
949 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
950 fr = (jack_nframes_t) round(pos + (n * frames_one_subdivisions_worth));
952 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
954 fr_has_changed = true;
959 if (!fr_has_changed) {
963 delete more_zoomed_bbt_points;
969 TempoMap::round_to_type (jack_nframes_t frame, int dir, BBTPointType type)
971 Metric metric = metric_at (frame);
974 bbt_time_with_metric (frame, bbt, metric);
981 } else if (dir > 0) {
986 if (bbt.beats > metric.meter().beats_per_bar()/2) {
998 } else if (dir > 0) {
1003 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1007 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1016 return metric.frame() + count_frames_between (metric.start(), bbt);
1019 TempoMap::BBTPointList *
1020 TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const
1023 Metrics::const_iterator i;
1024 BBTPointList *points;
1026 const MeterSection* meter;
1027 const MeterSection* m;
1028 const TempoSection* tempo;
1029 const TempoSection* t;
1033 meter = &first_meter ();
1034 tempo = &first_tempo ();
1036 /* find the starting point */
1038 for (i = metrics->begin(); i != metrics->end(); ++i) {
1040 if ((*i)->frame() > lower) {
1044 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1046 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1053 meter -> the Meter for "lower"
1054 tempo -> the Tempo for "lower"
1055 i -> for first new metric after "lower", possibly metrics->end()
1057 Now start generating points.
1060 if (meter->frame() > tempo->frame()) {
1061 bar = meter->start().bars;
1062 beat = meter->start().beats;
1063 current = meter->frame();
1065 bar = tempo->start().bars;
1066 beat = tempo->start().beats;
1067 current = tempo->frame();
1070 points = new BBTPointList;
1073 double beats_per_bar;
1076 double frames_per_bar;
1077 jack_nframes_t limit;
1079 beats_per_bar = meter->beats_per_bar ();
1080 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1081 beat_frames = tempo->frames_per_beat (_frame_rate);
1083 if (i == metrics->end()) {
1086 limit = (*i)->frame();
1089 limit = min (limit, upper);
1091 while (current < limit) {
1093 /* if we're at the start of a bar, add bar point */
1096 if (current >= lower) {
1097 points->push_back (BBTPoint (*meter, *tempo,(jack_nframes_t)rint(current), Bar, bar, 1));
1102 /* add some beats if we can */
1104 beat_frame = current;
1106 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1107 if (beat_frame >= lower) {
1108 points->push_back (BBTPoint (*meter, *tempo, (jack_nframes_t) rint(beat_frame), Beat, bar, beat));
1110 beat_frame += beat_frames;
1111 current+= beat_frames;
1116 if (beat > ceil(beats_per_bar) ) {
1118 /* we walked an entire bar. its
1119 important to move `current' forward
1120 by the actual frames_per_bar, not move it to
1121 an integral beat_frame, so that metrics with
1122 non-integral beats-per-bar have
1123 their bar positions set
1124 correctly. consider a metric with
1125 9-1/2 beats-per-bar. the bar we
1126 just filled had 10 beat marks,
1127 but the bar end is 1/2 beat before
1129 And it is also possible that a tempo
1130 change occured in the middle of a bar,
1131 so we subtract the possible extra fraction from the current
1134 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1142 /* if we're done, then we're done */
1144 if (current >= upper) {
1148 /* i is an iterator that refers to the next metric (or none).
1149 if there is a next metric, move to it, and continue.
1152 if (i != metrics->end()) {
1154 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1156 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1158 /* new MeterSection, beat always returns to 1 */
1171 TempoMap::tempo_at (jack_nframes_t frame)
1173 Metric m (metric_at (frame));
1179 TempoMap::meter_at (jack_nframes_t frame)
1181 Metric m (metric_at (frame));
1186 TempoMap::get_state ()
1188 LockMonitor lm (lock, __LINE__, __FILE__);
1189 Metrics::const_iterator i;
1190 XMLNode *root = new XMLNode ("TempoMap");
1192 for (i = metrics->begin(); i != metrics->end(); ++i) {
1193 root->add_child_nocopy ((*i)->get_state());
1200 TempoMap::set_state (const XMLNode& node)
1203 LockMonitor lm (lock, __LINE__, __FILE__);
1206 XMLNodeConstIterator niter;
1207 Metrics old_metrics (*metrics);
1209 in_set_state = true;
1213 nlist = node.children();
1215 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1216 XMLNode* child = *niter;
1218 if (child->name() == TempoSection::xml_state_node_name) {
1221 metrics->push_back (new TempoSection (*child));
1224 catch (failed_constructor& err){
1225 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1226 *metrics = old_metrics;
1230 } else if (child->name() == MeterSection::xml_state_node_name) {
1233 metrics->push_back (new MeterSection (*child));
1236 catch (failed_constructor& err) {
1237 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1238 *metrics = old_metrics;
1244 if (niter == nlist.end()) {
1246 MetricSectionSorter cmp;
1247 metrics->sort (cmp);
1248 timestamp_metrics ();
1251 in_set_state = false;
1254 send_state_changed (Change (0));
1260 TempoMap::dump (std::ostream& o) const
1262 const MeterSection* m;
1263 const TempoSection* t;
1265 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1267 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1268 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1269 << t->movable() << ')' << endl;
1270 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1271 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1272 << " (move? " << m->movable() << ')' << endl;
1278 TempoMap::get_memento () const
1280 return sigc::bind (mem_fun (*(const_cast<TempoMap *> (this)), &StateManager::use_state), _current_state_id);
1284 TempoMap::restore_state (StateManager::State& state)
1286 LockMonitor lm (lock, __LINE__, __FILE__);
1288 TempoMapState* tmstate = dynamic_cast<TempoMapState*> (&state);
1290 metrics = tmstate->metrics;
1291 last_bbt_valid = false;
1296 StateManager::State*
1297 TempoMap::state_factory (std::string why) const
1299 TempoMapState* state = new TempoMapState (why);
1301 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1305 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1306 state->metrics->push_back (new TempoSection (*ts));
1307 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1308 state->metrics->push_back (new MeterSection (*ms));
1316 TempoMap::save_state (std::string why)
1318 if (!in_set_state) {
1319 StateManager::save_state (why);