#include <cmath>
-
#include <glibmm/thread.h>
#include "pbd/xml++.h"
+#include "evoral/types.hpp"
#include "ardour/debug.h"
#include "ardour/tempo.h"
#include "ardour/utils.h"
void
TempoMap::do_insert (MetricSection* section, bool with_bbt)
{
+ /* First of all, check to see if the new MetricSection is in the
+ middle of a bar. If so, we need to fix the bar that we are in
+ to have a different meter.
+ */
+
+ assert (section->start().ticks == 0);
+
+ if (section->start().beats != 1) {
+
+ /* Here's the tempo and metric where we are proposing to insert `section' */
+ TempoMetric tm = metric_at (section->start ());
+
+ /* This is where we will put the `corrective' new meter; at the start of
+ the bar that we are inserting into the middle of.
+ */
+ BBT_Time where_correction = section->start();
+ where_correction.beats = 1;
+ where_correction.ticks = 0;
+
+ /* Put in the meter change to make the bar before our `section' the right
+ length.
+ */
+ do_insert (new MeterSection (where_correction, section->start().beats, tm.meter().note_divisor ()), true);
+
+ /* This is where the new stuff will now go; the start of the next bar
+ (after the one whose meter we just fixed).
+ */
+ BBT_Time where_new (where_correction.bars + 1, 1, 0);
+
+ /* Change back to the original meter */
+ do_insert (new MeterSection (where_new, tm.meter().beats_per_bar(), tm.meter().note_divisor()), true);
+
+ /* And set up `section' for where it should be, ready to be inserted */
+ section->set_start (where_new);
+ }
+
Metrics::iterator i;
/* Look for any existing MetricSection that is of the same type and
{
framecnt_t frame_diff;
- // 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()) / BBT_Time::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);
{
BBT_Time start ; /* 1|1|0 */
- return count_frames_between ( start, bbt);
+ return count_frames_between (start, bbt);
}
framecnt_t
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
/* 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) ?
+ 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 );
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) ?
+ 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);
-
}
uint32_t b = bbt.beats;
/* count beats */
- while( b > when.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 ;
}
- 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);
}
difference = mod;
}
- try {
+ 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 */
midbar_beats = metric.meter().beats_per_bar() / 2 + 1;
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) {
/* find beat position preceding frame */
try {
- bbt = bbt_subtract (bbt, one_beat);
+ bbt = bbt_subtract (bbt, one_beat);
}
catch (...) {
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));
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);
}
}
/* 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
+ 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;
++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++;
} 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.
*/
/* 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
+ 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
+ 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
+ and set next_meter to the previous one
*/
-
+
if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
meter = *next_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) {
/* 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 {
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.
*/
return result;
}
+/** Add some (fractional) beats to a frame position, and return the result in frames */
+framepos_t
+TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
+{
+ Metrics::const_iterator i;
+ const TempoSection* tempo;
+ const MeterSection* meter;
+
+ /* Find the starting metrics for tempo & meter */
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+
+ if ((*i)->frame() > pos) {
+ break;
+ }
+
+ const TempoSection* t;
+ const MeterSection* m;
+
+ 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()
+ */
+
+ while (beats) {
+
+ /* End of this section */
+ framepos_t end = i == metrics->end() ? max_framepos : (*i)->frame ();
+
+ /* Distance to the end in beats */
+ Evoral::MusicalTime distance_beats = (end - pos) / tempo->frames_per_beat (_frame_rate, *meter);
+
+ /* Amount to subtract this time */
+ double const sub = min (distance_beats, beats);
+
+ /* Update */
+ beats -= sub;
+ pos += sub * tempo->frames_per_beat (_frame_rate, *meter);
+
+ /* Move on if there's anything to move to */
+ if (i != metrics->end ()) {
+ const TempoSection* t;
+ const MeterSection* m;
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+
+ ++i;
+ }
+ }
+
+ return pos;
+}
+
/** 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;
+ double frames_per_beat;
meter = &first_meter ();
tempo = &first_tempo ();
/* 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);
+ uint64_t bars = 0;
+
while (op.bars) {
- pos += llrint (frames_per_beat * meter->beats_per_bar());
+ bars++;
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) {
+ /* about to change tempo or meter, so add the
+ * number of frames for the bars we've just
+ * traversed before we change the
+ * frames_per_beat value.
+ */
+
+ pos += llrint (frames_per_beat * (bars * meter->beats_per_bar()));
+ bars = 0;
+
if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
tempo = t;
} else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
}
+ pos += llrint (frames_per_beat * (bars * meter->beats_per_bar()));
+
+ uint64_t beats = 0;
+
while (op.beats) {
-
+
/* given the current meter, have we gone past the end of the bar ? */
- pos += frames_per_beat;
+ beats++;
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) {
+ /* about to change tempo or meter, so add the
+ * number of frames for the beats we've just
+ * traversed before we change the
+ * frames_per_beat value.
+ */
+
+ pos += llrint (beats * frames_per_beat);
+ beats = 0;
+
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);
+ frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
}
}
}
+ pos += llrint (beats * frames_per_beat);
+
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));
+ pos += llrint (frames_per_beat + /* extra beat */
+ (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
/* 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);
+ double last_dpos = 0;
+
while (ddist > 0) {
/* if we're nearly at the end, but have a fractional beat left,
/* walk one beat */
+ last_dpos = dpos;
ddist -= frames_per_beat;
dpos += frames_per_beat;
beats += 1.0;
*/
if (i != metrics->end()) {
- if ((*i)->frame() <= (framepos_t) dpos) {
+
+ double const f = (*i)->frame ();
+
+ if (f <= (framepos_t) dpos) {
+
+ /* We just went past a tempo/meter section start at (*i)->frame(),
+ which will be on a beat.
+
+ So what we have is
+
+ (*i)->frame() [f]
+ beat beat beat beat
+ | | | |
+ | | | |
+ ^ ^
+ | |
+ | new
+ | dpos [q]
+ last
+ dpos [p]
+
+ We need to go back to last_dpos (1 beat ago) and re-add
+ (f - p) beats using the old frames per beat and (q - f) beats
+ using the new.
+ */
+
+ beats -= 1;
+ beats += (f - last_dpos) / frames_per_beat;
if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
tempo = t;
}
++i;
frames_per_beat = tempo->frames_per_beat (_frame_rate, *meter);
+
+ beats += (dpos - f) / frames_per_beat;
}
}
-
}
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.
*/