#include <sigc++/bind.h>
#include <glibmm/thread.h>
-#include <pbd/xml++.h>
-#include <ardour/tempo.h>
-#include <ardour/utils.h>
+#include "pbd/xml++.h"
+#include "ardour/tempo.h"
+#include "ardour/utils.h"
#include "i18n.h"
#include <locale.h>
if (prev) {
metric.set_start (prev->start());
+ metric.set_frame (prev->frame());
} else {
// metric will be at frames=0 bbt=1|1|0 by default
// which is correct for our purpose
void
TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
{
- {
- Glib::RWLock::ReaderLock lm (lock);
+ {
+ Glib::RWLock::ReaderLock lm (lock);
bbt_time_unlocked (frame, bbt);
}
}
{
nframes_t frame_diff;
- uint32_t xtra_bars = 0;
- double xtra_beats = 0;
- double beats = 0;
-
// 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 frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
- const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
+ const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat;
/* now compute how far beyond that point we actually are. */
frame_diff = frame - metric.frame();
-
- // cerr << "----\tdelta = " << frame_diff << endl;
-
- xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
- frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
- xtra_beats = (double) frame_diff / beat_frames;
- // cerr << "---\tmeaning " << xtra_bars << " xtra bars and " << xtra_beats << " xtra beats\n";
+ 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 -= ticks_on_last_beat;
+ bbt.beats = 0;
+ bbt.bars++;
+ }
+
+ bbt.beats++; // correction for 1-based counting, see above for matching operation.
- /* and set the returned value */
-
- /* and correct beat/bar shifts to match the meter.
- remember: beat and bar counting is 1-based,
- not zero-based
- also the meter may contain a fraction
- */
-
- bbt.bars = metric.start().bars + xtra_bars;
-
- beats = (double) metric.start().beats + xtra_beats;
-
- bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
-
- beats = fmod(beats - 1, beats_per_bar )+ 1.0;
- bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
- bbt.beats = (uint32_t) floor(beats);
-
// cerr << "-----\t RETURN " << bbt << endl;
}
nframes_t frames = 0;
BBT_Time when;
- bbt_time(pos,when);
+ bbt_time(pos, when);
{
Glib::RWLock::ReaderLock lm (lock);
double beats_per_bar;
BBT_Time result;
- result.bars = max(1U,when.bars + dir * bbt.bars) ;
+ result.bars = max(1U, when.bars + dir * bbt.bars) ;
result.beats = 1;
result.ticks = 0;
result.beats = when.beats + bbt.beats;
result.ticks = when.ticks + bbt.ticks;
- while (result.beats >= (beats_per_bar+1)) {
+ while (result.beats >= (beats_per_bar + 1)) {
result.bars++;
result.beats -= (uint32_t) ceil(beats_per_bar);
metric = metric_at(result); // maybe there is a meter change
while (result.ticks >= ticks_at_beat) {
result.beats++;
result.ticks -= ticks_at_beat;
- if (result.beats >= (beats_per_bar+1)) {
+ if (result.beats >= (beats_per_bar + 1)) {
result.bars++;
result.beats = 1;
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
+ (1 - (ceil(beats_per_bar) - beats_per_bar) ) * Meter::ticks_per_beat
: Meter::ticks_per_beat);
}
b -= (uint32_t) ceil(beats_per_bar);
} else {
- b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
+ b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
}
}
result.beats = when.beats - b;
do {
if (result.beats == 1) {
- result.bars = max(1U,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)) * Meter::ticks_per_beat) ;
} else {
result.beats --;
ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
}
nframes_t
-
-TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
+TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num, int dir)
{
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);
ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
+
+ if (dir > 0) {
+
+ /* round to next */
- if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
- uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
- if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
- the_beat.beats++;
- the_beat.ticks += difference;
- the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
- } else {
- the_beat.ticks += difference;
- }
- } else {
- the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
+ 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;
+
+ } else {
+ /* not on subdivision, compute distance to next subdivision */
+
+ difference = ticks_one_subdivisions_worth - mod;
+ }
+
+ if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
+ the_beat.beats++;
+ the_beat.ticks += difference;
+ the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
+ } else {
+ the_beat.ticks += difference;
+ }
+
+ } else if (dir < 0) {
+
+ /* round to previous */
+
+ 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;
+ cerr << "On the sub, move by 1 sub = " << difference << endl;
+ } else {
+ /* not on subdivision, compute distance to previous subdivision, which
+ is just the modulus.
+ */
+
+ difference = mod;
+ cerr << "off the sub, move by 1 sub = " << difference << endl;
+ }
+
+
+ cerr << "ticks = " << the_beat.ticks << endl;
+
+ if (the_beat.ticks < difference) {
+ cerr << "backup beats, set ticks to "
+ << (uint32_t)Meter::ticks_per_beat - difference << endl;
+ the_beat.beats--;
+ the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference;
+ } else {
+ cerr << " reduce ticks\n";
+ 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);
+ if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
+ the_beat.beats++;
+ the_beat.ticks += difference;
+ the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
+ } else {
+ the_beat.ticks += difference;
+ }
+ } else {
+ // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
+ the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
+ }
}
return frame_time (the_beat);
switch (type) {
case Bar:
if (dir < 0) {
- /* relax */
+ if (bbt.bars > 1) {
+ bbt.bars--;
+ }
} else if (dir > 0) {
if (bbt.beats > 0) {
bbt.bars++;
case Beat:
if (dir < 0) {
- /* relax */
+ if (bbt.beats > 1) {
+ bbt.beats--;
+ }
} else if (dir > 0) {
if (bbt.ticks > 0) {
bbt.beats++;
}
- /*
+ /*
cerr << "for " << frame << " round to " << bbt << " using "
<< metric.start()
<< endl;
*/
+
return metric.frame() + count_frames_between (metric.start(), bbt);
}
return cnt;
}
+
+void
+TempoMap::insert_time (nframes_t where, nframes_t amount)
+{
+ for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if ((*i)->frame() >= where) {
+ (*i)->set_frame ((*i)->frame() + amount);
+ }
+ }
+
+ timestamp_metrics (false);
+
+ StateChanged (Change (0));
+}