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.
25 #include <sigc++/bind.h>
27 #include <glibmm/thread.h>
28 #include <pbd/xml++.h>
29 #include <ardour/tempo.h>
30 #include <ardour/utils.h>
36 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 double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
48 return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
51 /***********************************************************************/
54 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
56 return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
59 /***********************************************************************/
61 const string TempoSection::xml_state_node_name = "Tempo";
63 TempoSection::TempoSection (const XMLNode& node)
64 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
66 const XMLProperty *prop;
68 LocaleGuard lg (X_("POSIX"));
70 if ((prop = node.property ("start")) == 0) {
71 error << _("TempoSection XML node has no \"start\" property") << endmsg;
72 throw failed_constructor();
75 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
79 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
80 throw failed_constructor();
85 if ((prop = node.property ("beats-per-minute")) == 0) {
86 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
87 throw failed_constructor();
90 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
91 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
92 throw failed_constructor();
95 if ((prop = node.property ("note-type")) == 0) {
96 /* older session, make note type be quarter by default */
99 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
100 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
101 throw failed_constructor();
105 if ((prop = node.property ("movable")) == 0) {
106 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
107 throw failed_constructor();
110 set_movable (prop->value() == "yes");
114 TempoSection::get_state() const
116 XMLNode *root = new XMLNode (xml_state_node_name);
118 LocaleGuard lg (X_("POSIX"));
120 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
124 root->add_property ("start", buf);
125 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
126 root->add_property ("beats-per-minute", buf);
127 snprintf (buf, sizeof (buf), "%f", _note_type);
128 root->add_property ("note-type", buf);
129 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
130 root->add_property ("movable", buf);
135 /***********************************************************************/
137 const string MeterSection::xml_state_node_name = "Meter";
139 MeterSection::MeterSection (const XMLNode& node)
140 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
142 const XMLProperty *prop;
144 LocaleGuard lg (X_("POSIX"));
146 if ((prop = node.property ("start")) == 0) {
147 error << _("MeterSection XML node has no \"start\" property") << endmsg;
148 throw failed_constructor();
151 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
155 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
156 throw failed_constructor();
161 if ((prop = node.property ("beats-per-bar")) == 0) {
162 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
163 throw failed_constructor();
166 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
167 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
168 throw failed_constructor();
171 if ((prop = node.property ("note-type")) == 0) {
172 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
173 throw failed_constructor();
176 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
177 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
178 throw failed_constructor();
181 if ((prop = node.property ("movable")) == 0) {
182 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
183 throw failed_constructor();
186 set_movable (prop->value() == "yes");
190 MeterSection::get_state() const
192 XMLNode *root = new XMLNode (xml_state_node_name);
194 LocaleGuard lg (X_("POSIX"));
196 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
200 root->add_property ("start", buf);
201 snprintf (buf, sizeof (buf), "%f", _note_type);
202 root->add_property ("note-type", buf);
203 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
204 root->add_property ("beats-per-bar", buf);
205 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
206 root->add_property ("movable", buf);
211 /***********************************************************************/
213 struct MetricSectionSorter {
214 bool operator() (const MetricSection* a, const MetricSection* b) {
215 return a->start() < b->start();
219 TempoMap::TempoMap (nframes_t fr)
221 metrics = new Metrics;
223 last_bbt_valid = false;
230 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
231 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
233 t->set_movable (false);
234 m->set_movable (false);
236 /* note: frame time is correct (zero) for both of these */
238 metrics->push_back (t);
239 metrics->push_back (m);
242 TempoMap::~TempoMap ()
247 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
249 if (when == section.start()) {
253 if (!section.movable()) {
257 Glib::RWLock::WriterLock lm (lock);
258 MetricSectionSorter cmp;
259 BBT_Time corrected (when);
261 if (dynamic_cast<MeterSection*>(§ion) != 0) {
262 if (corrected.beats > 1) {
269 section.set_start (corrected);
271 timestamp_metrics ();
277 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
279 if (move_metric_section (tempo, when) == 0) {
280 StateChanged (Change (0));
285 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
287 if (move_metric_section (meter, when) == 0) {
288 StateChanged (Change (0));
294 TempoMap::remove_tempo (const TempoSection& tempo)
296 bool removed = false;
299 Glib::RWLock::WriterLock lm (lock);
302 for (i = metrics->begin(); i != metrics->end(); ++i) {
303 if (dynamic_cast<TempoSection*> (*i) != 0) {
304 if (tempo.frame() == (*i)->frame()) {
305 if ((*i)->movable()) {
316 StateChanged (Change (0));
321 TempoMap::remove_meter (const MeterSection& tempo)
323 bool removed = false;
326 Glib::RWLock::WriterLock lm (lock);
329 for (i = metrics->begin(); i != metrics->end(); ++i) {
330 if (dynamic_cast<MeterSection*> (*i) != 0) {
331 if (tempo.frame() == (*i)->frame()) {
332 if ((*i)->movable()) {
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(), tempo.note_type()));
382 StateChanged (Change (0));
386 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
388 bool replaced = false;
391 Glib::RWLock::WriterLock lm (lock);
394 for (i = metrics->begin(); i != metrics->end(); ++i) {
397 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
399 *((Tempo *) ts) = replacement;
402 timestamp_metrics ();
409 StateChanged (Change (0));
414 TempoMap::add_meter (const Meter& meter, BBT_Time where)
417 Glib::RWLock::WriterLock lm (lock);
419 /* a new meter always starts a new bar on the first beat. so
420 round the start time appropriately. remember that
421 `where' is based on the existing tempo map, not
422 the result after we insert the new meter.
426 if (where.beats != 1) {
431 /* new meters *always* start on a beat. */
435 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()));
438 StateChanged (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 ();
464 StateChanged (Change (0));
469 TempoMap::first_meter () const
471 const MeterSection *m = 0;
473 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
474 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
479 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
485 TempoMap::first_tempo () const
487 const TempoSection *t = 0;
489 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
490 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
495 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
501 TempoMap::timestamp_metrics ()
509 nframes_t section_frames;
513 meter = &first_meter ();
514 tempo = &first_tempo ();
517 for (i = metrics->begin(); i != metrics->end(); ++i) {
521 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
523 current += section_frames;
527 (*i)->set_frame (current);
529 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
531 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
534 fatal << _("programming error: unhandled MetricSection type") << endmsg;
541 TempoMap::metric_at (nframes_t frame) const
543 Metric m (first_meter(), first_tempo());
547 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
548 at something, because we insert the default tempo and meter during
549 TempoMap construction.
551 now see if we can find better candidates.
554 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
556 if ((*i)->frame() > frame) {
560 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
561 m.set_tempo (*tempo);
562 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
563 m.set_meter (*meter);
566 m.set_frame ((*i)->frame ());
567 m.set_start ((*i)->start ());
574 TempoMap::metric_at (BBT_Time bbt) const
576 Metric m (first_meter(), first_tempo());
580 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
581 at something, because we insert the default tempo and meter during
582 TempoMap construction.
584 now see if we can find better candidates.
587 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
589 BBT_Time section_start ((*i)->start());
591 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
595 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
596 m.set_tempo (*tempo);
597 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
598 m.set_meter (*meter);
601 m.set_frame ((*i)->frame ());
602 m.set_start (section_start);
609 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
612 Glib::RWLock::ReaderLock lm (lock);
613 bbt_time_unlocked (frame, bbt);
618 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
620 bbt_time_with_metric (frame, bbt, metric_at (frame));
624 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
626 nframes_t frame_diff;
628 uint32_t xtra_bars = 0;
629 double xtra_beats = 0;
632 const double beats_per_bar = metric.meter().beats_per_bar();
633 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
634 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
636 /* now compute how far beyond that point we actually are. */
638 frame_diff = frame - metric.frame();
640 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
641 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
642 xtra_beats = (double) frame_diff / beat_frames;
645 /* and set the returned value */
647 /* and correct beat/bar shifts to match the meter.
648 remember: beat and bar counting is 1-based,
650 also the meter may contain a fraction
653 bbt.bars = metric.start().bars + xtra_bars;
655 beats = (double) metric.start().beats + xtra_beats;
657 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
659 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
660 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
661 bbt.beats = (uint32_t) floor(beats);
667 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
670 /* for this to work with fractional measure types, start and end have to "legal" BBT types,
671 that means that the beats and ticks should be inside a bar
675 nframes_t frames = 0;
676 nframes_t start_frame = 0;
677 nframes_t end_frame = 0;
679 Metric m = metric_at(start);
681 uint32_t bar_offset = start.bars - m.start().bars;
683 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
684 + start.ticks/Meter::ticks_per_beat;
687 start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
691 bar_offset = end.bars - m.start().bars;
693 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
694 + end.ticks/Meter::ticks_per_beat;
696 end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
698 frames = end_frame - start_frame;
705 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
707 /*this is used in timestamping the metrics by actually counting the beats */
709 nframes_t frames = 0;
710 uint32_t bar = start.bars;
711 double beat = (double) start.beats;
712 double beats_counted = 0;
713 double beats_per_bar = 0;
714 double beat_frames = 0;
716 beats_per_bar = meter.beats_per_bar();
717 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
721 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
723 if (beat >= beats_per_bar) {
730 if (beat > beats_per_bar) {
731 /* this is a fractional beat at the end of a fractional bar
732 so it should only count for the fraction */
733 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
738 frames = (nframes_t) floor (beats_counted * beat_frames);
745 TempoMap::frame_time (const BBT_Time& bbt) const
747 BBT_Time start ; /* 1|1|0 */
749 return count_frames_between ( start, bbt);
753 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
755 nframes_t frames = 0;
761 Glib::RWLock::ReaderLock lm (lock);
762 frames = bbt_duration_at_unlocked (when, bbt,dir);
769 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
772 nframes_t frames = 0;
774 double beats_per_bar;
777 result.bars = max(1U,when.bars + dir * bbt.bars) ;
781 Metric metric = metric_at(result);
782 beats_per_bar = metric.meter().beats_per_bar();
786 /*reduce things to legal bbt values
787 we have to handle possible fractional=shorter beats at the end of measures
788 and things like 0|11|9000 as a duration in a 4.5/4 measure
789 the musical decision is that the fractional beat is also a beat , although a shorter one
794 result.beats = when.beats + bbt.beats;
795 result.ticks = when.ticks + bbt.ticks;
797 while (result.beats >= (beats_per_bar+1)) {
799 result.beats -= (uint32_t) ceil(beats_per_bar);
800 metric = metric_at(result); // maybe there is a meter change
801 beats_per_bar = metric.meter().beats_per_bar();
804 /*we now counted the beats and landed in the target measure, now deal with ticks
805 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
806 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
809 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
812 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
813 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
814 : Meter::ticks_per_beat );
816 while (result.ticks >= ticks_at_beat) {
818 result.ticks -= ticks_at_beat;
819 if (result.beats >= (beats_per_bar+1)) {
822 metric = metric_at(result); // maybe there is a meter change
823 beats_per_bar = metric.meter().beats_per_bar();
825 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
826 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
827 : Meter::ticks_per_beat);
833 uint32_t b = bbt.beats;
836 while( b > when.beats ) {
838 result.bars = max(1U,result.bars-- ) ;
839 metric = metric_at(result); // maybe there is a meter change
840 beats_per_bar = metric.meter().beats_per_bar();
841 if (b >= ceil(beats_per_bar)) {
843 b -= (uint32_t) ceil(beats_per_bar);
845 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
848 result.beats = when.beats - b;
852 if (bbt.ticks <= when.ticks) {
853 result.ticks = when.ticks - bbt.ticks;
856 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
857 uint32_t t = bbt.ticks - when.ticks;
861 if (result.beats == 1) {
862 result.bars = max(1U,result.bars-- ) ;
863 metric = metric_at(result); // maybe there is a meter change
864 beats_per_bar = metric.meter().beats_per_bar();
865 result.beats = (uint32_t) ceil(beats_per_bar);
866 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
869 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
872 if (t <= ticks_at_beat) {
873 result.ticks = ticks_at_beat - t;
877 } while (t > ticks_at_beat);
885 frames = count_frames_between( result,when);
887 frames = count_frames_between(when,result);
896 TempoMap::round_to_bar (nframes_t fr, int dir)
899 Glib::RWLock::ReaderLock lm (lock);
900 return round_to_type (fr, dir, Bar);
906 TempoMap::round_to_beat (nframes_t fr, int dir)
909 Glib::RWLock::ReaderLock lm (lock);
910 return round_to_type (fr, dir, Beat);
916 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
920 uint32_t ticks_one_half_subdivisions_worth;
921 uint32_t ticks_one_subdivisions_worth;
923 bbt_time(fr, the_beat);
925 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
926 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
928 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
929 uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
930 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
932 the_beat.ticks += difference;
933 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
935 the_beat.ticks += difference;
938 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
941 return frame_time (the_beat);
945 /*****************************
946 XXX just keeping this for reference
948 TempoMap::BBTPointList::iterator i;
949 TempoMap::BBTPointList *more_zoomed_bbt_points;
950 nframes_t frame_one_beats_worth;
952 nframes_t next_pos = 0 ;
954 double frames_one_subdivisions_worth;
955 bool fr_has_changed = false;
959 frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
961 Glib::RWLock::ReaderLock lm (lock);
962 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
963 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
965 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
969 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
970 if ((*i).frame <= fr) {
972 tempo = (*i).tempo->beats_per_minute();
976 next_pos = (*i).frame;
980 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
982 for (n = sub_num; n > 0; n--) {
983 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
984 fr = (nframes_t) round(pos + (n * frames_one_subdivisions_worth));
986 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
988 fr_has_changed = true;
993 if (!fr_has_changed) {
997 delete more_zoomed_bbt_points;
1000 ******************************/
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;
1079 meter = &first_meter ();
1080 tempo = &first_tempo ();
1082 /* find the starting point */
1084 for (i = metrics->begin(); i != metrics->end(); ++i) {
1086 if ((*i)->frame() > lower) {
1090 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1092 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1099 meter -> the Meter for "lower"
1100 tempo -> the Tempo for "lower"
1101 i -> for first new metric after "lower", possibly metrics->end()
1103 Now start generating points.
1106 beats_per_bar = meter->beats_per_bar ();
1107 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1108 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1110 if (meter->frame() > tempo->frame()) {
1111 bar = meter->start().bars;
1112 beat = meter->start().beats;
1113 current = meter->frame();
1115 bar = tempo->start().bars;
1116 beat = tempo->start().beats;
1117 current = tempo->frame();
1120 /* initialize current to point to the bar/beat just prior to the
1121 lower frame bound passed in. assumes that current is initialized
1122 above to be on a beat.
1125 delta_bars = (lower-current) / frames_per_bar;
1126 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1127 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1129 // adjust bars and beats too
1130 bar += (uint32_t) (floor(delta_bars));
1131 beat += (uint32_t) (floor(delta_beats));
1133 points = new BBTPointList;
1137 if (i == metrics->end()) {
1140 limit = (*i)->frame();
1143 limit = min (limit, upper);
1145 while (current < limit) {
1147 /* if we're at the start of a bar, add bar point */
1150 if (current >= lower) {
1151 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1156 /* add some beats if we can */
1158 beat_frame = current;
1160 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1161 if (beat_frame >= lower) {
1162 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1164 beat_frame += beat_frames;
1165 current+= beat_frames;
1170 if (beat > ceil(beats_per_bar) ) {
1172 /* we walked an entire bar. its
1173 important to move `current' forward
1174 by the actual frames_per_bar, not move it to
1175 an integral beat_frame, so that metrics with
1176 non-integral beats-per-bar have
1177 their bar positions set
1178 correctly. consider a metric with
1179 9-1/2 beats-per-bar. the bar we
1180 just filled had 10 beat marks,
1181 but the bar end is 1/2 beat before
1183 And it is also possible that a tempo
1184 change occured in the middle of a bar,
1185 so we subtract the possible extra fraction from the current
1188 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1196 /* if we're done, then we're done */
1198 if (current >= upper) {
1202 /* i is an iterator that refers to the next metric (or none).
1203 if there is a next metric, move to it, and continue.
1206 if (i != metrics->end()) {
1208 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1210 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1212 /* new MeterSection, beat always returns to 1 */
1216 beats_per_bar = meter->beats_per_bar ();
1217 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1218 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1229 TempoMap::tempo_at (nframes_t frame)
1231 Metric m (metric_at (frame));
1237 TempoMap::meter_at (nframes_t frame)
1239 Metric m (metric_at (frame));
1244 TempoMap::get_state ()
1246 Metrics::const_iterator i;
1247 XMLNode *root = new XMLNode ("TempoMap");
1250 Glib::RWLock::ReaderLock lm (lock);
1251 for (i = metrics->begin(); i != metrics->end(); ++i) {
1252 root->add_child_nocopy ((*i)->get_state());
1260 TempoMap::set_state (const XMLNode& node)
1263 Glib::RWLock::WriterLock lm (lock);
1266 XMLNodeConstIterator niter;
1267 Metrics old_metrics (*metrics);
1271 nlist = node.children();
1273 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1274 XMLNode* child = *niter;
1276 if (child->name() == TempoSection::xml_state_node_name) {
1279 metrics->push_back (new TempoSection (*child));
1282 catch (failed_constructor& err){
1283 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1284 *metrics = old_metrics;
1288 } else if (child->name() == MeterSection::xml_state_node_name) {
1291 metrics->push_back (new MeterSection (*child));
1294 catch (failed_constructor& err) {
1295 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1296 *metrics = old_metrics;
1302 if (niter == nlist.end()) {
1304 MetricSectionSorter cmp;
1305 metrics->sort (cmp);
1306 timestamp_metrics ();
1310 StateChanged (Change (0));
1316 TempoMap::dump (std::ostream& o) const
1318 const MeterSection* m;
1319 const TempoSection* t;
1321 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1323 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1324 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1325 << t->movable() << ')' << endl;
1326 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1327 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1328 << " (move? " << m->movable() << ')' << endl;