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
This commit is contained in:
Paul Davis 2012-01-06 16:39:40 +00:00
parent bdb15f9422
commit 355183f1ab
17 changed files with 424 additions and 195 deletions

View File

@ -1779,7 +1779,7 @@ AudioClock::bbt_validate_edit (const string& str)
return false;
}
if (any.bbt.ticks > Timecode::BBT_Time::ticks_per_bar_division) {
if (any.bbt.ticks > Timecode::BBT_Time::ticks_per_beat) {
return false;
}

View File

@ -4448,7 +4448,7 @@ NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
framecnt_t length = abs (_note[0] - _note[1]);
framecnt_t const g = grid_frames (start);
double const one_tick = 1 / Timecode::BBT_Time::ticks_per_bar_division;
double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
if (_editor->snap_mode() == SnapNormal && length < g) {
length = g - one_tick;

View File

@ -1261,7 +1261,7 @@ Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper)
bbt_ruler_scale = bbt_show_ticks_detail;
}
if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_bar_division / 4)) {
if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_beat / 4)) {
bbt_ruler_scale = bbt_show_ticks_super_detail;
}
}
@ -1386,14 +1386,14 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp
frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
frame_skip_error -= frame_skip;
skip = (uint32_t) (Timecode::BBT_Time::ticks_per_bar_division / bbt_beat_subdivision);
skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
pos = (*i).frame + frame_skip;
accumulated_error = frame_skip_error;
tick = skip;
for (t = 0; (tick < Timecode::BBT_Time::ticks_per_bar_division) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
i_am_accented = true;
@ -1476,14 +1476,14 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp
frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
frame_skip_error -= frame_skip;
skip = (uint32_t) (Timecode::BBT_Time::ticks_per_bar_division / bbt_beat_subdivision);
skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
pos = (*i).frame + frame_skip;
accumulated_error = frame_skip_error;
tick = skip;
for (t = 0; (tick < Timecode::BBT_Time::ticks_per_bar_division) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
i_am_accented = true;
@ -1571,14 +1571,14 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp
frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
frame_skip_error -= frame_skip;
skip = (uint32_t) (Timecode::BBT_Time::ticks_per_bar_division / bbt_beat_subdivision);
skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
pos = (*i).frame + frame_skip;
accumulated_error = frame_skip_error;
tick = skip;
for (t = 0; (tick < Timecode::BBT_Time::ticks_per_bar_division) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
i_am_accented = true;

View File

@ -169,7 +169,7 @@ Editor::compute_current_bbt_points (framepos_t leftmost, framepos_t rightmost)
}
next_beat.ticks = 0;
_session->tempo_map().map (current_bbt_points_begin, current_bbt_points_end, _session->tempo_map().frame_time (previous_beat), _session->tempo_map().frame_time (next_beat) + 1);
_session->tempo_map().get_grid (current_bbt_points_begin, current_bbt_points_end, _session->tempo_map().frame_time (previous_beat), _session->tempo_map().frame_time (next_beat) + 1);
}
void

View File

@ -221,7 +221,7 @@ MidiListEditor::redisplay_model ()
bbt.bars = 0;
dur = (*i)->end_time() - (*i)->time();
bbt.beats = floor (dur);
bbt.ticks = (uint32_t) lrint (fmod (dur, 1.0) * Timecode::BBT_Time::ticks_per_bar_division);
bbt.ticks = (uint32_t) lrint (fmod (dur, 1.0) * Timecode::BBT_Time::ticks_per_beat);
_session->tempo_map().bbt_duration_at (region->position(), bbt, 0);

View File

@ -485,7 +485,7 @@ MidiRegionView::button_release (GdkEventButton* ev)
/* Shorten the length by 1 tick so that we can add a new note at the next
grid snap without it overlapping this one.
*/
beats -= 1.0 / Timecode::BBT_Time::ticks_per_bar_division;
beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
}
@ -504,7 +504,7 @@ MidiRegionView::button_release (GdkEventButton* ev)
/* Shorten the length by 1 tick so that we can add a new note at the next
grid snap without it overlapping this one.
*/
beats -= 1.0 / Timecode::BBT_Time::ticks_per_bar_division;
beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);

View File

@ -272,8 +272,8 @@ StepEditor::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evo
up by 1 tick from where the last note ended
*/
at += 1.0/Timecode::BBT_Time::ticks_per_bar_division;
len -= 1.0/Timecode::BBT_Time::ticks_per_bar_division;
at += 1.0/Timecode::BBT_Time::ticks_per_beat;
len -= 1.0/Timecode::BBT_Time::ticks_per_beat;
}
step_edit_region_view->step_add_note (channel, pitch, velocity, at, len);
@ -293,7 +293,7 @@ StepEditor::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evo
step_edit_beat_pos += beat_duration;
step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
} else {
step_edit_beat_pos += 1.0/Timecode::BBT_Time::ticks_per_bar_division; // tiny, but no longer overlapping
step_edit_beat_pos += 1.0/Timecode::BBT_Time::ticks_per_beat; // tiny, but no longer overlapping
_step_edit_chord_duration = max (_step_edit_chord_duration, beat_duration);
}

