13
0

more tempo fixes, including handling multiple metrics at the same place, and fixing round_to_beat_subdivision(). almost done now...

git-svn-id: svn://localhost/ardour2/branches/3.0@11145 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2012-01-03 17:59:47 +00:00
parent 19126ddc09
commit 0d4658e0af
2 changed files with 101 additions and 249 deletions

View File

@ -310,7 +310,7 @@ class TempoMap : public PBD::StatefulDestructible
framepos_t round_to_type (framepos_t fr, int dir, BBTPointType);
void bbt_time_unlocked (framepos_t, Timecode::BBT_Time&);
void bbt_time_unlocked (framepos_t, Timecode::BBT_Time&, const BBTPointList::const_iterator&);
framecnt_t bbt_duration_at_unlocked (const Timecode::BBT_Time& when, const Timecode::BBT_Time& bbt, int dir);

View File

@ -704,7 +704,8 @@ TempoMap::timestamp_metrics_from_audio_time ()
// which is correct for our purpose
}
bbt_time_unlocked ((*i)->frame(), bbt);
BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
bbt_time_unlocked ((*i)->frame(), bbt, bi);
// cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
@ -775,6 +776,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
TempoSection* tempo;
TempoSection* ts;
MeterSection* ms;
MetricSection* last_metric_section;
double divisions_per_bar;
double beat_frames;
double current_frame;
@ -812,6 +814,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
current_frame = 0;
meter->set_frame (0);
tempo->set_frame (0);
last_metric_section = tempo;
/* assumes that the first meter & tempo are at 1|1|0 */
current.bars = 1;
@ -868,13 +871,15 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
if (next_metric != metrics->end()) {
bool exact_start_match_required = true;
/* no operator >= so invert operator < */
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
if (!(current < (*next_metric)->start())) {
set_metrics:
if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
tempo = ts;
@ -907,7 +912,13 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames));
/* advance to the location of the new (adjusted) beat */
current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames);
/* next metric doesn't have to
* match this precisely to
* merit a reloop ...
*/
exact_start_match_required = false;
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
} else {
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
@ -923,8 +934,8 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
* start of a bar.
*/
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 (%2)\n",
meter->start(), current_frame));
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
meter->start(), current, current_frame));
assert (current.beats == 1);
@ -937,7 +948,14 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n",
beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
last_metric_section = *next_metric;
++next_metric;
if (next_metric != metrics->end() && ((*next_metric)->start() == current)) {
/* same position so go back and set this one up before advancing
*/
goto set_metrics;
}
}
}
@ -1029,22 +1047,21 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
{
{
Glib::RWLock::ReaderLock lm (lock);
bbt_time_unlocked (frame, bbt);
BBTPointList::const_iterator i = bbt_before_or_at (frame);
bbt_time_unlocked (frame, bbt, i);
}
}
void
TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt)
TempoMap::bbt_time_unlocked (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
{
BBTPointList::const_iterator i = bbt_before_or_at (frame);
bbt.bars = (*i).bar;
bbt.beats = (*i).beat;
if ((*i).frame == frame) {
bbt.ticks = 0;
} else {
bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) /
bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
BBT_Time::ticks_per_bar_division);
}
}
@ -1145,37 +1162,46 @@ TempoMap::round_to_beat (framepos_t fr, int dir)
framepos_t
TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
{
Glib::RWLock::ReaderLock lm (lock);
BBTPointList::const_iterator i = bbt_before_or_at (fr);
BBT_Time the_beat;
uint32_t ticks_one_half_subdivisions_worth;
uint32_t ticks_one_subdivisions_worth;
uint32_t difference;
bbt_time(fr, the_beat);
bbt_time_unlocked (fr, the_beat, i);
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_bar_division / sub_num;
ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
if (dir > 0) {
/* round to next */
/* round to next (even if we're on a subdivision */
uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
if (mod == 0) {
/* right on the subdivision, so the difference is just the subdivision ticks */
difference = ticks_one_subdivisions_worth;
the_beat.ticks += ticks_one_subdivisions_worth;
} else {
/* not on subdivision, compute distance to next subdivision */
difference = ticks_one_subdivisions_worth - mod;
the_beat.ticks += ticks_one_subdivisions_worth - mod;
}
the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
assert (i != _map.end());
++i;
assert (i != _map.end());
the_beat.ticks -= BBT_Time::ticks_per_bar_division;
}
} else if (dir < 0) {
/* round to previous */
/* round to previous (even if we're on a subdivision) */
uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
@ -1190,26 +1216,64 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
difference = mod;
}
try {
the_beat = bbt_subtract (the_beat, BBT_Time (0, 0, difference));
} catch (...) {
/* can't go backwards from wherever pos is, so just return it */
return fr;
if (the_beat.ticks < difference) {
if (i == _map.begin()) {
/* can't go backwards from wherever pos is, so just return it */
return fr;
}
--i;
the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
} else {
the_beat.ticks -= difference;
}
} else {
/* round to nearest */
if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
double rem;
/* compute the distance to the previous and next subdivision */
if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
/* closer to the next subdivision, so shift forward */
the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
assert (i != _map.end());
++i;
assert (i != _map.end());
the_beat.ticks -= BBT_Time::ticks_per_bar_division;
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
}
} else if (rem > 0) {
/* closer to previous subdivision, so shift backward */
if (rem > the_beat.ticks) {
if (i == _map.begin()) {
/* can't go backwards past zero, so ... */
return 0;
}
/* step back to previous beat */
--i;
the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
} else {
the_beat.ticks = lrint (the_beat.ticks - rem);
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
}
} else {
// difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
/* on the subdivision, do nothing */
}
}
return frame_time (the_beat);
return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
(*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
}
framepos_t
@ -1273,6 +1337,10 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
BBTPointList::const_iterator prev = fi;
BBTPointList::const_iterator next = fi;
if ((*fi).frame == frame) {
return frame;
}
while ((*prev).beat != 1) {
if (prev == _map.begin()) {
break;
@ -1544,226 +1612,6 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
PropertyChanged (PropertyChange ());
}
BBT_Time
TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
{
TempoMetric metric = metric_at (start);
return bbt_add (start, other, metric);
}
/**
* add the BBT interval @param increment to @param start and return the result
*/
BBT_Time
TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
{
BBT_Time result = start;
BBT_Time op = increment; /* argument is const, but we need to modify it */
uint32_t ticks = result.ticks + op.ticks;
if (ticks >= BBT_Time::ticks_per_bar_division) {
op.beats++;
result.ticks = ticks % (uint32_t) BBT_Time::ticks_per_bar_division;
} else {
result.ticks += op.ticks;
}
/* now comes the complicated part. we have to add one beat a time,
checking for a new metric on every beat.
*/
/* grab all meter sections */
list<const MeterSection*> meter_sections;
for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
const MeterSection* ms;
if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
meter_sections.push_back (ms);
}
}
assert (!meter_sections.empty());
list<const MeterSection*>::const_iterator next_meter;
const Meter* meter = 0;
/* go forwards through the meter sections till we get to the one
covering the current value of result. this positions i to point to
the next meter section too, or the end.
*/
for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
if (result < (*next_meter)->start()) {
/* this metric is past the result time. stop looking, we have what we need */
break;
}
if (result == (*next_meter)->start()) {
/* this meter section starts at result, push i beyond it so that it points
to the NEXT section, opwise we will get stuck later, and use this meter section.
*/
meter = *next_meter;
++next_meter;
break;
}
meter = *next_meter;
}
assert (meter != 0);
/* OK, now have the meter for the bar start we are on, and i is an iterator
that points to the metric after the one we are currently dealing with
(or to metrics->end(), of course)
*/
while (op.beats) {
/* given the current meter, have we gone past the end of the bar ? */
if (result.beats >= meter->divisions_per_bar()) {
/* move to next bar, first beat */
result.bars++;
result.beats = 1;
} else {
result.beats++;
}
/* one down ... */
op.beats--;
/* check if we need to use a new meter section: has adding beats to result taken us
to or after the start of the next meter section? in which case, use it.
*/
if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
meter = *next_meter;
++next_meter;
}
}
/* finally, add bars */
result.bars += op.bars++;
return result;
}
/**
* subtract the BBT interval @param decrement from @param start and return the result
*/
BBT_Time
TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
{
BBT_Time result = start;
BBT_Time op = decrement; /* argument is const, but we need to modify it */
if (op.ticks > result.ticks) {
/* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
op.beats++;
result.ticks = BBT_Time::ticks_per_bar_division - (op.ticks - result.ticks);
} else {
result.ticks -= op.ticks;
}
/* now comes the complicated part. we have to subtract one beat a time,
checking for a new metric on every beat.
*/
/* grab all meter sections */
list<const MeterSection*> meter_sections;
for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
const MeterSection* ms;
if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
meter_sections.push_back (ms);
}
}
assert (!meter_sections.empty());
/* go backwards through the meter sections till we get to the one
covering the current value of result. this positions i to point to
the next (previous) meter section too, or the end.
*/
const MeterSection* meter = 0;
list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't
// support const_reverse_iterator::operator!=()
for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
/* when we find the first meter section that is before or at result, use it,
and set next_meter to the previous one
*/
if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
meter = *next_meter;
++next_meter;
break;
}
}
assert (meter != 0);
/* OK, now have the meter for the bar start we are on, and i is an iterator
that points to the metric after the one we are currently dealing with
(or to metrics->end(), of course)
*/
while (op.beats) {
/* have we reached the start of the bar? if so, move to the last beat of the previous
bar. opwise, just step back 1 beat.
*/
if (result.beats == 1) {
/* move to previous bar, last beat */
if (result.bars <= 1) {
/* i'm sorry dave, i can't do that */
throw std::out_of_range ("illegal BBT subtraction");
}
result.bars--;
result.beats = meter->divisions_per_bar();
} else {
/* back one beat */
result.beats--;
}
/* one down ... */
op.beats--;
/* check if we need to use a new meter section: has subtracting beats to result taken us
to before the start of the current meter section? in which case, use the prior one.
*/
if (result < meter->start() && next_meter != meter_sections.rend()) {
meter = *next_meter;
++next_meter;
}
}
/* finally, subtract bars */
if (op.bars >= result.bars) {
/* i'm sorry dave, i can't do that */
throw std::out_of_range ("illegal BBT subtraction");
}
result.bars -= op.bars;
return result;
}
/** Add some (fractional) beats to a session frame position, and return the result in frames.
* pos can be -ve, if required.
*/
@ -2093,6 +1941,10 @@ TempoMap::bbt_before_or_at (framepos_t pos)
require_map_to (pos);
BBTPointList::const_iterator i = lower_bound (_map.begin(), _map.end(), pos);
assert (i != _map.end());
if ((*i).frame > pos) {
assert (i != _map.begin());
--i;
}
return i;
}