summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2011-12-01 22:54:50 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2011-12-01 22:54:50 +0000
commit93d8451d9ea57fbf04625773edc0390c8c92daf5 (patch)
treee15b36e6159b5afde26e386bac3698046c6808be /libs
parentbcaccc5ece1f75999001bd03d732f1c7a43677e0 (diff)
significantly reduce rounding error when doing framepos_t + BBT math
git-svn-id: svn://localhost/ardour2/branches/3.0@10852 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/tempo.cc44
1 files changed, 32 insertions, 12 deletions
diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc
index 6dd92aa119..017ea20011 100644
--- a/libs/ardour/tempo.cc
+++ b/libs/ardour/tempo.cc
@@ -1939,18 +1939,12 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
framepos_t
TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
{
- /* XXX: this is a little inaccurate as small errors are introduced
- every time a probably-fractional product of something and
- frames_per_beat is rounded. Other errors can be introduced
- by op.ticks' integer nature.
- */
-
Metrics::const_iterator i;
const MeterSection* meter;
const MeterSection* m;
const TempoSection* tempo;
const TempoSection* t;
- framecnt_t frames_per_beat;
+ double frames_per_beat;
meter = &first_meter ();
tempo = &first_tempo ();
@@ -1986,9 +1980,11 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
+ uint64_t bars = 0;
+
while (op.bars) {
- pos += llrint (frames_per_beat * meter->beats_per_bar());
+ bars++;
op.bars--;
/* check if we need to use a new metric section: has adding frames moved us
@@ -1998,6 +1994,15 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
if (i != metrics->end()) {
if ((*i)->frame() <= pos) {
+ /* about to change tempo or meter, so add the
+ * number of frames for the bars we've just
+ * traversed before we change the
+ * frames_per_beat value.
+ */
+
+ pos += llrint (frames_per_beat * (bars * meter->beats_per_bar()));
+ bars = 0;
+
if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
tempo = t;
} else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
@@ -2011,11 +2016,15 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
}
+ pos += llrint (frames_per_beat * (bars * meter->beats_per_bar()));
+
+ uint64_t beats = 0;
+
while (op.beats) {
/* given the current meter, have we gone past the end of the bar ? */
- pos += frames_per_beat;
+ beats++;
op.beats--;
/* check if we need to use a new metric section: has adding frames moved us
@@ -2025,6 +2034,15 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
if (i != metrics->end()) {
if ((*i)->frame() <= pos) {
+ /* about to change tempo or meter, so add the
+ * number of frames for the beats we've just
+ * traversed before we change the
+ * frames_per_beat value.
+ */
+
+ pos += llrint (beats * frames_per_beat);
+ beats = 0;
+
if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
tempo = t;
} else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
@@ -2036,10 +2054,13 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
}
}
+ pos += llrint (beats * frames_per_beat);
+
if (op.ticks) {
if (op.ticks >= BBT_Time::ticks_per_beat) {
- pos += frames_per_beat;
- pos += llrint (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / (double) BBT_Time::ticks_per_beat));
+ pos += llrint (frames_per_beat + /* extra beat */
+ (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) /
+ (double) BBT_Time::ticks_per_beat)));
} else {
pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
}
@@ -2130,7 +2151,6 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
}
}
-
}
return beats;