X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Ftempo.cc;h=579b523fa08b475596aeb4ebb6e8bab9374a7068;hb=9756b7569accf82d1c2de87cd632845acd107f9d;hp=adb207c752610f24fdc0d3b97954416d56c44715;hpb=4b861e603972148a29638cade51cee27ebc997a2;p=ardour.git diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index adb207c752..579b523fa0 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -45,8 +45,6 @@ using Timecode::BBT_Time; Meter TempoMap::_default_meter (4.0, 4.0); Tempo TempoMap::_default_tempo (120.0); -const double Meter::ticks_per_beat = 1920.0; - double Tempo::frames_per_beat (framecnt_t sr, const Meter& meter) const { return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type)); @@ -707,7 +705,7 @@ TempoMap::timestamp_metrics (bool use_bbt) first = false; } else { - if (bbt.ticks > Meter::ticks_per_beat/2) { + if (bbt.ticks > BBT_Time::ticks_per_beat/2) { /* round up to next beat */ bbt.beats += 1; } @@ -836,40 +834,40 @@ TempoMap::bbt_time_with_metric (framepos_t frame, BBT_Time& bbt, const TempoMetr // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl; const double beats_per_bar = metric.meter().beats_per_bar(); - const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat; + const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / BBT_Time::ticks_per_beat; /* 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)Meter::ticks_per_beat; - bbt.ticks %= (uint32_t)Meter::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 / beats_per_bar); - bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_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. */ - double beat_fraction = beats_per_bar - floor(beats_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. - */ - uint32_t ticks_on_last_beat = (uint32_t)floor(Meter::ticks_per_beat * beat_fraction); - - if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) { + 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 / beats_per_bar); + bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_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. */ + double beat_fraction = beats_per_bar - floor(beats_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. + */ + uint32_t ticks_on_last_beat = (uint32_t)floor(BBT_Time::ticks_per_beat * beat_fraction); + + if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) { bbt.ticks -= ticks_on_last_beat; bbt.beats = 0; bbt.bars++; - } + } - bbt.beats++; // correction for 1-based counting, see above for matching operation. + bbt.beats++; // correction for 1-based counting, see above for matching operation. // cerr << "-----\t RETURN " << bbt << endl; } @@ -877,8 +875,9 @@ TempoMap::bbt_time_with_metric (framepos_t frame, BBT_Time& bbt, const TempoMetr framecnt_t TempoMap::count_frames_between (const BBT_Time& start, const BBT_Time& end) const { - /* for this to work with fractional measure types, start and end have to be "legal" BBT types, - that means that the beats and ticks should be inside a bar + /* for this to work with fractional measure types, start and end have to be + "legal" BBT types, that means that the beats and ticks should be inside + a bar */ framecnt_t frames = 0; @@ -890,17 +889,17 @@ TempoMap::count_frames_between (const BBT_Time& start, const BBT_Time& end) cons uint32_t bar_offset = start.bars - m.start().bars; double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1) - + start.ticks/Meter::ticks_per_beat; + + start.ticks/BBT_Time::ticks_per_beat; - start_frame = m.frame() + (framepos_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter())); + start_frame = m.frame() + (framepos_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter())); m = metric_at(end); bar_offset = end.bars - m.start().bars; beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1) - + end.ticks/Meter::ticks_per_beat; + + end.ticks/BBT_Time::ticks_per_beat; end_frame = m.frame() + (framepos_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter())); @@ -913,7 +912,7 @@ TempoMap::count_frames_between (const BBT_Time& start, const BBT_Time& end) cons framecnt_t TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const { - /* this is used in timestamping the metrics by actually counting the beats */ + /* this is used in timestamping the metrics by actually counting the beats */ framecnt_t frames = 0; uint32_t bar = start.bars; @@ -965,7 +964,7 @@ TempoMap::frame_time (const BBT_Time& bbt) const { BBT_Time start ; /* 1|1|0 */ - return count_frames_between ( start, bbt); + return count_frames_between (start, bbt); } framecnt_t @@ -999,15 +998,12 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i TempoMetric metric = metric_at(result); beats_per_bar = metric.meter().beats_per_bar(); - - - /*reduce things to legal bbt values - we have to handle possible fractional=shorter beats at the end of measures - and things like 0|11|9000 as a duration in a 4.5/4 measure - the musical decision is that the fractional beat is also a beat , although a shorter one + /* Reduce things to legal bbt values we have to handle possible + fractional=shorter beats at the end of measures and things like 0|11|9000 + as a duration in a 4.5/4 measure the musical decision is that the + fractional beat is also a beat , although a shorter one */ - if (dir >= 0) { result.beats = when.beats + bbt.beats; result.ticks = when.ticks + bbt.ticks; @@ -1019,17 +1015,20 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i beats_per_bar = metric.meter().beats_per_bar(); } - /*we now counted the beats and landed in the target measure, now deal with ticks - 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 - and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks + + /* We now counted the beats and landed in the target measure, now deal + with ticks 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 and + with request like bbt = 3|2|9000 ,so we repeat the same loop but add + ticks */ /* of course gtk_ardour only allows bar with at least 1.0 beats ..... */ - uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ? - (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat - : Meter::ticks_per_beat ); + uint32_t ticks_at_beat = (uint32_t) (result.beats == ceil(beats_per_bar) ? + (1 - (ceil(beats_per_bar) - beats_per_bar))* BBT_Time::ticks_per_beat + : BBT_Time::ticks_per_beat ); while (result.ticks >= ticks_at_beat) { result.beats++; @@ -1040,24 +1039,22 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i metric = metric_at(result); // maybe there is a meter change beats_per_bar = metric.meter().beats_per_bar(); } - ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ? - (1 - (ceil(beats_per_bar) - beats_per_bar) ) * Meter::ticks_per_beat - : Meter::ticks_per_beat); - + ticks_at_beat= (uint32_t) (result.beats == ceil(beats_per_bar) ? + (1 - (ceil(beats_per_bar) - beats_per_bar) ) * BBT_Time::ticks_per_beat + : BBT_Time::ticks_per_beat); } } else { uint32_t b = bbt.beats; - /* count beats */ - while( b > when.beats ) { - - result.bars = max(1U,result.bars-- ) ; + /* count beats */ + while (b > when.beats) { + --result.bars; + result.bars = max(1U, result.bars); metric = metric_at(result); // maybe there is a meter change beats_per_bar = metric.meter().beats_per_bar(); if (b >= ceil(beats_per_bar)) { - b -= (uint32_t) ceil(beats_per_bar); } else { b = (uint32_t) ceil(beats_per_bar) - b + when.beats ; @@ -1065,26 +1062,27 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i } result.beats = when.beats - b; - /*count ticks */ + /* count ticks */ if (bbt.ticks <= when.ticks) { result.ticks = when.ticks - bbt.ticks; } else { - uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat; + uint32_t ticks_at_beat= (uint32_t) BBT_Time::ticks_per_beat; uint32_t t = bbt.ticks - when.ticks; do { if (result.beats == 1) { - result.bars = max(1U, result.bars-- ) ; + --result.bars; + result.bars = max(1U, result.bars) ; metric = metric_at(result); // maybe there is a meter change beats_per_bar = metric.meter().beats_per_bar(); result.beats = (uint32_t) ceil(beats_per_bar); - ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * Meter::ticks_per_beat) ; + ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * BBT_Time::ticks_per_beat) ; } else { - result.beats --; - ticks_at_beat = (uint32_t) Meter::ticks_per_beat; + --result.beats; + ticks_at_beat = (uint32_t) BBT_Time::ticks_per_beat; } if (t <= ticks_at_beat) { @@ -1099,8 +1097,8 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i } - if (dir < 0 ) { - frames = count_frames_between( result,when); + if (dir < 0) { + frames = count_frames_between(result, when); } else { frames = count_frames_between(when,result); } @@ -1113,8 +1111,8 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i framepos_t TempoMap::round_to_bar (framepos_t fr, int dir) { - { - Glib::RWLock::ReaderLock lm (lock); + { + Glib::RWLock::ReaderLock lm (lock); return round_to_type (fr, dir, Bar); } } @@ -1123,8 +1121,8 @@ TempoMap::round_to_bar (framepos_t fr, int dir) framepos_t TempoMap::round_to_beat (framepos_t fr, int dir) { - { - Glib::RWLock::ReaderLock lm (lock); + { + Glib::RWLock::ReaderLock lm (lock); return round_to_type (fr, dir, Beat); } } @@ -1139,7 +1137,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir) bbt_time(fr, the_beat); - ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num; + ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num; ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2; if (dir > 0) { @@ -1177,13 +1175,12 @@ 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; - } - + 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; + } } else { /* round to nearest */ @@ -1247,9 +1244,9 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) float midbar_ticks; midbar_beats = metric.meter().beats_per_bar() / 2 + 1; - midbar_ticks = Meter::ticks_per_beat * fmod (midbar_beats, 1.0f); + midbar_ticks = BBT_Time::ticks_per_beat * fmod (midbar_beats, 1.0f); midbar_beats = floor (midbar_beats); - + BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks)); if (bbt < midbar) { @@ -1276,7 +1273,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) /* find beat position preceding frame */ try { - bbt = bbt_subtract (bbt, one_beat); + bbt = bbt_subtract (bbt, one_beat); } catch (...) { @@ -1300,7 +1297,7 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) /* "true" rounding */ /* round to nearest beat */ - if (bbt.ticks >= (Meter::ticks_per_beat/2)) { + if (bbt.ticks >= (BBT_Time::ticks_per_beat/2)) { try { bbt = bbt_add (bbt, one_beat, metric); @@ -1426,7 +1423,7 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const beat_frame = current; - while (beat <= ceil( beats_per_bar) && beat_frame < limit) { + while (beat <= ceil(beats_per_bar) && beat_frame < limit) { if (beat_frame >= lower) { // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl; points->push_back (BBTPoint (*meter, *tempo, (framepos_t) rint(beat_frame), Beat, bar, beat)); @@ -1510,10 +1507,10 @@ TempoMap::get_points (framepos_t lower, framepos_t upper) const } const TempoSection& -TempoMap::tempo_section_at (framepos_t frame) +TempoMap::tempo_section_at (framepos_t frame) const { Glib::RWLock::ReaderLock lm (lock); - Metrics::iterator i; + Metrics::const_iterator i; TempoSection* prev = 0; for (i = metrics->begin(); i != metrics->end(); ++i) { @@ -1558,9 +1555,9 @@ TempoMap::get_state () XMLNode *root = new XMLNode ("TempoMap"); { - Glib::RWLock::ReaderLock lm (lock); + Glib::RWLock::ReaderLock lm (lock); for (i = metrics->begin(); i != metrics->end(); ++i) { - root->add_child_nocopy ((*i)->get_state()); + root->add_child_nocopy ((*i)->get_state()); } } @@ -1675,7 +1672,7 @@ void TempoMap::insert_time (framepos_t where, framecnt_t amount) { for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) { - if ((*i)->frame() >= where) { + if ((*i)->frame() >= where && (*i)->movable ()) { (*i)->set_frame ((*i)->frame() + amount); } } @@ -1702,9 +1699,9 @@ TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const Tempo BBT_Time op = increment; /* argument is const, but we need to modify it */ uint32_t ticks = result.ticks + op.ticks; - if (ticks >= Meter::ticks_per_beat) { + if (ticks >= BBT_Time::ticks_per_beat) { op.beats++; - result.ticks = ticks % (uint32_t) Meter::ticks_per_beat; + result.ticks = ticks % (uint32_t) BBT_Time::ticks_per_beat; } else { result.ticks += op.ticks; } @@ -1712,30 +1709,30 @@ TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const Tempo /* 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 meter_sections; - + for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) { const MeterSection* ms; if ((ms = dynamic_cast(*x)) != 0) { meter_sections.push_back (ms); } } - + assert (!meter_sections.empty()); - + list::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 + 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; @@ -1749,21 +1746,21 @@ TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const Tempo ++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) + + /* 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->beats_per_bar()) { /* move to next bar, first beat */ result.bars++; @@ -1771,12 +1768,12 @@ TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const Tempo } else { result.beats++; } - + /* one down ... */ - + op.beats--; - - /* check if we need to use a new meter section: has adding beats to result taken us + + /* 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. */ @@ -1805,7 +1802,7 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const if (op.ticks > result.ticks) { /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */ op.beats++; - result.ticks = Meter::ticks_per_beat - (op.ticks - result.ticks); + result.ticks = BBT_Time::ticks_per_beat - (op.ticks - result.ticks); } else { result.ticks -= op.ticks; } @@ -1813,35 +1810,35 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const /* 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 meter_sections; - + for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) { const MeterSection* ms; if ((ms = dynamic_cast(*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 + 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::reverse_iterator next_meter; // older versions of GCC don't + list::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 + and set next_meter to the previous one */ - + if ((*next_meter)->start() < result || (*next_meter)->start() == result) { meter = *next_meter; ++next_meter; @@ -1850,27 +1847,27 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const } 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) + + /* 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->beats_per_bar(); } else { @@ -1879,11 +1876,11 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const result.beats--; } - + /* one down ... */ op.beats--; - - /* check if we need to use a new meter section: has subtracting beats to result taken us + + /* 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. */ @@ -1904,8 +1901,210 @@ TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const return result; } +/** Add the BBT interval op to pos and return the result */ +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; + + meter = &first_meter (); + tempo = &first_tempo (); + + assert (meter); + assert (tempo); + + /* find the starting metrics for tempo & meter */ + + for (i = metrics->begin(); i != metrics->end(); ++i) { + + if ((*i)->frame() > pos) { + break; + } + + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + } else if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } + } + + /* We now have: + + meter -> the Meter for "pos" + tempo -> the Tempo for "pos" + i -> for first new metric after "pos", possibly metrics->end() + */ + + /* now comes the complicated part. we have to add one beat a time, + checking for a new metric on every beat. + */ + + frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter); + + while (op.bars) { + + pos += llrint (frames_per_beat * meter->beats_per_bar()); + op.bars--; + + /* check if we need to use a new metric section: has adding frames moved us + to or after the start of the next metric section? in which case, use it. + */ + + if (i != metrics->end()) { + if ((*i)->frame() <= pos) { + + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + } else if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } + ++i; + frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter); + + } + } + + } + + while (op.beats) { + + /* given the current meter, have we gone past the end of the bar ? */ + + pos += frames_per_beat; + op.beats--; + + /* check if we need to use a new metric section: has adding frames moved us + to or after the start of the next metric section? in which case, use it. + */ + + if (i != metrics->end()) { + if ((*i)->frame() <= pos) { + + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + } else if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } + ++i; + frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter); + } + } + } + + 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)); + } else { + pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat)); + } + } + + return pos; +} + +/** Count the number of beats that are equivalent to distance when starting at pos */ +double +TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const +{ + Metrics::const_iterator i; + double beats = 0; + const MeterSection* meter; + const MeterSection* m; + const TempoSection* tempo; + const TempoSection* t; + double frames_per_beat; + + double ddist = distance; + double dpos = pos; + + meter = &first_meter (); + tempo = &first_tempo (); + + assert (meter); + assert (tempo); + + /* find the starting metrics for tempo & meter */ + + for (i = metrics->begin(); i != metrics->end(); ++i) { + + if ((*i)->frame() > pos) { + break; + } + + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + } else if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } + } + + /* We now have: + + meter -> the Meter for "pos" + tempo -> the Tempo for "pos" + i -> for first new metric after "pos", possibly metrics->end() + */ + + /* now comes the complicated part. we have to add one beat a time, + checking for a new metric on every beat. + */ + + frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter); + + while (ddist > 0) { + + /* if we're nearly at the end, but have a fractional beat left, + compute the fraction and then its all over + */ + + if (ddist < frames_per_beat) { + beats += ddist / frames_per_beat; + break; + } + + /* walk one beat */ + + ddist -= frames_per_beat; + dpos += frames_per_beat; + beats += 1.0; + + /* check if we need to use a new metric section: has adding frames moved us + to or after the start of the next metric section? in which case, use it. + */ + + if (i != metrics->end()) { + if ((*i)->frame() <= (framepos_t) dpos) { + + if ((t = dynamic_cast(*i)) != 0) { + tempo = t; + } else if ((m = dynamic_cast(*i)) != 0) { + meter = m; + } + ++i; + frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter); + } + } + + } + + return beats; +} + + /** Compare the time of this with that of another MetricSection. - * @param with_bbt True to compare using ::start(), false to use ::frame(). + * @param with_bbt True to compare using start(), false to use frame(). * @return -1 for less than, 0 for equal, 1 for greater than. */