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, jack_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 (jack_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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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 ()
512 jack_nframes_t current;
513 jack_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 (jack_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 (jack_nframes_t frame, BBT_Time& bbt) const
615 Glib::Mutex::Lock lm (lock);
616 bbt_time_unlocked (frame, bbt);
620 TempoMap::bbt_time_unlocked (jack_nframes_t frame, BBT_Time& bbt) const
622 bbt_time_with_metric (frame, bbt, metric_at (frame));
626 TempoMap::bbt_time_with_metric (jack_nframes_t frame, BBT_Time& bbt, const Metric& metric) const
628 jack_nframes_t frame_diff;
630 uint32_t xtra_bars = 0;
631 double xtra_beats = 0;
634 const double beats_per_bar = metric.meter().beats_per_bar();
635 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
636 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
638 /* now compute how far beyond that point we actually are. */
640 frame_diff = frame - metric.frame();
642 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
643 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
644 xtra_beats = (double) frame_diff / beat_frames;
647 /* and set the returned value */
649 /* and correct beat/bar shifts to match the meter.
650 remember: beat and bar counting is 1-based,
652 also the meter may contain a fraction
655 bbt.bars = metric.start().bars + xtra_bars;
657 beats = (double) metric.start().beats + xtra_beats;
659 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
661 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
662 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
663 bbt.beats = (uint32_t) floor(beats);
669 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
672 /* for this to work with fractional measure types, start and end have to "legal" BBT types,
673 that means that the beats and ticks should be inside a bar
677 jack_nframes_t frames = 0;
678 jack_nframes_t start_frame = 0;
679 jack_nframes_t end_frame = 0;
681 Metric m = metric_at(start);
683 uint32_t bar_offset = start.bars - m.start().bars;
685 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
686 + start.ticks/Meter::ticks_per_beat;
689 start_frame = m.frame() + (jack_nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
693 bar_offset = end.bars - m.start().bars;
695 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
696 + end.ticks/Meter::ticks_per_beat;
698 end_frame = m.frame() + (jack_nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
700 frames = end_frame - start_frame;
707 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
709 /*this is used in timestamping the metrics by actually counting the beats */
711 jack_nframes_t frames = 0;
712 uint32_t bar = start.bars;
713 double beat = (double) start.beats;
714 double beats_counted = 0;
715 double beats_per_bar = 0;
716 double beat_frames = 0;
718 beats_per_bar = meter.beats_per_bar();
719 beat_frames = tempo.frames_per_beat (_frame_rate);
723 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
725 if (beat >= beats_per_bar) {
732 if (beat > beats_per_bar) {
733 /* this is a fractional beat at the end of a fractional bar
734 so it should only count for the fraction */
735 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
740 frames = (jack_nframes_t) floor (beats_counted * beat_frames);
747 TempoMap::frame_time (const BBT_Time& bbt) const
749 BBT_Time start ; /* 1|1|0 */
751 return count_frames_between ( start, bbt);
755 TempoMap::bbt_duration_at (jack_nframes_t pos, const BBT_Time& bbt, int dir) const
757 jack_nframes_t frames = 0;
763 Glib::Mutex::Lock lm (lock);
764 frames = bbt_duration_at_unlocked (when, bbt,dir);
771 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
774 jack_nframes_t frames = 0;
776 double beats_per_bar;
779 result.bars = max(1U,when.bars + dir * bbt.bars) ;
783 Metric metric = metric_at(result);
784 beats_per_bar = metric.meter().beats_per_bar();
788 /*reduce things to legal bbt values
789 we have to handle possible fractional=shorter beats at the end of measures
790 and things like 0|11|9000 as a duration in a 4.5/4 measure
791 the musical decision is that the fractional beat is also a beat , although a shorter one
796 result.beats = when.beats + bbt.beats;
797 result.ticks = when.ticks + bbt.ticks;
799 while (result.beats >= (beats_per_bar+1)) {
801 result.beats -= (uint32_t) ceil(beats_per_bar);
802 metric = metric_at(result); // maybe there is a meter change
803 beats_per_bar = metric.meter().beats_per_bar();
806 /*we now counted the beats and landed in the target measure, now deal with ticks
807 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
808 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
811 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
814 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
815 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
816 : Meter::ticks_per_beat );
818 while (result.ticks >= ticks_at_beat) {
820 result.ticks -= ticks_at_beat;
821 if (result.beats >= (beats_per_bar+1)) {
824 metric = metric_at(result); // maybe there is a meter change
825 beats_per_bar = metric.meter().beats_per_bar();
827 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
828 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
829 : Meter::ticks_per_beat);
835 uint32_t b = bbt.beats;
838 while( b > when.beats ) {
840 result.bars = max(1U,result.bars-- ) ;
841 metric = metric_at(result); // maybe there is a meter change
842 beats_per_bar = metric.meter().beats_per_bar();
843 if (b >= ceil(beats_per_bar)) {
845 b -= (uint32_t) ceil(beats_per_bar);
847 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
850 result.beats = when.beats - b;
854 if (bbt.ticks <= when.ticks) {
855 result.ticks = when.ticks - bbt.ticks;
858 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
859 uint32_t t = bbt.ticks - when.ticks;
863 if (result.beats == 1) {
864 result.bars = max(1U,result.bars-- ) ;
865 metric = metric_at(result); // maybe there is a meter change
866 beats_per_bar = metric.meter().beats_per_bar();
867 result.beats = (uint32_t) ceil(beats_per_bar);
868 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
871 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
874 if (t <= ticks_at_beat) {
875 result.ticks = ticks_at_beat - t;
879 } while (t > ticks_at_beat);
887 frames = count_frames_between( result,when);
889 frames = count_frames_between(when,result);
898 TempoMap::round_to_bar (jack_nframes_t fr, int dir)
900 Glib::Mutex::Lock lm (lock);
901 return round_to_type (fr, dir, Bar);
906 TempoMap::round_to_beat (jack_nframes_t fr, int dir)
908 Glib::Mutex::Lock lm (lock);
909 return round_to_type (fr, dir, Beat);
914 TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num)
916 Glib::Mutex::Lock lm (lock);
917 TempoMap::BBTPointList::iterator i;
918 TempoMap::BBTPointList *more_zoomed_bbt_points;
919 jack_nframes_t frame_one_beats_worth;
920 jack_nframes_t pos = 0;
921 jack_nframes_t next_pos = 0 ;
923 double frames_one_subdivisions_worth;
924 bool fr_has_changed = false;
928 frame_one_beats_worth = (jack_nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
929 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
930 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
932 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
936 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
937 if ((*i).frame <= fr) {
939 tempo = (*i).tempo->beats_per_minute();
943 next_pos = (*i).frame;
947 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
949 for (n = sub_num; n > 0; n--) {
950 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
951 fr = (jack_nframes_t) round(pos + (n * frames_one_subdivisions_worth));
953 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
955 fr_has_changed = true;
960 if (!fr_has_changed) {
964 delete more_zoomed_bbt_points;
970 TempoMap::round_to_type (jack_nframes_t frame, int dir, BBTPointType type)
972 Metric metric = metric_at (frame);
975 bbt_time_with_metric (frame, bbt, metric);
982 } else if (dir > 0) {
987 if (bbt.beats > metric.meter().beats_per_bar()/2) {
999 } else if (dir > 0) {
1000 if (bbt.ticks > 0) {
1004 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1008 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1017 return metric.frame() + count_frames_between (metric.start(), bbt);
1020 TempoMap::BBTPointList *
1021 TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const
1024 Metrics::const_iterator i;
1025 BBTPointList *points;
1027 const MeterSection* meter;
1028 const MeterSection* m;
1029 const TempoSection* tempo;
1030 const TempoSection* t;
1034 meter = &first_meter ();
1035 tempo = &first_tempo ();
1037 /* find the starting point */
1039 for (i = metrics->begin(); i != metrics->end(); ++i) {
1041 if ((*i)->frame() > lower) {
1045 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1047 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1054 meter -> the Meter for "lower"
1055 tempo -> the Tempo for "lower"
1056 i -> for first new metric after "lower", possibly metrics->end()
1058 Now start generating points.
1061 if (meter->frame() > tempo->frame()) {
1062 bar = meter->start().bars;
1063 beat = meter->start().beats;
1064 current = meter->frame();
1066 bar = tempo->start().bars;
1067 beat = tempo->start().beats;
1068 current = tempo->frame();
1071 points = new BBTPointList;
1074 double beats_per_bar;
1077 double frames_per_bar;
1078 jack_nframes_t limit;
1080 beats_per_bar = meter->beats_per_bar ();
1081 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1082 beat_frames = tempo->frames_per_beat (_frame_rate);
1084 if (i == metrics->end()) {
1087 limit = (*i)->frame();
1090 limit = min (limit, upper);
1092 while (current < limit) {
1094 /* if we're at the start of a bar, add bar point */
1097 if (current >= lower) {
1098 points->push_back (BBTPoint (*meter, *tempo,(jack_nframes_t)rint(current), Bar, bar, 1));
1103 /* add some beats if we can */
1105 beat_frame = current;
1107 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1108 if (beat_frame >= lower) {
1109 points->push_back (BBTPoint (*meter, *tempo, (jack_nframes_t) rint(beat_frame), Beat, bar, beat));
1111 beat_frame += beat_frames;
1112 current+= beat_frames;
1117 if (beat > ceil(beats_per_bar) ) {
1119 /* we walked an entire bar. its
1120 important to move `current' forward
1121 by the actual frames_per_bar, not move it to
1122 an integral beat_frame, so that metrics with
1123 non-integral beats-per-bar have
1124 their bar positions set
1125 correctly. consider a metric with
1126 9-1/2 beats-per-bar. the bar we
1127 just filled had 10 beat marks,
1128 but the bar end is 1/2 beat before
1130 And it is also possible that a tempo
1131 change occured in the middle of a bar,
1132 so we subtract the possible extra fraction from the current
1135 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1143 /* if we're done, then we're done */
1145 if (current >= upper) {
1149 /* i is an iterator that refers to the next metric (or none).
1150 if there is a next metric, move to it, and continue.
1153 if (i != metrics->end()) {
1155 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1157 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1159 /* new MeterSection, beat always returns to 1 */
1172 TempoMap::tempo_at (jack_nframes_t frame)
1174 Metric m (metric_at (frame));
1180 TempoMap::meter_at (jack_nframes_t frame)
1182 Metric m (metric_at (frame));
1187 TempoMap::get_state ()
1189 Glib::Mutex::Lock lm (lock);
1190 Metrics::const_iterator i;
1191 XMLNode *root = new XMLNode ("TempoMap");
1193 for (i = metrics->begin(); i != metrics->end(); ++i) {
1194 root->add_child_nocopy ((*i)->get_state());
1201 TempoMap::set_state (const XMLNode& node)
1204 Glib::Mutex::Lock lm (lock);
1207 XMLNodeConstIterator niter;
1208 Metrics old_metrics (*metrics);
1210 in_set_state = true;
1214 nlist = node.children();
1216 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1217 XMLNode* child = *niter;
1219 if (child->name() == TempoSection::xml_state_node_name) {
1222 metrics->push_back (new TempoSection (*child));
1225 catch (failed_constructor& err){
1226 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1227 *metrics = old_metrics;
1231 } else if (child->name() == MeterSection::xml_state_node_name) {
1234 metrics->push_back (new MeterSection (*child));
1237 catch (failed_constructor& err) {
1238 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1239 *metrics = old_metrics;
1245 if (niter == nlist.end()) {
1247 MetricSectionSorter cmp;
1248 metrics->sort (cmp);
1249 timestamp_metrics ();
1252 in_set_state = false;
1255 /* This state needs to be saved. This string will never be a part of the
1256 object's history though, because the allow_save flag is false during
1257 session load. This state will eventually be tagged "initial state",
1258 by a call to StateManager::allow_save from Session::set_state.
1260 If this state is not saved, there is no way to reach it through undo actions.
1262 save_state(_("load XML data"));
1264 send_state_changed (Change (0));
1270 TempoMap::dump (std::ostream& o) const
1272 const MeterSection* m;
1273 const TempoSection* t;
1275 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1277 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1278 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1279 << t->movable() << ')' << endl;
1280 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1281 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1282 << " (move? " << m->movable() << ')' << endl;
1288 TempoMap::get_memento () const
1290 return sigc::bind (mem_fun (*(const_cast<TempoMap *> (this)), &StateManager::use_state), _current_state_id);
1294 TempoMap::restore_state (StateManager::State& state)
1296 Glib::Mutex::Lock lm (lock);
1298 TempoMapState* tmstate = dynamic_cast<TempoMapState*> (&state);
1300 /* We can't just set the metrics pointer to the address of the metrics list
1301 stored in the state, cause this would ruin this state for restoring in
1302 the future. If they have the same address, they are the same list.
1303 Thus we need to copy all the elements from the state metrics list to the
1304 current metrics list.
1307 for (Metrics::iterator i = tmstate->metrics->begin(); i != tmstate->metrics->end(); ++i) {
1311 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1312 metrics->push_back (new TempoSection (*ts));
1313 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1314 metrics->push_back (new MeterSection (*ms));
1318 last_bbt_valid = false;
1323 StateManager::State*
1324 TempoMap::state_factory (std::string why) const
1326 TempoMapState* state = new TempoMapState (why);
1328 for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1332 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1333 state->metrics->push_back (new TempoSection (*ts));
1334 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1335 state->metrics->push_back (new MeterSection (*ms));
1343 TempoMap::save_state (std::string why)
1345 if (!in_set_state) {
1346 StateManager::save_state (why);