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;
209 in_set_state = false;
215 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute());
216 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
218 t->set_movable (false);
219 m->set_movable (false);
221 /* note: frame time is correct (zero) for both of these */
223 metrics->push_back (t);
224 metrics->push_back (m);
226 save_state (_("initial"));
229 TempoMap::~TempoMap ()
234 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
236 if (when == section.start()) {
240 if (!section.movable()) {
244 Glib::RWLock::WriterLock lm (lock);
245 MetricSectionSorter cmp;
246 BBT_Time corrected (when);
248 if (dynamic_cast<MeterSection*>(§ion) != 0) {
249 if (corrected.beats > 1) {
256 section.set_start (corrected);
258 timestamp_metrics ();
259 save_state (_("move metric"));
265 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
267 if (move_metric_section (tempo, when) == 0) {
268 send_state_changed (Change (0));
273 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
275 if (move_metric_section (meter, when) == 0) {
276 send_state_changed (Change (0));
282 TempoMap::remove_tempo (const TempoSection& tempo)
284 bool removed = false;
287 Glib::RWLock::WriterLock lm (lock);
290 for (i = metrics->begin(); i != metrics->end(); ++i) {
291 if (dynamic_cast<TempoSection*> (*i) != 0) {
292 if (tempo.frame() == (*i)->frame()) {
293 if ((*i)->movable()) {
304 send_state_changed (Change (0));
309 TempoMap::remove_meter (const MeterSection& tempo)
311 bool removed = false;
314 Glib::RWLock::WriterLock lm (lock);
317 for (i = metrics->begin(); i != metrics->end(); ++i) {
318 if (dynamic_cast<MeterSection*> (*i) != 0) {
319 if (tempo.frame() == (*i)->frame()) {
320 if ((*i)->movable()) {
330 save_state (_("metric removed"));
335 send_state_changed (Change (0));
340 TempoMap::do_insert (MetricSection* section)
344 for (i = metrics->begin(); i != metrics->end(); ++i) {
346 if ((*i)->start() < section->start()) {
350 metrics->insert (i, section);
354 if (i == metrics->end()) {
355 metrics->insert (metrics->end(), section);
358 timestamp_metrics ();
362 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
365 Glib::RWLock::WriterLock lm (lock);
367 /* new tempos always start on a beat */
371 do_insert (new TempoSection (where, tempo.beats_per_minute()));
373 save_state (_("add tempo"));
376 send_state_changed (Change (0));
380 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
382 bool replaced = false;
385 Glib::RWLock::WriterLock lm (lock);
388 for (i = metrics->begin(); i != metrics->end(); ++i) {
391 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
393 *((Tempo *) ts) = replacement;
396 timestamp_metrics ();
402 save_state (_("replace tempo"));
407 send_state_changed (Change (0));
412 TempoMap::add_meter (const Meter& meter, BBT_Time where)
415 Glib::RWLock::WriterLock lm (lock);
417 /* a new meter always starts a new bar on the first beat. so
418 round the start time appropriately. remember that
419 `where' is based on the existing tempo map, not
420 the result after we insert the new meter.
424 if (where.beats != 1) {
429 /* new meters *always* start on a beat. */
433 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()));
435 save_state (_("add meter"));
438 send_state_changed (Change (0));
442 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
444 bool replaced = false;
447 Glib::RWLock::WriterLock lm (lock);
450 for (i = metrics->begin(); i != metrics->end(); ++i) {
452 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
454 *((Meter*) ms) = replacement;
457 timestamp_metrics ();
463 save_state (_("replaced meter"));
468 send_state_changed (Change (0));
473 TempoMap::first_meter () const
475 const MeterSection *m = 0;
477 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
478 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
483 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
489 TempoMap::first_tempo () const
491 const TempoSection *t = 0;
493 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
494 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
499 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
505 TempoMap::timestamp_metrics ()
513 nframes_t section_frames;
517 meter = &first_meter ();
518 tempo = &first_tempo ();
521 for (i = metrics->begin(); i != metrics->end(); ++i) {
525 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
527 current += section_frames;
531 (*i)->set_frame (current);
533 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
535 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
538 fatal << _("programming error: unhandled MetricSection type") << endmsg;
545 TempoMap::metric_at (nframes_t frame) const
547 Metric m (first_meter(), first_tempo());
551 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
552 at something, because we insert the default tempo and meter during
553 TempoMap construction.
555 now see if we can find better candidates.
558 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
560 if ((*i)->frame() > frame) {
564 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
565 m.set_tempo (*tempo);
566 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
567 m.set_meter (*meter);
570 m.set_frame ((*i)->frame ());
571 m.set_start ((*i)->start ());
578 TempoMap::metric_at (BBT_Time bbt) const
580 Metric m (first_meter(), first_tempo());
584 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
585 at something, because we insert the default tempo and meter during
586 TempoMap construction.
588 now see if we can find better candidates.
591 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
593 BBT_Time section_start ((*i)->start());
595 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
599 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
600 m.set_tempo (*tempo);
601 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
602 m.set_meter (*meter);
605 m.set_frame ((*i)->frame ());
606 m.set_start (section_start);
613 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
616 Glib::RWLock::ReaderLock lm (lock);
617 bbt_time_unlocked (frame, bbt);
622 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
624 bbt_time_with_metric (frame, bbt, metric_at (frame));
628 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
630 nframes_t frame_diff;
632 uint32_t xtra_bars = 0;
633 double xtra_beats = 0;
636 const double beats_per_bar = metric.meter().beats_per_bar();
637 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
638 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
640 /* now compute how far beyond that point we actually are. */
642 frame_diff = frame - metric.frame();
644 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
645 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
646 xtra_beats = (double) frame_diff / beat_frames;
649 /* and set the returned value */
651 /* and correct beat/bar shifts to match the meter.
652 remember: beat and bar counting is 1-based,
654 also the meter may contain a fraction
657 bbt.bars = metric.start().bars + xtra_bars;
659 beats = (double) metric.start().beats + xtra_beats;
661 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
663 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
664 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
665 bbt.beats = (uint32_t) floor(beats);
671 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
674 /* for this to work with fractional measure types, start and end have to "legal" BBT types,
675 that means that the beats and ticks should be inside a bar
679 nframes_t frames = 0;
680 nframes_t start_frame = 0;
681 nframes_t end_frame = 0;
683 Metric m = metric_at(start);
685 uint32_t bar_offset = start.bars - m.start().bars;
687 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
688 + start.ticks/Meter::ticks_per_beat;
691 start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
695 bar_offset = end.bars - m.start().bars;
697 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
698 + end.ticks/Meter::ticks_per_beat;
700 end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
702 frames = end_frame - start_frame;
709 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
711 /*this is used in timestamping the metrics by actually counting the beats */
713 nframes_t frames = 0;
714 uint32_t bar = start.bars;
715 double beat = (double) start.beats;
716 double beats_counted = 0;
717 double beats_per_bar = 0;
718 double beat_frames = 0;
720 beats_per_bar = meter.beats_per_bar();
721 beat_frames = tempo.frames_per_beat (_frame_rate);
725 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
727 if (beat >= beats_per_bar) {
734 if (beat > beats_per_bar) {
735 /* this is a fractional beat at the end of a fractional bar
736 so it should only count for the fraction */
737 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
742 frames = (nframes_t) floor (beats_counted * beat_frames);
749 TempoMap::frame_time (const BBT_Time& bbt) const
751 BBT_Time start ; /* 1|1|0 */
753 return count_frames_between ( start, bbt);
757 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
759 nframes_t frames = 0;
765 Glib::RWLock::ReaderLock lm (lock);
766 frames = bbt_duration_at_unlocked (when, bbt,dir);
773 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
776 nframes_t frames = 0;
778 double beats_per_bar;
781 result.bars = max(1U,when.bars + dir * bbt.bars) ;
785 Metric metric = metric_at(result);
786 beats_per_bar = metric.meter().beats_per_bar();
790 /*reduce things to legal bbt values
791 we have to handle possible fractional=shorter beats at the end of measures
792 and things like 0|11|9000 as a duration in a 4.5/4 measure
793 the musical decision is that the fractional beat is also a beat , although a shorter one
798 result.beats = when.beats + bbt.beats;
799 result.ticks = when.ticks + bbt.ticks;
801 while (result.beats >= (beats_per_bar+1)) {
803 result.beats -= (uint32_t) ceil(beats_per_bar);
804 metric = metric_at(result); // maybe there is a meter change
805 beats_per_bar = metric.meter().beats_per_bar();
808 /*we now counted the beats and landed in the target measure, now deal with ticks
809 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
810 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
813 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
816 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
817 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
818 : Meter::ticks_per_beat );
820 while (result.ticks >= ticks_at_beat) {
822 result.ticks -= ticks_at_beat;
823 if (result.beats >= (beats_per_bar+1)) {
826 metric = metric_at(result); // maybe there is a meter change
827 beats_per_bar = metric.meter().beats_per_bar();
829 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
830 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
831 : Meter::ticks_per_beat);
837 uint32_t b = bbt.beats;
840 while( b > when.beats ) {
842 result.bars = max(1U,result.bars-- ) ;
843 metric = metric_at(result); // maybe there is a meter change
844 beats_per_bar = metric.meter().beats_per_bar();
845 if (b >= ceil(beats_per_bar)) {
847 b -= (uint32_t) ceil(beats_per_bar);
849 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
852 result.beats = when.beats - b;
856 if (bbt.ticks <= when.ticks) {
857 result.ticks = when.ticks - bbt.ticks;
860 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
861 uint32_t t = bbt.ticks - when.ticks;
865 if (result.beats == 1) {
866 result.bars = max(1U,result.bars-- ) ;
867 metric = metric_at(result); // maybe there is a meter change
868 beats_per_bar = metric.meter().beats_per_bar();
869 result.beats = (uint32_t) ceil(beats_per_bar);
870 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
873 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
876 if (t <= ticks_at_beat) {
877 result.ticks = ticks_at_beat - t;
881 } while (t > ticks_at_beat);
889 frames = count_frames_between( result,when);
891 frames = count_frames_between(when,result);
900 TempoMap::round_to_bar (nframes_t fr, int dir)
903 Glib::RWLock::ReaderLock lm (lock);
904 return round_to_type (fr, dir, Bar);
910 TempoMap::round_to_beat (nframes_t fr, int dir)
913 Glib::RWLock::ReaderLock lm (lock);
914 return round_to_type (fr, dir, Beat);
920 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
924 uint32_t ticks_one_half_subdivisions_worth;
925 uint32_t ticks_one_subdivisions_worth;
927 bbt_time(fr, the_beat);
929 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
930 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
932 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
933 uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
934 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
936 the_beat.ticks += difference;
937 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
939 the_beat.ticks += difference;
942 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
945 return frame_time (the_beat);
947 /* XXX just keeping this for reference
949 TempoMap::BBTPointList::iterator i;
950 TempoMap::BBTPointList *more_zoomed_bbt_points;
951 nframes_t frame_one_beats_worth;
953 nframes_t next_pos = 0 ;
955 double frames_one_subdivisions_worth;
956 bool fr_has_changed = false;
960 frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
962 Glib::RWLock::ReaderLock lm (lock);
963 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
964 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
966 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
970 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
971 if ((*i).frame <= fr) {
973 tempo = (*i).tempo->beats_per_minute();
977 next_pos = (*i).frame;
981 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
983 for (n = sub_num; n > 0; n--) {
984 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
985 fr = (nframes_t) round(pos + (n * frames_one_subdivisions_worth));
987 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
989 fr_has_changed = true;
994 if (!fr_has_changed) {
998 delete more_zoomed_bbt_points;
1007 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1009 Metric metric = metric_at (frame);
1012 bbt_time_with_metric (frame, bbt, metric);
1019 } else if (dir > 0) {
1020 if (bbt.beats > 0) {
1024 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1036 } else if (dir > 0) {
1037 if (bbt.ticks > 0) {
1041 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1045 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1054 return metric.frame() + count_frames_between (metric.start(), bbt);
1057 TempoMap::BBTPointList *
1058 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1061 Metrics::const_iterator i;
1062 BBTPointList *points;
1064 const MeterSection* meter;
1065 const MeterSection* m;
1066 const TempoSection* tempo;
1067 const TempoSection* t;
1070 double beats_per_bar;
1073 double frames_per_bar;
1076 meter = &first_meter ();
1077 tempo = &first_tempo ();
1079 /* find the starting point */
1081 for (i = metrics->begin(); i != metrics->end(); ++i) {
1083 if ((*i)->frame() > lower) {
1087 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1089 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1096 meter -> the Meter for "lower"
1097 tempo -> the Tempo for "lower"
1098 i -> for first new metric after "lower", possibly metrics->end()
1100 Now start generating points.
1103 if (meter->frame() > tempo->frame()) {
1104 bar = meter->start().bars;
1105 beat = meter->start().beats;
1106 current = meter->frame();
1108 bar = tempo->start().bars;
1109 beat = tempo->start().beats;
1110 current = tempo->frame();
1113 points = new BBTPointList;
1115 beats_per_bar = meter->beats_per_bar ();
1116 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1117 beat_frames = tempo->frames_per_beat (_frame_rate);
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 */
1209 TempoMap::tempo_at (nframes_t frame)
1211 Metric m (metric_at (frame));
1217 TempoMap::meter_at (nframes_t frame)
1219 Metric m (metric_at (frame));
1224 TempoMap::get_state ()
1226 Metrics::const_iterator i;
1227 XMLNode *root = new XMLNode ("TempoMap");
1230 Glib::RWLock::ReaderLock lm (lock);
1231 for (i = metrics->begin(); i != metrics->end(); ++i) {
1232 root->add_child_nocopy ((*i)->get_state());
1240 TempoMap::set_state (const XMLNode& node)
1243 Glib::RWLock::WriterLock lm (lock);
1246 XMLNodeConstIterator niter;
1247 Metrics old_metrics (*metrics);
1249 in_set_state = true;
1253 nlist = node.children();
1255 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1256 XMLNode* child = *niter;
1258 if (child->name() == TempoSection::xml_state_node_name) {
1261 metrics->push_back (new TempoSection (*child));
1264 catch (failed_constructor& err){
1265 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1266 *metrics = old_metrics;
1270 } else if (child->name() == MeterSection::xml_state_node_name) {
1273 metrics->push_back (new MeterSection (*child));
1276 catch (failed_constructor& err) {
1277 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1278 *metrics = old_metrics;
1284 if (niter == nlist.end()) {
1286 MetricSectionSorter cmp;
1287 metrics->sort (cmp);
1288 timestamp_metrics ();
1291 in_set_state = false;
1294 /* This state needs to be saved. This string will never be a part of the
1295 object's history though, because the allow_save flag is false during
1296 session load. This state will eventually be tagged "initial state",
1297 by a call to StateManager::allow_save from Session::set_state.
1299 If this state is not saved, there is no way to reach it through undo actions.
1301 save_state(_("load XML data"));
1303 send_state_changed (Change (0));
1309 TempoMap::dump (std::ostream& o) const
1311 const MeterSection* m;
1312 const TempoSection* t;
1314 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1316 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1317 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1318 << t->movable() << ')' << endl;
1319 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1320 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1321 << " (move? " << m->movable() << ')' << endl;
1327 TempoMap::get_memento () const
1329 return sigc::bind (mem_fun (*(const_cast<TempoMap *> (this)), &StateManager::use_state), _current_state_id);
1333 TempoMap::restore_state (StateManager::State& state)
1335 Glib::RWLock::ReaderLock lm (lock);
1337 TempoMapState* tmstate = dynamic_cast<TempoMapState*> (&state);
1339 /* We can't just set the metrics pointer to the address of the metrics list
1340 stored in the state, cause this would ruin this state for restoring in
1341 the future. If they have the same address, they are the same list.
1342 Thus we need to copy all the elements from the state metrics list to the
1343 current metrics list.
1346 for (Metrics::iterator i = tmstate->metrics->begin(); i != tmstate->metrics->end(); ++i) {
1350 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1351 metrics->push_back (new TempoSection (*ts));
1352 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1353 metrics->push_back (new MeterSection (*ms));
1357 last_bbt_valid = false;
1362 StateManager::State*
1363 TempoMap::state_factory (std::string why) const
1365 TempoMapState* state = new TempoMapState (why);
1367 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1371 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1372 state->metrics->push_back (new TempoSection (*ts));
1373 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1374 state->metrics->push_back (new MeterSection (*ms));
1382 TempoMap::save_state (std::string why)
1384 if (!in_set_state) {
1385 StateManager::save_state (why);