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 /***********************************************************************/
49 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
51 return ((60.0 * sr * _beats_per_bar) / tempo.beats_per_minute());
54 /***********************************************************************/
56 const string TempoSection::xml_state_node_name = "Tempo";
58 TempoSection::TempoSection (const XMLNode& node)
59 : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
61 const XMLProperty *prop;
63 LocaleGuard lg (X_("POSIX"));
65 if ((prop = node.property ("start")) == 0) {
66 error << _("TempoSection XML node has no \"start\" property") << endmsg;
67 throw failed_constructor();
70 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
74 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
75 throw failed_constructor();
80 if ((prop = node.property ("beats-per-minute")) == 0) {
81 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
82 throw failed_constructor();
85 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
86 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
87 throw failed_constructor();
90 if ((prop = node.property ("movable")) == 0) {
91 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
92 throw failed_constructor();
95 set_movable (prop->value() == "yes");
99 TempoSection::get_state() const
101 XMLNode *root = new XMLNode (xml_state_node_name);
103 LocaleGuard lg (X_("POSIX"));
105 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
109 root->add_property ("start", buf);
110 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
111 root->add_property ("beats-per-minute", buf);
112 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
113 root->add_property ("movable", buf);
118 /***********************************************************************/
120 const string MeterSection::xml_state_node_name = "Meter";
122 MeterSection::MeterSection (const XMLNode& node)
123 : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
125 const XMLProperty *prop;
127 LocaleGuard lg (X_("POSIX"));
129 if ((prop = node.property ("start")) == 0) {
130 error << _("MeterSection XML node has no \"start\" property") << endmsg;
131 throw failed_constructor();
134 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
138 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
139 throw failed_constructor();
144 if ((prop = node.property ("beats-per-bar")) == 0) {
145 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
146 throw failed_constructor();
149 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
150 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
151 throw failed_constructor();
154 if ((prop = node.property ("note-type")) == 0) {
155 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
156 throw failed_constructor();
159 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
160 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
161 throw failed_constructor();
164 if ((prop = node.property ("movable")) == 0) {
165 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
166 throw failed_constructor();
169 set_movable (prop->value() == "yes");
173 MeterSection::get_state() const
175 XMLNode *root = new XMLNode (xml_state_node_name);
177 LocaleGuard lg (X_("POSIX"));
179 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
183 root->add_property ("start", buf);
184 snprintf (buf, sizeof (buf), "%f", _note_type);
185 root->add_property ("note-type", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
187 root->add_property ("beats-per-bar", buf);
188 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
189 root->add_property ("movable", buf);
194 /***********************************************************************/
196 struct MetricSectionSorter {
197 bool operator() (const MetricSection* a, const MetricSection* b) {
198 return a->start() < b->start();
202 TempoMap::TempoMap (nframes_t fr)
204 metrics = new Metrics;
206 last_bbt_valid = false;
213 TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute());
214 MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
216 t->set_movable (false);
217 m->set_movable (false);
219 /* note: frame time is correct (zero) for both of these */
221 metrics->push_back (t);
222 metrics->push_back (m);
225 TempoMap::~TempoMap ()
230 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
232 if (when == section.start()) {
236 if (!section.movable()) {
240 Glib::RWLock::WriterLock lm (lock);
241 MetricSectionSorter cmp;
242 BBT_Time corrected (when);
244 if (dynamic_cast<MeterSection*>(§ion) != 0) {
245 if (corrected.beats > 1) {
252 section.set_start (corrected);
254 timestamp_metrics ();
260 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
262 if (move_metric_section (tempo, when) == 0) {
263 StateChanged (Change (0));
268 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
270 if (move_metric_section (meter, when) == 0) {
271 StateChanged (Change (0));
277 TempoMap::remove_tempo (const TempoSection& tempo)
279 bool removed = false;
282 Glib::RWLock::WriterLock lm (lock);
285 for (i = metrics->begin(); i != metrics->end(); ++i) {
286 if (dynamic_cast<TempoSection*> (*i) != 0) {
287 if (tempo.frame() == (*i)->frame()) {
288 if ((*i)->movable()) {
299 StateChanged (Change (0));
304 TempoMap::remove_meter (const MeterSection& tempo)
306 bool removed = false;
309 Glib::RWLock::WriterLock lm (lock);
312 for (i = metrics->begin(); i != metrics->end(); ++i) {
313 if (dynamic_cast<MeterSection*> (*i) != 0) {
314 if (tempo.frame() == (*i)->frame()) {
315 if ((*i)->movable()) {
326 StateChanged (Change (0));
331 TempoMap::do_insert (MetricSection* section)
335 for (i = metrics->begin(); i != metrics->end(); ++i) {
337 if ((*i)->start() < section->start()) {
341 metrics->insert (i, section);
345 if (i == metrics->end()) {
346 metrics->insert (metrics->end(), section);
349 timestamp_metrics ();
353 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
356 Glib::RWLock::WriterLock lm (lock);
358 /* new tempos always start on a beat */
362 do_insert (new TempoSection (where, tempo.beats_per_minute()));
365 StateChanged (Change (0));
369 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
371 bool replaced = false;
374 Glib::RWLock::WriterLock lm (lock);
377 for (i = metrics->begin(); i != metrics->end(); ++i) {
380 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
382 *((Tempo *) ts) = replacement;
385 timestamp_metrics ();
392 StateChanged (Change (0));
397 TempoMap::add_meter (const Meter& meter, BBT_Time where)
400 Glib::RWLock::WriterLock lm (lock);
402 /* a new meter always starts a new bar on the first beat. so
403 round the start time appropriately. remember that
404 `where' is based on the existing tempo map, not
405 the result after we insert the new meter.
409 if (where.beats != 1) {
414 /* new meters *always* start on a beat. */
418 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()));
421 StateChanged (Change (0));
425 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
427 bool replaced = false;
430 Glib::RWLock::WriterLock lm (lock);
433 for (i = metrics->begin(); i != metrics->end(); ++i) {
435 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
437 *((Meter*) ms) = replacement;
440 timestamp_metrics ();
447 StateChanged (Change (0));
452 TempoMap::first_meter () const
454 const MeterSection *m = 0;
456 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
457 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
462 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
468 TempoMap::first_tempo () const
470 const TempoSection *t = 0;
472 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
473 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
478 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
484 TempoMap::timestamp_metrics ()
492 nframes_t section_frames;
496 meter = &first_meter ();
497 tempo = &first_tempo ();
500 for (i = metrics->begin(); i != metrics->end(); ++i) {
504 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
506 current += section_frames;
510 (*i)->set_frame (current);
512 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
514 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
517 fatal << _("programming error: unhandled MetricSection type") << endmsg;
524 TempoMap::metric_at (nframes_t frame) const
526 Metric m (first_meter(), first_tempo());
530 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
531 at something, because we insert the default tempo and meter during
532 TempoMap construction.
534 now see if we can find better candidates.
537 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
539 if ((*i)->frame() > frame) {
543 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
544 m.set_tempo (*tempo);
545 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
546 m.set_meter (*meter);
549 m.set_frame ((*i)->frame ());
550 m.set_start ((*i)->start ());
557 TempoMap::metric_at (BBT_Time bbt) const
559 Metric m (first_meter(), first_tempo());
563 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
564 at something, because we insert the default tempo and meter during
565 TempoMap construction.
567 now see if we can find better candidates.
570 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
572 BBT_Time section_start ((*i)->start());
574 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
578 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
579 m.set_tempo (*tempo);
580 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
581 m.set_meter (*meter);
584 m.set_frame ((*i)->frame ());
585 m.set_start (section_start);
592 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
595 Glib::RWLock::ReaderLock lm (lock);
596 bbt_time_unlocked (frame, bbt);
601 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
603 bbt_time_with_metric (frame, bbt, metric_at (frame));
607 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
609 nframes_t frame_diff;
611 uint32_t xtra_bars = 0;
612 double xtra_beats = 0;
615 const double beats_per_bar = metric.meter().beats_per_bar();
616 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
617 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
619 /* now compute how far beyond that point we actually are. */
621 frame_diff = frame - metric.frame();
623 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
624 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
625 xtra_beats = (double) frame_diff / beat_frames;
628 /* and set the returned value */
630 /* and correct beat/bar shifts to match the meter.
631 remember: beat and bar counting is 1-based,
633 also the meter may contain a fraction
636 bbt.bars = metric.start().bars + xtra_bars;
638 beats = (double) metric.start().beats + xtra_beats;
640 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
642 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
643 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
644 bbt.beats = (uint32_t) floor(beats);
650 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
653 /* for this to work with fractional measure types, start and end have to "legal" BBT types,
654 that means that the beats and ticks should be inside a bar
658 nframes_t frames = 0;
659 nframes_t start_frame = 0;
660 nframes_t end_frame = 0;
662 Metric m = metric_at(start);
664 uint32_t bar_offset = start.bars - m.start().bars;
666 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
667 + start.ticks/Meter::ticks_per_beat;
670 start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
674 bar_offset = end.bars - m.start().bars;
676 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
677 + end.ticks/Meter::ticks_per_beat;
679 end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
681 frames = end_frame - start_frame;
688 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
690 /*this is used in timestamping the metrics by actually counting the beats */
692 nframes_t frames = 0;
693 uint32_t bar = start.bars;
694 double beat = (double) start.beats;
695 double beats_counted = 0;
696 double beats_per_bar = 0;
697 double beat_frames = 0;
699 beats_per_bar = meter.beats_per_bar();
700 beat_frames = tempo.frames_per_beat (_frame_rate);
704 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
706 if (beat >= beats_per_bar) {
713 if (beat > beats_per_bar) {
714 /* this is a fractional beat at the end of a fractional bar
715 so it should only count for the fraction */
716 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
721 frames = (nframes_t) floor (beats_counted * beat_frames);
728 TempoMap::frame_time (const BBT_Time& bbt) const
730 BBT_Time start ; /* 1|1|0 */
732 return count_frames_between ( start, bbt);
736 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
738 nframes_t frames = 0;
744 Glib::RWLock::ReaderLock lm (lock);
745 frames = bbt_duration_at_unlocked (when, bbt,dir);
752 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
755 nframes_t frames = 0;
757 double beats_per_bar;
760 result.bars = max(1U,when.bars + dir * bbt.bars) ;
764 Metric metric = metric_at(result);
765 beats_per_bar = metric.meter().beats_per_bar();
769 /*reduce things to legal bbt values
770 we have to handle possible fractional=shorter beats at the end of measures
771 and things like 0|11|9000 as a duration in a 4.5/4 measure
772 the musical decision is that the fractional beat is also a beat , although a shorter one
777 result.beats = when.beats + bbt.beats;
778 result.ticks = when.ticks + bbt.ticks;
780 while (result.beats >= (beats_per_bar+1)) {
782 result.beats -= (uint32_t) ceil(beats_per_bar);
783 metric = metric_at(result); // maybe there is a meter change
784 beats_per_bar = metric.meter().beats_per_bar();
787 /*we now counted the beats and landed in the target measure, now deal with ticks
788 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
789 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
792 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
795 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
796 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
797 : Meter::ticks_per_beat );
799 while (result.ticks >= ticks_at_beat) {
801 result.ticks -= ticks_at_beat;
802 if (result.beats >= (beats_per_bar+1)) {
805 metric = metric_at(result); // maybe there is a meter change
806 beats_per_bar = metric.meter().beats_per_bar();
808 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
809 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
810 : Meter::ticks_per_beat);
816 uint32_t b = bbt.beats;
819 while( b > when.beats ) {
821 result.bars = max(1U,result.bars-- ) ;
822 metric = metric_at(result); // maybe there is a meter change
823 beats_per_bar = metric.meter().beats_per_bar();
824 if (b >= ceil(beats_per_bar)) {
826 b -= (uint32_t) ceil(beats_per_bar);
828 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
831 result.beats = when.beats - b;
835 if (bbt.ticks <= when.ticks) {
836 result.ticks = when.ticks - bbt.ticks;
839 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
840 uint32_t t = bbt.ticks - when.ticks;
844 if (result.beats == 1) {
845 result.bars = max(1U,result.bars-- ) ;
846 metric = metric_at(result); // maybe there is a meter change
847 beats_per_bar = metric.meter().beats_per_bar();
848 result.beats = (uint32_t) ceil(beats_per_bar);
849 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
852 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
855 if (t <= ticks_at_beat) {
856 result.ticks = ticks_at_beat - t;
860 } while (t > ticks_at_beat);
868 frames = count_frames_between( result,when);
870 frames = count_frames_between(when,result);
879 TempoMap::round_to_bar (nframes_t fr, int dir)
882 Glib::RWLock::ReaderLock lm (lock);
883 return round_to_type (fr, dir, Bar);
889 TempoMap::round_to_beat (nframes_t fr, int dir)
892 Glib::RWLock::ReaderLock lm (lock);
893 return round_to_type (fr, dir, Beat);
899 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
903 uint32_t ticks_one_half_subdivisions_worth;
904 uint32_t ticks_one_subdivisions_worth;
906 bbt_time(fr, the_beat);
908 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
909 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
911 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
912 uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
913 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
915 the_beat.ticks += difference;
916 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
918 the_beat.ticks += difference;
921 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
924 return frame_time (the_beat);
928 /*****************************
929 XXX just keeping this for reference
931 TempoMap::BBTPointList::iterator i;
932 TempoMap::BBTPointList *more_zoomed_bbt_points;
933 nframes_t frame_one_beats_worth;
935 nframes_t next_pos = 0 ;
937 double frames_one_subdivisions_worth;
938 bool fr_has_changed = false;
942 frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
944 Glib::RWLock::ReaderLock lm (lock);
945 more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
946 fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
948 if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
952 for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
953 if ((*i).frame <= fr) {
955 tempo = (*i).tempo->beats_per_minute();
959 next_pos = (*i).frame;
963 frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
965 for (n = sub_num; n > 0; n--) {
966 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
967 fr = (nframes_t) round(pos + (n * frames_one_subdivisions_worth));
969 fr = next_pos; //take care of fractional beats that don't match the subdivision asked
971 fr_has_changed = true;
976 if (!fr_has_changed) {
980 delete more_zoomed_bbt_points;
983 ******************************/
990 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
992 Metric metric = metric_at (frame);
995 bbt_time_with_metric (frame, bbt, metric);
1002 } else if (dir > 0) {
1003 if (bbt.beats > 0) {
1007 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1019 } else if (dir > 0) {
1020 if (bbt.ticks > 0) {
1024 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1028 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1037 return metric.frame() + count_frames_between (metric.start(), bbt);
1040 TempoMap::BBTPointList *
1041 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1044 Metrics::const_iterator i;
1045 BBTPointList *points;
1047 const MeterSection* meter;
1048 const MeterSection* m;
1049 const TempoSection* tempo;
1050 const TempoSection* t;
1053 double beats_per_bar;
1056 double frames_per_bar;
1062 meter = &first_meter ();
1063 tempo = &first_tempo ();
1065 /* find the starting point */
1067 for (i = metrics->begin(); i != metrics->end(); ++i) {
1069 if ((*i)->frame() > lower) {
1073 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1075 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1082 meter -> the Meter for "lower"
1083 tempo -> the Tempo for "lower"
1084 i -> for first new metric after "lower", possibly metrics->end()
1086 Now start generating points.
1089 beats_per_bar = meter->beats_per_bar ();
1090 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1091 beat_frames = tempo->frames_per_beat (_frame_rate);
1093 if (meter->frame() > tempo->frame()) {
1094 bar = meter->start().bars;
1095 beat = meter->start().beats;
1096 current = meter->frame();
1098 bar = tempo->start().bars;
1099 beat = tempo->start().beats;
1100 current = tempo->frame();
1103 /* initialize current to point to the bar/beat just prior to the
1104 lower frame bound passed in. assumes that current is initialized
1105 above to be on a beat.
1108 delta_bars = (lower-current) / frames_per_bar;
1109 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1110 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1112 // adjust bars and beats too
1113 bar += (uint32_t) (floor(delta_bars));
1114 beat += (uint32_t) (floor(delta_beats));
1116 points = new BBTPointList;
1120 if (i == metrics->end()) {
1123 limit = (*i)->frame();
1126 limit = min (limit, upper);
1128 while (current < limit) {
1130 /* if we're at the start of a bar, add bar point */
1133 if (current >= lower) {
1134 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1139 /* add some beats if we can */
1141 beat_frame = current;
1143 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1144 if (beat_frame >= lower) {
1145 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1147 beat_frame += beat_frames;
1148 current+= beat_frames;
1153 if (beat > ceil(beats_per_bar) ) {
1155 /* we walked an entire bar. its
1156 important to move `current' forward
1157 by the actual frames_per_bar, not move it to
1158 an integral beat_frame, so that metrics with
1159 non-integral beats-per-bar have
1160 their bar positions set
1161 correctly. consider a metric with
1162 9-1/2 beats-per-bar. the bar we
1163 just filled had 10 beat marks,
1164 but the bar end is 1/2 beat before
1166 And it is also possible that a tempo
1167 change occured in the middle of a bar,
1168 so we subtract the possible extra fraction from the current
1171 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1179 /* if we're done, then we're done */
1181 if (current >= upper) {
1185 /* i is an iterator that refers to the next metric (or none).
1186 if there is a next metric, move to it, and continue.
1189 if (i != metrics->end()) {
1191 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1193 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1195 /* new MeterSection, beat always returns to 1 */
1199 beats_per_bar = meter->beats_per_bar ();
1200 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1201 beat_frames = tempo->frames_per_beat (_frame_rate);
1212 TempoMap::tempo_at (nframes_t frame)
1214 Metric m (metric_at (frame));
1220 TempoMap::meter_at (nframes_t frame)
1222 Metric m (metric_at (frame));
1227 TempoMap::get_state ()
1229 Metrics::const_iterator i;
1230 XMLNode *root = new XMLNode ("TempoMap");
1233 Glib::RWLock::ReaderLock lm (lock);
1234 for (i = metrics->begin(); i != metrics->end(); ++i) {
1235 root->add_child_nocopy ((*i)->get_state());
1243 TempoMap::set_state (const XMLNode& node)
1246 Glib::RWLock::WriterLock lm (lock);
1249 XMLNodeConstIterator niter;
1250 Metrics old_metrics (*metrics);
1254 nlist = node.children();
1256 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1257 XMLNode* child = *niter;
1259 if (child->name() == TempoSection::xml_state_node_name) {
1262 metrics->push_back (new TempoSection (*child));
1265 catch (failed_constructor& err){
1266 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1267 *metrics = old_metrics;
1271 } else if (child->name() == MeterSection::xml_state_node_name) {
1274 metrics->push_back (new MeterSection (*child));
1277 catch (failed_constructor& err) {
1278 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1279 *metrics = old_metrics;
1285 if (niter == nlist.end()) {
1287 MetricSectionSorter cmp;
1288 metrics->sort (cmp);
1289 timestamp_metrics ();
1293 StateChanged (Change (0));
1299 TempoMap::dump (std::ostream& o) const
1301 const MeterSection* m;
1302 const TempoSection* t;
1304 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1306 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1307 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1308 << t->movable() << ')' << endl;
1309 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1310 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1311 << " (move? " << m->movable() << ')' << endl;