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;
210 in_set_state = false;
217 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute());
218 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
220 t->set_movable (false);
221 m->set_movable (false);
223 /* note: frame time is correct (zero) for both of these */
225 metrics->push_back (t);
226 metrics->push_back (m);
229 save_state (_("initial"));
233 TempoMap::~TempoMap ()
238 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
240 if (when == section.start()) {
244 if (!section.movable()) {
248 Glib::RWLock::WriterLock lm (lock);
249 MetricSectionSorter cmp;
250 BBT_Time corrected (when);
252 if (dynamic_cast<MeterSection*>(§ion) != 0) {
253 if (corrected.beats > 1) {
260 section.set_start (corrected);
262 timestamp_metrics ();
264 save_state (_("move metric"));
271 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
273 if (move_metric_section (tempo, when) == 0) {
274 StateChanged (Change (0));
279 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
281 if (move_metric_section (meter, when) == 0) {
282 StateChanged (Change (0));
288 TempoMap::remove_tempo (const TempoSection& tempo)
290 bool removed = false;
293 Glib::RWLock::WriterLock lm (lock);
296 for (i = metrics->begin(); i != metrics->end(); ++i) {
297 if (dynamic_cast<TempoSection*> (*i) != 0) {
298 if (tempo.frame() == (*i)->frame()) {
299 if ((*i)->movable()) {
310 StateChanged (Change (0));
315 TempoMap::remove_meter (const MeterSection& tempo)
317 bool removed = false;
320 Glib::RWLock::WriterLock lm (lock);
323 for (i = metrics->begin(); i != metrics->end(); ++i) {
324 if (dynamic_cast<MeterSection*> (*i) != 0) {
325 if (tempo.frame() == (*i)->frame()) {
326 if ((*i)->movable()) {
337 save_state (_("metric removed"));
343 StateChanged (Change (0));
348 TempoMap::do_insert (MetricSection* section)
352 for (i = metrics->begin(); i != metrics->end(); ++i) {
354 if ((*i)->start() < section->start()) {
358 metrics->insert (i, section);
362 if (i == metrics->end()) {
363 metrics->insert (metrics->end(), section);
366 timestamp_metrics ();
370 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
373 Glib::RWLock::WriterLock lm (lock);
375 /* new tempos always start on a beat */
379 do_insert (new TempoSection (where, tempo.beats_per_minute()));
382 save_state (_("add tempo"));
386 StateChanged (Change (0));
390 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
392 bool replaced = false;
395 Glib::RWLock::WriterLock lm (lock);
398 for (i = metrics->begin(); i != metrics->end(); ++i) {
401 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
403 *((Tempo *) ts) = replacement;
406 timestamp_metrics ();
413 save_state (_("replace tempo"));
419 StateChanged (Change (0));
424 TempoMap::add_meter (const Meter& meter, BBT_Time where)
427 Glib::RWLock::WriterLock lm (lock);
429 /* a new meter always starts a new bar on the first beat. so
430 round the start time appropriately. remember that
431 `where' is based on the existing tempo map, not
432 the result after we insert the new meter.
436 if (where.beats != 1) {
441 /* new meters *always* start on a beat. */
445 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()));
448 save_state (_("add meter"));
452 StateChanged (Change (0));
456 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
458 bool replaced = false;
461 Glib::RWLock::WriterLock lm (lock);
464 for (i = metrics->begin(); i != metrics->end(); ++i) {
466 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
468 *((Meter*) ms) = replacement;
471 timestamp_metrics ();
478 save_state (_("replaced meter"));
484 StateChanged (Change (0));
489 TempoMap::first_meter () const
491 const MeterSection *m = 0;
493 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
494 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
499 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
505 TempoMap::first_tempo () const
507 const TempoSection *t = 0;
509 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
510 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
515 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
521 TempoMap::timestamp_metrics ()
529 nframes_t section_frames;
533 meter = &first_meter ();
534 tempo = &first_tempo ();
537 for (i = metrics->begin(); i != metrics->end(); ++i) {
541 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
543 current += section_frames;
547 (*i)->set_frame (current);
549 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
551 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
554 fatal << _("programming error: unhandled MetricSection type") << endmsg;
561 TempoMap::metric_at (nframes_t frame) const
563 Metric m (first_meter(), first_tempo());
567 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
568 at something, because we insert the default tempo and meter during
569 TempoMap construction.
571 now see if we can find better candidates.
574 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
576 if ((*i)->frame() > frame) {
580 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
581 m.set_tempo (*tempo);
582 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
583 m.set_meter (*meter);
586 m.set_frame ((*i)->frame ());
587 m.set_start ((*i)->start ());
594 TempoMap::metric_at (BBT_Time bbt) const
596 Metric m (first_meter(), first_tempo());
600 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
601 at something, because we insert the default tempo and meter during
602 TempoMap construction.
604 now see if we can find better candidates.
607 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
609 BBT_Time section_start ((*i)->start());
611 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
615 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
616 m.set_tempo (*tempo);
617 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
618 m.set_meter (*meter);
621 m.set_frame ((*i)->frame ());
622 m.set_start (section_start);
629 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
632 Glib::RWLock::ReaderLock lm (lock);
633 bbt_time_unlocked (frame, bbt);
638 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
640 bbt_time_with_metric (frame, bbt, metric_at (frame));
644 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
646 nframes_t frame_diff;
648 uint32_t xtra_bars = 0;
649 double xtra_beats = 0;
652 const double beats_per_bar = metric.meter().beats_per_bar();
653 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
654 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
656 /* now compute how far beyond that point we actually are. */
658 frame_diff = frame - metric.frame();
660 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
661 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
662 xtra_beats = (double) frame_diff / beat_frames;
665 /* and set the returned value */
667 /* and correct beat/bar shifts to match the meter.
668 remember: beat and bar counting is 1-based,
670 also the meter may contain a fraction
673 bbt.bars = metric.start().bars + xtra_bars;
675 beats = (double) metric.start().beats + xtra_beats;
677 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
679 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
680 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
681 bbt.beats = (uint32_t) floor(beats);
687 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
690 /* for this to work with fractional measure types, start and end have to "legal" BBT types,
691 that means that the beats and ticks should be inside a bar
695 nframes_t frames = 0;
696 nframes_t start_frame = 0;
697 nframes_t end_frame = 0;
699 Metric m = metric_at(start);
701 uint32_t bar_offset = start.bars - m.start().bars;
703 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
704 + start.ticks/Meter::ticks_per_beat;
707 start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
711 bar_offset = end.bars - m.start().bars;
713 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
714 + end.ticks/Meter::ticks_per_beat;
716 end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
718 frames = end_frame - start_frame;
725 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
727 /*this is used in timestamping the metrics by actually counting the beats */
729 nframes_t frames = 0;
730 uint32_t bar = start.bars;
731 double beat = (double) start.beats;
732 double beats_counted = 0;
733 double beats_per_bar = 0;
734 double beat_frames = 0;
736 beats_per_bar = meter.beats_per_bar();
737 beat_frames = tempo.frames_per_beat (_frame_rate);
741 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
743 if (beat >= beats_per_bar) {
750 if (beat > beats_per_bar) {
751 /* this is a fractional beat at the end of a fractional bar
752 so it should only count for the fraction */
753 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
758 frames = (nframes_t) floor (beats_counted * beat_frames);
765 TempoMap::frame_time (const BBT_Time& bbt) const
767 BBT_Time start ; /* 1|1|0 */
769 return count_frames_between ( start, bbt);
773 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
775 nframes_t frames = 0;
781 Glib::RWLock::ReaderLock lm (lock);
782 frames = bbt_duration_at_unlocked (when, bbt,dir);
789 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
792 nframes_t frames = 0;
794 double beats_per_bar;
797 result.bars = max(1U,when.bars + dir * bbt.bars) ;
801 Metric metric = metric_at(result);
802 beats_per_bar = metric.meter().beats_per_bar();
806 /*reduce things to legal bbt values
807 we have to handle possible fractional=shorter beats at the end of measures
808 and things like 0|11|9000 as a duration in a 4.5/4 measure
809 the musical decision is that the fractional beat is also a beat , although a shorter one
814 result.beats = when.beats + bbt.beats;
815 result.ticks = when.ticks + bbt.ticks;
817 while (result.beats >= (beats_per_bar+1)) {
819 result.beats -= (uint32_t) ceil(beats_per_bar);
820 metric = metric_at(result); // maybe there is a meter change
821 beats_per_bar = metric.meter().beats_per_bar();
824 /*we now counted the beats and landed in the target measure, now deal with ticks
825 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
826 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
829 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
832 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
833 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
834 : Meter::ticks_per_beat );
836 while (result.ticks >= ticks_at_beat) {
838 result.ticks -= ticks_at_beat;
839 if (result.beats >= (beats_per_bar+1)) {
842 metric = metric_at(result); // maybe there is a meter change
843 beats_per_bar = metric.meter().beats_per_bar();
845 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
846 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
847 : Meter::ticks_per_beat);
853 uint32_t b = bbt.beats;
856 while( b > when.beats ) {
858 result.bars = max(1U,result.bars-- ) ;
859 metric = metric_at(result); // maybe there is a meter change
860 beats_per_bar = metric.meter().beats_per_bar();
861 if (b >= ceil(beats_per_bar)) {
863 b -= (uint32_t) ceil(beats_per_bar);
865 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
868 result.beats = when.beats - b;
872 if (bbt.ticks <= when.ticks) {
873 result.ticks = when.ticks - bbt.ticks;
876 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
877 uint32_t t = bbt.ticks - when.ticks;
881 if (result.beats == 1) {
882 result.bars = max(1U,result.bars-- ) ;
883 metric = metric_at(result); // maybe there is a meter change
884 beats_per_bar = metric.meter().beats_per_bar();
885 result.beats = (uint32_t) ceil(beats_per_bar);
886 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
889 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
892 if (t <= ticks_at_beat) {
893 result.ticks = ticks_at_beat - t;
897 } while (t > ticks_at_beat);
905 frames = count_frames_between( result,when);
907 frames = count_frames_between(when,result);
916 TempoMap::round_to_bar (nframes_t fr, int dir)
919 Glib::RWLock::ReaderLock lm (lock);
920 return round_to_type (fr, dir, Bar);
926 TempoMap::round_to_beat (nframes_t fr, int dir)
929 Glib::RWLock::ReaderLock lm (lock);
930 return round_to_type (fr, dir, Beat);
936 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
940 uint32_t ticks_one_half_subdivisions_worth;
941 uint32_t ticks_one_subdivisions_worth;
943 bbt_time(fr, the_beat);
945 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
946 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
948 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
949 uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
950 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
952 the_beat.ticks += difference;
953 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
955 the_beat.ticks += difference;
958 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
961 return frame_time (the_beat);
963 /* XXX just keeping this for reference
965 TempoMap::BBTPointList::iterator i;
966 TempoMap::BBTPointList *more_zoomed_bbt_points;
967 nframes_t frame_one_beats_worth;
969 nframes_t next_pos = 0 ;
971 double frames_one_subdivisions_worth;
972 bool fr_has_changed = false;
976 frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
978 Glib::RWLock::ReaderLock lm (lock);
979 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
980 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
982 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
986 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
987 if ((*i).frame <= fr) {
989 tempo = (*i).tempo->beats_per_minute();
993 next_pos = (*i).frame;
997 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
999 for (n = sub_num; n > 0; n--) {
1000 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
1001 fr = (nframes_t) round(pos + (n * frames_one_subdivisions_worth));
1002 if (fr > next_pos) {
1003 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
1005 fr_has_changed = true;
1010 if (!fr_has_changed) {
1014 delete more_zoomed_bbt_points;
1023 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1025 Metric metric = metric_at (frame);
1028 bbt_time_with_metric (frame, bbt, metric);
1035 } else if (dir > 0) {
1036 if (bbt.beats > 0) {
1040 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1052 } else if (dir > 0) {
1053 if (bbt.ticks > 0) {
1057 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1061 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1070 return metric.frame() + count_frames_between (metric.start(), bbt);
1073 TempoMap::BBTPointList *
1074 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1077 Metrics::const_iterator i;
1078 BBTPointList *points;
1080 const MeterSection* meter;
1081 const MeterSection* m;
1082 const TempoSection* tempo;
1083 const TempoSection* t;
1086 double beats_per_bar;
1089 double frames_per_bar;
1095 meter = &first_meter ();
1096 tempo = &first_tempo ();
1098 /* find the starting point */
1100 for (i = metrics->begin(); i != metrics->end(); ++i) {
1102 if ((*i)->frame() > lower) {
1106 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1108 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1115 meter -> the Meter for "lower"
1116 tempo -> the Tempo for "lower"
1117 i -> for first new metric after "lower", possibly metrics->end()
1119 Now start generating points.
1122 beats_per_bar = meter->beats_per_bar ();
1123 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1124 beat_frames = tempo->frames_per_beat (_frame_rate);
1126 if (meter->frame() > tempo->frame()) {
1127 bar = meter->start().bars;
1128 beat = meter->start().beats;
1129 current = meter->frame();
1131 bar = tempo->start().bars;
1132 beat = tempo->start().beats;
1133 current = tempo->frame();
1136 /* initialize current to point to the bar/beat just prior to the
1137 lower frame bound passed in. assumes that current is initialized
1138 above to be on a beat.
1141 delta_bars = (lower-current) / frames_per_bar;
1142 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1143 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1145 // adjust bars and beats too
1146 bar += (uint32_t) (floor(delta_bars));
1147 beat += (uint32_t) (floor(delta_beats));
1149 points = new BBTPointList;
1153 if (i == metrics->end()) {
1156 limit = (*i)->frame();
1159 limit = min (limit, upper);
1161 while (current < limit) {
1163 /* if we're at the start of a bar, add bar point */
1166 if (current >= lower) {
1167 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1172 /* add some beats if we can */
1174 beat_frame = current;
1176 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1177 if (beat_frame >= lower) {
1178 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1180 beat_frame += beat_frames;
1181 current+= beat_frames;
1186 if (beat > ceil(beats_per_bar) ) {
1188 /* we walked an entire bar. its
1189 important to move `current' forward
1190 by the actual frames_per_bar, not move it to
1191 an integral beat_frame, so that metrics with
1192 non-integral beats-per-bar have
1193 their bar positions set
1194 correctly. consider a metric with
1195 9-1/2 beats-per-bar. the bar we
1196 just filled had 10 beat marks,
1197 but the bar end is 1/2 beat before
1199 And it is also possible that a tempo
1200 change occured in the middle of a bar,
1201 so we subtract the possible extra fraction from the current
1204 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1212 /* if we're done, then we're done */
1214 if (current >= upper) {
1218 /* i is an iterator that refers to the next metric (or none).
1219 if there is a next metric, move to it, and continue.
1222 if (i != metrics->end()) {
1224 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1226 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1228 /* new MeterSection, beat always returns to 1 */
1232 beats_per_bar = meter->beats_per_bar ();
1233 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1234 beat_frames = tempo->frames_per_beat (_frame_rate);
1245 TempoMap::tempo_at (nframes_t frame)
1247 Metric m (metric_at (frame));
1253 TempoMap::meter_at (nframes_t frame)
1255 Metric m (metric_at (frame));
1260 TempoMap::get_state ()
1262 Metrics::const_iterator i;
1263 XMLNode *root = new XMLNode ("TempoMap");
1266 Glib::RWLock::ReaderLock lm (lock);
1267 for (i = metrics->begin(); i != metrics->end(); ++i) {
1268 root->add_child_nocopy ((*i)->get_state());
1276 TempoMap::set_state (const XMLNode& node)
1279 Glib::RWLock::WriterLock lm (lock);
1282 XMLNodeConstIterator niter;
1283 Metrics old_metrics (*metrics);
1285 #ifdef STATE_MANAGER
1286 in_set_state = true;
1291 nlist = node.children();
1293 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1294 XMLNode* child = *niter;
1296 if (child->name() == TempoSection::xml_state_node_name) {
1299 metrics->push_back (new TempoSection (*child));
1302 catch (failed_constructor& err){
1303 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1304 *metrics = old_metrics;
1308 } else if (child->name() == MeterSection::xml_state_node_name) {
1311 metrics->push_back (new MeterSection (*child));
1314 catch (failed_constructor& err) {
1315 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1316 *metrics = old_metrics;
1322 if (niter == nlist.end()) {
1324 MetricSectionSorter cmp;
1325 metrics->sort (cmp);
1326 timestamp_metrics ();
1329 #ifdef STATE_MANAGER
1330 in_set_state = false;
1334 /* This state needs to be saved. This string will never be a part of the
1335 object's history though, because the allow_save flag is false during
1336 session load. This state will eventually be tagged "initial state",
1337 by a call to StateManager::allow_save from Session::set_state.
1339 If this state is not saved, there is no way to reach it through undo actions.
1342 #ifdef STATE_MANAGER
1343 save_state(_("load XML data"));
1346 StateChanged (Change (0));
1352 TempoMap::dump (std::ostream& o) const
1354 const MeterSection* m;
1355 const TempoSection* t;
1357 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1359 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1360 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1361 << t->movable() << ')' << endl;
1362 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1363 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1364 << " (move? " << m->movable() << ')' << endl;
1369 #ifdef STATE_MANAGER
1371 TempoMap::get_memento () const
1373 return sigc::bind (mem_fun (*(const_cast<TempoMap *> (this)), &StateManager::use_state), _current_state_id);
1377 TempoMap::restore_state (StateManager::State& state)
1379 Glib::RWLock::ReaderLock lm (lock);
1381 TempoMapState* tmstate = dynamic_cast<TempoMapState*> (&state);
1383 /* We can't just set the metrics pointer to the address of the metrics list
1384 stored in the state, cause this would ruin this state for restoring in
1385 the future. If they have the same address, they are the same list.
1386 Thus we need to copy all the elements from the state metrics list to the
1387 current metrics list.
1390 for (Metrics::iterator i = tmstate->metrics->begin(); i != tmstate->metrics->end(); ++i) {
1394 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1395 metrics->push_back (new TempoSection (*ts));
1396 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1397 metrics->push_back (new MeterSection (*ms));
1401 last_bbt_valid = false;
1406 StateManager::State*
1407 TempoMap::state_factory (std::string why) const
1409 TempoMapState* state = new TempoMapState (why);
1411 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1415 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1416 state->metrics->push_back (new TempoSection (*ts));
1417 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1418 state->metrics->push_back (new MeterSection (*ms));
1426 TempoMap::save_state (std::string why)
1428 if (!in_set_state) {
1429 StateManager::save_state (why);