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() || !section
.movable()) {
253 Glib::RWLock::WriterLock
lm (lock
);
254 MetricSectionSorter cmp
;
256 if (when
.beats
!= 1) {
258 /* position by audio frame, then recompute BBT timestamps from the audio ones */
260 nframes_t frame
= frame_time (when
);
261 // cerr << "nominal frame time = " << frame << endl;
263 nframes_t prev_frame
= round_to_type (frame
, -1, Beat
);
264 nframes_t next_frame
= round_to_type (frame
, 1, Beat
);
266 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
268 /* use the closest beat */
270 if ((frame
- prev_frame
) < (next_frame
- frame
)) {
276 // cerr << "actual frame time = " << frame << endl;
277 section
.set_frame (frame
);
278 // cerr << "frame time = " << section.frame() << endl;
279 timestamp_metrics (false);
280 // cerr << "new BBT time = " << section.start() << endl;
285 /* positioned at bar start already, so just put it there */
287 section
.set_start (when
);
289 timestamp_metrics (true);
297 TempoMap::move_tempo (TempoSection
& tempo
, const BBT_Time
& when
)
299 if (move_metric_section (tempo
, when
) == 0) {
300 StateChanged (Change (0));
305 TempoMap::move_meter (MeterSection
& meter
, const BBT_Time
& when
)
307 if (move_metric_section (meter
, when
) == 0) {
308 StateChanged (Change (0));
313 TempoMap::remove_tempo (const TempoSection
& tempo
)
315 bool removed
= false;
318 Glib::RWLock::WriterLock
lm (lock
);
321 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
322 if (dynamic_cast<TempoSection
*> (*i
) != 0) {
323 if (tempo
.frame() == (*i
)->frame()) {
324 if ((*i
)->movable()) {
335 StateChanged (Change (0));
340 TempoMap::remove_meter (const MeterSection
& tempo
)
342 bool removed
= false;
345 Glib::RWLock::WriterLock
lm (lock
);
348 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
349 if (dynamic_cast<MeterSection
*> (*i
) != 0) {
350 if (tempo
.frame() == (*i
)->frame()) {
351 if ((*i
)->movable()) {
362 StateChanged (Change (0));
367 TempoMap::do_insert (MetricSection
* section
, bool with_bbt
)
371 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
374 if ((*i
)->start() < section
->start()) {
378 if ((*i
)->frame() < section
->frame()) {
383 metrics
->insert (i
, section
);
387 if (i
== metrics
->end()) {
388 metrics
->insert (metrics
->end(), section
);
391 timestamp_metrics (with_bbt
);
395 TempoMap::add_tempo (const Tempo
& tempo
, BBT_Time where
)
398 Glib::RWLock::WriterLock
lm (lock
);
400 /* new tempos always start on a beat */
404 do_insert (new TempoSection (where
, tempo
.beats_per_minute(), tempo
.note_type()), true);
407 StateChanged (Change (0));
411 TempoMap::add_tempo (const Tempo
& tempo
, nframes_t where
)
414 Glib::RWLock::WriterLock
lm (lock
);
415 do_insert (new TempoSection (where
, tempo
.beats_per_minute(), tempo
.note_type()), false);
418 StateChanged (Change (0));
422 TempoMap::replace_tempo (TempoSection
& existing
, const Tempo
& replacement
)
424 bool replaced
= false;
427 Glib::RWLock::WriterLock
lm (lock
);
430 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
433 if ((ts
= dynamic_cast<TempoSection
*>(*i
)) != 0 && ts
== &existing
) {
435 *((Tempo
*) ts
) = replacement
;
438 timestamp_metrics (true);
446 StateChanged (Change (0));
451 TempoMap::add_meter (const Meter
& meter
, BBT_Time where
)
454 Glib::RWLock::WriterLock
lm (lock
);
456 /* a new meter always starts a new bar on the first beat. so
457 round the start time appropriately. remember that
458 `where' is based on the existing tempo map, not
459 the result after we insert the new meter.
463 if (where
.beats
!= 1) {
468 /* new meters *always* start on a beat. */
472 do_insert (new MeterSection (where
, meter
.beats_per_bar(), meter
.note_divisor()), true);
475 StateChanged (Change (0));
479 TempoMap::add_meter (const Meter
& meter
, nframes_t where
)
482 Glib::RWLock::WriterLock
lm (lock
);
483 do_insert (new MeterSection (where
, meter
.beats_per_bar(), meter
.note_divisor()), false);
486 StateChanged (Change (0));
490 TempoMap::replace_meter (MeterSection
& existing
, const Meter
& replacement
)
492 bool replaced
= false;
495 Glib::RWLock::WriterLock
lm (lock
);
498 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
500 if ((ms
= dynamic_cast<MeterSection
*>(*i
)) != 0 && ms
== &existing
) {
502 *((Meter
*) ms
) = replacement
;
505 timestamp_metrics (true);
512 StateChanged (Change (0));
517 TempoMap::change_initial_tempo (double beats_per_minute
, double note_type
)
519 Tempo
newtempo (beats_per_minute
, note_type
);
522 for (Metrics::iterator i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
523 if ((t
= dynamic_cast<TempoSection
*> (*i
)) != 0) {
524 *((Tempo
*) t
) = newtempo
;
525 StateChanged (Change (0));
532 TempoMap::change_existing_tempo_at (nframes_t where
, double beats_per_minute
, double note_type
)
534 Tempo
newtempo (beats_per_minute
, note_type
);
540 /* find the TempoSection immediately preceding "where"
543 for (first
= 0, i
= metrics
->begin(), prev
= 0; i
!= metrics
->end(); ++i
) {
545 if ((*i
)->frame() > where
) {
551 if ((t
= dynamic_cast<TempoSection
*>(*i
)) != 0) {
561 error
<< string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where
) << endmsg
;
570 *((Tempo
*)prev
) = newtempo
;
571 StateChanged (Change (0));
575 TempoMap::first_meter () const
577 const MeterSection
*m
= 0;
579 for (Metrics::const_iterator i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
580 if ((m
= dynamic_cast<const MeterSection
*> (*i
)) != 0) {
585 fatal
<< _("programming error: no tempo section in tempo map!") << endmsg
;
591 TempoMap::first_tempo () const
593 const TempoSection
*t
= 0;
595 for (Metrics::const_iterator i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
596 if ((t
= dynamic_cast<const TempoSection
*> (*i
)) != 0) {
601 fatal
<< _("programming error: no tempo section in tempo map!") << endmsg
;
607 TempoMap::timestamp_metrics (bool use_bbt
)
615 meter
= &first_meter ();
616 tempo
= &first_tempo ();
620 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
622 nframes_t current
= 0;
623 nframes_t section_frames
;
627 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
631 section_frames
= count_frames_between_metrics (*meter
, *tempo
, start
, end
);
633 current
+= section_frames
;
637 (*i
)->set_frame (current
);
639 if ((t
= dynamic_cast<TempoSection
*>(*i
)) != 0) {
641 } else if ((m
= dynamic_cast<MeterSection
*>(*i
)) != 0) {
644 fatal
<< _("programming error: unhandled MetricSection type") << endmsg
;
651 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
654 MetricSection
* prev
= 0;
656 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
659 Metric
metric (*meter
, *tempo
);
662 metric
.set_start (prev
->start());
664 // metric will be at frames=0 bbt=1|1|0 by default
665 // which is correct for our purpose
668 bbt_time_with_metric ((*i
)->frame(), bbt
, metric
);
670 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
677 if (bbt
.ticks
> Meter::ticks_per_beat
/2) {
678 /* round up to next beat */
684 if (bbt
.beats
!= 1) {
685 /* round up to next bar */
691 //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
693 (*i
)->set_start (bbt
);
695 if ((t
= dynamic_cast<TempoSection
*>(*i
)) != 0) {
697 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
698 } else if ((m
= dynamic_cast<MeterSection
*>(*i
)) != 0) {
700 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
702 fatal
<< _("programming error: unhandled MetricSection type") << endmsg
;
711 // cerr << "###############################################\n\n\n" << endl;
716 TempoMap::metric_at (nframes_t frame
) const
718 Metric
m (first_meter(), first_tempo());
722 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
723 at something, because we insert the default tempo and meter during
724 TempoMap construction.
726 now see if we can find better candidates.
729 for (Metrics::const_iterator i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
731 if ((*i
)->frame() > frame
) {
735 if ((tempo
= dynamic_cast<const TempoSection
*>(*i
)) != 0) {
736 m
.set_tempo (*tempo
);
737 } else if ((meter
= dynamic_cast<const MeterSection
*>(*i
)) != 0) {
738 m
.set_meter (*meter
);
741 m
.set_frame ((*i
)->frame ());
742 m
.set_start ((*i
)->start ());
749 TempoMap::metric_at (BBT_Time bbt
) const
751 Metric
m (first_meter(), first_tempo());
755 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
756 at something, because we insert the default tempo and meter during
757 TempoMap construction.
759 now see if we can find better candidates.
762 for (Metrics::const_iterator i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
764 BBT_Time
section_start ((*i
)->start());
766 if (section_start
.bars
> bbt
.bars
|| (section_start
.bars
== bbt
.bars
&& section_start
.beats
> bbt
.beats
)) {
770 if ((tempo
= dynamic_cast<const TempoSection
*>(*i
)) != 0) {
771 m
.set_tempo (*tempo
);
772 } else if ((meter
= dynamic_cast<const MeterSection
*>(*i
)) != 0) {
773 m
.set_meter (*meter
);
776 m
.set_frame ((*i
)->frame ());
777 m
.set_start (section_start
);
784 TempoMap::bbt_time (nframes_t frame
, BBT_Time
& bbt
) const
787 Glib::RWLock::ReaderLock
lm (lock
);
788 bbt_time_unlocked (frame
, bbt
);
793 TempoMap::bbt_time_unlocked (nframes_t frame
, BBT_Time
& bbt
) const
795 bbt_time_with_metric (frame
, bbt
, metric_at (frame
));
799 TempoMap::bbt_time_with_metric (nframes_t frame
, BBT_Time
& bbt
, const Metric
& metric
) const
801 nframes_t frame_diff
;
803 uint32_t xtra_bars
= 0;
804 double xtra_beats
= 0;
807 // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
809 const double beats_per_bar
= metric
.meter().beats_per_bar();
810 const double frames_per_bar
= metric
.meter().frames_per_bar (metric
.tempo(), _frame_rate
);
811 const double beat_frames
= metric
.tempo().frames_per_beat (_frame_rate
, metric
.meter());
813 /* now compute how far beyond that point we actually are. */
815 frame_diff
= frame
- metric
.frame();
817 // cerr << "----\tdelta = " << frame_diff << endl;
819 xtra_bars
= (uint32_t) floor (frame_diff
/ frames_per_bar
);
820 frame_diff
-= (uint32_t) floor (xtra_bars
* frames_per_bar
);
821 xtra_beats
= (double) frame_diff
/ beat_frames
;
823 // cerr << "---\tmeaning " << xtra_bars << " xtra bars and " << xtra_beats << " xtra beats\n";
825 /* and set the returned value */
827 /* and correct beat/bar shifts to match the meter.
828 remember: beat and bar counting is 1-based,
830 also the meter may contain a fraction
833 bbt
.bars
= metric
.start().bars
+ xtra_bars
;
835 beats
= (double) metric
.start().beats
+ xtra_beats
;
837 bbt
.bars
+= (uint32_t) floor(beats
/ (beats_per_bar
+1) );
839 beats
= fmod(beats
- 1, beats_per_bar
)+ 1.0;
840 bbt
.ticks
= (uint32_t)( round((beats
- floor(beats
)) *(double) Meter::ticks_per_beat
));
841 bbt
.beats
= (uint32_t) floor(beats
);
843 // cerr << "-----\t RETURN " << bbt << endl;
847 TempoMap::count_frames_between ( const BBT_Time
& start
, const BBT_Time
& end
) const
849 /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
850 that means that the beats and ticks should be inside a bar
853 nframes_t frames
= 0;
854 nframes_t start_frame
= 0;
855 nframes_t end_frame
= 0;
857 Metric m
= metric_at (start
);
859 uint32_t bar_offset
= start
.bars
- m
.start().bars
;
861 double beat_offset
= bar_offset
*m
.meter().beats_per_bar() - (m
.start().beats
-1) + (start
.beats
-1)
862 + start
.ticks
/Meter::ticks_per_beat
;
865 start_frame
= m
.frame() + (nframes_t
) rint( beat_offset
* m
.tempo().frames_per_beat(_frame_rate
, m
.meter()));
869 bar_offset
= end
.bars
- m
.start().bars
;
871 beat_offset
= bar_offset
* m
.meter().beats_per_bar() - (m
.start().beats
-1) + (end
.beats
- 1)
872 + end
.ticks
/Meter::ticks_per_beat
;
874 end_frame
= m
.frame() + (nframes_t
) rint(beat_offset
* m
.tempo().frames_per_beat(_frame_rate
, m
.meter()));
876 frames
= end_frame
- start_frame
;
883 TempoMap::count_frames_between_metrics (const Meter
& meter
, const Tempo
& tempo
, const BBT_Time
& start
, const BBT_Time
& end
) const
885 /* this is used in timestamping the metrics by actually counting the beats */
887 nframes_t frames
= 0;
888 uint32_t bar
= start
.bars
;
889 double beat
= (double) start
.beats
;
890 double beats_counted
= 0;
891 double beats_per_bar
= 0;
892 double beat_frames
= 0;
894 beats_per_bar
= meter
.beats_per_bar();
895 beat_frames
= tempo
.frames_per_beat (_frame_rate
,meter
);
899 while (bar
< end
.bars
|| (bar
== end
.bars
&& beat
< end
.beats
)) {
901 if (beat
>= beats_per_bar
) {
906 if (beat
> beats_per_bar
) {
908 /* this is a fractional beat at the end of a fractional bar
909 so it should only count for the fraction
912 beats_counted
-= (ceil(beats_per_bar
) - beats_per_bar
);
921 // cerr << "Counted " << beats_counted << " from " << start << " to " << end
922 // << " bpb were " << beats_per_bar
923 // << " fpb was " << beat_frames
926 frames
= (nframes_t
) floor (beats_counted
* beat_frames
);
933 TempoMap::frame_time (const BBT_Time
& bbt
) const
935 BBT_Time start
; /* 1|1|0 */
937 return count_frames_between ( start
, bbt
);
941 TempoMap::bbt_duration_at (nframes_t pos
, const BBT_Time
& bbt
, int dir
) const
943 nframes_t frames
= 0;
949 Glib::RWLock::ReaderLock
lm (lock
);
950 frames
= bbt_duration_at_unlocked (when
, bbt
,dir
);
957 TempoMap::bbt_duration_at_unlocked (const BBT_Time
& when
, const BBT_Time
& bbt
, int dir
) const
960 nframes_t frames
= 0;
962 double beats_per_bar
;
965 result
.bars
= max(1U,when
.bars
+ dir
* bbt
.bars
) ;
969 Metric metric
= metric_at(result
);
970 beats_per_bar
= metric
.meter().beats_per_bar();
974 /*reduce things to legal bbt values
975 we have to handle possible fractional=shorter beats at the end of measures
976 and things like 0|11|9000 as a duration in a 4.5/4 measure
977 the musical decision is that the fractional beat is also a beat , although a shorter one
982 result
.beats
= when
.beats
+ bbt
.beats
;
983 result
.ticks
= when
.ticks
+ bbt
.ticks
;
985 while (result
.beats
>= (beats_per_bar
+1)) {
987 result
.beats
-= (uint32_t) ceil(beats_per_bar
);
988 metric
= metric_at(result
); // maybe there is a meter change
989 beats_per_bar
= metric
.meter().beats_per_bar();
992 /*we now counted the beats and landed in the target measure, now deal with ticks
993 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
994 and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
997 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1000 uint32_t ticks_at_beat
= (uint32_t) ( result
.beats
== ceil(beats_per_bar
) ?
1001 (1 - (ceil(beats_per_bar
) - beats_per_bar
))* Meter::ticks_per_beat
1002 : Meter::ticks_per_beat
);
1004 while (result
.ticks
>= ticks_at_beat
) {
1006 result
.ticks
-= ticks_at_beat
;
1007 if (result
.beats
>= (beats_per_bar
+1)) {
1010 metric
= metric_at(result
); // maybe there is a meter change
1011 beats_per_bar
= metric
.meter().beats_per_bar();
1013 ticks_at_beat
= (uint32_t) ( result
.beats
== ceil(beats_per_bar
) ?
1014 (1 - (ceil(beats_per_bar
) - beats_per_bar
) )* Meter::ticks_per_beat
1015 : Meter::ticks_per_beat
);
1021 uint32_t b
= bbt
.beats
;
1024 while( b
> when
.beats
) {
1026 result
.bars
= max(1U,result
.bars
-- ) ;
1027 metric
= metric_at(result
); // maybe there is a meter change
1028 beats_per_bar
= metric
.meter().beats_per_bar();
1029 if (b
>= ceil(beats_per_bar
)) {
1031 b
-= (uint32_t) ceil(beats_per_bar
);
1033 b
= (uint32_t) ceil(beats_per_bar
)- b
+ when
.beats
;
1036 result
.beats
= when
.beats
- b
;
1040 if (bbt
.ticks
<= when
.ticks
) {
1041 result
.ticks
= when
.ticks
- bbt
.ticks
;
1044 uint32_t ticks_at_beat
= (uint32_t) Meter::ticks_per_beat
;
1045 uint32_t t
= bbt
.ticks
- when
.ticks
;
1049 if (result
.beats
== 1) {
1050 result
.bars
= max(1U,result
.bars
-- ) ;
1051 metric
= metric_at(result
); // maybe there is a meter change
1052 beats_per_bar
= metric
.meter().beats_per_bar();
1053 result
.beats
= (uint32_t) ceil(beats_per_bar
);
1054 ticks_at_beat
= (uint32_t) ((1 - (ceil(beats_per_bar
) - beats_per_bar
))* Meter::ticks_per_beat
) ;
1057 ticks_at_beat
= (uint32_t) Meter::ticks_per_beat
;
1060 if (t
<= ticks_at_beat
) {
1061 result
.ticks
= ticks_at_beat
- t
;
1065 } while (t
> ticks_at_beat
);
1073 frames
= count_frames_between( result
,when
);
1075 frames
= count_frames_between(when
,result
);
1084 TempoMap::round_to_bar (nframes_t fr
, int dir
)
1087 Glib::RWLock::ReaderLock
lm (lock
);
1088 return round_to_type (fr
, dir
, Bar
);
1094 TempoMap::round_to_beat (nframes_t fr
, int dir
)
1097 Glib::RWLock::ReaderLock
lm (lock
);
1098 return round_to_type (fr
, dir
, Beat
);
1104 TempoMap::round_to_beat_subdivision (nframes_t fr
, int sub_num
)
1108 uint32_t ticks_one_half_subdivisions_worth
;
1109 uint32_t ticks_one_subdivisions_worth
;
1111 bbt_time(fr
, the_beat
);
1113 ticks_one_subdivisions_worth
= (uint32_t)Meter::ticks_per_beat
/ sub_num
;
1114 ticks_one_half_subdivisions_worth
= ticks_one_subdivisions_worth
/ 2;
1116 if (the_beat
.ticks
% ticks_one_subdivisions_worth
> ticks_one_half_subdivisions_worth
) {
1117 uint32_t difference
= ticks_one_subdivisions_worth
- (the_beat
.ticks
% ticks_one_subdivisions_worth
);
1118 if (the_beat
.ticks
+ difference
>= (uint32_t)Meter::ticks_per_beat
) {
1120 the_beat
.ticks
+= difference
;
1121 the_beat
.ticks
-= (uint32_t)Meter::ticks_per_beat
;
1123 the_beat
.ticks
+= difference
;
1126 the_beat
.ticks
-= the_beat
.ticks
% ticks_one_subdivisions_worth
;
1129 return frame_time (the_beat
);
1133 TempoMap::round_to_type (nframes_t frame
, int dir
, BBTPointType type
)
1135 Metric metric
= metric_at (frame
);
1138 bbt_time_with_metric (frame
, bbt
, metric
);
1144 } else if (dir
> 0) {
1145 if (bbt
.beats
> 0) {
1147 } else if (metric
.frame() < frame
) {
1151 if (bbt
.beats
> metric
.meter().beats_per_bar()/2) {
1162 } else if (dir
> 0) {
1163 if (bbt
.ticks
> 0) {
1165 } else if (metric
.frame() < frame
) {
1169 if (bbt
.ticks
>= (Meter::ticks_per_beat
/2)) {
1173 if (bbt
.beats
> ceil(metric
.meter().beats_per_bar()) ) {
1183 cerr << "for " << frame << " round to " << bbt << " using "
1187 return metric
.frame() + count_frames_between (metric
.start(), bbt
);
1190 TempoMap::BBTPointList
*
1191 TempoMap::get_points (nframes_t lower
, nframes_t upper
) const
1194 Metrics::const_iterator i
;
1195 BBTPointList
*points
;
1197 const MeterSection
* meter
;
1198 const MeterSection
* m
;
1199 const TempoSection
* tempo
;
1200 const TempoSection
* t
;
1203 double beats_per_bar
;
1206 double frames_per_bar
;
1212 meter
= &first_meter ();
1213 tempo
= &first_tempo ();
1215 /* find the starting point */
1217 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
1219 if ((*i
)->frame() > lower
) {
1223 if ((t
= dynamic_cast<const TempoSection
*>(*i
)) != 0) {
1225 } else if ((m
= dynamic_cast<const MeterSection
*>(*i
)) != 0) {
1232 meter -> the Meter for "lower"
1233 tempo -> the Tempo for "lower"
1234 i -> for first new metric after "lower", possibly metrics->end()
1236 Now start generating points.
1239 beats_per_bar
= meter
->beats_per_bar ();
1240 frames_per_bar
= meter
->frames_per_bar (*tempo
, _frame_rate
);
1241 beat_frames
= tempo
->frames_per_beat (_frame_rate
, *meter
);
1243 if (meter
->frame() > tempo
->frame()) {
1244 bar
= meter
->start().bars
;
1245 beat
= meter
->start().beats
;
1246 current
= meter
->frame();
1248 bar
= tempo
->start().bars
;
1249 beat
= tempo
->start().beats
;
1250 current
= tempo
->frame();
1253 /* initialize current to point to the bar/beat just prior to the
1254 lower frame bound passed in. assumes that current is initialized
1255 above to be on a beat.
1258 delta_bars
= (lower
-current
) / frames_per_bar
;
1259 delta_beats
= modf(delta_bars
, &dummy
) * beats_per_bar
;
1260 current
+= (floor(delta_bars
) * frames_per_bar
) + (floor(delta_beats
) * beat_frames
);
1262 // adjust bars and beats too
1263 bar
+= (uint32_t) (floor(delta_bars
));
1264 beat
+= (uint32_t) (floor(delta_beats
));
1266 points
= new BBTPointList
;
1270 if (i
== metrics
->end()) {
1272 // cerr << "== limit set to end of request @ " << limit << endl;
1274 // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1275 limit
= (*i
)->frame();
1278 limit
= min (limit
, upper
);
1280 while (current
< limit
) {
1282 /* if we're at the start of a bar, add bar point */
1285 if (current
>= lower
) {
1286 // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1287 points
->push_back (BBTPoint (*meter
, *tempo
,(nframes_t
)rint(current
), Bar
, bar
, 1));
1292 /* add some beats if we can */
1294 beat_frame
= current
;
1296 while (beat
<= ceil( beats_per_bar
) && beat_frame
< limit
) {
1297 if (beat_frame
>= lower
) {
1298 // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1299 points
->push_back (BBTPoint (*meter
, *tempo
, (nframes_t
) rint(beat_frame
), Beat
, bar
, beat
));
1301 beat_frame
+= beat_frames
;
1302 current
+= beat_frames
;
1307 // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1308 // << (beat > ceil(beats_per_bar))
1311 if (beat
> ceil(beats_per_bar
) || i
!= metrics
->end()) {
1313 /* we walked an entire bar. its
1314 important to move `current' forward
1315 by the actual frames_per_bar, not move it to
1316 an integral beat_frame, so that metrics with
1317 non-integral beats-per-bar have
1318 their bar positions set
1319 correctly. consider a metric with
1320 9-1/2 beats-per-bar. the bar we
1321 just filled had 10 beat marks,
1322 but the bar end is 1/2 beat before
1324 And it is also possible that a tempo
1325 change occured in the middle of a bar,
1326 so we subtract the possible extra fraction from the current
1329 if (beat
> ceil (beats_per_bar
)) {
1330 /* next bar goes where the numbers suggest */
1331 current
-= beat_frames
* (ceil(beats_per_bar
)-beats_per_bar
);
1332 // cerr << "++ next bar from numbers\n";
1334 /* next bar goes where the next metric is */
1336 // cerr << "++ next bar at next metric\n";
1344 /* if we're done, then we're done */
1346 if (current
>= upper
) {
1350 /* i is an iterator that refers to the next metric (or none).
1351 if there is a next metric, move to it, and continue.
1354 if (i
!= metrics
->end()) {
1356 if ((t
= dynamic_cast<const TempoSection
*>(*i
)) != 0) {
1358 } else if ((m
= dynamic_cast<const MeterSection
*>(*i
)) != 0) {
1360 /* new MeterSection, beat always returns to 1 */
1364 current
= (*i
)->frame ();
1365 // cerr << "loop around with current @ " << current << endl;
1367 beats_per_bar
= meter
->beats_per_bar ();
1368 frames_per_bar
= meter
->frames_per_bar (*tempo
, _frame_rate
);
1369 beat_frames
= tempo
->frames_per_beat (_frame_rate
, *meter
);
1380 TempoMap::tempo_section_at (nframes_t frame
)
1382 Glib::RWLock::ReaderLock
lm (lock
);
1383 Metrics::iterator i
;
1384 TempoSection
* prev
= 0;
1386 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
1389 if ((t
= dynamic_cast<TempoSection
*> (*i
)) != 0) {
1391 if ((*i
)->frame() > frame
) {
1407 TempoMap::tempo_at (nframes_t frame
)
1409 Metric
m (metric_at (frame
));
1415 TempoMap::meter_at (nframes_t frame
)
1417 Metric
m (metric_at (frame
));
1422 TempoMap::get_state ()
1424 Metrics::const_iterator i
;
1425 XMLNode
*root
= new XMLNode ("TempoMap");
1428 Glib::RWLock::ReaderLock
lm (lock
);
1429 for (i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
1430 root
->add_child_nocopy ((*i
)->get_state());
1438 TempoMap::set_state (const XMLNode
& node
)
1441 Glib::RWLock::WriterLock
lm (lock
);
1444 XMLNodeConstIterator niter
;
1445 Metrics
old_metrics (*metrics
);
1449 nlist
= node
.children();
1451 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
1452 XMLNode
* child
= *niter
;
1454 if (child
->name() == TempoSection::xml_state_node_name
) {
1457 metrics
->push_back (new TempoSection (*child
));
1460 catch (failed_constructor
& err
){
1461 error
<< _("Tempo map: could not set new state, restoring old one.") << endmsg
;
1462 *metrics
= old_metrics
;
1466 } else if (child
->name() == MeterSection::xml_state_node_name
) {
1469 metrics
->push_back (new MeterSection (*child
));
1472 catch (failed_constructor
& err
) {
1473 error
<< _("Tempo map: could not set new state, restoring old one.") << endmsg
;
1474 *metrics
= old_metrics
;
1480 if (niter
== nlist
.end()) {
1482 MetricSectionSorter cmp
;
1483 metrics
->sort (cmp
);
1484 timestamp_metrics (true);
1488 StateChanged (Change (0));
1494 TempoMap::dump (std::ostream
& o
) const
1496 const MeterSection
* m
;
1497 const TempoSection
* t
;
1499 for (Metrics::const_iterator i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
1501 if ((t
= dynamic_cast<const TempoSection
*>(*i
)) != 0) {
1502 o
<< "Tempo @ " << *i
<< ' ' << t
->beats_per_minute() << " BPM (denom = " << t
->note_type() << ") at " << t
->start() << " frame= " << t
->frame() << " (move? "
1503 << t
->movable() << ')' << endl
;
1504 } else if ((m
= dynamic_cast<const MeterSection
*>(*i
)) != 0) {
1505 o
<< "Meter @ " << *i
<< ' ' << m
->beats_per_bar() << '/' << m
->note_divisor() << " at " << m
->start() << " frame= " << m
->frame()
1506 << " (move? " << m
->movable() << ')' << endl
;
1512 TempoMap::n_tempos() const
1514 Glib::RWLock::ReaderLock
lm (lock
);
1517 for (Metrics::const_iterator i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
1518 if (dynamic_cast<const TempoSection
*>(*i
) != 0) {
1527 TempoMap::n_meters() const
1529 Glib::RWLock::ReaderLock
lm (lock
);
1532 for (Metrics::const_iterator i
= metrics
->begin(); i
!= metrics
->end(); ++i
) {
1533 if (dynamic_cast<const MeterSection
*>(*i
) != 0) {