diff options
-rw-r--r-- | gtk2_ardour/editor.cc | 32 | ||||
-rw-r--r-- | gtk2_ardour/editor.h | 14 | ||||
-rw-r--r-- | gtk2_ardour/editor_drag.cc | 6 | ||||
-rw-r--r-- | gtk2_ardour/editor_ops.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/editor_rulers.cc | 249 | ||||
-rw-r--r-- | gtk2_ardour/editor_tempodisplay.cc | 41 | ||||
-rw-r--r-- | gtk2_ardour/tempo_dialog.cc | 30 | ||||
-rw-r--r-- | gtk2_ardour/tempo_dialog.h | 6 | ||||
-rw-r--r-- | gtk2_ardour/tempo_lines.cc | 40 | ||||
-rw-r--r-- | gtk2_ardour/tempo_lines.h | 9 | ||||
-rw-r--r-- | libs/ardour/ardour/tempo.h | 95 | ||||
-rw-r--r-- | libs/ardour/session_click.cc | 9 | ||||
-rw-r--r-- | libs/ardour/session_vst.cc | 26 | ||||
-rw-r--r-- | libs/ardour/tempo.cc | 1430 | ||||
-rw-r--r-- | libs/backends/jack/jack_session.cc | 2 | ||||
-rw-r--r-- | libs/canvas/ruler.cc | 4 |
16 files changed, 901 insertions, 1094 deletions
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 5129a1ffaa..7353b6ff38 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -2185,14 +2185,10 @@ Editor::set_snap_to (SnapType st) case SnapToBeatDiv4: case SnapToBeatDiv3: case SnapToBeatDiv2: { - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin; - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end; - - compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end); + std::vector<TempoMap::BBTPoint> grid; + compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples()); + compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame + current_page_samples()); + update_tempo_based_rulers (grid); break; } @@ -3952,11 +3948,9 @@ Editor::set_show_measures (bool yn) tempo_lines->show(); } - ARDOUR::TempoMap::BBTPointList::const_iterator begin; - ARDOUR::TempoMap::BBTPointList::const_iterator end; - - compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), begin, end); - draw_measures (begin, end); + std::vector<TempoMap::BBTPoint> grid; + compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples()); + draw_measures (grid); } instant_save (); @@ -4582,14 +4576,10 @@ Editor::visual_changer (const VisualChange& vc) compute_fixed_ruler_scale (); - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin; - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end; - - compute_current_bbt_points (vc.time_origin, pending_visual_change.time_origin + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - compute_bbt_ruler_scale (vc.time_origin, pending_visual_change.time_origin + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end); + std::vector<TempoMap::BBTPoint> grid; + compute_current_bbt_points (grid, vc.time_origin, pending_visual_change.time_origin + current_page_samples()); + compute_bbt_ruler_scale (grid, vc.time_origin, pending_visual_change.time_origin + current_page_samples()); + update_tempo_based_rulers (grid); update_video_timeline(); } diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 665e9cc54c..139e044eae 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -885,8 +885,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void update_just_timecode (); void compute_fixed_ruler_scale (); //calculates the RulerScale of the fixed rulers void update_fixed_rulers (); - void update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin, - ARDOUR::TempoMap::BBTPointList::const_iterator& end); + void update_tempo_based_rulers (std::vector<ARDOUR::TempoMap::BBTPoint>& grid); void popup_ruler_menu (framepos_t where = 0, ItemType type = RegionItem); void update_ruler_visibility (); void set_ruler_visible (RulerType, bool); @@ -949,9 +948,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD gint bbt_nmarks; uint32_t bbt_bar_helper_on; uint32_t bbt_accent_modulo; - void compute_bbt_ruler_scale (framepos_t lower, framepos_t upper, - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin, - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end); + void compute_bbt_ruler_scale (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t lower, framepos_t upper); ArdourCanvas::Ruler* timecode_ruler; ArdourCanvas::Ruler* bbt_ruler; @@ -1636,8 +1633,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD ArdourCanvas::Container* time_line_group; void hide_measures (); - void draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin, - ARDOUR::TempoMap::BBTPointList::const_iterator& end); + void draw_measures (std::vector<ARDOUR::TempoMap::BBTPoint>&); void new_tempo_section (); @@ -1698,9 +1694,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void remove_metric_marks (); void draw_metric_marks (const ARDOUR::Metrics& metrics); - void compute_current_bbt_points (framepos_t left, framepos_t right, - ARDOUR::TempoMap::BBTPointList::const_iterator& begin, - ARDOUR::TempoMap::BBTPointList::const_iterator& end); + void compute_current_bbt_points (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t left, framepos_t right); void tempo_map_changed (const PBD::PropertyChange&); void redisplay_tempo (bool immediate_redraw); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index d04b52c306..f6f7f2b67c 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -3344,14 +3344,14 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred) if (_copy == true) { _editor->begin_reversible_command (_("copy tempo mark")); XMLNode &before = map.get_state(); - map.add_tempo (_marker->tempo(), when); + map.add_tempo (_marker->tempo(), when, _marker->tempo().type()); XMLNode &after = map.get_state(); _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after)); _editor->commit_reversible_command (); } else { /* we removed it before, so add it back now */ - map.add_tempo (_marker->tempo(), when); + map.add_tempo (_marker->tempo(), when, _marker->tempo().type()); XMLNode &after = map.get_state(); _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after)); _editor->commit_reversible_command (); @@ -3369,7 +3369,7 @@ TempoMarkerDrag::aborted (bool moved) if (moved) { TempoMap& map (_editor->session()->tempo_map()); /* we removed it before, so add it back now */ - map.add_tempo (_marker->tempo(), _marker->tempo().start()); + map.add_tempo (_marker->tempo(), _marker->tempo().start(), _marker->tempo().type()); // delete the dummy marker we used for visual representation while moving. // a new visual marker will show up automatically. delete _marker; diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 66ef92baa7..8302f1b778 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -6552,7 +6552,7 @@ Editor::define_one_bar (framepos_t start, framepos_t end) } else { Timecode::BBT_Time bbt; _session->tempo_map().bbt_time (start, bbt); - _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt); + _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt, TempoSection::TempoSectionType::Ramp); } XMLNode& after (_session->tempo_map().get_state()); diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index bb45e97bb3..ca450594e6 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -710,15 +710,13 @@ Editor::update_fixed_rulers () } void -Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin, - ARDOUR::TempoMap::BBTPointList::const_iterator& end) +Editor::update_tempo_based_rulers (std::vector<TempoMap::BBTPoint>& grid) { if (_session == 0) { return; } - compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(), - begin, end); + compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame+current_page_samples()); _bbt_metric->units_per_pixel = samples_per_pixel; @@ -1011,19 +1009,19 @@ Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdou } void -Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper, - ARDOUR::TempoMap::BBTPointList::const_iterator begin, - ARDOUR::TempoMap::BBTPointList::const_iterator end) +Editor::compute_bbt_ruler_scale (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t lower, framepos_t upper) { if (_session == 0) { return; } - TempoMap::BBTPointList::const_iterator i; + std::vector<TempoMap::BBTPoint>::const_iterator i; Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler + framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor(_session->tempo_map().beat_at_frame (lower))); + framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (_session->tempo_map().beat_at_frame (upper)) + 1.0); - _session->bbt_time (lower, lower_beat); - _session->bbt_time (upper, upper_beat); + _session->bbt_time (beat_before_lower_pos, lower_beat); + _session->bbt_time (beat_after_upper_pos, upper_beat); uint32_t beats = 0; bbt_accent_modulo = 1; @@ -1103,19 +1101,21 @@ Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper, bbt_beat_subdivision = 4; break; } - - if (distance (begin, end) == 0) { + if (distance (grid.begin(), grid.end()) == 0) { return; } - i = end; + i = grid.end(); i--; - if ((*i).beat >= (*begin).beat) { - bbt_bars = (*i).bar - (*begin).bar; + + /* XX ?? */ + if ((*i).beat >= (*grid.begin()).beat) { + bbt_bars = (*i).bar - (*grid.begin()).bar; } else { - bbt_bars = (*i).bar - (*begin).bar - 1; + bbt_bars = (*i).bar - (*grid.begin()).bar; } - beats = distance (begin, end) - bbt_bars; + + beats = distance (grid.begin(), grid.end()) - bbt_bars; /* Only show the bar helper if there aren't many bars on the screen */ if ((bbt_bars < 2) || (beats < 5)) { @@ -1140,7 +1140,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_beat / 4)) { + if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) { bbt_ruler_scale = bbt_show_ticks_super_detail; } } @@ -1161,38 +1161,34 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l return; } - TempoMap::BBTPointList::const_iterator i; + std::vector<TempoMap::BBTPoint>::const_iterator i; char buf[64]; gint n = 0; framepos_t pos; Timecode::BBT_Time next_beat; - framepos_t next_beat_pos; uint32_t beats = 0; uint32_t tick = 0; uint32_t skip; uint32_t t; - framepos_t frame_skip; - double frame_skip_error; double bbt_position_of_helper; - double accumulated_error; bool i_am_accented = false; bool helper_active = false; ArdourCanvas::Ruler::Mark mark; - ARDOUR::TempoMap::BBTPointList::const_iterator begin; - ARDOUR::TempoMap::BBTPointList::const_iterator end; + std::vector<TempoMap::BBTPoint> grid; - compute_current_bbt_points (lower, upper, begin, end); + compute_current_bbt_points (grid, lower, upper); - if (distance (begin, end) == 0) { + if (distance (grid.begin(), grid.end()) == 0) { return; } switch (bbt_ruler_scale) { case bbt_show_beats: - beats = distance (begin, end); + + beats = distance (grid.begin(), grid.end()); bbt_nmarks = beats + 2; mark.label = ""; @@ -1200,7 +1196,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l mark.style = ArdourCanvas::Ruler::Mark::Micro; marks.push_back (mark); - for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) { + for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) { if ((*i).frame < lower && (bbt_bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); @@ -1228,7 +1224,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l case bbt_show_ticks: - beats = distance (begin, end); + beats = distance (grid.begin(), grid.end()); bbt_nmarks = (beats + 2) * bbt_beat_subdivision; bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ()); @@ -1240,7 +1236,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l mark.style = ArdourCanvas::Ruler::Mark::Micro; marks.push_back (mark); - for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) { + for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) { if ((*i).frame < lower && (bbt_bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); @@ -1265,45 +1261,20 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l } /* Add the tick marks */ + skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision; + tick = skip; // the first non-beat tick + t = 0; + while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) { - /* Find the next beat */ - next_beat.beats = (*i).beat; - next_beat.bars = (*i).bar; - next_beat.ticks = 0; - - if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) { - next_beat.beats += 1; - } else { - next_beat.bars += 1; - next_beat.beats = 1; - } - - next_beat_pos = _session->tempo_map().frame_time(next_beat); + next_beat.beats = (*i).beat; + next_beat.bars = (*i).bar; + next_beat.ticks = tick; + pos = _session->tempo_map().frame_time (next_beat); - 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_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_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) { - - if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) { + if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) { i_am_accented = true; } - mark.label = ""; - - /* Error compensation for float to framepos_t*/ - accumulated_error += frame_skip_error; - if (accumulated_error > 1) { - pos += 1; - accumulated_error -= 1.0f; - } - mark.position = pos; if ((bbt_beat_subdivision > 4) && i_am_accented) { @@ -1313,7 +1284,10 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l } i_am_accented = false; marks.push_back (mark); - n++; + + tick += skip; + ++t; + ++n; } } @@ -1321,17 +1295,17 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l case bbt_show_ticks_detail: - beats = distance (begin, end); + beats = distance (grid.begin(), grid.end()); bbt_nmarks = (beats + 2) * bbt_beat_subdivision; - bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ()); + bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ()); mark.label = ""; mark.position = lower; mark.style = ArdourCanvas::Ruler::Mark::Micro; marks.push_back (mark); - for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) { + for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) { if ((*i).frame < lower && (bbt_bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); @@ -1356,36 +1330,20 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l } /* Add the tick marks */ + skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision; + tick = skip; // the first non-beat tick - /* Find the next beat */ - - next_beat.beats = (*i).beat; - next_beat.bars = (*i).bar; - - if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) { - next_beat.beats += 1; - } else { - next_beat.bars += 1; - next_beat.beats = 1; - } - - next_beat_pos = _session->tempo_map().frame_time(next_beat); - - 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_beat / bbt_beat_subdivision); + t = 0; + while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) { - pos = (*i).frame + frame_skip; - accumulated_error = frame_skip_error; + next_beat.beats = (*i).beat; + next_beat.bars = (*i).bar; + next_beat.ticks = tick; + pos = _session->tempo_map().frame_time (next_beat); - tick = skip; - - 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; + if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) { + i_am_accented = true; } - if (i_am_accented && (pos > bbt_position_of_helper)){ snprintf (buf, sizeof(buf), "%" PRIu32, tick); } else { @@ -1393,14 +1351,6 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l } mark.label = buf; - - /* Error compensation for float to framepos_t*/ - accumulated_error += frame_skip_error; - if (accumulated_error > 1) { - pos += 1; - accumulated_error -= 1.0f; - } - mark.position = pos; if ((bbt_beat_subdivision > 4) && i_am_accented) { @@ -1409,7 +1359,11 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l mark.style = ArdourCanvas::Ruler::Mark::Micro; } i_am_accented = false; - n++; + marks.push_back (mark); + + tick += skip; + ++t; + ++n; } } @@ -1417,17 +1371,17 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l case bbt_show_ticks_super_detail: - beats = distance (begin, end); + beats = distance (grid.begin(), grid.end()); bbt_nmarks = (beats + 2) * bbt_beat_subdivision; - bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ()); + bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ()); mark.label = ""; mark.position = lower; mark.style = ArdourCanvas::Ruler::Mark::Micro; marks.push_back (mark); - for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) { + for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) { if ((*i).frame < lower && (bbt_bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); @@ -1452,61 +1406,40 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l } /* Add the tick marks */ - - /* Find the next beat */ + skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision; next_beat.beats = (*i).beat; next_beat.bars = (*i).bar; + tick = skip; // the first non-beat tick + t = 0; + while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) { - if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) { - next_beat.beats += 1; - } else { - next_beat.bars += 1; - next_beat.beats = 1; - } - - next_beat_pos = _session->tempo_map().frame_time(next_beat); - - 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_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_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; - } - - if (pos > bbt_position_of_helper) { - snprintf (buf, sizeof(buf), "%" PRIu32, tick); - } else { - buf[0] = '\0'; - } + next_beat.ticks = tick; + pos = _session->tempo_map().frame_time (next_beat); + if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) { + i_am_accented = true; + } - mark.label = buf; + if (pos > bbt_position_of_helper) { + snprintf (buf, sizeof(buf), "%" PRIu32, tick); + } else { + buf[0] = '\0'; + } - /* Error compensation for float to framepos_t*/ - accumulated_error += frame_skip_error; - if (accumulated_error > 1) { - pos += 1; - accumulated_error -= 1.0f; - } + mark.label = buf; + mark.position = pos; - mark.position = pos; + if ((bbt_beat_subdivision > 4) && i_am_accented) { + mark.style = ArdourCanvas::Ruler::Mark::Minor; + } else { + mark.style = ArdourCanvas::Ruler::Mark::Micro; + } + i_am_accented = false; + marks.push_back (mark); - if ((bbt_beat_subdivision > 4) && i_am_accented) { - mark.style = ArdourCanvas::Ruler::Mark::Minor; - } else { - mark.style = ArdourCanvas::Ruler::Mark::Micro; - } - i_am_accented = false; - marks.push_back (mark); - n++; + tick += skip; + ++t; + ++n; } } @@ -1523,7 +1456,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l case bbt_show_64: bbt_nmarks = (gint) (bbt_bars / 64) + 1; - for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) { + for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) { if ((*i).is_bar()) { if ((*i).bar % 64 == 1) { if ((*i).bar % 256 == 1) { @@ -1548,7 +1481,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l case bbt_show_16: bbt_nmarks = (bbt_bars / 16) + 1; - for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) { + for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) { if ((*i).is_bar()) { if ((*i).bar % 16 == 1) { if ((*i).bar % 64 == 1) { @@ -1573,7 +1506,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l case bbt_show_4: bbt_nmarks = (bbt_bars / 4) + 1; - for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) { + for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) { if ((*i).is_bar()) { if ((*i).bar % 4 == 1) { if ((*i).bar % 16 == 1) { @@ -1599,7 +1532,7 @@ Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble l case bbt_show_1: // default: bbt_nmarks = bbt_bars + 2; - for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) { + for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) { if ((*i).is_bar()) { if ((*i).bar % 4 == 1) { snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar); diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index 38685e5dd6..2d5a3e47d5 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -114,13 +114,12 @@ Editor::tempo_map_changed (const PropertyChange& /*ignored*/) tempo_lines->tempo_map_changed(); } - ARDOUR::TempoMap::BBTPointList::const_iterator begin; - ARDOUR::TempoMap::BBTPointList::const_iterator end; + std::vector<TempoMap::BBTPoint> grid; - compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), begin, end); + compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples()); _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers - draw_measures (begin, end); - update_tempo_based_rulers (begin, end); + draw_measures (grid); + update_tempo_based_rulers (grid); } void @@ -131,32 +130,31 @@ Editor::redisplay_tempo (bool immediate_redraw) } if (immediate_redraw) { - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin; - ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end; + std::vector<TempoMap::BBTPoint> grid; - compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_samples(), - current_bbt_points_begin, current_bbt_points_end); - draw_measures (current_bbt_points_begin, current_bbt_points_end); - update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end); // redraw rulers and measures + compute_current_bbt_points (grid, leftmost_frame, leftmost_frame + current_page_samples()); + draw_measures (grid); + update_tempo_based_rulers (grid); // redraw rulers and measure lines } else { Glib::signal_idle().connect (sigc::bind_return (sigc::bind (sigc::mem_fun (*this, &Editor::redisplay_tempo), true), false)); } } +/* computes a grid starting a beat before and ending a beat after leftmost and rightmost respectively */ void -Editor::compute_current_bbt_points (framepos_t leftmost, framepos_t rightmost, - ARDOUR::TempoMap::BBTPointList::const_iterator& begin, - ARDOUR::TempoMap::BBTPointList::const_iterator& end) +Editor::compute_current_bbt_points (std::vector<TempoMap::BBTPoint>& grid, framepos_t leftmost, framepos_t rightmost) { if (!_session) { return; } + framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor(_session->tempo_map().beat_at_frame (leftmost))); + framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (_session->tempo_map().beat_at_frame (rightmost)) + 1.0); + /* prevent negative values of leftmost from creeping into tempomap */ - - _session->tempo_map().get_grid (begin, end, max (leftmost, (framepos_t) 0), rightmost); + _session->tempo_map().get_grid (grid, max (beat_before_lower_pos, (framepos_t) 0), beat_after_upper_pos); } void @@ -168,10 +166,9 @@ Editor::hide_measures () } void -Editor::draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin, - ARDOUR::TempoMap::BBTPointList::const_iterator& end) +Editor::draw_measures (std::vector<ARDOUR::TempoMap::BBTPoint>& grid) { - if (_session == 0 || _show_measures == false || distance (begin, end) == 0) { + if (_session == 0 || _show_measures == false || distance (grid.begin(), grid.end()) == 0) { return; } @@ -180,7 +177,7 @@ Editor::draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin, } const unsigned divisions = get_grid_beat_divisions(leftmost_frame); - tempo_lines->draw (begin, end, divisions, leftmost_frame, _session->frame_rate()); + tempo_lines->draw (grid, divisions, leftmost_frame, _session->frame_rate()); } void @@ -214,7 +211,7 @@ Editor::mouse_add_new_tempo_event (framepos_t frame) begin_reversible_command (_("add tempo mark")); XMLNode &before = map.get_state(); - map.add_tempo (Tempo (bpm,nt), requested); + map.add_tempo (Tempo (bpm,nt), requested, tempo_dialog.get_tempo_type()); XMLNode &after = map.get_state(); _session->add_command(new MementoCommand<TempoMap>(map, &before, &after)); commit_reversible_command (); @@ -329,7 +326,7 @@ Editor::edit_tempo_section (TempoSection* section) begin_reversible_command (_("replace tempo mark")); XMLNode &before = _session->tempo_map().get_state(); - _session->tempo_map().replace_tempo (*section, Tempo (bpm, nt), when); + _session->tempo_map().replace_tempo (*section, Tempo (bpm, nt), when, tempo_dialog.get_tempo_type()); XMLNode &after = _session->tempo_map().get_state(); _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after)); commit_reversible_command (); diff --git a/gtk2_ardour/tempo_dialog.cc b/gtk2_ardour/tempo_dialog.cc index a7a6f4ae75..0d6a6cc75e 100644 --- a/gtk2_ardour/tempo_dialog.cc +++ b/gtk2_ardour/tempo_dialog.cc @@ -106,12 +106,21 @@ TempoDialog::init (const Timecode::BBT_Time& when, double bpm, double note_type, pulse_selector.set_active_text (strings[3]); // "quarter" } + strings.clear(); + + tempo_types.insert (make_pair (_("ramped"), TempoSection::TempoSectionType::Ramp)); + strings.push_back (_("ramped")); + tempo_types.insert (make_pair (_("constant"), TempoSection::TempoSectionType::Constant)); + strings.push_back (_("constant")); + set_popdown_strings (tempo_type, strings); + tempo_type.set_active_text (strings[0]); // "ramped" + Table* table; if (UIConfiguration::instance().get_allow_non_quarter_pulse()) { - table = manage (new Table (5, 5)); + table = manage (new Table (5, 6)); } else { - table = manage (new Table (5, 4)); + table = manage (new Table (5, 5)); } table->set_spacings (6); @@ -156,8 +165,12 @@ TempoDialog::init (const Timecode::BBT_Time& when, double bpm, double note_type, table->attach (*when_label, 0, 1, row, row+1); } + Label* tempo_type_label = manage (new Label(_("Tempo Type:"), ALIGN_LEFT, ALIGN_CENTER)); + table->attach (*tempo_type_label, 0, 1, row+1, row+2); + table->attach (tempo_type, 1, 2, row+1, row + 2); get_vbox()->set_border_width (12); get_vbox()->pack_end (*table); + table->show_all (); add_button (Stock::CANCEL, RESPONSE_CANCEL); @@ -258,6 +271,19 @@ TempoDialog::get_note_type () return x->second; } +TempoSection::TempoSectionType +TempoDialog::get_tempo_type () +{ + TempoTypes::iterator x = tempo_types.find (tempo_type.get_active_text()); + + if (x == tempo_types.end()) { + error << string_compose(_("incomprehensible pulse note type (%1)"), tempo_type.get_active_text()) << endmsg; + return TempoSection::TempoSectionType::Constant; + } + + return x->second; +} + void TempoDialog::pulse_change () { diff --git a/gtk2_ardour/tempo_dialog.h b/gtk2_ardour/tempo_dialog.h index 06c5db196d..f04a5417f7 100644 --- a/gtk2_ardour/tempo_dialog.h +++ b/gtk2_ardour/tempo_dialog.h @@ -44,6 +44,7 @@ public: double get_bpm (); double get_note_type (); bool get_bbt_time (Timecode::BBT_Time&); + ARDOUR::TempoSection::TempoSectionType get_tempo_type (); private: void init (const Timecode::BBT_Time& start, double, double, bool); @@ -59,6 +60,9 @@ private: typedef std::map<std::string,float> NoteTypes; NoteTypes note_types; + typedef std::map<std::string, ARDOUR::TempoSection::TempoSectionType> TempoTypes; + TempoTypes tempo_types; + bool tapped; // whether the tap-tempo button has been clicked double sum_x, sum_xx, sum_xy, sum_y; double tap_count; @@ -74,6 +78,8 @@ private: Gtk::Label when_beat_label; Gtk::Label pulse_selector_label; Gtk::Button tap_tempo_button; + Gtk::ComboBoxText tempo_type; + }; class MeterDialog : public ArdourDialog diff --git a/gtk2_ardour/tempo_lines.cc b/gtk2_ardour/tempo_lines.cc index bb86011458..c320486108 100644 --- a/gtk2_ardour/tempo_lines.cc +++ b/gtk2_ardour/tempo_lines.cc @@ -54,12 +54,12 @@ TempoLines::hide () } void -TempoLines::draw_ticks (const ARDOUR::TempoMap::BBTPointList::const_iterator& b, - unsigned divisions, +TempoLines::draw_ticks (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, + unsigned divisions, framecnt_t leftmost_frame, framecnt_t frame_rate) { - const double fpb = b->tempo->frames_per_beat(frame_rate); + const double fpb = grid.begin()->tempo->frames_per_beat(frame_rate); const uint32_t base = UIConfiguration::instance().color_mod("measure line beat", "measure line beat"); for (unsigned l = 1; l < divisions; ++l) { @@ -74,7 +74,8 @@ TempoLines::draw_ticks (const ARDOUR::TempoMap::BBTPointList::const_iterator& b, /* draw line with alpha corresponding to coarsest level */ const uint8_t a = max(8, (int)rint(UINT_RGBA_A(base) / (0.8 * log2(level)))); const uint32_t c = UINT_RGBA_CHANGE_A(base, a); - const framepos_t f = b->frame + (l * (fpb / (double)divisions)); + const framepos_t f = grid.begin()->frame + (l * (fpb / (double)divisions)); + //const framepos_t f = frame_at_tick (last_beat_in_ticks + (l * (BBT_Time::ticks_per_beat / (double)divisions))); if (f > leftmost_frame) { lines.add (PublicEditor::instance().sample_to_pixel_unrounded (f), 1.0, c); } @@ -82,13 +83,12 @@ TempoLines::draw_ticks (const ARDOUR::TempoMap::BBTPointList::const_iterator& b, } void -TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, - const ARDOUR::TempoMap::BBTPointList::const_iterator& end, - unsigned divisions, +TempoLines::draw (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, + unsigned divisions, framecnt_t leftmost_frame, framecnt_t frame_rate) { - ARDOUR::TempoMap::BBTPointList::const_iterator i; + std::vector<ARDOUR::TempoMap::BBTPoint>::const_iterator i; double beat_density; uint32_t beats = 0; @@ -97,10 +97,10 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, /* get the first bar spacing */ - i = end; + i = grid.end(); i--; - bars = (*i).bar - (*begin).bar; - beats = distance (begin, end) - bars; + bars = (*i).bar - (*grid.begin()).bar; + beats = distance (grid.begin(), grid.end()) - bars; beat_density = (beats * 10.0f) / lines.canvas()->width(); @@ -116,15 +116,14 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, } lines.clear (); - - if (beat_density <= 0.12 && begin != end && begin->frame > 0) { - /* draw subdivisions of the beat before the first visible beat line */ - ARDOUR::TempoMap::BBTPointList::const_iterator prev = begin; - --prev; - draw_ticks(prev, divisions, leftmost_frame, frame_rate); + if (beat_density <= 0.12 && grid.begin() != grid.end() && grid.begin()->frame > 0) { + /* draw subdivisions of the beat before the first visible beat line XX this shouldn't happen now */ + std::vector<ARDOUR::TempoMap::BBTPoint> vec; + vec.push_back (*i); + draw_ticks (vec, divisions, leftmost_frame, frame_rate); } - for (i = begin; i != end; ++i) { + for (i = grid.begin(); i != grid.end(); ++i) { if ((*i).is_bar()) { color = UIConfiguration::instance().color ("measure line bar"); @@ -141,7 +140,10 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, if (beat_density <= 0.12) { /* draw subdivisions of this beat */ - draw_ticks(i, divisions, leftmost_frame, frame_rate); + std::vector<ARDOUR::TempoMap::BBTPoint> vec; + vec.push_back (*i); + + draw_ticks(vec, divisions, leftmost_frame, frame_rate); } } } diff --git a/gtk2_ardour/tempo_lines.h b/gtk2_ardour/tempo_lines.h index 6d40a2d1a7..7096028981 100644 --- a/gtk2_ardour/tempo_lines.h +++ b/gtk2_ardour/tempo_lines.h @@ -29,9 +29,8 @@ public: void tempo_map_changed(); - void draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin, - const ARDOUR::TempoMap::BBTPointList::const_iterator& end, - unsigned divisions, + void draw (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, + unsigned divisions, ARDOUR::framecnt_t leftmost_frame, ARDOUR::framecnt_t frame_rate); @@ -39,8 +38,8 @@ public: void hide(); private: - void draw_ticks (const ARDOUR::TempoMap::BBTPointList::const_iterator& b, - unsigned divisions, + void draw_ticks (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, + unsigned divisions, ARDOUR::framecnt_t leftmost_frame, ARDOUR::framecnt_t frame_rate); diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 1b43ff8c8c..9741d34adb 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -55,6 +55,7 @@ class LIBARDOUR_API Tempo { : _beats_per_minute (bpm), _note_type(type) {} double beats_per_minute () const { return _beats_per_minute;} + double ticks_per_minute () const { return _beats_per_minute * Timecode::BBT_Time::ticks_per_beat;} double note_type () const { return _note_type;} /** audio samples per beat * @param sr samplerate @@ -146,10 +147,15 @@ class LIBARDOUR_API MeterSection : public MetricSection, public Meter { /** A section of timeline with a certain Tempo. */ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { public: - TempoSection (const Timecode::BBT_Time& start, double qpm, double note_type) - : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0) {} - TempoSection (framepos_t start, double qpm, double note_type) - : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0) {} + enum TempoSectionType { + Ramp, + Constant, + }; + + TempoSection (const Timecode::BBT_Time& start, double qpm, double note_type, TempoSectionType tempo_type) + : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type) {} + TempoSection (framepos_t start, double qpm, double note_type, TempoSectionType tempo_type) + : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0), _type (tempo_type) {} TempoSection (const XMLNode&); static const std::string xml_state_node_name; @@ -160,7 +166,41 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { void update_bbt_time_from_bar_offset (const Meter&); double bar_offset() const { return _bar_offset; } + void set_type (TempoSectionType type); + TempoSectionType type () const { return _type; } + + double tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const; + framepos_t frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const; + + double tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const; + framepos_t frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const; + + double beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const; + framepos_t frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const; + private: + + framecnt_t minute_to_frame (double time, framecnt_t frame_rate) const; + double frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const; + + /* tempo ramp functions. zero-based with time in minutes, + * 'tick tempo' in ticks per minute and tempo in bpm. + * time relative to section start. + */ + double c_func (double end_tpm, double end_time) const; + double a_func (double begin_tpm, double end_tpm, double end_time) const; + + double tempo_at_time (double time, double end_bpm, double end_time) const; + double time_at_tempo (double tempo, double end_bpm, double end_time) const; + double tick_tempo_at_time (double time, double end_tpm, double end_time) const; + double time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const; + + double tick_at_time (double time, double end_tpm, double end_time) const; + double time_at_tick (double tick, double end_tpm, double end_time) const; + + double beat_at_time (double time, double end_tpm, double end_time) const; + double time_at_beat (double beat, double end_tpm, double end_time) const; + /* this value provides a fractional offset into the bar in which the tempo section is located in. A value of 0.0 indicates that it occurs on the first beat of the bar, a value of 0.5 indicates @@ -170,6 +210,7 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { position within the bar if/when the meter changes. */ double _bar_offset; + TempoSectionType _type; }; typedef std::list<MetricSection*> Metrics; @@ -231,11 +272,11 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible struct BBTPoint { framepos_t frame; const MeterSection* meter; - const TempoSection* tempo; + const Tempo* tempo; uint32_t bar; uint32_t beat; - BBTPoint (const MeterSection& m, const TempoSection& t, framepos_t f, + BBTPoint (const MeterSection& m, const Tempo& t, framepos_t f, uint32_t b, uint32_t e) : frame (f), meter (&m), tempo (&t), bar (b), beat (e) {} @@ -245,19 +286,18 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible bool is_bar() const { return beat == 1; } }; - typedef std::vector<BBTPoint> BBTPointList; - template<class T> void apply_with_metrics (T& obj, void (T::*method)(const Metrics&)) { Glib::Threads::RWLock::ReaderLock lm (lock); (obj.*method)(metrics); } - void get_grid (BBTPointList::const_iterator&, BBTPointList::const_iterator&, + void get_grid (std::vector<BBTPoint>&, framepos_t start, framepos_t end); /* TEMPO- AND METER-SENSITIVE FUNCTIONS - bbt_time(), bbt_time_rt(), frame_time() and bbt_duration_at() + bbt_time(), beat_at_frame(), frame_at_beat(), tick_at_frame(), + frame_at_tick(),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. @@ -267,11 +307,12 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible 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&); + double tick_at_frame (framecnt_t frame) const; + framecnt_t frame_at_tick (double tick) const; + + double beat_at_frame (framecnt_t frame) const; + framecnt_t frame_at_beat (double beat) const; + framepos_t frame_time (const Timecode::BBT_Time&); framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir); @@ -293,19 +334,19 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible static const Tempo& default_tempo() { return _default_tempo; } static const Meter& default_meter() { return _default_meter; } - const Tempo& tempo_at (framepos_t) const; + const Tempo tempo_at (framepos_t) const; const Meter& meter_at (framepos_t) const; const TempoSection& tempo_section_at (framepos_t) const; const MeterSection& meter_section_at (framepos_t) const; - void add_tempo (const Tempo&, Timecode::BBT_Time where); + void add_tempo (const Tempo&, Timecode::BBT_Time where, TempoSection::TempoSectionType type); void add_meter (const Meter&, Timecode::BBT_Time where); void remove_tempo (const TempoSection&, bool send_signal); void remove_meter (const MeterSection&, bool send_signal); - void replace_tempo (const TempoSection&, const Tempo&, const Timecode::BBT_Time& where); + void replace_tempo (const TempoSection&, const Tempo&, const Timecode::BBT_Time& where, TempoSection::TempoSectionType type); void replace_meter (const MeterSection&, const Meter&, const Timecode::BBT_Time& where); framepos_t round_to_bar (framepos_t frame, RoundMode dir); @@ -352,32 +393,26 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible Metrics metrics; framecnt_t _frame_rate; mutable Glib::Threads::RWLock lock; - BBTPointList _map; void recompute_map (bool reassign_tempo_bbt, framepos_t end = -1); - void extend_map (framepos_t end); - void require_map_to (framepos_t pos); - void require_map_to (const Timecode::BBT_Time&); + void _extend_map (TempoSection* tempo, MeterSection* meter, Metrics::iterator next_metric, - Timecode::BBT_Time current, framepos_t current_frame, framepos_t end); - - BBTPointList::const_iterator bbt_before_or_at (framepos_t); - BBTPointList::const_iterator bbt_before_or_at (const Timecode::BBT_Time&); - BBTPointList::const_iterator bbt_after_or_at (framepos_t); + Timecode::BBT_Time current, framepos_t current_frame, framepos_t end); framepos_t round_to_type (framepos_t fr, RoundMode dir, BBTPointType); - void bbt_time (framepos_t, Timecode::BBT_Time&, const BBTPointList::const_iterator&); - framecnt_t bbt_duration_at_unlocked (const Timecode::BBT_Time& when, const Timecode::BBT_Time& bbt, int dir); const MeterSection& first_meter() const; MeterSection& first_meter(); const TempoSection& first_tempo() const; TempoSection& first_tempo(); + Timecode::BBT_Time beats_to_bbt (double beats); + int32_t bars_in_meter_section (MeterSection* ms) const; + void do_insert (MetricSection* section); - void add_tempo_locked (const Tempo&, Timecode::BBT_Time where, bool recompute); + void add_tempo_locked (const Tempo&, Timecode::BBT_Time where, bool recompute, TempoSection::TempoSectionType type); void add_meter_locked (const Meter&, Timecode::BBT_Time where, bool recompute); bool remove_tempo_locked (const TempoSection&); diff --git a/libs/ardour/session_click.cc b/libs/ardour/session_click.cc index 4cb556c8a6..872828e57e 100644 --- a/libs/ardour/session_click.cc +++ b/libs/ardour/session_click.cc @@ -42,8 +42,7 @@ Pool Click::pool ("click", sizeof (Click), 1024); void Session::click (framepos_t start, framecnt_t nframes) { - TempoMap::BBTPointList::const_iterator points_begin; - TempoMap::BBTPointList::const_iterator points_end; + vector<TempoMap::BBTPoint> points; Sample *buf; framecnt_t click_distance; @@ -72,13 +71,13 @@ 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->get_grid (points_begin, points_end, start, end); + _tempo_map->get_grid (points, start, end); - if (distance (points_begin, points_end) == 0) { + if (distance (points.begin(), points.end()) == 0) { goto run_clicks; } - for (TempoMap::BBTPointList::const_iterator i = points_begin; i != points_end; ++i) { + for (vector<TempoMap::BBTPoint>::iterator i = points.begin(); i != points.end(); ++i) { switch ((*i).beat) { case 1: if (click_emphasis_data && Config->get_use_click_emphasis () == true) { diff --git a/libs/ardour/session_vst.cc b/libs/ardour/session_vst.cc index 21875ece46..34df6bd52e 100644 --- a/libs/ardour/session_vst.cc +++ b/libs/ardour/session_vst.cc @@ -223,9 +223,29 @@ intptr_t Session::vst_callback ( Timecode::BBT_Time bbt; try { - session->tempo_map().bbt_time_rt (now, bbt); - double ppqBar; - double ppqPos = vst_ppq (tm, bbt, ppqBar); + session->tempo_map().bbt_time (now, bbt); + + /* PPQ = pulse per quarter + * VST's "pulse" is our "division". + * + * 8 divisions per bar, 1 division = quarter, so 8 quarters per bar, ppq = 1 + * 8 divisions per bar, 1 division = eighth, so 4 quarters per bar, ppq = 2 + * 4 divisions per bar, 1 division = quarter, so 4 quarters per bar, ppq = 1 + * 4 divisions per bar, 1 division = half, so 8 quarters per bar, ppq = 0.5 + * 4 divisions per bar, 1 division = fifth, so (4 * 5/4) quarters per bar, ppq = 5/4 + * + * general: divs_per_bar / (note_type / 4.0) + */ + double ppq_scaling = tm.meter().note_divisor() / 4.0; + + /* Note that this assumes constant meter/tempo throughout the session. Stupid VST */ + double ppqBar = double(bbt.bars - 1) * tm.meter().divisions_per_bar(); + double ppqBeat = double(bbt.beats - 1); + double ppqTick = double(bbt.ticks) / Timecode::BBT_Time::ticks_per_beat; + + ppqBar *= ppq_scaling; + ppqBeat *= ppq_scaling; + ppqTick *= ppq_scaling; if (value & (kVstPpqPosValid)) { timeinfo->ppqPos = ppqPos; diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 445fc7bab3..383abfa3af 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -126,6 +126,16 @@ TempoSection::TempoSection (const XMLNode& node) throw failed_constructor(); } } + + if ((prop = node.property ("tempo-type")) == 0) { + _type = TempoSectionType::Ramp; + } else { + if (strstr(prop->value().c_str(),"Constant")) { + _type = TempoSectionType::Constant; + } else { + _type = TempoSectionType::Ramp; + } + } } XMLNode& @@ -149,6 +159,9 @@ TempoSection::get_state() const snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no"); root->add_property ("movable", buf); + snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp"); + root->add_property ("tempo-type", buf); + return *root; } @@ -163,6 +176,143 @@ TempoSection::update_bar_offset_from_bbt (const Meter& m) } void +TempoSection::set_type (TempoSectionType type) +{ + _type = type; +} + +double +TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const +{ + + if (_type == Constant) { + return beats_per_minute(); + } + + return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat; +} + +framepos_t +TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const +{ + if (_type == Constant) { + return 0; + } + + return minute_to_frame (time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate); +} + +double +TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const +{ + if (_type == Constant) { + return frame / frames_per_beat (frame_rate); + } + + return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)); +} + +framepos_t +TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const +{ + if (_type == Constant) { + return (framepos_t) floor (tick * frames_per_beat(frame_rate)); + } + + return minute_to_frame (time_at_tick (tick, end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate); +} + +double TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const +{ + return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat; +} + +framepos_t TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const +{ + return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate); +} + +framecnt_t +TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const +{ + return time * 60.0 * frame_rate; +} + +double +TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const +{ + return (frame / (double) frame_rate) / 60.0; +} + +/* constant for exp */ +double +TempoSection::a_func (double begin_tpm, double end_tpm, double end_time) const +{ + return log (end_tpm / ticks_per_minute()) / c_func (end_tpm, end_time); +} +double +TempoSection::c_func (double end_tpm, double end_time) const +{ + return log (end_tpm / ticks_per_minute()) / end_time; +} + +/* tempo in tpm at time in minutes */ +double +TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const +{ + return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute(); +} + +/* time in minutes at tempo in tpm */ +double +TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const +{ + return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time); +} + +/* tempo in bpm at time in minutes */ +double +TempoSection::tempo_at_time (double time, double end_bpm, double end_time) const +{ + return tick_tempo_at_time (time, end_bpm * BBT_Time::ticks_per_beat, end_time) / BBT_Time::ticks_per_beat; +} + +/* time in minutes at tempo in bpm */ +double +TempoSection::time_at_tempo (double tempo, double end_bpm, double end_time) const +{ + return time_at_tick_tempo (tempo * BBT_Time::ticks_per_beat, end_bpm * BBT_Time::ticks_per_beat, end_time); +} + +/* tick at time in minutes */ +double +TempoSection::tick_at_time (double time, double end_tpm, double end_time) const +{ + return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time); +} + +/* time in minutes at tick */ +double +TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const +{ + return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time); +} + +/* beat at time in minutes */ +double +TempoSection::beat_at_time (double time, double end_tpm, double end_time) const +{ + return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat; +} + +/* time in munutes at beat */ +double +TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const +{ + return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time); +} + +void TempoSection::update_bbt_time_from_bar_offset (const Meter& meter) { BBT_Time new_start; @@ -180,7 +330,6 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter) /* remember the 1-based counting properties of beats */ new_start.beats += 1; - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n", _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats)); @@ -284,8 +433,7 @@ TempoMap::TempoMap (framecnt_t fr) start.beats = 1; start.ticks = 0; - // these leak memory, well Metrics does - TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type()); + TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::TempoSectionType::Ramp); MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor()); t->set_movable (false); @@ -489,15 +637,16 @@ TempoMap::do_insert (MetricSection* section) } void -TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where) +TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where, TempoSection::TempoSectionType type) { { Glib::Threads::RWLock::WriterLock lm (lock); TempoSection& first (first_tempo()); + TempoSection::TempoSectionType tt = first.type(); if (ts.start() != first.start()) { remove_tempo_locked (ts); - add_tempo_locked (tempo, where, true); + add_tempo_locked (tempo, where, true, tt); } else { { /* cannot move the first tempo section */ @@ -511,11 +660,11 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T } void -TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) +TempoMap::add_tempo (const Tempo& tempo, BBT_Time where, ARDOUR::TempoSection::TempoSectionType type) { { Glib::Threads::RWLock::WriterLock lm (lock); - add_tempo_locked (tempo, where, true); + add_tempo_locked (tempo, where, true, type); } @@ -523,12 +672,11 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) } void -TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute) +TempoMap::add_tempo_locked (const Tempo& tempo, BBT_Time where, bool recompute, ARDOUR::TempoSection::TempoSectionType type) { /* new tempos always start on a beat */ where.ticks = 0; - - TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()); + TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type); /* find the meter to use to set the bar offset of this * tempo section. @@ -768,38 +916,6 @@ TempoMap::first_tempo () } void -TempoMap::require_map_to (framepos_t pos) -{ - Glib::Threads::RWLock::WriterLock lm (lock); - - if (_map.empty() || _map.back().frame < pos) { - extend_map (pos); - } -} - -void -TempoMap::require_map_to (const BBT_Time& bbt) -{ - Glib::Threads::RWLock::WriterLock lm (lock); - - /* since we have no idea where BBT is if its off the map, see the last - * point in the map is past BBT, and if not add an arbitrary amount of - * time until it is. - */ - - int additional_minutes = 1; - - while (1) { - if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) { - break; - } - /* add some more distance, using bigger steps each time */ - extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes)); - additional_minutes *= 2; - } -} - -void TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) { /* CALLER MUST HOLD WRITE LOCK */ @@ -818,10 +934,12 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) end = max_framepos; } else { + /* if (!_map.empty ()) { - /* never allow the map to be shortened */ + /* never allow the map to be shortened / end = max (end, _map.back().frame); } + */ } DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end)); @@ -857,7 +975,6 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) current.bars = 1; current.beats = 1; current.ticks = 0; - if (reassign_tempo_bbt) { MeterSection* rmeter = meter; @@ -892,10 +1009,7 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) ++next_metric; // skip meter (or tempo) ++next_metric; // skip tempo (or meter) - _map.clear (); - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame)); - _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1)); if (end == 0) { /* silly call from Session::process() during startup @@ -907,208 +1021,72 @@ TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) } void -TempoMap::extend_map (framepos_t end) -{ - /* CALLER MUST HOLD WRITE LOCK */ - - if (_map.empty()) { - recompute_map (false, end); - return; - } - - BBTPointList::const_iterator i = _map.end(); - Metrics::iterator next_metric; - - --i; - - BBT_Time last_metric_start; - - if ((*i).tempo->frame() > (*i).meter->frame()) { - last_metric_start = (*i).tempo->start(); - } else { - last_metric_start = (*i).meter->start(); - } - - /* find the metric immediately after the tempo + meter sections for the - * last point in the map - */ - - for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) { - if ((*next_metric)->start() > last_metric_start) { - break; - } - } - - /* we cast away const here because this is the one place where we need - * to actually modify the frame time of each metric section. - */ - - _extend_map (const_cast<TempoSection*> ((*i).tempo), - const_cast<MeterSection*> ((*i).meter), - next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end); -} - -void TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, Metrics::iterator next_metric, BBT_Time current, framepos_t current_frame, framepos_t end) { /* CALLER MUST HOLD WRITE LOCK */ - TempoSection* ts; - MeterSection* ms; - double beat_frames; - double current_frame_exact; - framepos_t bar_start_frame; - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame)); - - if (current.beats == 1) { - bar_start_frame = current_frame; - } else { - bar_start_frame = 0; - } - - beat_frames = meter->frames_per_grid (*tempo,_frame_rate); - current_frame_exact = current_frame; - - while (current_frame < end) { - - current.beats++; - current_frame_exact += beat_frames; - current_frame = llrint(current_frame_exact); - - if (current.beats > meter->divisions_per_bar()) { - current.bars++; - current.beats = 1; - } - - if (next_metric != metrics.end()) { - - /* no operator >= so invert operator < */ - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start())); - - if (!(current < (*next_metric)->start())) { + uint32_t first_tick_in_new_meter = 0; + Metrics::const_iterator i; + TempoSection* prev_ts = tempo; - set_metrics: - if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) { + for (i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* m = 0; - tempo = ts; + if ((m = dynamic_cast<MeterSection*> (*i)) != 0) { - /* new tempo section: if its on a beat, - * we don't have to do anything other - * than recompute various distances, - * done further below as we transition - * the next metric section. - * - * if its not on the beat, we have to - * compute the duration of the beat it - * is within, which will be different - * from the preceding following ones - * since it takes part of its duration - * from the preceding tempo and part - * from this new tempo. - */ + if (m->start() >= prev_ts->start()) { + first_tick_in_new_meter = ((((m->start().bars - 1) * meter->divisions_per_bar()) + (m->start().beats - 1)) * BBT_Time::ticks_per_beat) + m->start().ticks; // expressed in ticks from the previous meter + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; - if (tempo->start().ticks != 0) { + if ((t = dynamic_cast<TempoSection*> (*i)) != 0) { - double next_beat_frames = tempo->frames_per_beat (_frame_rate); + if (t->start() >= m->start() && t->start() > prev_ts->start()) { + //cerr << "new ts start bars = " << t->start().bars << " beats = " << t->start().beats << " ticks = " << t->start().ticks << endl; + //cerr << "prev ts start bars = " << prev_ts->start().bars << " beats = " << prev_ts->start().beats << " ticks = " << prev_ts->start().ticks << endl; - 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())); + /*tempo section (t) lies in the previous meter */ + double ticks_at_ts = ((((t->start().bars - 1 ) * meter->divisions_per_bar()) + (t->start().beats - 1) ) * BBT_Time::ticks_per_beat) + t->start().ticks; - /* back up to previous beat */ - current_frame_exact -= beat_frames; - current_frame = llrint(current_frame_exact); - /* set tempo section location - * based on offset from last - * bar start - */ - tempo->set_frame (bar_start_frame + - llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames))); + double ticks_at_prev_ts = ((((prev_ts->start().bars - 1) * meter->divisions_per_bar()) + (prev_ts->start().beats - 1)) * BBT_Time::ticks_per_beat) + prev_ts->start().ticks; - /* advance to the location of - * the new (adjusted) beat. do - * this by figuring out the - * offset within the beat that - * would have been there - * without the tempo - * change. then stretch the - * beat accordingly. - */ + double ticks_relative_to_prev_ts = ticks_at_ts - ticks_at_prev_ts; + /* assume (falsely) that the target tempo is constant */ + double length_estimate = (ticks_relative_to_prev_ts / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate); + double system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()); + cerr << " system_precision_at_target_tempo = " << system_precision_at_target_tempo << endl; + double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf - double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames; + while (fabs (tick_error) >= system_precision_at_target_tempo) { - current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames); - current_frame = llrint(current_frame_exact); + double actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate); + tick_error = ticks_relative_to_prev_ts - actual_ticks; + length_estimate += (tick_error / BBT_Time::ticks_per_beat) * meter->frames_per_grid (*t, _frame_rate); + cerr << "actual ticks = " << actual_ticks << endl; - /* next metric doesn't have to - * match this precisely to - * merit a reloop ... - */ - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame)); + cerr << "tick error = " << tick_error << endl; + } + t->set_frame (length_estimate + prev_ts->frame()); - } else { - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n", - tempo->start(), current_frame)); - tempo->set_frame (current_frame); + if (m->start() < t->start() && m->start() == prev_ts->start()) { + m->set_frame (prev_ts->frame()); + } else if (m->start() < t->start() && m->start() > prev_ts->start()) { + m->set_frame (prev_ts->frame_at_tick ((first_tick_in_new_meter - ticks_at_prev_ts), t->beats_per_minute(), (framepos_t) length_estimate, _frame_rate)); + } + } + prev_ts = t; } - - } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) { - - meter = ms; - - /* new meter section: always defines the - * start of a bar. - */ - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n", - meter->start(), current, current_frame)); - - assert (current.beats == 1); - - meter->set_frame (current_frame); - } - - 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, meter->divisions_per_bar(), *((Meter*)meter), *((Tempo*)tempo))); - - ++next_metric; - - if (next_metric != metrics.end() && ((*next_metric)->start() == current)) { - /* same position so go back and set this one up before advancing - */ - goto set_metrics; } - - } - } - - if (current.beats == 1) { - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame)); - _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1)); - bar_start_frame = current_frame; - } else { - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame)); - _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats)); - } - - if (next_metric == metrics.end()) { - /* no more metrics - we've timestamped them all, stop here */ - if (end == max_framepos) { - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stop extending map now that we've reach the end @ %1|%2 = %3\n", - current.bars, current.beats, current_frame)); - break; } + meter = m; } } } + TempoMetric TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const { @@ -1168,8 +1146,6 @@ TempoMap::metric_at (BBT_Time bbt) const void TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) { - require_map_to (frame); - Glib::Threads::RWLock::ReaderLock lm (lock); if (frame < 0) { @@ -1179,40 +1155,262 @@ TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt) warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg; return; } + bbt = beats_to_bbt (beat_at_frame (frame)); +} - return bbt_time (frame, bbt, bbt_before_or_at (frame)); +int32_t +TempoMap::bars_in_meter_section (MeterSection* ms) const +{ + /* YOU MUST HAVE THE READ LOCK */ + Metrics::const_iterator i; + + MeterSection* next_ms = 0; + const MeterSection* prev_ms = &first_meter(); + + for (i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* m; + if ((m = dynamic_cast<MeterSection*> (*i)) != 0) { + if (ms->frame() < m->frame()) { + next_ms = m; + break; + } + prev_ms = m; + } + } + if (next_ms) { + double ticks_at_next = tick_at_frame (next_ms->frame()); + double ticks_at_prev = tick_at_frame (prev_ms->frame()); + double ticks_in_meter = ticks_at_next - ticks_at_prev; + + return (int32_t) floor ((ticks_in_meter / BBT_Time::ticks_per_beat) / prev_ms->note_divisor()); + } + return -1; } -void -TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt) +Timecode::BBT_Time +TempoMap::beats_to_bbt (double beats) { - Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK); + /* CALLER HOLDS READ LOCK */ + BBT_Time ret; + MeterSection* prev_ms = &first_meter(); - if (!lm.locked()) { - throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map"); + framecnt_t frame = frame_at_beat (beats); + uint32_t cnt = 0; + + if (n_meters() < 2) { + uint32_t bars = (uint32_t) floor (beats / prev_ms->note_divisor()); + double remaining_beats = beats - (bars * prev_ms->note_divisor()); + double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat; + + ret.ticks = (uint32_t) floor (remaining_ticks + 0.5); + ret.beats = (uint32_t) floor (remaining_beats); + ret.bars = bars; + + /* 0 0 0 to 1 1 0 - based mapping*/ + ++ret.bars; + ++ret.beats; + + if (ret.ticks >= BBT_Time::ticks_per_beat) { + ++ret.beats; + ret.ticks -= BBT_Time::ticks_per_beat; + } + + if (ret.beats > prev_ms->note_divisor()) { + ++ret.bars; + ret.beats = 1; + } + + return ret; } - if (_map.empty() || _map.back().frame < frame) { - throw std::logic_error (string_compose ("map not long enough to reach %1", frame)); + uint32_t first_beat_in_meter = 0; + uint32_t accumulated_bars = 0; + Metrics::const_iterator i; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* m = 0; + + if ((m = dynamic_cast<MeterSection*> (*i)) != 0) { + first_beat_in_meter = beat_at_frame (m->frame()); + + if (beats < first_beat_in_meter) { + /* this is the meter after the one our beat is on*/ + break; + } + int32_t const bars_in_ms = bars_in_meter_section (m); + + if (bars_in_ms > 0) { + accumulated_bars += bars_in_ms; + } + + prev_ms = m; + ++cnt; + } + } + //cerr << "beats to bbr with beats = " << beats << " first_beat_in_meter = " << first_beat_in_meter << " accumulated_bars = " << accumulated_bars << endl; + + if (beats > first_beat_in_meter) { + /* prev_ms is the relevant one here */ + + /* now get the ticks at frame */ + double ticks_at_frame = tick_at_frame (frame); + + /* find the number of ticks at the beginning of the meter section (bar 1)*/ + double ticks_at_ms = tick_at_frame (prev_ms->frame()); + + double beats_used_by_ms = (ticks_at_frame - ticks_at_ms) / BBT_Time::ticks_per_beat; + + uint32_t bars = (uint32_t) floor (beats_used_by_ms / prev_ms->note_divisor()); + double remaining_beats = beats_used_by_ms - (bars * prev_ms->note_divisor()); + double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat; + + ret.bars = bars + accumulated_bars; + ret.beats = (uint32_t) floor (remaining_beats); + ret.ticks = (uint32_t) floor (remaining_ticks + 0.5); + + /* now ensure we srtart at 1 1 0 */ + ++ret.bars; + ++ret.beats; + //cerr << "part 1 ret bars = " << ret.bars << " ret beats = " << ret.beats << " ret ticks = " << ret.ticks << endl; + if (ret.ticks >= BBT_Time::ticks_per_beat) { + ++ret.beats; + ret.ticks -= BBT_Time::ticks_per_beat; + } + + if (ret.beats > prev_ms->note_divisor()) { + ++ret.bars; + ret.beats = 1; + } + + return ret; + } + + /* find the number of ticks at the beginning of the meter section (bar 1)*/ + double ticks_at_ms = tick_at_frame (prev_ms->frame()); + + /* now get the ticks at frame */ + double ticks_at_frame = tick_at_frame (frame); + + double ticks_within_ms = ticks_at_frame - ticks_at_ms; + + ret.bars = (uint32_t) floor (((ticks_within_ms / BBT_Time::ticks_per_beat) / prev_ms->note_divisor())) + accumulated_bars; + uint32_t remaining_ticks = ticks_within_ms - (ret.bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat); + ret.beats = (uint32_t) floor (remaining_ticks); + remaining_ticks -= ret.beats * BBT_Time::ticks_per_beat; + + /* only round ticks */ + ret.ticks = (uint32_t) floor (remaining_ticks + 0.5); + + /* now ensure we srtart at 1 1 0 */ + ++ret.bars; + ++ret.beats; + if (ret.ticks >= BBT_Time::ticks_per_beat) { + ++ret.beats; + ret.ticks -= BBT_Time::ticks_per_beat; } - return bbt_time (frame, bbt, bbt_before_or_at (frame)); + if (ret.beats > prev_ms->note_divisor()) { + ++ret.bars; + ret.beats = 1; + } + + return ret; } -void -TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i) +double +TempoMap::tick_at_frame (framecnt_t frame) const { - /* CALLER MUST HOLD READ LOCK */ + Metrics::const_iterator i; + const TempoSection* prev_ts = &first_tempo(); + double accumulated_ticks = 0.0; + uint32_t cnt = 0; - bbt.bars = (*i).bar; - bbt.beats = (*i).beat; + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; - if ((*i).frame == frame) { - bbt.ticks = 0; - } else { - bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) * - BBT_Time::ticks_per_beat); + if ((t = dynamic_cast<TempoSection*> (*i)) != 0) { + + if (frame < t->frame()) { + /*the previous ts is the one containing the frame */ + + framepos_t time = frame - prev_ts->frame(); + framepos_t last_frame = t->frame() - prev_ts->frame(); + double last_beats_per_minute = t->beats_per_minute(); + + return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks; + } + + if (cnt > 0 && t->frame() > prev_ts->frame()) { + framepos_t time = t->frame() - prev_ts->frame(); + framepos_t last_frame = t->frame() - prev_ts->frame(); + double last_beats_per_minute = t->beats_per_minute(); + accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate); + } + + prev_ts = t; + ++cnt; + } + } + + /* treated s linear for this ts */ + framecnt_t frames_in_section = frame - prev_ts->frame(); + double ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat; + + return ticks_in_section + accumulated_ticks; + +} + +framecnt_t +TempoMap::frame_at_tick (double tick) const +{ + double accumulated_ticks = 0.0; + const TempoSection* prev_ts = &first_tempo(); + uint32_t cnt = 0; + + Metrics::const_iterator i; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; + if ((t = dynamic_cast<TempoSection*> (*i)) != 0) { + + if (cnt > 0 && t->frame() > prev_ts->frame()) { + framepos_t time = t->frame() - prev_ts->frame(); + framepos_t last_time = t->frame() - prev_ts->frame(); + double last_beats_per_minute = t->beats_per_minute(); + accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate); + } + + if (tick < accumulated_ticks) { + /* prev_ts is the one affecting us. */ + + double ticks_in_section = tick - tick_at_frame (prev_ts->frame()); + framepos_t section_start = prev_ts->frame(); + framepos_t last_time = t->frame() - prev_ts->frame(); + double last_beats_per_minute = t->beats_per_minute(); + return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start; + } + + prev_ts = t; + ++cnt; + } } + double ticks_in_section = tick - tick_at_frame (prev_ts->frame()); + double dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate); + framecnt_t ret = ((framecnt_t) floor (dtime)) + prev_ts->frame(); + + return ret; +} + +double +TempoMap::beat_at_frame (framecnt_t frame) const +{ + return tick_at_frame (frame) / BBT_Time::ticks_per_beat; +} + +framecnt_t +TempoMap::frame_at_beat (double beat) const +{ + return frame_at_tick (beat * BBT_Time::ticks_per_beat); } framepos_t @@ -1227,69 +1425,112 @@ TempoMap::frame_time (const BBT_Time& bbt) throw std::logic_error ("beats are counted from one"); } - require_map_to (bbt); - Glib::Threads::RWLock::ReaderLock lm (lock); - BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0)); - BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0)); + Metrics::const_iterator i; + uint32_t accumulated_bars = 0; - if (bbt.ticks != 0) { - return ((*e).frame - (*s).frame) + - llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat)); - } else { - return ((*e).frame - (*s).frame); + MeterSection* prev_ms = &first_meter(); + + for (i = metrics.begin(); i != metrics.end(); ++i) { + MeterSection* m; + if ((m = dynamic_cast<MeterSection*> (*i)) != 0) { + int32_t const bims = bars_in_meter_section (m); + + if (bims < 0 || bbt.bars <= (accumulated_bars + bims)) { + break; + } + if (bims > 0 ) { + accumulated_bars += bims; + } + prev_ms = m; + } } + + uint32_t remaining_bars = bbt.bars - accumulated_bars - 1; // back to zero - based bars + double const ticks_within_prev_taken_by_remaining_bars = remaining_bars * prev_ms->note_divisor() * BBT_Time::ticks_per_beat; + double const ticks_after_space_used_by_bars = ((bbt.beats - 1) * BBT_Time::ticks_per_beat) + bbt.ticks; // back to zero - based beats + double const ticks_target = ticks_within_prev_taken_by_remaining_bars + ticks_after_space_used_by_bars; + + TempoSection* prev_ts = &first_tempo(); + double accumulated_ticks = 0.0; + uint32_t cnt = 0; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; + if ((t = dynamic_cast<TempoSection*> (*i)) != 0) { + if (t->frame() < prev_ms->frame()) { + continue; + } + + if (cnt > 0 && t->frame() > prev_ts->frame()) { + /*find the number of ticke in this section */ + framepos_t const time = t->frame() - prev_ts->frame(); + framepos_t const last_time = t->frame() - prev_ts->frame(); + double const last_beats_per_minute = t->beats_per_minute(); + accumulated_ticks += prev_ts->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate); + } + + if (ticks_target < accumulated_ticks) { + double const ticks_in_section = ticks_target - tick_at_frame (prev_ts->frame()); + framepos_t const section_start_time = prev_ts->frame(); + framepos_t const last_time = t->frame() - prev_ts->frame(); + double const last_beats_per_minute = t->beats_per_minute(); + framepos_t const ret = prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + section_start_time; + return ret; + } + + prev_ts = t; + ++cnt; + } + } + + /*treat this ts as constant tempo */ + double const ticks_in_this_ts = ticks_target - tick_at_frame (prev_ts->frame()); + double const dtime = (ticks_in_this_ts / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat(_frame_rate); + framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame(); + return ret; } + framecnt_t TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) { - BBT_Time when; - bbt_time (pos, when); Glib::Threads::RWLock::ReaderLock lm (lock); - return bbt_duration_at_unlocked (when, bbt, dir); -} -framecnt_t -TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int /*dir*/) -{ - if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) { - return 0; - } + Metrics::const_iterator i; + TempoSection* first = 0; + TempoSection* second = 0; - /* round back to the previous precise beat */ - BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0)); - BBTPointList::const_iterator start (wi); + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; - assert (wi != _map.end()); + if ((t = dynamic_cast<TempoSection*> (*i)) != 0) { - uint32_t bars = 0; - uint32_t beats = 0; + if ((*i)->frame() > pos) { + second = t; + break; + } - while (wi != _map.end() && bars < bbt.bars) { - ++wi; - if ((*wi).is_bar()) { - ++bars; + first = t; } } - assert (wi != _map.end()); + if (first && second) { + framepos_t const last_time = second->frame() - first->frame(); + double const last_beats_per_minute = second->beats_per_minute(); - while (wi != _map.end() && beats < bbt.beats) { - ++wi; - ++beats; - } - assert (wi != _map.end()); + framepos_t const time = pos - first->frame(); + double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate); + double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat); - /* add any additional frames related to ticks in the added value */ + double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate); - if (bbt.ticks != 0) { - return ((*wi).frame - (*start).frame) + - (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat); - } else { - return ((*wi).frame - (*start).frame); + return time_at_bbt - time; } + + double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat); + return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate)); } framepos_t @@ -1307,52 +1548,38 @@ TempoMap::round_to_beat (framepos_t fr, RoundMode dir) framepos_t TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir) { - require_map_to (fr); - - Glib::Threads::RWLock::ReaderLock lm (lock); - BBTPointList::const_iterator i = bbt_before_or_at (fr); - BBT_Time the_beat; - uint32_t ticks_one_subdivisions_worth; - - bbt_time (fr, the_beat, i); + uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5); + uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat); + uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num; - 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_beat / sub_num; + ticks -= beats * BBT_Time::ticks_per_beat; if (dir > 0) { - /* round to next (or same iff dir == RoundUpMaybe) */ - uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth; + uint32_t mod = ticks % ticks_one_subdivisions_worth; if (mod == 0 && dir == RoundUpMaybe) { /* right on the subdivision, which is fine, so do nothing */ } else if (mod == 0) { /* right on the subdivision, so the difference is just the subdivision ticks */ - the_beat.ticks += ticks_one_subdivisions_worth; + ticks += ticks_one_subdivisions_worth; } else { /* not on subdivision, compute distance to next subdivision */ - the_beat.ticks += ticks_one_subdivisions_worth - mod; + ticks += ticks_one_subdivisions_worth - mod; } - 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_beat; + if (ticks >= BBT_Time::ticks_per_beat) { + ticks -= BBT_Time::ticks_per_beat; } - - } else if (dir < 0) { /* round to previous (or same iff dir == RoundDownMaybe) */ - uint32_t difference = the_beat.ticks % ticks_one_subdivisions_worth; + uint32_t difference = ticks % ticks_one_subdivisions_worth; if (difference == 0 && dir == RoundDownAlways) { /* right on the subdivision, but force-rounding down, @@ -1360,236 +1587,136 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir) difference = ticks_one_subdivisions_worth; } - if (the_beat.ticks < difference) { - if (i == _map.begin()) { - /* can't go backwards from wherever pos is, so just return it */ - return fr; - } - --i; - the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks; + if (ticks < difference) { + ticks = BBT_Time::ticks_per_beat - ticks; } else { - the_beat.ticks -= difference; + ticks -= difference; } } else { /* round to nearest */ - double rem; /* compute the distance to the previous and next subdivision */ - if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) { + if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) { /* closer to the next subdivision, so shift forward */ - the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem)); + ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem)); - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks)); + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks)); - 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_beat; - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat)); + if (ticks > BBT_Time::ticks_per_beat) { + ++beats; + ticks -= BBT_Time::ticks_per_beat; + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats)); } } else if (rem > 0) { /* closer to previous subdivision, so shift backward */ - if (rem > the_beat.ticks) { - if (i == _map.begin()) { + if (rem > ticks) { + if (beats == 0) { /* can't go backwards past zero, so ... */ return 0; } /* step back to previous beat */ - --i; - the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem); - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat)); + --beats; + ticks = lrint (BBT_Time::ticks_per_beat - rem); + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats)); } else { - the_beat.ticks = lrint (the_beat.ticks - rem); - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks)); + ticks = lrint (ticks - rem); + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks)); } } else { /* on the subdivision, do nothing */ } } - - return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) * - (*i).tempo->frames_per_beat (_frame_rate); + return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks); } framepos_t TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type) { - require_map_to (frame); - Glib::Threads::RWLock::ReaderLock lm (lock); - BBTPointList::const_iterator fi; - - if (dir > 0) { - fi = bbt_after_or_at (frame); - } else { - fi = bbt_before_or_at (frame); - } - assert (fi != _map.end()); + double const beat_at_framepos = beat_at_frame (frame); - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to %6 in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame, - (type == Bar ? "bar" : "beat"))); + BBT_Time bbt (beats_to_bbt (beat_at_framepos)); switch (type) { case Bar: if (dir < 0) { /* find bar previous to 'frame' */ - - if (fi == _map.begin()) { - return 0; - } - - if ((*fi).is_bar() && (*fi).frame == frame) { - if (dir == RoundDownMaybe) { - return frame; - } - --fi; - } - - while (!(*fi).is_bar()) { - if (fi == _map.begin()) { - break; - } - fi--; - } - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", - (*fi).bar, (*fi).beat, (*fi).frame)); - return (*fi).frame; + bbt.beats = 1; + bbt.ticks = 0; + return frame_time (bbt); } else if (dir > 0) { - /* find bar following 'frame' */ - - if ((*fi).is_bar() && (*fi).frame == frame) { - if (dir == RoundUpMaybe) { - return frame; - } - ++fi; - } - - while (!(*fi).is_bar()) { - fi++; - if (fi == _map.end()) { - --fi; - break; - } - } - - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", - (*fi).bar, (*fi).beat, (*fi).frame)); - return (*fi).frame; - + ++bbt.bars; + bbt.beats = 1; + bbt.ticks = 0; + return frame_time (bbt); } else { - /* true rounding: find nearest bar */ - BBTPointList::const_iterator prev = fi; - BBTPointList::const_iterator next = fi; + framepos_t raw_ft = frame_time (bbt); + bbt.beats = 1; + bbt.ticks = 0; + framepos_t prev_ft = frame_time (bbt); + ++bbt.bars; + framepos_t next_ft = frame_time (bbt); - if ((*fi).frame == frame) { - return frame; - } - - while ((*prev).beat != 1) { - if (prev == _map.begin()) { - break; - } - prev--; - } - - while ((next != _map.end()) && (*next).beat != 1) { - next++; - } - - if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) { - return (*prev).frame; + if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { + return next_ft; } else { - return (*next).frame; + return prev_ft; } - } break; case Beat: if (dir < 0) { - - if (fi == _map.begin()) { - return 0; - } - - if ((*fi).frame > frame || ((*fi).frame == frame && dir == RoundDownAlways)) { - DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n"); - --fi; - } - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", - (*fi).bar, (*fi).beat, (*fi).frame)); - return (*fi).frame; + return frame_at_beat (floor (beat_at_framepos)); } else if (dir > 0) { - if ((*fi).frame < frame || ((*fi).frame == frame && dir == RoundUpAlways)) { - DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n"); - ++fi; - } - DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", - (*fi).bar, (*fi).beat, (*fi).frame)); - return (*fi).frame; + return frame_at_beat (ceil (beat_at_framepos)); } else { - /* find beat nearest to frame */ - if ((*fi).frame == frame) { - return frame; - } - - BBTPointList::const_iterator prev = fi; - BBTPointList::const_iterator next = fi; - - /* fi is already the beat before_or_at frame, and - we've just established that its not at frame, so its - the beat before frame. - */ - ++next; - - if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) { - return (*prev).frame; - } else { - return (*next).frame; - } + return frame_at_beat (floor (beat_at_framepos + 0.5)); } break; } - abort(); /* NOTREACHED */ return 0; } void -TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, - TempoMap::BBTPointList::const_iterator& end, +TempoMap::get_grid (vector<TempoMap::BBTPoint>& points, framepos_t lower, framepos_t upper) { - { - Glib::Threads::RWLock::WriterLock lm (lock); - if (_map.empty() || (_map.back().frame < upper)) { - recompute_map (false, upper); - } - } + Glib::Threads::RWLock::ReaderLock lm (lock); + uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper)); + uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower)); + + while (cnt <= upper_beat) { + framecnt_t const pos = frame_at_beat (cnt); + MeterSection const meter = meter_section_at (pos); + Tempo const tempo = tempo_at (pos); + BBT_Time const bbt = beats_to_bbt ((double) cnt); - begin = lower_bound (_map.begin(), _map.end(), lower); - end = upper_bound (_map.begin(), _map.end(), upper); + points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats)); + ++cnt; + } } const TempoSection& TempoMap::tempo_section_at (framepos_t frame) const { Glib::Threads::RWLock::ReaderLock lm (lock); + Metrics::const_iterator i; TempoSection* prev = 0; @@ -1614,11 +1741,34 @@ TempoMap::tempo_section_at (framepos_t frame) const return *prev; } -const Tempo& +const Tempo TempoMap::tempo_at (framepos_t frame) const { + Glib::Threads::RWLock::ReaderLock lm (lock); + TempoMetric m (metric_at (frame)); + TempoSection* prev_ts = 0; + + Metrics::const_iterator i; + + for (i = metrics.begin(); i != metrics.end(); ++i) { + TempoSection* t; + if ((t = dynamic_cast<TempoSection*> (*i)) != 0) { + if ((prev_ts) && t->frame() > frame) { + /* this is the one past frame */ + framepos_t const time = frame - prev_ts->frame(); + framepos_t const last_time = t->frame() - prev_ts->frame(); + double const last_beats_per_minute = t->beats_per_minute(); + double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate); + Tempo const ret_tempo (ret, m.tempo().note_type ()); + return ret_tempo; + } + prev_ts = t; + } + } + return m.tempo(); + } const MeterSection& @@ -1856,8 +2006,7 @@ TempoMap::insert_time (framepos_t where, framecnt_t amount) // which is correct for our purpose } - BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame()); - bbt_time ((*i)->frame(), bbt, bi); + bbt_time ((*i)->frame(), bbt); // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => "; @@ -1970,205 +2119,14 @@ TempoMap::remove_time (framepos_t where, framecnt_t amount) framepos_t TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const { - Glib::Threads::RWLock::ReaderLock lm (lock); - Metrics::const_iterator next_tempo; - const TempoSection* tempo = 0; - - /* 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() - */ - assert(tempo); - - DEBUG_TRACE (DEBUG::TempoMath, - string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n", - pos, beats, *((const 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::Beats distance_beats = Evoral::Beats::ticks_at_rate( - distance_frames, tempo->frames_per_beat (_frame_rate)); - - /* Amount to subtract this time */ - Evoral::Beats 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.to_ticks(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", - *((const 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; + return frame_at_beat (beat_at_frame (pos) + beats.to_double()); } /** Subtract some (fractional) beats from a frame position, and return the result in frames */ framepos_t TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const { - Glib::Threads::RWLock::ReaderLock lm (lock); - Metrics::const_reverse_iterator prev_tempo; - const TempoSection* tempo = 0; - - /* Find the starting tempo metric */ - - for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) { - - const TempoSection* t; - - 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; - } - } - } - } - - assert(tempo); - DEBUG_TRACE (DEBUG::TempoMath, - string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n", - pos, beats, *((const Tempo*)tempo), tempo->frame(), - prev_tempo == metrics.rend())); - - /* 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 = (pos - tempo->frame()); - - /* Distance to the start in beats */ - Evoral::Beats distance_beats = Evoral::Beats::ticks_at_rate( - distance_frames, tempo->frames_per_beat (_frame_rate)); - - /* Amount to subtract this time */ - Evoral::Beats const sub = min (distance_beats, beats); - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n", - tempo->frame(), distance_frames, distance_beats)); - /* Update */ - - beats -= sub; - pos -= sub.to_double() * tempo->frames_per_beat (_frame_rate); - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats, - (prev_tempo == metrics.rend()))); - - /* step backwards to prior TempoSection */ - - if (prev_tempo != metrics.rend()) { - - tempo = dynamic_cast<const TempoSection*>(*prev_tempo); - - DEBUG_TRACE (DEBUG::TempoMath, - string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n", - *((const Tempo*)tempo), tempo->frame(), - tempo->frames_per_beat (_frame_rate))); - - while (prev_tempo != metrics.rend ()) { - - ++prev_tempo; - - if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) { - break; - } - } - } else { - pos -= llrint (beats.to_double() * tempo->frames_per_beat (_frame_rate)); - beats = Evoral::Beats(); - } - } - - return pos; + return frame_at_beat (beat_at_frame (pos) - beats.to_double()); } /** Add the BBT interval op to pos and return the result */ @@ -2305,6 +2263,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const } return pos; + } /** Count the number of beats that are equivalent to distance when going forward, @@ -2313,126 +2272,7 @@ TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const Evoral::Beats TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const { - Glib::Threads::RWLock::ReaderLock lm (lock); - Metrics::const_iterator next_tempo; - const TempoSection* tempo = 0; - framepos_t effective_pos = max (pos, (framepos_t) 0); - - /* 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() > effective_pos) { - break; - } - - tempo = t; - } - } - - /* We now have: - - tempo -> the Tempo for "pos" - next_tempo -> the next tempo after "pos", possibly metrics.end() - */ - assert (tempo); - - DEBUG_TRACE (DEBUG::TempoMath, - string_compose ("frame %1 walk by %2 frames, start with tempo = %3 @ %4\n", - pos, distance, *((const Tempo*)tempo), tempo->frame())); - - Evoral::Beats beats = Evoral::Beats(); - - while (distance) { - - /* End of this section */ - framepos_t end; - /* Distance to `end' in frames */ - framepos_t distance_to_end; - - if (next_tempo == metrics.end ()) { - /* We can't do (end - pos) if end is max_framepos, as it will overflow if pos is -ve */ - end = max_framepos; - distance_to_end = max_framepos; - } else { - end = (*next_tempo)->frame (); - distance_to_end = end - pos; - } - - /* Amount to subtract this time in frames */ - framecnt_t const sub = min (distance, distance_to_end); - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("to reach end at %1 (end ? %2), distance= %3 sub=%4\n", end, (next_tempo == metrics.end()), - distance_to_end, sub)); - - /* Update */ - pos += sub; - distance -= sub; - assert (tempo); - beats += Evoral::Beats::ticks_at_rate(sub, tempo->frames_per_beat (_frame_rate)); - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1, beats = %2 distance left %3\n", - pos, beats, distance)); - - /* Move on if there's anything to move to */ - - 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", - *((const 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; - } - } - - if (next_tempo == metrics.end()) { - DEBUG_TRACE (DEBUG::TempoMath, "no more tempo sections\n"); - } else { - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("next tempo section is %1 @ %2\n", - **next_tempo, (*next_tempo)->frame())); - } - - } - assert (tempo); - } - - return beats; -} - -TempoMap::BBTPointList::const_iterator -TempoMap::bbt_before_or_at (framepos_t pos) -{ - /* CALLER MUST HOLD READ LOCK */ - - BBTPointList::const_iterator i; - - if (pos < 0) { - /* not really correct, but we should catch pos < 0 at a higher - level - */ - return _map.begin(); - } - - i = lower_bound (_map.begin(), _map.end(), pos); - assert (i != _map.end()); - if ((*i).frame > pos) { - assert (i != _map.begin()); - --i; - } - return i; + return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos)); } struct bbtcmp { @@ -2441,40 +2281,6 @@ struct bbtcmp { } }; -TempoMap::BBTPointList::const_iterator -TempoMap::bbt_before_or_at (const BBT_Time& bbt) -{ - BBTPointList::const_iterator i; - bbtcmp cmp; - - i = lower_bound (_map.begin(), _map.end(), bbt, cmp); - assert (i != _map.end()); - if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) { - assert (i != _map.begin()); - --i; - } - return i; -} - -TempoMap::BBTPointList::const_iterator -TempoMap::bbt_after_or_at (framepos_t pos) -{ - /* CALLER MUST HOLD READ LOCK */ - - BBTPointList::const_iterator i; - - if (_map.back().frame == pos) { - i = _map.end(); - assert (i != _map.begin()); - --i; - return i; - } - - i = upper_bound (_map.begin(), _map.end(), pos); - assert (i != _map.end()); - return i; -} - std::ostream& operator<< (std::ostream& o, const Meter& m) { return o << m.divisions_per_bar() << '/' << m.note_divisor(); diff --git a/libs/backends/jack/jack_session.cc b/libs/backends/jack/jack_session.cc index 1e9d956876..430ee5b0e7 100644 --- a/libs/backends/jack/jack_session.cc +++ b/libs/backends/jack/jack_session.cc @@ -121,7 +121,7 @@ JACKSession::timebase_callback (jack_transport_state_t /*state*/, TempoMetric metric (tempo_map.metric_at (tf)); try { - tempo_map.bbt_time_rt (tf, bbt); + tempo_map.bbt_time (tf, bbt); pos->bar = bbt.bars; pos->beat = bbt.beats; diff --git a/libs/canvas/ruler.cc b/libs/canvas/ruler.cc index e3f23fb10c..74755f6ecc 100644 --- a/libs/canvas/ruler.cc +++ b/libs/canvas/ruler.cc @@ -165,10 +165,10 @@ Ruler::render (Rect const & area, Cairo::RefPtr<Cairo::Context> cr) const } break; case Mark::Minor: - cr->rel_line_to (0, -height/4.0); + cr->rel_line_to (0, -height/3.0); break; case Mark::Micro: - cr->rel_line_to (0, -height/16.0); + cr->rel_line_to (0, -height/5.0); break; } cr->stroke (); |