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 (true);
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, bool with_bbt)
352 for (i = metrics->begin(); i != metrics->end(); ++i) {
355 if ((*i)->start() < section->start()) {
359 if ((*i)->frame() < section->frame()) {
364 metrics->insert (i, section);
368 if (i == metrics->end()) {
369 metrics->insert (metrics->end(), section);
372 timestamp_metrics (with_bbt);
376 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
379 Glib::RWLock::WriterLock lm (lock);
381 /* new tempos always start on a beat */
385 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
388 StateChanged (Change (0));
392 TempoMap::add_tempo (const Tempo& tempo, nframes_t where)
395 Glib::RWLock::WriterLock lm (lock);
396 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
399 StateChanged (Change (0));
403 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
405 bool replaced = false;
408 Glib::RWLock::WriterLock lm (lock);
411 for (i = metrics->begin(); i != metrics->end(); ++i) {
414 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
416 *((Tempo *) ts) = replacement;
419 timestamp_metrics (true);
426 StateChanged (Change (0));
431 TempoMap::add_meter (const Meter& meter, BBT_Time where)
434 Glib::RWLock::WriterLock lm (lock);
436 /* a new meter always starts a new bar on the first beat. so
437 round the start time appropriately. remember that
438 `where' is based on the existing tempo map, not
439 the result after we insert the new meter.
443 if (where.beats != 1) {
448 /* new meters *always* start on a beat. */
452 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
455 StateChanged (Change (0));
459 TempoMap::add_meter (const Meter& meter, nframes_t where)
462 Glib::RWLock::WriterLock lm (lock);
463 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
466 StateChanged (Change (0));
470 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
472 bool replaced = false;
475 Glib::RWLock::WriterLock lm (lock);
478 for (i = metrics->begin(); i != metrics->end(); ++i) {
480 if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
482 *((Meter*) ms) = replacement;
485 timestamp_metrics (true);
492 StateChanged (Change (0));
497 TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type)
499 Tempo newtempo (beats_per_minute, note_type);
505 /* find the TempoSection immediately preceding "where"
508 for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
510 if ((*i)->frame() > where) {
516 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
526 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
535 *((Tempo*)prev) = newtempo;
536 StateChanged (Change (0));
540 TempoMap::first_meter () const
542 const MeterSection *m = 0;
544 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
545 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
550 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
556 TempoMap::first_tempo () const
558 const TempoSection *t = 0;
560 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
561 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
566 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
572 TempoMap::timestamp_metrics (bool use_bbt)
580 meter = &first_meter ();
581 tempo = &first_tempo ();
585 nframes_t current = 0;
586 nframes_t section_frames;
590 for (i = metrics->begin(); i != metrics->end(); ++i) {
594 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
596 current += section_frames;
600 (*i)->set_frame (current);
602 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
604 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
607 fatal << _("programming error: unhandled MetricSection type") << endmsg;
616 for (i = metrics->begin(); i != metrics->end(); ++i) {
620 bbt_time_with_metric ((*i)->frame(), bbt, Metric (*meter, *tempo));
622 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
627 if (bbt.beats != 1 || bbt.ticks != 0) {
634 // cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
636 (*i)->set_start (bbt);
638 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
640 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
643 fatal << _("programming error: unhandled MetricSection type") << endmsg;
653 TempoMap::metric_at (nframes_t frame) const
655 Metric m (first_meter(), first_tempo());
659 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
660 at something, because we insert the default tempo and meter during
661 TempoMap construction.
663 now see if we can find better candidates.
666 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
668 if ((*i)->frame() > frame) {
672 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
673 m.set_tempo (*tempo);
674 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
675 m.set_meter (*meter);
678 m.set_frame ((*i)->frame ());
679 m.set_start ((*i)->start ());
686 TempoMap::metric_at (BBT_Time bbt) const
688 Metric m (first_meter(), first_tempo());
692 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
693 at something, because we insert the default tempo and meter during
694 TempoMap construction.
696 now see if we can find better candidates.
699 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
701 BBT_Time section_start ((*i)->start());
703 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
707 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
708 m.set_tempo (*tempo);
709 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
710 m.set_meter (*meter);
713 m.set_frame ((*i)->frame ());
714 m.set_start (section_start);
721 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
724 Glib::RWLock::ReaderLock lm (lock);
725 bbt_time_unlocked (frame, bbt);
730 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
732 bbt_time_with_metric (frame, bbt, metric_at (frame));
736 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
738 nframes_t frame_diff;
740 uint32_t xtra_bars = 0;
741 double xtra_beats = 0;
744 const double beats_per_bar = metric.meter().beats_per_bar();
745 const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
746 const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
748 /* now compute how far beyond that point we actually are. */
750 frame_diff = frame - metric.frame();
752 xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
753 frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
754 xtra_beats = (double) frame_diff / beat_frames;
757 /* and set the returned value */
759 /* and correct beat/bar shifts to match the meter.
760 remember: beat and bar counting is 1-based,
762 also the meter may contain a fraction
765 bbt.bars = metric.start().bars + xtra_bars;
767 beats = (double) metric.start().beats + xtra_beats;
769 bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
771 beats = fmod(beats - 1, beats_per_bar )+ 1.0;
772 bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
773 bbt.beats = (uint32_t) floor(beats);
779 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
781 /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
782 that means that the beats and ticks should be inside a bar
785 nframes_t frames = 0;
786 nframes_t start_frame = 0;
787 nframes_t end_frame = 0;
789 Metric m = metric_at (start);
791 uint32_t bar_offset = start.bars - m.start().bars;
793 double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
794 + start.ticks/Meter::ticks_per_beat;
797 start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
801 bar_offset = end.bars - m.start().bars;
803 beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
804 + end.ticks/Meter::ticks_per_beat;
806 end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
808 frames = end_frame - start_frame;
815 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
817 /*this is used in timestamping the metrics by actually counting the beats */
819 nframes_t frames = 0;
820 uint32_t bar = start.bars;
821 double beat = (double) start.beats;
822 double beats_counted = 0;
823 double beats_per_bar = 0;
824 double beat_frames = 0;
826 beats_per_bar = meter.beats_per_bar();
827 beat_frames = tempo.frames_per_beat (_frame_rate,meter);
831 while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
833 if (beat >= beats_per_bar) {
840 if (beat > beats_per_bar) {
841 /* this is a fractional beat at the end of a fractional bar
842 so it should only count for the fraction */
843 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
848 frames = (nframes_t) floor (beats_counted * beat_frames);
855 TempoMap::frame_time (const BBT_Time& bbt) const
857 BBT_Time start ; /* 1|1|0 */
859 return count_frames_between ( start, bbt);
863 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
865 nframes_t frames = 0;
871 Glib::RWLock::ReaderLock lm (lock);
872 frames = bbt_duration_at_unlocked (when, bbt,dir);
879 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
882 nframes_t frames = 0;
884 double beats_per_bar;
887 result.bars = max(1U,when.bars + dir * bbt.bars) ;
891 Metric metric = metric_at(result);
892 beats_per_bar = metric.meter().beats_per_bar();
896 /*reduce things to legal bbt values
897 we have to handle possible fractional=shorter beats at the end of measures
898 and things like 0|11|9000 as a duration in a 4.5/4 measure
899 the musical decision is that the fractional beat is also a beat , although a shorter one
904 result.beats = when.beats + bbt.beats;
905 result.ticks = when.ticks + bbt.ticks;
907 while (result.beats >= (beats_per_bar+1)) {
909 result.beats -= (uint32_t) ceil(beats_per_bar);
910 metric = metric_at(result); // maybe there is a meter change
911 beats_per_bar = metric.meter().beats_per_bar();
914 /*we now counted the beats and landed in the target measure, now deal with ticks
915 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
916 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
919 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
922 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
923 (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
924 : Meter::ticks_per_beat );
926 while (result.ticks >= ticks_at_beat) {
928 result.ticks -= ticks_at_beat;
929 if (result.beats >= (beats_per_bar+1)) {
932 metric = metric_at(result); // maybe there is a meter change
933 beats_per_bar = metric.meter().beats_per_bar();
935 ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
936 (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
937 : Meter::ticks_per_beat);
943 uint32_t b = bbt.beats;
946 while( b > when.beats ) {
948 result.bars = max(1U,result.bars-- ) ;
949 metric = metric_at(result); // maybe there is a meter change
950 beats_per_bar = metric.meter().beats_per_bar();
951 if (b >= ceil(beats_per_bar)) {
953 b -= (uint32_t) ceil(beats_per_bar);
955 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
958 result.beats = when.beats - b;
962 if (bbt.ticks <= when.ticks) {
963 result.ticks = when.ticks - bbt.ticks;
966 uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
967 uint32_t t = bbt.ticks - when.ticks;
971 if (result.beats == 1) {
972 result.bars = max(1U,result.bars-- ) ;
973 metric = metric_at(result); // maybe there is a meter change
974 beats_per_bar = metric.meter().beats_per_bar();
975 result.beats = (uint32_t) ceil(beats_per_bar);
976 ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
979 ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
982 if (t <= ticks_at_beat) {
983 result.ticks = ticks_at_beat - t;
987 } while (t > ticks_at_beat);
995 frames = count_frames_between( result,when);
997 frames = count_frames_between(when,result);
1006 TempoMap::round_to_bar (nframes_t fr, int dir)
1009 Glib::RWLock::ReaderLock lm (lock);
1010 return round_to_type (fr, dir, Bar);
1016 TempoMap::round_to_beat (nframes_t fr, int dir)
1019 Glib::RWLock::ReaderLock lm (lock);
1020 return round_to_type (fr, dir, Beat);
1026 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
1030 uint32_t ticks_one_half_subdivisions_worth;
1031 uint32_t ticks_one_subdivisions_worth;
1033 bbt_time(fr, the_beat);
1035 ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1036 ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1038 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1039 uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1040 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1042 the_beat.ticks += difference;
1043 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1045 the_beat.ticks += difference;
1048 the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1051 return frame_time (the_beat);
1056 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1058 Metric metric = metric_at (frame);
1061 bbt_time_with_metric (frame, bbt, metric);
1068 } else if (dir > 0) {
1069 if (bbt.beats > 0) {
1073 if (bbt.beats > metric.meter().beats_per_bar()/2) {
1085 } else if (dir > 0) {
1086 if (bbt.ticks > 0) {
1090 if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1094 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1104 cerr << "for " << frame << " round to " << bbt << " using "
1109 return metric.frame() + count_frames_between (metric.start(), bbt);
1112 TempoMap::BBTPointList *
1113 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1116 Metrics::const_iterator i;
1117 BBTPointList *points;
1119 const MeterSection* meter;
1120 const MeterSection* m;
1121 const TempoSection* tempo;
1122 const TempoSection* t;
1125 double beats_per_bar;
1128 double frames_per_bar;
1134 meter = &first_meter ();
1135 tempo = &first_tempo ();
1137 /* find the starting point */
1139 for (i = metrics->begin(); i != metrics->end(); ++i) {
1141 if ((*i)->frame() > lower) {
1145 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1147 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1154 meter -> the Meter for "lower"
1155 tempo -> the Tempo for "lower"
1156 i -> for first new metric after "lower", possibly metrics->end()
1158 Now start generating points.
1161 beats_per_bar = meter->beats_per_bar ();
1162 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1163 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1165 if (meter->frame() > tempo->frame()) {
1166 bar = meter->start().bars;
1167 beat = meter->start().beats;
1168 current = meter->frame();
1170 bar = tempo->start().bars;
1171 beat = tempo->start().beats;
1172 current = tempo->frame();
1175 /* initialize current to point to the bar/beat just prior to the
1176 lower frame bound passed in. assumes that current is initialized
1177 above to be on a beat.
1180 delta_bars = (lower-current) / frames_per_bar;
1181 delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1182 current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
1184 // adjust bars and beats too
1185 bar += (uint32_t) (floor(delta_bars));
1186 beat += (uint32_t) (floor(delta_beats));
1188 points = new BBTPointList;
1192 if (i == metrics->end()) {
1195 limit = (*i)->frame();
1198 limit = min (limit, upper);
1200 while (current < limit) {
1202 /* if we're at the start of a bar, add bar point */
1205 if (current >= lower) {
1206 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1207 points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1212 /* add some beats if we can */
1214 beat_frame = current;
1216 while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1217 if (beat_frame >= lower) {
1218 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1219 points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1221 beat_frame += beat_frames;
1222 current+= beat_frames;
1227 if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1229 /* we walked an entire bar. its
1230 important to move `current' forward
1231 by the actual frames_per_bar, not move it to
1232 an integral beat_frame, so that metrics with
1233 non-integral beats-per-bar have
1234 their bar positions set
1235 correctly. consider a metric with
1236 9-1/2 beats-per-bar. the bar we
1237 just filled had 10 beat marks,
1238 but the bar end is 1/2 beat before
1240 And it is also possible that a tempo
1241 change occured in the middle of a bar,
1242 so we subtract the possible extra fraction from the current
1245 if (beat > ceil (beats_per_bar)) {
1246 /* next bar goes where the numbers suggest */
1247 current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1249 /* next bar goes where the next metric is */
1258 /* if we're done, then we're done */
1260 if (current >= upper) {
1264 /* i is an iterator that refers to the next metric (or none).
1265 if there is a next metric, move to it, and continue.
1268 if (i != metrics->end()) {
1270 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1272 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1274 /* new MeterSection, beat always returns to 1 */
1278 beats_per_bar = meter->beats_per_bar ();
1279 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1280 beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1291 TempoMap::tempo_section_at (nframes_t frame)
1293 Glib::RWLock::ReaderLock lm (lock);
1294 Metrics::iterator i;
1295 TempoSection* prev = 0;
1297 for (i = metrics->begin(); i != metrics->end(); ++i) {
1300 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1302 if ((*i)->frame() > frame) {
1318 TempoMap::tempo_at (nframes_t frame)
1320 Metric m (metric_at (frame));
1326 TempoMap::meter_at (nframes_t frame)
1328 Metric m (metric_at (frame));
1333 TempoMap::get_state ()
1335 Metrics::const_iterator i;
1336 XMLNode *root = new XMLNode ("TempoMap");
1339 Glib::RWLock::ReaderLock lm (lock);
1340 for (i = metrics->begin(); i != metrics->end(); ++i) {
1341 root->add_child_nocopy ((*i)->get_state());
1349 TempoMap::set_state (const XMLNode& node)
1352 Glib::RWLock::WriterLock lm (lock);
1355 XMLNodeConstIterator niter;
1356 Metrics old_metrics (*metrics);
1360 nlist = node.children();
1362 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1363 XMLNode* child = *niter;
1365 if (child->name() == TempoSection::xml_state_node_name) {
1368 metrics->push_back (new TempoSection (*child));
1371 catch (failed_constructor& err){
1372 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1373 *metrics = old_metrics;
1377 } else if (child->name() == MeterSection::xml_state_node_name) {
1380 metrics->push_back (new MeterSection (*child));
1383 catch (failed_constructor& err) {
1384 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1385 *metrics = old_metrics;
1391 if (niter == nlist.end()) {
1393 MetricSectionSorter cmp;
1394 metrics->sort (cmp);
1395 timestamp_metrics (true);
1399 StateChanged (Change (0));
1405 TempoMap::dump (std::ostream& o) const
1407 const MeterSection* m;
1408 const TempoSection* t;
1410 for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1412 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1413 o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1414 << t->movable() << ')' << endl;
1415 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1416 o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1417 << " (move? " << m->movable() << ')' << endl;