summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2012-01-06 16:39:40 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2012-01-06 16:39:40 +0000
commit355183f1abea75d8fab0926cd7e7130796574cb0 (patch)
tree78cc092b820fda137e752529f2d45c794c375313 /libs/ardour
parentbdb15f942250ad3e8a42c6bef927374eedb0299f (diff)
partially revert some of the recent work on tempo to reflect new understanding of the problem. behaviour is now believed to be totally correct but awaiting a bit more testing
git-svn-id: svn://localhost/ardour2/branches/3.0@11171 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/ardour/tempo.h38
-rw-r--r--libs/ardour/audio_unit.cc8
-rw-r--r--libs/ardour/beats_frames_converter.cc6
-rw-r--r--libs/ardour/session_click.cc2
-rw-r--r--libs/ardour/session_time.cc2
-rw-r--r--libs/ardour/tempo.cc496
6 files changed, 393 insertions, 159 deletions
diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h
index 7d9a9feb7d..e6f072eb0d 100644
--- a/libs/ardour/ardour/tempo.h
+++ b/libs/ardour/ardour/tempo.h
@@ -68,7 +68,7 @@ class Meter {
double note_divisor() const { return _note_type; }
double frames_per_bar (const Tempo&, framecnt_t sr) const;
- double frames_per_division (const Tempo&, framecnt_t sr) const;
+ double frames_per_grid (const Tempo&, framecnt_t sr) const;
protected:
/** The number of divisions in a bar. This is a floating point value because
@@ -226,20 +226,43 @@ class TempoMap : public PBD::StatefulDestructible
(obj.*method)(metrics);
}
- void map (BBTPointList::const_iterator&, BBTPointList::const_iterator&,
- framepos_t start, framepos_t end);
+ void get_grid (BBTPointList::const_iterator&, BBTPointList::const_iterator&,
+ framepos_t start, framepos_t end);
+ /* TEMPO- AND METER-SENSITIVE FUNCTIONS
+
+ bbt_time(), bbt_time_rt(), frame_time() and bbt_duration_at()
+ are all sensitive to tempo and meter, and will give answers
+ that align with the grid formed by tempo and meter sections.
+
+ They SHOULD NOT be used to determine the position of events
+ whose location is canonically defined in beats.
+ */
+
void bbt_time (framepos_t when, Timecode::BBT_Time&);
/* realtime safe variant of ::bbt_time(), will throw
std::logic_error if the map is not large enough
to provide an answer.
*/
void bbt_time_rt (framepos_t when, Timecode::BBT_Time&);
-
-
framecnt_t frame_time (const Timecode::BBT_Time&);
framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir);
+ /* TEMPO-SENSITIVE FUNCTIONS
+
+ These next 4 functions will all take tempo in account and should be
+ used to determine position (and in the last case, distance in beats)
+ when tempo matters but meter does not.
+
+ They SHOULD be used to determine the position of events
+ whose location is canonically defined in beats.
+ */
+
+ framepos_t framepos_plus_bbt (framepos_t pos, Timecode::BBT_Time b) const;
+ framepos_t framepos_plus_beats (framepos_t, Evoral::MusicalTime) const;
+ framepos_t framepos_minus_beats (framepos_t, Evoral::MusicalTime) const;
+ Evoral::MusicalTime framewalk_to_beats (framepos_t pos, framecnt_t distance) const;
+
static const Tempo& default_tempo() { return _default_tempo; }
static const Meter& default_meter() { return _default_meter; }
@@ -273,11 +296,6 @@ class TempoMap : public PBD::StatefulDestructible
TempoMetric metric_at (Timecode::BBT_Time bbt) const;
TempoMetric metric_at (framepos_t) const;
- framepos_t framepos_plus_bbt (framepos_t pos, Timecode::BBT_Time b);
- framepos_t framepos_plus_beats (framepos_t, Evoral::MusicalTime);
- framepos_t framepos_minus_bbt (framepos_t pos, Timecode::BBT_Time b);
- framepos_t framepos_minus_beats (framepos_t, Evoral::MusicalTime);
- Evoral::MusicalTime framewalk_to_beats (framepos_t pos, framecnt_t distance);
void change_existing_tempo_at (framepos_t, double bpm, double note_type);
void change_initial_tempo (double bpm, double note_type);
diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc
index 3b1e91c329..5932b73949 100644
--- a/libs/ardour/audio_unit.cc
+++ b/libs/ardour/audio_unit.cc
@@ -1420,7 +1420,7 @@ AUPlugin::get_beat_and_tempo_callback (Float64* outCurrentBeat,
float beat;
beat = metric.meter().divisions_per_bar() * bbt.bars;
beat += bbt.beats;
- beat += bbt.ticks / Timecode::BBT_Time::ticks_per_bar_division;
+ beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
*outCurrentBeat = beat;
}
@@ -1461,7 +1461,7 @@ AUPlugin::get_musical_time_location_callback (UInt32* outDeltaSampleOffsetToNe
*outDeltaSampleOffsetToNextBeat = 0;
} else {
*outDeltaSampleOffsetToNextBeat = (UInt32)
- floor (((Timecode::BBT_Time::ticks_per_bar_division - bbt.ticks)/Timecode::BBT_Time::ticks_per_bar_division) * // fraction of a beat to next beat
+ floor (((Timecode::BBT_Time::ticks_per_beat - bbt.ticks)/Timecode::BBT_Time::ticks_per_beat) * // fraction of a beat to next beat
metric.meter().frames_per_division (metric.tempo(), _session.frame_rate())); // frames per beat
}
}
@@ -1553,7 +1553,7 @@ AUPlugin::get_transport_state_callback (Boolean* outIsPlaying,
float beat;
beat = metric.meter().divisions_per_bar() * bbt.bars;
beat += bbt.beats;
- beat += bbt.ticks / Timecode::BBT_Time::ticks_per_bar_division;
+ beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
*outCycleStartBeat = beat;
}
@@ -1565,7 +1565,7 @@ AUPlugin::get_transport_state_callback (Boolean* outIsPlaying,
float beat;
beat = metric.meter().divisions_per_bar() * bbt.bars;
beat += bbt.beats;
- beat += bbt.ticks / Timecode::BBT_Time::ticks_per_bar_division;
+ beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
*outCycleEndBeat = beat;
}
diff --git a/libs/ardour/beats_frames_converter.cc b/libs/ardour/beats_frames_converter.cc
index 215292f6c1..584732ff03 100644
--- a/libs/ardour/beats_frames_converter.cc
+++ b/libs/ardour/beats_frames_converter.cc
@@ -32,7 +32,8 @@ framecnt_t
BeatsFramesConverter::to (double beats) const
{
assert (beats >= 0);
- return _tempo_map.framepos_plus_beats (_origin_b, beats) - _origin_b;
+ framecnt_t r = _tempo_map.framepos_plus_beats (_origin_b, beats) - _origin_b;
+ return r;
}
/** Takes a duration in frames and considers it as a distance from the origin
@@ -42,7 +43,8 @@ BeatsFramesConverter::to (double beats) const
double
BeatsFramesConverter::from (framecnt_t frames) const
{
- return _tempo_map.framewalk_to_beats (_origin_b, frames);
+ double b = _tempo_map.framewalk_to_beats (_origin_b, frames);
+ return b;
}
} /* namespace ARDOUR */
diff --git a/libs/ardour/session_click.cc b/libs/ardour/session_click.cc
index 1fbf255448..d5a3bc5e42 100644
--- a/libs/ardour/session_click.cc
+++ b/libs/ardour/session_click.cc
@@ -60,7 +60,7 @@ Session::click (framepos_t start, framecnt_t nframes)
BufferSet& bufs = get_scratch_buffers(ChanCount(DataType::AUDIO, 1));
buf = bufs.get_audio(0).data();
- _tempo_map->map (points_begin, points_end, start, end);
+ _tempo_map->get_grid (points_begin, points_end, start, end);
if (distance (points_begin, points_end) == 0) {
goto run_clicks;
diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc
index 04d8cf2fbb..205e2a0ba4 100644
--- a/libs/ardour/session_time.cc
+++ b/libs/ardour/session_time.cc
@@ -496,7 +496,7 @@ Session::jack_timebase_callback (jack_transport_state_t /*state*/,
pos->beats_per_bar = metric.meter().divisions_per_bar();
pos->beat_type = metric.meter().note_divisor();
- pos->ticks_per_beat = Timecode::BBT_Time::ticks_per_bar_division;
+ pos->ticks_per_beat = Timecode::BBT_Time::ticks_per_beat;
pos->beats_per_minute = metric.tempo().beats_per_minute();
pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT);
diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc
index 8d1aba0210..096bcd8a67 100644
--- a/libs/ardour/tempo.cc
+++ b/libs/ardour/tempo.cc
@@ -54,15 +54,22 @@ Tempo::frames_per_beat (framecnt_t sr) const
/***********************************************************************/
double
-Meter::frames_per_division (const Tempo& tempo, framecnt_t sr) const
+Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
{
+ /* This is tempo- and meter-sensitive. The number it returns
+ is based on the interval between any two lines in the
+ grid that is constructed from tempo and meter sections.
+
+ The return value IS NOT interpretable in terms of "beats".
+ */
+
return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
}
double
Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
{
- return frames_per_division (tempo, sr) * _divisions_per_bar;
+ return frames_per_grid (tempo, sr) * _divisions_per_bar;
}
/***********************************************************************/
@@ -156,8 +163,8 @@ void
TempoSection::update_bar_offset_from_bbt (const Meter& m)
{
- _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_bar_division + start().ticks) /
- (m.divisions_per_bar() * BBT_Time::ticks_per_bar_division);
+ _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) /
+ (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
}
@@ -174,9 +181,9 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
new_start.bars = start().bars;
- double ticks = BBT_Time::ticks_per_bar_division * meter.divisions_per_bar() * _bar_offset;
- new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_bar_division);
- new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_bar_division);
+ double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
+ new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_beat);
+ new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat);
/* remember the 1-based counting properties of beats */
new_start.beats += 1;
@@ -548,7 +555,6 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T
/* cannot move the first meter section */
*((Meter*)&first) = meter;
recompute_map (true);
-
}
}
@@ -881,7 +887,7 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
double beat_frames;
divisions_per_bar = meter->divisions_per_bar ();
- beat_frames = meter->frames_per_division (*tempo,_frame_rate);
+ beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
while (current_frame < end) {
@@ -923,7 +929,7 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
if (tempo->start().ticks != 0) {
- double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate);
+ double next_beat_frames = tempo->frames_per_beat (_frame_rate);
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
tempo->start(), current_frame, tempo->bar_offset()));
@@ -964,7 +970,7 @@ TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter,
}
divisions_per_bar = meter->divisions_per_bar ();
- beat_frames = meter->frames_per_division (*tempo, _frame_rate);
+ beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
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)));
@@ -1098,8 +1104,8 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_i
if ((*i).frame == frame) {
bbt.ticks = 0;
} else {
- bbt.ticks = llrint (((frame - (*i).frame) / (*i).meter->frames_per_division(*((*i).tempo), _frame_rate)) *
- BBT_Time::ticks_per_bar_division);
+ bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
+ BBT_Time::ticks_per_beat);
}
}
@@ -1115,7 +1121,7 @@ TempoMap::frame_time (const BBT_Time& bbt)
if (bbt.ticks != 0) {
return ((*e).frame - (*s).frame) +
- llrint ((*e).meter->frames_per_division (*(*e).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division));
+ llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
} else {
return ((*e).frame - (*s).frame);
}
@@ -1151,7 +1157,7 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i
/* compute how much rounding we did because of non-zero ticks */
if (when.ticks != 0) {
- tick_frames = (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (when.ticks/BBT_Time::ticks_per_bar_division);
+ tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
}
uint32_t bars = 0;
@@ -1174,7 +1180,7 @@ TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, i
/* add any additional frames related to ticks in the added value */
if (bbt.ticks != 0) {
- tick_frames += (*wi).meter->frames_per_division (*(*wi).tempo, _frame_rate) * (bbt.ticks/BBT_Time::ticks_per_bar_division);
+ tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
}
return ((*wi).frame - (*start).frame) + llrint (tick_frames);
@@ -1208,7 +1214,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
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_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
if (dir > 0) {
@@ -1226,11 +1232,11 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
the_beat.ticks += ticks_one_subdivisions_worth - mod;
}
- if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
+ if (the_beat.ticks > BBT_Time::ticks_per_beat) {
assert (i != _map.end());
++i;
assert (i != _map.end());
- the_beat.ticks -= BBT_Time::ticks_per_bar_division;
+ the_beat.ticks -= BBT_Time::ticks_per_beat;
}
@@ -1257,7 +1263,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
return fr;
}
--i;
- the_beat.ticks = BBT_Time::ticks_per_bar_division - the_beat.ticks;
+ the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
} else {
the_beat.ticks -= difference;
}
@@ -1277,11 +1283,11 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
- if (the_beat.ticks > BBT_Time::ticks_per_bar_division) {
+ if (the_beat.ticks > BBT_Time::ticks_per_beat) {
assert (i != _map.end());
++i;
assert (i != _map.end());
- the_beat.ticks -= BBT_Time::ticks_per_bar_division;
+ the_beat.ticks -= BBT_Time::ticks_per_beat;
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
}
@@ -1296,7 +1302,7 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
}
/* step back to previous beat */
--i;
- the_beat.ticks = lrint (BBT_Time::ticks_per_bar_division - rem);
+ the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
} else {
the_beat.ticks = lrint (the_beat.ticks - rem);
@@ -1307,8 +1313,8 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
}
}
- return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_bar_division) *
- (*i).meter->frames_per_division (*((*i).tempo), _frame_rate);
+ return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) *
+ (*i).tempo->frames_per_beat (_frame_rate);
}
framepos_t
@@ -1454,9 +1460,9 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
}
void
-TempoMap::map (TempoMap::BBTPointList::const_iterator& begin,
- TempoMap::BBTPointList::const_iterator& end,
- framepos_t lower, framepos_t upper)
+TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin,
+ TempoMap::BBTPointList::const_iterator& end,
+ framepos_t lower, framepos_t upper)
{
{
Glib::RWLock::WriterLock lm (lock);
@@ -1697,7 +1703,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
first = false;
} else {
- if (bbt.ticks > BBT_Time::ticks_per_bar_division/2) {
+ if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
/* round up to next beat */
bbt.beats += 1;
}
@@ -1740,175 +1746,383 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount)
* pos can be -ve, if required.
*/
framepos_t
-TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats)
+TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
{
- return framepos_plus_bbt (pos, BBT_Time (beats));
-}
+ Glib::RWLock::ReaderLock lm (lock);
+ Metrics::const_iterator next_tempo;
+ const TempoSection* tempo;
-/** Subtract some (fractional) beats to a frame position, and return the result in frames */
-framepos_t
-TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats)
-{
- return framepos_minus_bbt (pos, BBT_Time (beats));
+ /* Find the starting tempo metric */
+
+ for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
+
+ const TempoSection* t;
+
+ if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
+
+ /* This is a bit of a hack, but pos could be -ve, and if it is,
+ we consider the initial metric changes (at time 0) to actually
+ be in effect at pos.
+ */
+
+ framepos_t f = (*next_tempo)->frame ();
+
+ if (pos < 0 && f == 0) {
+ f = pos;
+ }
+
+ if (f > pos) {
+ break;
+ }
+
+ tempo = t;
+ }
+ }
+
+ /* We now have:
+
+ tempo -> the Tempo for "pos"
+ next_tempo -> first tempo after "pos", possibly metrics.end()
+ */
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
+ pos, beats, *((Tempo*)tempo), tempo->frame()));
+
+ while (beats) {
+
+ /* Distance to the end of this section in frames */
+ framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
+
+ /* Distance to the end in beats */
+ Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
+
+ /* Amount to subtract this time */
+ double const delta = min (distance_beats, beats);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
+ (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
+ distance_frames, distance_beats));
+
+ /* Update */
+ beats -= delta;
+ pos += delta * tempo->frames_per_beat (_frame_rate);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
+
+ /* step forwards to next tempo section */
+
+ if (next_tempo != metrics.end()) {
+
+ tempo = dynamic_cast<const TempoSection*>(*next_tempo);
+
+ DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
+ *((Tempo*)tempo), tempo->frame(),
+ tempo->frames_per_beat (_frame_rate)));
+
+ while (next_tempo != metrics.end ()) {
+
+ ++next_tempo;
+
+ if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
+ break;
+ }
+ }
+ }
+ }
+
+ return pos;
}
+/** Subtract some (fractional) beats to a frame position, and return the result in frames */
framepos_t
-TempoMap::framepos_minus_bbt (framepos_t pos, BBT_Time op)
+TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
{
Glib::RWLock::ReaderLock lm (lock);
- BBTPointList::const_iterator i;
- framecnt_t extra_frames = 0;
- bool had_bars = (op.bars != 0);
+ Metrics::const_reverse_iterator prev_tempo;
+ const TempoSection* tempo = 0;
- /* start from the bar|beat right before (or at) pos */
+ /* Find the starting tempo metric */
- i = bbt_before_or_at (pos);
-
- /* we know that (*i).frame is less than or equal to pos */
- extra_frames = pos - (*i).frame;
-
- /* walk backwards */
+ for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
- while (i != _map.begin() && (op.bars || op.beats)) {
- --i;
+ const TempoSection* t;
- if (had_bars) {
- if ((*i).is_bar()) {
- if (op.bars) {
- op.bars--;
- }
+ if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
+
+ /* This is a bit of a hack, but pos could be -ve, and if it is,
+ we consider the initial metric changes (at time 0) to actually
+ be in effect at pos.
+ */
+
+ framepos_t f = (*prev_tempo)->frame ();
+
+ if (pos < 0 && f == 0) {
+ f = pos;
}
- }
- if ((had_bars && op.bars == 0) || !had_bars) {
- /* finished counting bars, or none to count,
- so decrement beat count
+ /* this is slightly more complex than the forward case
+ because we reach the tempo in effect at pos after
+ passing through pos (rather before, as in the
+ forward case). having done that, we then need to
+ keep going to get the previous tempo (or
+ metrics.rend())
*/
- if (op.beats) {
- op.beats--;
+
+ if (f <= pos) {
+ if (tempo == 0) {
+ /* first tempo with position at or
+ before pos
+ */
+ tempo = t;
+ } else if (f < pos) {
+ /* some other tempo section that
+ is even earlier than 'tempo'
+ */
+ break;
+ }
}
}
}
-
- /* handle ticks (assumed to be less than
- * BBT_Time::ticks_per_bar_division, as always.
- */
- if (op.ticks) {
- frameoffset_t tick_frames = llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
- framepos_t pre_tick_frames = (*i).frame + extra_frames;
- if (tick_frames < pre_tick_frames) {
- return pre_tick_frames - tick_frames;
- }
- return 0;
- } else {
- return (*i).frame + extra_frames;
+ /* We now have:
+
+ tempo -> the Tempo for "pos"
+ prev_tempo -> the first metric before "pos", possibly metrics.rend()
+ */
+
+ while (beats) {
+
+ /* Distance to the start of this section in frames */
+ framecnt_t distance_frames = ((prev_tempo == metrics.rend()) ? max_framepos : (pos - (*prev_tempo)->frame()));
+
+ /* Distance to the start in beats */
+ Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
+
+ /* Amount to subtract this time */
+ double const sub = min (distance_beats, beats);
+
+ /* Update */
+
+ beats -= sub;
+ pos -= sub * tempo->frames_per_beat (_frame_rate);
+
+ /* step backwards to prior TempoSection */
+
+ if (prev_tempo != metrics.rend()) {
+
+ tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
+
+ while (prev_tempo != metrics.rend ()) {
+
+ ++prev_tempo;
+
+ if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
+ break;
+ }
+ }
+ }
}
+
+ 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)
+TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
{
Glib::RWLock::ReaderLock lm (lock);
- BBT_Time op_copy (op);
- int additional_minutes = 1;
- BBTPointList::const_iterator i;
- framecnt_t backup_frames = 0;
- bool had_bars = (op.bars != 0);
-
- while (true) {
+ Metrics::const_iterator i;
+ const MeterSection* meter;
+ const MeterSection* m;
+ const TempoSection* tempo;
+ const TempoSection* t;
+ double frames_per_beat;
- i = bbt_before_or_at (pos);
+ meter = &first_meter ();
+ tempo = &first_tempo ();
- op = op_copy;
+ assert (meter);
+ assert (tempo);
- /* we know that (*i).frame is before or equal to pos */
- backup_frames = pos - (*i).frame;
+ /* find the starting metrics for tempo & meter */
- while (i != _map.end() && (op.bars || op.beats)) {
+ for (i = metrics.begin(); i != metrics.end(); ++i) {
- ++i;
+ if ((*i)->frame() > pos) {
+ break;
+ }
- if (had_bars) {
- if ((*i).is_bar()) {
- if (op.bars) {
- op.bars--;
- }
- }
- }
-
- if ((had_bars && op.bars == 0) || !had_bars) {
- /* finished counting bars, or none to count,
- so decrement beat count
- */
+ 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);
- if (op.beats) {
- op.beats--;
+ uint64_t bars = 0;
+
+ while (op.bars) {
+
+ 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->divisions_per_bar()));
+ bars = 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);
+
}
}
-
- if (i != _map.end()) {
- break;
- }
- /* we hit the end of the map before finish the bbt walk.
- */
+ }
- recompute_map (false, pos + (_frame_rate * 60 * additional_minutes));
- additional_minutes *= 2;
+ pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
+
+ uint64_t beats = 0;
+
+ while (op.beats) {
+
+ /* given the current meter, have we gone past the end of the bar ? */
+
+ beats++;
+ op.beats--;
- /* go back and try again */
- warning << "reached end of map with op now at " << op << " end = "
- << _map.back().frame << ' ' << _map.back().bar << '|' << _map.back().beat << ", trying to walk "
- << op_copy << " ... retry"
- << endmsg;
+ /* 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);
+ }
+ }
}
-
+
+ pos += llrint (beats * frames_per_beat);
+
if (op.ticks) {
- return (*i).frame - backup_frames +
- llrint ((*i).meter->frames_per_division (*(*i).tempo, _frame_rate) * (op.ticks/BBT_Time::ticks_per_bar_division));
- } else {
- return (*i).frame - backup_frames;
+ if (op.ticks >= 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 going forward,
starting at pos.
*/
Evoral::MusicalTime
-TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance)
+TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
{
- framepos_t end = pos + distance;
+ Glib::RWLock::ReaderLock lm (lock);
+ Metrics::const_iterator next_tempo;
+ const TempoSection* tempo;
+
+ /* Find the relevant initial tempo metric */
- require_map_to (end);
+ for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
- Glib::RWLock::ReaderLock lm (lock);
- BBTPointList::const_iterator i = bbt_after_or_at (pos);
- Evoral::MusicalTime beats = 0;
+ const TempoSection* t;
- /* if our starting BBTPoint is after pos, add a fractional beat
- to represent that distance.
- */
+ if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
- if ((*i).frame != pos) {
- beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
- }
+ if ((*next_tempo)->frame() > pos) {
+ break;
+ }
- while (i != _map.end() && (*i).frame < end) {
- ++i;
- beats++;
+ tempo = t;
+ }
}
- assert (i != _map.end());
-
- /* if our ending BBTPoint is after the end, subtract a fractional beat
- to represent that distance.
+ /* We now have:
+
+ tempo -> the Tempo for "pos"
+ next_tempo -> the next tempo after "pos", possibly metrics.end()
*/
- if ((*i).frame > end) {
- beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
+ Evoral::MusicalTime beats = 0;
+
+ while (distance) {
+
+ /* End of this section */
+ framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
+
+ /* Distance to the end in frames */
+ framecnt_t const distance_to_end = end - pos;
+
+ /* Amount to subtract this time */
+ double const sub = min (distance, distance_to_end);
+
+ /* Update */
+ pos += sub;
+ distance -= sub;
+ beats += sub / tempo->frames_per_beat (_frame_rate);
+
+ /* Move on if there's anything to move to */
+ while (next_tempo != metrics.end ()) {
+ const TempoSection* t;
+
+ ++next_tempo;
+
+ if (next_tempo != metrics.end() && (t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
+ tempo = t;
+ break;
+ }
+
+ }
}
return beats;