diff options
-rw-r--r-- | gtk2_ardour/audio_clock.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/editor_drag.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/editor_rulers.cc | 14 | ||||
-rw-r--r-- | gtk2_ardour/editor_tempodisplay.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/midi_list_editor.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/midi_region_view.cc | 4 | ||||
-rw-r--r-- | gtk2_ardour/step_editor.cc | 6 | ||||
-rw-r--r-- | gtk2_ardour/verbose_cursor.cc | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/tempo.h | 38 | ||||
-rw-r--r-- | libs/ardour/audio_unit.cc | 8 | ||||
-rw-r--r-- | libs/ardour/beats_frames_converter.cc | 6 | ||||
-rw-r--r-- | libs/ardour/session_click.cc | 2 | ||||
-rw-r--r-- | libs/ardour/session_time.cc | 2 | ||||
-rw-r--r-- | libs/ardour/tempo.cc | 496 | ||||
-rw-r--r-- | libs/surfaces/mackie/mackie_control_protocol.cc | 4 | ||||
-rw-r--r-- | libs/timecode/src/bbt_time.cc | 13 | ||||
-rw-r--r-- | libs/timecode/timecode/bbt_time.h | 2 |
17 files changed, 417 insertions, 188 deletions
diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index 37ed13595a..a4cb473a72 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -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; } diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index cda4a26440..830efbfeef 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -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; diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index c9fb7fc65d..f11e584c18 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -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; diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index 25fb5acbe7..14782e4bc5 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -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 diff --git a/gtk2_ardour/midi_list_editor.cc b/gtk2_ardour/midi_list_editor.cc index 5b42070e65..879b1a9f2f 100644 --- a/gtk2_ardour/midi_list_editor.cc +++ b/gtk2_ardour/midi_list_editor.cc @@ -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); diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index ac3ac92ee4..bab797c8c7 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -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); diff --git a/gtk2_ardour/step_editor.cc b/gtk2_ardour/step_editor.cc index aa0095b1d5..45e286dc70 100644 --- a/gtk2_ardour/step_editor.cc +++ b/gtk2_ardour/step_editor.cc @@ -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); } diff --git a/gtk2_ardour/verbose_cursor.cc b/gtk2_ardour/verbose_cursor.cc index 89f8c19c56..f80b74c4f8 100644 --- a/gtk2_ardour/verbose_cursor.cc +++ b/gtk2_ardour/verbose_cursor.cc @@ -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; } 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; diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index 6fda7d7404..7e208686ba 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -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; diff --git a/libs/timecode/src/bbt_time.cc b/libs/timecode/src/bbt_time.cc index 6de822524d..cdff66fd69 100644 --- a/libs/timecode/src/bbt_time.cc +++ b/libs/timecode/src/bbt_time.cc @@ -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))); } diff --git a/libs/timecode/timecode/bbt_time.h b/libs/timecode/timecode/bbt_time.h index 030ac18afc..50a67f3d79 100644 --- a/libs/timecode/timecode/bbt_time.h +++ b/libs/timecode/timecode/bbt_time.h @@ -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; |