View File

@ -209,7 +209,7 @@ VerboseCursor::set_duration (framepos_t start, framepos_t end, double x, double
ticks -= sbbt.ticks;
if (ticks < 0) {
ticks += int (Timecode::BBT_Time::ticks_per_bar_division);
ticks += int (Timecode::BBT_Time::ticks_per_beat);
--beats;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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;
/* 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_beats (framepos_t pos, Evoral::MusicalTime beats)
{
return framepos_minus_bbt (pos, BBT_Time (beats));
}
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;
}
/* 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 (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;
}
}
}
}
if ((had_bars && op.bars == 0) || !had_bars) {
/* finished counting bars, or none to count,
so decrement beat count
*/
if (op.beats) {
op.beats--;
/* 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;
}
}
}
}
/* 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;
}
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 (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 (op.beats) {
op.beats--;
}
}
}
if (i != _map.end()) {
if ((*i)->frame() > pos) {
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;
/* 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;
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);
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);
}
}
}
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--;
/* 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;
require_map_to (end);
Glib::RWLock::ReaderLock lm (lock);
BBTPointList::const_iterator i = bbt_after_or_at (pos);
Metrics::const_iterator next_tempo;
const TempoSection* tempo;
/* Find the relevant initial 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) {
if ((*next_tempo)->frame() > pos) {
break;
}
tempo = t;
}
}
/* We now have:
tempo -> the Tempo for "pos"
next_tempo -> the next tempo after "pos", possibly metrics.end()
*/
Evoral::MusicalTime beats = 0;
/* if our starting BBTPoint is after pos, add a fractional beat
to represent that distance.
*/
while (distance) {
if ((*i).frame != pos) {
beats += ((*i).frame - pos) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
}
/* End of this section */
framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
while (i != _map.end() && (*i).frame < end) {
++i;
beats++;
}
/* Distance to the end in frames */
framecnt_t const distance_to_end = end - pos;
assert (i != _map.end());
/* if our ending BBTPoint is after the end, subtract a fractional beat
to represent that distance.
*/
/* Amount to subtract this time */
double const sub = min (distance, distance_to_end);
if ((*i).frame > end) {
beats -= ((*i).frame - end) / (*i).meter->frames_per_division (*(*i).tempo, _frame_rate);
/* 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;

View File

@ -1025,8 +1025,8 @@ MackieControlProtocol::format_bbt_timecode (framepos_t now_frame)
subdiv = 3;
}
uint32_t subdivisions = bbt_time.ticks / uint32_t (Timecode::BBT_Time::ticks_per_bar_division / subdiv);
uint32_t ticks = bbt_time.ticks % uint32_t (Timecode::BBT_Time::ticks_per_bar_division / subdiv);
uint32_t subdivisions = bbt_time.ticks / uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv);
uint32_t ticks = bbt_time.ticks % uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv);
os << setw(2) << setfill('0') << subdivisions + 1;
os << setw(3) << setfill('0') << ticks;

View File

@ -23,20 +23,15 @@
using namespace Timecode;
/* This number doesn't describe the smallest division of a "beat" (which is
only defined contextually anyway), but rather the smallest division of the the
divisions of a bar. If using a meter of 4/8, there are 4 divisions per bar, and
we can divide each one into ticks_per_bar_division pieces; in a separate meter
(section) of 3/8, there are 3 divisions per bar, each of which can be divided
into ticks_per_bar_division pieces.
/* This defines the smallest division of a "beat".
The number is intended to have as many integer factors as possible so that
1/Nth divisions are integer numbers of ticks.
1920 is the largest legal value that be used inside an SMF file, and has many factors.
1920 has many factors, though going up to 3840 gets a couple more.
*/
const double BBT_Time::ticks_per_bar_division = 1920.0;
const double BBT_Time::ticks_per_beat = 1920.0;
BBT_Time::BBT_Time (double dbeats)
{
@ -49,5 +44,5 @@ BBT_Time::BBT_Time (double dbeats)
bars = 0;
beats = rint (floor (dbeats));
ticks = rint (floor (BBT_Time::ticks_per_bar_division * fmod (dbeats, 1.0)));
ticks = rint (floor (BBT_Time::ticks_per_beat * fmod (dbeats, 1.0)));
}

View File

@ -27,7 +27,7 @@ namespace Timecode {
/** Bar, Beat, Tick Time (i.e. Tempo-Based Time) */
struct BBT_Time {
static const double ticks_per_bar_division;
static const double ticks_per_beat;
uint32_t bars;
uint32_t beats;