using namespace ARDOUR;
using namespace PBD;
+using Timecode::BBT_Time;
+
/* _default tempo is 4/4 qtr=120 */
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 (nframes_t sr, const Meter& meter) const
+double Tempo::frames_per_beat (framecnt_t sr, const Meter& meter) const
{
return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
}
/***********************************************************************/
double
-Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
+Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
{
return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
}
}
};
-TempoMap::TempoMap (nframes_t fr)
+TempoMap::TempoMap (framecnt_t fr)
{
metrics = new Metrics;
_frame_rate = fr;
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;
}
// 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;
+ 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);
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);
+ 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;
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()));
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()));
*/
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 );
+ (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++;
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);
+ (1 - (ceil(beats_per_bar) - beats_per_bar) ) * BBT_Time::ticks_per_beat
+ : BBT_Time::ticks_per_beat);
}
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 {
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;
+ ticks_at_beat = (uint32_t) BBT_Time::ticks_per_beat;
}
if (t <= ticks_at_beat) {
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) {
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));
/* "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);
}
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) {
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;
}
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;
}
return result;
}
+/**
+ * add the BBT interval @param increment to @param start and return the result
+ */
+framepos_t
+TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
+{
+ 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<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*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<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*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<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*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;
+}
+
+/**
+ * add the BBT interval @param increment to @param start and return the result
+ */
+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<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*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 += BBT_Time::ticks_per_beat * (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<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*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().
* @return -1 for less than, 0 for equal, 1 for greater than.