13
0

fix a number of errors with BBT time computations, mostly notably ones related to tempo-only change

git-svn-id: svn://localhost/ardour2/branches/3.0@11020 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2011-12-19 18:32:06 +00:00
parent fb5f590b11
commit 71bba8053e

View File

@ -54,15 +54,15 @@ Tempo::frames_per_beat (framecnt_t sr) const
/***********************************************************************/
double
Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
Meter::frames_per_division (const Tempo& tempo, framecnt_t sr) const
{
return (60.0 * sr * _divisions_per_bar) / tempo.beats_per_minute();
return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
}
double
Meter::frames_per_division (const Tempo& tempo, framecnt_t sr) const
Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
{
return (60.0 * sr) / (tempo.beats_per_minute() * _note_type/tempo.note_type());
return frames_per_division (tempo, sr) * _divisions_per_bar;
}
/***********************************************************************/
@ -862,52 +862,54 @@ TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt) const
void
TempoMap::bbt_time_with_metric (framepos_t frame, BBT_Time& bbt, const TempoMetric& metric) const
{
framecnt_t frame_diff;
const double divisions_per_bar = metric.meter().divisions_per_bar();
const double ticks_per_frame = metric.meter().frames_per_division (metric.tempo(),_frame_rate) / BBT_Time::ticks_per_beat;
const double frames_per_tick = metric.meter().frames_per_division (metric.tempo(),_frame_rate) / BBT_Time::ticks_per_beat;
// cerr << "*** Compute BBT time for " << frame
// << " from metric at " << metric.frame() << " tempo = " << metric.tempo().beats_per_minute () << " meter "
// << metric.meter().divisions_per_bar() << '/' << metric.meter().note_divisor()
// << endl;
/* now compute how far beyond that point we actually are. */
frame_diff = frame - metric.frame();
bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
uint32_t xtra_beats = bbt.ticks / (uint32_t)BBT_Time::ticks_per_beat;
bbt.ticks %= (uint32_t)BBT_Time::ticks_per_beat;
bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / divisions_per_bar);
bbt.beats = (uint32_t)fmod((double)bbt.beats, divisions_per_bar);
/* if we have a fractional number of beats per bar, we see if
we're in the last beat (the fractional one). if so, we
round ticks appropriately and bump to the next bar.
/* now compute how far beyond the metric we actually are, and add the
* relevant number of ticks to the metric's BBT time
*/
double beat_fraction = divisions_per_bar - floor(divisions_per_bar);
/* XXX one problem here is that I'm not sure how to handle
fractional beats that don't evenly divide ticks_per_beat.
If they aren't handled consistently, I would guess we'll
continue to have strange discrepancies occuring. Perhaps
this will also behave badly in the case of meters like
0.1/4, but I can't be bothered to test that.
framecnt_t frame_diff = frame - metric.frame();
uint32_t tick_diff = (uint32_t) lrint ((double) frame_diff / frames_per_tick);
bbt.ticks = metric.start().ticks + tick_diff;
uint32_t beat_overflow = bbt.ticks / (uint32_t) BBT_Time::ticks_per_beat;
bbt.ticks = bbt.ticks % (uint32_t) BBT_Time::ticks_per_beat;
bbt.beats = metric.start().beats + beat_overflow;
/* bbt.beats uses 1-based counting, so adjust to get the right answer */
uint32_t bar_overflow = (bbt.beats - 1) / (uint32_t) divisions_per_bar;
bbt.bars = metric.start().bars + bar_overflow;
/* fmod will map bbt.beats as follows:
Beats divisions per bar Normalized beat
0 N => 0
1 N => 1
2 N => 2
3 N => 3
.
.
.
N-1 N => N-1
N N => 0
N+1 N => 1
.
.
.
2N-1 N => N-1
2N N => 0
so, the only special cases are 0, N, 2N etc. however bbt.beats is
never zero, so the only actual special cases are N, 2N and so on,
allowing us to use a special case check for fmod () == 0 and
changing the value to divisions per bar
*/
uint32_t ticks_on_last_beat = (uint32_t)floor(BBT_Time::ticks_per_beat * beat_fraction);
if (bbt.beats > (uint32_t)floor(divisions_per_bar) && bbt.ticks >= ticks_on_last_beat) {
bbt.ticks -= ticks_on_last_beat;
bbt.beats = 0;
bbt.bars++;
bbt.beats = (uint32_t) fmod (bbt.beats, divisions_per_bar);
if (bbt.beats == 0) {
bbt.beats = divisions_per_bar;
}
bbt.beats++; // correction for 1-based counting, see above for matching operation.
// cerr << "-----\t RETURN " << bbt << endl;
}
framecnt_t
@ -1417,6 +1419,8 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
beat_frames = meter->frames_per_division (*tempo,_frame_rate);
// cerr << "Start with beat frames = " << beat_frames << " bar = " << frames_per_bar << endl;
if (meter->frame() > tempo->frame()) {
bar = meter->start().bars;
beat = meter->start().beats;
@ -1448,7 +1452,7 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
limit = upper;
// cerr << "== limit set to end of request @ " << limit << endl;
} else {
// cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
// cerr << "== limit set to next meter section @ " << (*i)->frame() << endl;
limit = (*i)->frame();
}
@ -1486,22 +1490,22 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
// << " beat frame @ " << beat_frame << " vs. " << limit
// << endl;
if (beat > ceil(divisions_per_bar) || i != metrics->end()) {
if (beat > ceil(divisions_per_bar) || (i != metrics->end() && dynamic_cast<MeterSection*>(*i))) {
/* we walked an entire bar. its
important to move `current' forward
by the actual frames_per_bar, not move it to
an integral beat_frame, so that metrics with
non-integral beats-per-bar have
their bar positions set
correctly. consider a metric with
9-1/2 beats-per-bar. the bar we
just filled had 10 beat marks,
but the bar end is 1/2 beat before
the last beat mark.
And it is also possible that a tempo
change occured in the middle of a bar,
so we subtract the possible extra fraction from the current
/* we've arrived at either the end of a bar or
a new meter marker.
its important to move `current' forward by
the actual frames_per_bar, not move it to an
integral beat_frame, so that metrics with
non-integral beats-per-bar have their bar
positions set correctly. consider a metric
with 9-1/2 beats-per-bar. the bar we just
filled had 10 beat marks, but the bar end is
1/2 beat before the last beat mark. And it
is also possible that a tempo change occured
in the middle of a bar, so we subtract the
possible extra fraction from the current
*/
if (beat > ceil (divisions_per_bar)) {
@ -1516,7 +1520,6 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
bar++;
beat = 1;
}
}
/* if we're done, then we're done */
@ -1546,6 +1549,8 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const
frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
beat_frames = meter->frames_per_division (*tempo, _frame_rate);
// cerr << "New metric with beat frames = " << beat_frames << " bar = " << frames_per_bar << endl;
++i;
}