diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 7a74312119..6e2c311704 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -305,7 +305,6 @@ Editor::Editor () pre_press_cursor = 0; _drags = new DragManager (this); current_mixer_strip = 0; - current_bbt_points = 0; tempo_lines = 0; snap_type_strings = I18N (_snap_type_strings); @@ -5335,8 +5334,7 @@ Editor::session_going_away () hide_measures (); clear_marker_display (); - delete current_bbt_points; - current_bbt_points = 0; + current_bbt_points.clear (); /* get rid of any existing editor mixer strip */ diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index b1162287a9..6c099899d8 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1451,7 +1451,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD /// true if we scroll the tracks rather than the playhead bool _stationary_playhead; - ARDOUR::TempoMap::BBTPointList *current_bbt_points; + ARDOUR::TempoMap::BBTPointList current_bbt_points; TempoLines* tempo_lines; diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index f4bdd9291d..36eb84797f 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -1224,18 +1224,18 @@ Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper) break; } - if (current_bbt_points == 0 || current_bbt_points->empty()) { + if (current_bbt_points.empty()) { return; } - i = current_bbt_points->end(); + i = current_bbt_points.end(); i--; - if ((*i).beat >= (*current_bbt_points->begin()).beat) { - bbt_bars = (*i).bar - (*current_bbt_points->begin()).bar; + if ((*i).beat >= (*current_bbt_points.begin()).beat) { + bbt_bars = (*i).bar - (*current_bbt_points.begin()).bar; } else { - bbt_bars = (*i).bar - (*current_bbt_points->begin()).bar - 1; + bbt_bars = (*i).bar - (*current_bbt_points.begin()).bar - 1; } - beats = current_bbt_points->size() - bbt_bars; + beats = current_bbt_points.size() - bbt_bars; /* Only show the bar helper if there aren't many bars on the screen */ if ((bbt_bars < 2) || (beats < 5)) { @@ -1291,14 +1291,14 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp bool i_am_accented = false; bool helper_active = false; - if (current_bbt_points == 0 || current_bbt_points->empty()) { + if (current_bbt_points.empty()) { return 0; } switch (bbt_ruler_scale) { case bbt_show_beats: - beats = current_bbt_points->size(); + beats = current_bbt_points.size(); bbt_nmarks = beats + 2; *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks); @@ -1307,10 +1307,8 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp (*marks)[0].position = lower; (*marks)[0].style = GtkCustomRulerMarkMicro; - for (n = 1, i = current_bbt_points->begin(); n < bbt_nmarks && i != current_bbt_points->end(); ++i) { - if ((*i).type != TempoMap::Beat) { - continue; - } + for (n = 1, i = current_bbt_points.begin(); n < bbt_nmarks && i != current_bbt_points.end(); ++i) { + if ((*i).frame < lower && (bbt_bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); (*marks)[0].label = g_strdup (buf); @@ -1336,7 +1334,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp case bbt_show_ticks: - beats = current_bbt_points->size(); + beats = current_bbt_points.size(); bbt_nmarks = (beats + 2) * bbt_beat_subdivision; bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ()); @@ -1346,10 +1344,8 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp (*marks)[0].position = lower; (*marks)[0].style = GtkCustomRulerMarkMicro; - for (n = 1, i = current_bbt_points->begin(); n < bbt_nmarks && i != current_bbt_points->end(); ++i) { - if ((*i).type != TempoMap::Beat) { - continue; - } + for (n = 1, i = current_bbt_points.begin(); n < bbt_nmarks && i != current_bbt_points.end(); ++i) { + if ((*i).frame < lower && (bbt_bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); (*marks)[0].label = g_strdup (buf); @@ -1428,7 +1424,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp case bbt_show_ticks_detail: - beats = current_bbt_points->size(); + beats = current_bbt_points.size(); bbt_nmarks = (beats + 2) * bbt_beat_subdivision; bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ()); @@ -1438,10 +1434,8 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp (*marks)[0].position = lower; (*marks)[0].style = GtkCustomRulerMarkMicro; - for (n = 1, i = current_bbt_points->begin(); n < bbt_nmarks && i != current_bbt_points->end(); ++i) { - if ((*i).type != TempoMap::Beat) { - continue; - } + for (n = 1, i = current_bbt_points.begin(); n < bbt_nmarks && i != current_bbt_points.end(); ++i) { + if ((*i).frame < lower && (bbt_bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); (*marks)[0].label = g_strdup (buf); @@ -1525,7 +1519,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp case bbt_show_ticks_super_detail: - beats = current_bbt_points->size(); + beats = current_bbt_points.size(); bbt_nmarks = (beats + 2) * bbt_beat_subdivision; bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ()); @@ -1535,10 +1529,8 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp (*marks)[0].position = lower; (*marks)[0].style = GtkCustomRulerMarkMicro; - for (n = 1, i = current_bbt_points->begin(); n < bbt_nmarks && i != current_bbt_points->end(); ++i) { - if ((*i).type != TempoMap::Beat) { - continue; - } + for (n = 1, i = current_bbt_points.begin(); n < bbt_nmarks && i != current_bbt_points.end(); ++i) { + if ((*i).frame < lower && (bbt_bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); (*marks)[0].label = g_strdup (buf); @@ -1634,7 +1626,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp case bbt_show_64: bbt_nmarks = (gint) (bbt_bars / 64) + 1; *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks); - for (n = 0, i = current_bbt_points->begin(); i != current_bbt_points->end() && n < bbt_nmarks; i++) { + for (n = 0, i = current_bbt_points.begin(); i != current_bbt_points.end() && n < bbt_nmarks; i++) { if ((*i).type == TempoMap::Bar) { if ((*i).bar % 64 == 1) { if ((*i).bar % 256 == 1) { @@ -1659,7 +1651,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp case bbt_show_16: bbt_nmarks = (bbt_bars / 16) + 1; *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks); - for (n = 0, i = current_bbt_points->begin(); i != current_bbt_points->end() && n < bbt_nmarks; i++) { + for (n = 0, i = current_bbt_points.begin(); i != current_bbt_points.end() && n < bbt_nmarks; i++) { if ((*i).type == TempoMap::Bar) { if ((*i).bar % 16 == 1) { if ((*i).bar % 64 == 1) { @@ -1684,7 +1676,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp case bbt_show_4: bbt_nmarks = (bbt_bars / 4) + 1; *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks); - for (n = 0, i = current_bbt_points->begin(); i != current_bbt_points->end() && n < bbt_nmarks; ++i) { + for (n = 0, i = current_bbt_points.begin(); i != current_bbt_points.end() && n < bbt_nmarks; ++i) { if ((*i).type == TempoMap::Bar) { if ((*i).bar % 4 == 1) { if ((*i).bar % 16 == 1) { @@ -1710,7 +1702,7 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upp // default: bbt_nmarks = bbt_bars + 2; *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks ); - for (n = 0, i = current_bbt_points->begin(); i != current_bbt_points->end() && n < bbt_nmarks; i++) { + for (n = 0, i = current_bbt_points.begin(); i != current_bbt_points.end() && n < bbt_nmarks; i++) { if ((*i).type == TempoMap::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 89832c5d13..79f5caf82b 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -114,7 +114,7 @@ Editor::tempo_map_changed (const PropertyChange& /*ignored*/) tempo_lines->tempo_map_changed(); } - compute_current_bbt_points(leftmost_frame, leftmost_frame + current_page_frames()); + compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_frames()); _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers redraw_measures (); update_tempo_based_rulers (); @@ -169,10 +169,8 @@ Editor::compute_current_bbt_points (framepos_t leftmost, framepos_t rightmost) } next_beat.ticks = 0; - delete current_bbt_points; - current_bbt_points = 0; - - current_bbt_points = _session->tempo_map().get_points (_session->tempo_map().frame_time (previous_beat), _session->tempo_map().frame_time (next_beat) + 1); + current_bbt_points.clear(); + _session->tempo_map().map (current_bbt_points, _session->tempo_map().frame_time (previous_beat), _session->tempo_map().frame_time (next_beat) + 1); } void @@ -192,8 +190,7 @@ Editor::redraw_measures () void Editor::draw_measures () { - if (_session == 0 || _show_measures == false || - !current_bbt_points || current_bbt_points->empty()) { + if (_session == 0 || _show_measures == false || current_bbt_points.empty()) { return; } @@ -201,7 +198,7 @@ Editor::draw_measures () tempo_lines = new TempoLines(*track_canvas, time_line_group, physical_screen_height(get_window())); } - tempo_lines->draw(*current_bbt_points, frames_per_unit); + tempo_lines->draw (current_bbt_points, frames_per_unit); } void diff --git a/gtk2_ardour/tempo_lines.cc b/gtk2_ardour/tempo_lines.cc index 8fe877a27c..cab185026f 100644 --- a/gtk2_ardour/tempo_lines.cc +++ b/gtk2_ardour/tempo_lines.cc @@ -131,122 +131,114 @@ TempoLines::draw (ARDOUR::TempoMap::BBTPointList& points, double frames_per_unit for (i = points.begin(); i != points.end(); ++i) { - switch ((*i).type) { - case ARDOUR::TempoMap::Bar: - break; - - case ARDOUR::TempoMap::Beat: - if ((*i).beat == 1) { - color = ARDOUR_UI::config()->canvasvar_MeasureLineBar.get(); - } else { - color = ARDOUR_UI::config()->canvasvar_MeasureLineBeat.get(); - if (beat_density > 2.0) { - break; /* only draw beat lines if the gaps between beats are large. */ - } + if ((*i).type == ARDOUR::TempoMap::Bar) { + color = ARDOUR_UI::config()->canvasvar_MeasureLineBar.get(); + } else { + if (beat_density > 2.0) { + continue; /* only draw beat lines if the gaps between beats are large. */ } + color = ARDOUR_UI::config()->canvasvar_MeasureLineBeat.get(); + } - xpos = rint(((framepos_t)(*i).frame) / (double)frames_per_unit); - - if (inserted_last_time && !_lines.empty()) { - li = _lines.lower_bound(xpos); // first line >= xpos - } - - line = (li != _lines.end()) ? li->second : NULL; - assert(!line || line->property_x1() == li->first); - - Lines::iterator next = li; - if (next != _lines.end()) - ++next; - - exhausted = (next == _lines.end()); - - // Hooray, line is perfect - if (line && line->property_x1() == xpos) { - if (li != _lines.end()) - ++li; - - line->property_color_rgba() = color; - inserted_last_time = false; // don't search next time - + xpos = rint(((framepos_t)(*i).frame) / (double)frames_per_unit); + + if (inserted_last_time && !_lines.empty()) { + li = _lines.lower_bound(xpos); // first line >= xpos + } + + line = (li != _lines.end()) ? li->second : NULL; + assert(!line || line->property_x1() == li->first); + + Lines::iterator next = li; + if (next != _lines.end()) + ++next; + + exhausted = (next == _lines.end()); + + // Hooray, line is perfect + if (line && line->property_x1() == xpos) { + if (li != _lines.end()) + ++li; + + line->property_color_rgba() = color; + inserted_last_time = false; // don't search next time + // Use existing line, moving if necessary - } else if (!exhausted) { - Lines::iterator steal = _lines.end(); - --steal; - - // Steal from the right - if (left->first > needed_left && li != steal && steal->first > needed_right) { - //cout << "*** STEALING FROM RIGHT" << endl; - line = steal->second; - _lines.erase(steal); - line->property_x1() = xpos; - line->property_x2() = xpos; - line->property_color_rgba() = color; - _lines.insert(make_pair(xpos, line)); - inserted_last_time = true; // search next time - invalidated = true; - - // Shift clean range left - _clean_left = min(_clean_left, xpos); - _clean_right = min(_clean_right, steal->first); - - // Move this line to where we need it - } else { - Lines::iterator existing = _lines.find(xpos); - if (existing != _lines.end()) { - //cout << "*** EXISTING LINE" << endl; - li = existing; - li->second->property_color_rgba() = color; - inserted_last_time = false; // don't search next time - } else { - //cout << "*** MOVING LINE" << endl; - const double x1 = line->property_x1(); - const bool was_clean = x1 >= _clean_left && x1 <= _clean_right; - invalidated = invalidated || was_clean; - // Invalidate clean portion (XXX: too harsh?) - _clean_left = needed_left; - _clean_right = needed_right; - _lines.erase(li); - line->property_color_rgba() = color; - line->property_x1() = xpos; - line->property_x2() = xpos; - _lines.insert(make_pair(xpos, line)); - inserted_last_time = true; // search next time - } - } - - // Create a new line - } else if (_lines.size() < needed || _lines.size() < MAX_CACHED_LINES) { - //cout << "*** CREATING LINE" << endl; - assert(_lines.find(xpos) == _lines.end()); - line = new ArdourCanvas::SimpleLine (*_group); - line->property_x1() = xpos; - line->property_x2() = xpos; - line->property_y1() = 0.0; - line->property_y2() = _height; - line->property_color_rgba() = color; - _lines.insert(make_pair(xpos, line)); - inserted_last_time = true; - - // Steal from the left - } else { - //cout << "*** STEALING FROM LEFT" << endl; - assert(_lines.find(xpos) == _lines.end()); - Lines::iterator steal = _lines.begin(); + } else if (!exhausted) { + Lines::iterator steal = _lines.end(); + --steal; + + // Steal from the right + if (left->first > needed_left && li != steal && steal->first > needed_right) { + //cout << "*** STEALING FROM RIGHT" << endl; line = steal->second; _lines.erase(steal); - line->property_color_rgba() = color; line->property_x1() = xpos; line->property_x2() = xpos; + line->property_color_rgba() = color; _lines.insert(make_pair(xpos, line)); inserted_last_time = true; // search next time invalidated = true; - - // Shift clean range right - _clean_left = max(_clean_left, steal->first); - _clean_right = max(_clean_right, xpos); + + // Shift clean range left + _clean_left = min(_clean_left, xpos); + _clean_right = min(_clean_right, steal->first); + + // Move this line to where we need it + } else { + Lines::iterator existing = _lines.find(xpos); + if (existing != _lines.end()) { + //cout << "*** EXISTING LINE" << endl; + li = existing; + li->second->property_color_rgba() = color; + inserted_last_time = false; // don't search next time + } else { + //cout << "*** MOVING LINE" << endl; + const double x1 = line->property_x1(); + const bool was_clean = x1 >= _clean_left && x1 <= _clean_right; + invalidated = invalidated || was_clean; + // Invalidate clean portion (XXX: too harsh?) + _clean_left = needed_left; + _clean_right = needed_right; + _lines.erase(li); + line->property_color_rgba() = color; + line->property_x1() = xpos; + line->property_x2() = xpos; + _lines.insert(make_pair(xpos, line)); + inserted_last_time = true; // search next time + } } - - break; + + // Create a new line + } else if (_lines.size() < needed || _lines.size() < MAX_CACHED_LINES) { + //cout << "*** CREATING LINE" << endl; + assert(_lines.find(xpos) == _lines.end()); + line = new ArdourCanvas::SimpleLine (*_group); + line->property_x1() = xpos; + line->property_x2() = xpos; + line->property_y1() = 0.0; + line->property_y2() = _height; + line->property_color_rgba() = color; + _lines.insert(make_pair(xpos, line)); + inserted_last_time = true; + + // Steal from the left + } else { + //cout << "*** STEALING FROM LEFT" << endl; + assert(_lines.find(xpos) == _lines.end()); + Lines::iterator steal = _lines.begin(); + line = steal->second; + _lines.erase(steal); + line->property_color_rgba() = color; + line->property_x1() = xpos; + line->property_x2() = xpos; + _lines.insert(make_pair(xpos, line)); + inserted_last_time = true; // search next time + invalidated = true; + + // Shift clean range right + _clean_left = max(_clean_left, steal->first); + _clean_right = max(_clean_right, xpos); } } diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index c286d367c0..87dc77780e 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -218,8 +218,9 @@ class TempoMap : public PBD::StatefulDestructible (obj.*method)(*metrics); } - BBTPointList *get_points (framepos_t start, framepos_t end) const; - + const BBTPointList& map() const { return _map ; } + void map (BBTPointList&, framepos_t start, framepos_t end); + void bbt_time (framepos_t when, Timecode::BBT_Time&) const; framecnt_t frame_time (const Timecode::BBT_Time&) const; framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir) const; @@ -282,13 +283,15 @@ class TempoMap : public PBD::StatefulDestructible static Meter _default_meter; Metrics* metrics; - framecnt_t _frame_rate; + framecnt_t _frame_rate; framepos_t last_bbt_when; bool last_bbt_valid; Timecode::BBT_Time last_bbt; mutable Glib::RWLock lock; + BBTPointList _map; + + void recompute_map (bool reassign_tempo_bbt, framepos_t end = -1); - void timestamp_metrics (bool reassign_bar_references); void timestamp_metrics_from_audio_time (); framepos_t round_to_type (framepos_t fr, int dir, BBTPointType); @@ -304,7 +307,6 @@ class TempoMap : public PBD::StatefulDestructible framecnt_t count_frames_between (const Timecode::BBT_Time&, const Timecode::BBT_Time&) const; framecnt_t count_frames_with_metrics (const TempoMetric&, const TempoMetric&, const Timecode::BBT_Time&, const Timecode::BBT_Time&) const; - framecnt_t count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const Timecode::BBT_Time& start, const Timecode::BBT_Time& end) const; int move_metric_section (MetricSection&, const Timecode::BBT_Time& to); void do_insert (MetricSection* section); diff --git a/libs/ardour/session_click.cc b/libs/ardour/session_click.cc index 22f9de449d..86a5d4ad78 100644 --- a/libs/ardour/session_click.cc +++ b/libs/ardour/session_click.cc @@ -41,7 +41,7 @@ Pool Click::pool ("click", sizeof (Click), 128); void Session::click (framepos_t start, framecnt_t nframes) { - TempoMap::BBTPointList *points; + TempoMap::BBTPointList points; Sample *buf; if (_click_io == 0) { @@ -59,18 +59,13 @@ Session::click (framepos_t start, framecnt_t nframes) BufferSet& bufs = get_scratch_buffers(ChanCount(DataType::AUDIO, 1)); buf = bufs.get_audio(0).data(); - points = _tempo_map->get_points (start, end); + _tempo_map->map (points, start, end); - if (points == 0) { + if (points.empty()) { goto run_clicks; } - if (points->empty()) { - delete points; - goto run_clicks; - } - - for (TempoMap::BBTPointList::iterator i = points->begin(); i != points->end(); ++i) { + for (TempoMap::BBTPointList::iterator i = points.begin(); i != points.end(); ++i) { switch ((*i).type) { case TempoMap::Beat: if (click_emphasis_data == 0 || (click_emphasis_data && (*i).beat != 1)) { diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index d96c712c16..2ebe85f27b 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -156,8 +156,8 @@ void TempoSection::update_bar_offset_from_bbt (const Meter& m) { - _bar_offset = ((double) (start().beats - 1) + (start().ticks/Timecode::BBT_Time::ticks_per_bar_division)) / - (m.divisions_per_bar() - 1); + _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_bar_division + start().ticks) / + (m.divisions_per_bar() * BBT_Time::ticks_per_bar_division); DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar())); } @@ -174,18 +174,16 @@ TempoSection::update_bbt_time_from_bar_offset (const Meter& meter) new_start.bars = start().bars; - double ticks = BBT_Time::ticks_per_bar_division * (_bar_offset * (meter.divisions_per_bar() - 1)); + double ticks = BBT_Time::ticks_per_bar_division * meter.divisions_per_bar() * _bar_offset; new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_bar_division); new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_bar_division); - 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)); - /* remember the 1-based counting properties of beats */ new_start.beats += 1; - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("tempo updated BBT time to %1 from bar offset %2 w/dpb = %3\n", new_start, _bar_offset, meter.divisions_per_bar())); - + 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)); + set_start (new_start); } @@ -305,7 +303,7 @@ TempoMap::~TempoMap () } void -TempoMap::remove_tempo (const TempoSection& tempo, bool send_signal) +TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation) { bool removed = false; @@ -327,15 +325,15 @@ TempoMap::remove_tempo (const TempoSection& tempo, bool send_signal) } if (removed) { - timestamp_metrics (false); - if (send_signal) { + if (complete_operation) { + recompute_map (false); PropertyChanged (PropertyChange ()); } } } void -TempoMap::remove_meter (const MeterSection& tempo, bool send_signal) +TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation) { bool removed = false; @@ -357,8 +355,8 @@ TempoMap::remove_meter (const MeterSection& tempo, bool send_signal) } if (removed) { - timestamp_metrics (true); - if (send_signal) { + if (complete_operation) { + recompute_map (true); PropertyChanged (PropertyChange ()); } } @@ -448,7 +446,7 @@ TempoMap::do_insert (MetricSection* section) metrics->insert (metrics->end(), section); } - timestamp_metrics (reassign_tempo_bbt); + recompute_map (reassign_tempo_bbt); } void @@ -462,7 +460,7 @@ TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_T } else { /* cannot move the first tempo section */ *((Tempo*)&first) = tempo; - timestamp_metrics (false); + recompute_map (false); } PropertyChanged (PropertyChange ()); @@ -526,7 +524,7 @@ TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_T } else { /* cannot move the first meter section */ *((Meter*)&first) = meter; - timestamp_metrics (true); + recompute_map (true); } PropertyChanged (PropertyChange ()); @@ -737,41 +735,80 @@ TempoMap::timestamp_metrics_from_audio_time () } void -TempoMap::timestamp_metrics (bool reassign_tempo_bbt) +TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end) { - Metrics::iterator i; - const MeterSection* meter; - const TempoSection* tempo; - MeterSection *m; - TempoSection *t; + MeterSection* meter; + TempoSection* tempo; + TempoSection* ts; + MeterSection* ms; + double divisions_per_bar; + double beat_frames; + double frames_per_bar; + double current_frame; + BBT_Time current; + Metrics::iterator next_metric; - DEBUG_TRACE (DEBUG::TempoMath, "###################### TIMESTAMP via BBT ##############\n"); + if (end < 0) { + if (_map.empty()) { + /* compute 1 mins worth */ + end = _frame_rate * 60; + } else { + end = _map.back().frame; + } + } + + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end)); - framepos_t current = 0; - framepos_t section_frames; - BBT_Time start; - BBT_Time end; + _map.clear (); + + for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) { + if ((ms = dynamic_cast (*i)) != 0) { + meter = ms; + break; + } + } + + for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) { + if ((ts = dynamic_cast (*i)) != 0) { + tempo = ts; + break; + } + } + + /* assumes that the first meter & tempo are at frame zero */ + current_frame = 0; + meter->set_frame (0); + tempo->set_frame (0); + + /* assumes that the first meter & tempo are at 1|1|0 */ + current.bars = 1; + current.beats = 1; + current.ticks = 0; + + divisions_per_bar = meter->divisions_per_bar (); + frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); + beat_frames = meter->frames_per_division (*tempo,_frame_rate); if (reassign_tempo_bbt) { + TempoSection* rtempo = tempo; + MeterSection* rmeter = meter; + DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n"); - meter = &first_meter (); - tempo = &first_tempo (); - - for (i = metrics->begin(); i != metrics->end(); ++i) { + for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) { - if ((t = dynamic_cast(*i)) != 0) { + if ((ts = dynamic_cast(*i)) != 0) { /* reassign the BBT time of this tempo section * based on its bar offset position. */ - t->update_bbt_time_from_bar_offset (*meter); - tempo = t; + ts->update_bbt_time_from_bar_offset (*rmeter); + rtempo = ts; - } else if ((m = dynamic_cast(*i)) != 0) { - meter = m; + } else if ((ms = dynamic_cast(*i)) != 0) { + rmeter = ms; } else { fatal << _("programming error: unhandled MetricSection type") << endmsg; /*NOTREACHED*/ @@ -779,44 +816,110 @@ TempoMap::timestamp_metrics (bool reassign_tempo_bbt) } } - meter = &first_meter (); - tempo = &first_tempo (); + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2 dpb %3 fpb %4\n", + *((Meter*)meter), *((Tempo*)tempo), divisions_per_bar, beat_frames)); - for (i = metrics->begin(); i != metrics->end(); ++i) { + next_metric = metrics->begin(); + ++next_metric; // skip meter (or tempo) + ++next_metric; // skip tempo (or meter) - end = (*i)->start(); - - section_frames = count_frames_between_metrics (*meter, *tempo, start, end); - current += section_frames; + 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), Bar, 1, 1)); - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frames between %1 & %2 = %3 using %4 & %6 puts %7 at %8\n", - start, end, section_frames, - *((Meter*) meter), *((Tempo*) tempo), - (*i)->start(), current)); + while (current_frame < end) { + + current.beats++; + current_frame += beat_frames; - start = end; - - (*i)->set_frame (current); - - if ((t = dynamic_cast(*i)) != 0) { - tempo = t; - } else if ((m = dynamic_cast(*i)) != 0) { - meter = m; - } else { - fatal << _("programming error: unhandled MetricSection type") << endmsg; - /*NOTREACHED*/ + 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())) { + + + if (((ts = dynamic_cast (*next_metric)) != 0)) { + + tempo = ts; + + /* 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 (tempo->start().ticks != 0) { + + double next_beat_frames = meter->frames_per_division (*tempo,_frame_rate); + + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n", + tempo->start(), current_frame, tempo->bar_offset())); + + /* back up to previous beat */ + current_frame -= beat_frames; + /* set tempo section location based on offset from last beat */ + tempo->set_frame (current_frame + (ts->bar_offset() * beat_frames)); + /* advance to the location of the new (adjusted) beat */ + current_frame += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames); + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_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); + } + + } else if ((ms = dynamic_cast(*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 (%2)\n", + meter->start(), current_frame)); + + assert (current.beats == 1); + + meter->set_frame (current_frame); + } + + divisions_per_bar = meter->divisions_per_bar (); + frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); + beat_frames = meter->frames_per_division (*tempo, _frame_rate); + + DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n", + beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo))); + + ++next_metric; + } + } + + 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,(framepos_t) llrint(current_frame), Bar, current.bars, 1)); + } 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, (framepos_t) llrint(current_frame), Beat, current.bars, current.beats)); + } } - -#ifndef NDEBUG - if (DEBUG_ENABLED(DEBUG::TempoMath)) { - dump (cerr); - } -#endif - - DEBUG_TRACE (DEBUG::TempoMath, "###############################################\n"); - } TempoMetric @@ -1007,81 +1110,6 @@ TempoMap::count_frames_with_metrics (const TempoMetric& bm, const TempoMetric& e return frames; } -framecnt_t -TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const -{ - /* this is used in timestamping the metrics by actually counting the - * beats between two metrics ONLY. this means that we know we have a - * fixed divisions_per_bar and frames_per_division for the entire - * computation. - */ - - framecnt_t frames = 0; - uint32_t bar = start.bars; - double beat = (double) start.beats; - double divisions_counted = 0; - double divisions_per_bar = 0; - double division_frames = 0; - double max_divs; - int32_t ticks = 0; - - divisions_per_bar = meter.divisions_per_bar(); - max_divs = ceil (divisions_per_bar); - division_frames = meter.frames_per_division (tempo, _frame_rate); - - if (start.ticks > 0) { - ticks = -start.ticks; - - } - - frames = 0; - - while (bar < end.bars || (bar == end.bars && beat < end.beats)) { - - ++beat; - ++divisions_counted; - - if (beat > max_divs) { - - if (beat > divisions_per_bar) { - - /* this is a fractional beat at the end of a fractional bar - so it should only count for the fraction - */ - - divisions_counted -= (max_divs - divisions_per_bar); - } - - ++bar; - beat = 1; - } - } - - ticks += end.ticks; - -#if 0 - cerr << "for " << start.ticks << " and " << end.ticks << " adjust divs by " << ticks << " = " - << (ticks/BBT_Time::ticks_per_bar_division) << " divs => " - << ((ticks/BBT_Time::ticks_per_bar_division) * division_frames) - << " (fpd = " << division_frames << ')' - << endl; -#endif - - frames = (framecnt_t) llrint (floor ((divisions_counted + (ticks/BBT_Time::ticks_per_bar_division)) * division_frames)); - -#if 0 - cerr << "Counted " << divisions_counted << " + " << ticks << " from " << start << " to " << end - << " dpb were " << divisions_per_bar - << " fpd was " << division_frames - << " => " - << frames - << endl; -#endif - - return frames; - -} - framepos_t TempoMap::frame_time (const BBT_Time& bbt) const { @@ -1440,296 +1468,22 @@ TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type) return metric.frame() + count_frames_between (metric.start(), bbt); } -TempoMap::BBTPointList * -TempoMap::get_points (framepos_t lower, framepos_t upper) const +void +TempoMap::map (TempoMap::BBTPointList& points, framepos_t lower, framepos_t upper) { - Metrics::const_iterator next_metric; - BBTPointList *points; - double current; - const MeterSection* meter; - const MeterSection* m; - const TempoSection* tempo; - const TempoSection* t; - uint32_t bar; - uint32_t beat; - double divisions_per_bar; - double beat_frame; - double beat_frames; - double frames_per_bar; - double delta_bars; - double delta_beats; - double dummy; - framepos_t limit; - - meter = &first_meter (); - tempo = &first_tempo (); - - /* find the starting point */ - - for (next_metric = metrics->begin(); next_metric != metrics->end(); ++next_metric) { - - if ((*next_metric)->frame() > lower) { - break; - } - - if ((t = dynamic_cast(*next_metric)) != 0) { - tempo = t; - } else if ((m = dynamic_cast(*next_metric)) != 0) { - meter = m; - } + if (_map.empty() || upper >= _map.back().frame) { + recompute_map (false, upper); } - /* We now have: - - meter -> the Meter for "lower" - tempo -> the Tempo for "lower" - i -> for first new metric after "lower", possibly metrics->end() - - Now start generating points. - */ - - divisions_per_bar = meter->divisions_per_bar (); - frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); - beat_frames = meter->frames_per_division (*tempo,_frame_rate); - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Start with beat frames = %1 bar = %2\n", beat_frames, frames_per_bar)); - - if (meter->frame() > tempo->frame()) { - bar = meter->start().bars; - beat = meter->start().beats; - current = meter->frame(); - } else { - bar = tempo->start().bars; - beat = tempo->start().beats; - current = tempo->frame(); - } - - /* initialize current to point to the bar/beat just prior to the - lower frame bound passed in. assumes that current is initialized - above to be on a beat. - */ - - delta_bars = (lower-current) / frames_per_bar; - delta_beats = modf(delta_bars, &dummy) * divisions_per_bar; - current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames); - - // adjust bars and beats too - bar += (uint32_t) (floor(delta_bars)); - beat += (uint32_t) (floor(delta_beats)); - - points = new BBTPointList; - - do { - - /* we're going to add bar or beat points until we hit the - earlier of: - - (1) the end point of this request - (2) the next metric section - */ - - if (next_metric == metrics->end()) { - limit = upper; - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("== limit set to end of request @ %1\n", limit)); - } else { - limit = (*next_metric)->frame(); - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("== limit set to next metric section @ %1\n", limit)); + for (BBTPointList::const_iterator i = _map.begin(); i != _map.end(); ++i) { + if ((*i).frame < lower) { + continue; } - - limit = min (limit, upper); - - bool reset_current_to_metric_section = true; - bool bar_adjusted = false; - TempoSection* ts; - - while (current < limit) { - - /* if we're at the start of a bar, add bar point */ - - if (beat == 1) { - if (current >= lower) { - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", bar, current)); - points->push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current), Bar, bar, 1)); - - } - } - - /* add some beats if we can */ - - beat_frame = current; - - const uint32_t max_divs = ceil (divisions_per_bar); - - while (beat <= max_divs && beat_frame < limit) { - - if (beat_frame >= lower) { - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", bar, beat, beat_frame)); - points->push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(beat_frame), Beat, bar, beat)); - } - - beat_frame += beat_frames; - current = beat_frame; - beat++; - } - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("break in beats addition @ end ? %1 out of bpb ? %2 beat frame @ %3 vs %4 beat @ %5 vs %6\n", - (next_metric == metrics->end()), (beat > max_divs), beat_frame, limit, beat, max_divs)); - - if (beat <= max_divs) { - - /* we didn't reach the end of the bar. - - this could be be because we hit "upper" - or a new metric section. - - */ - - if (next_metric != metrics->end() && limit == (*next_metric)->frame()) { - - /* we bumped into a new metric - * section. meter sections are always - * at bar boundaries, but tempo - * sections can begin anywhere and need - * special handling if they are not on - * a beat boundary. - */ - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("stopped at metric at %1 @ 2\n", (*next_metric)->start(), (*next_metric)->frame())); - - if (((ts = dynamic_cast (*next_metric)) != 0) && ts->start().ticks != 0) { - - /* compute current at the *next* beat, - using the tempo section we just - bumped into. - */ - - /* recompute how many frames per - * division using the tempo we've just - * found - */ - - double next_beat_frames = meter->frames_per_division (*ts,_frame_rate); - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n", - (*next_metric)->start(), (*next_metric)->frame(), ts->bar_offset())); - - current -= beat_frames; - current += (ts->bar_offset() * beat_frames) + ((1.0 - ts->bar_offset()) * next_beat_frames); - - /* avoid resetting current to position - of the next metric section as we - iterate through "metrics" - further on below. - */ - - reset_current_to_metric_section = false; - - } else if (dynamic_cast (*next_metric)) { - - /* we hit a new meter - * section. nothing to do - the - * right thing will happen as - * we move to the next metric - * section down below. - */ - - } else { - - /* we hit a tempo mark that is - * precisely on beat. nothing - * to do here - the - * right thing will happen as - * we move to the next metric - * section down below. - */ - } - - } else { - - /* we hit either: - - - the end of the requested range - - we'll exit from the outer loop soon. - */ - } - - } else if ((beat > max_divs) || (next_metric != metrics->end() && dynamic_cast(*next_metric))) { - - /* we've arrived at either the end of a bar or - a new **meter** marker (not tempo marker). - - its important to move `current' forward by - the actual frames_per_bar, not move it to an - integral beat_frame, so that metrics with - non-integral beats-per-bar have their bar - positions set correctly. consider a metric - with 9-1/2 beats-per-bar. the bar we just - filled had 10 beat marks, but the bar end is - 1/2 beat before the last beat mark. And it - is also possible that a tempo change occured - in the middle of a bar, so we subtract the - possible extra fraction from the current - */ - - if (beat > max_divs) { - /* next bar goes where the numbers suggest */ - current -= beat_frames * (max_divs - divisions_per_bar); - DEBUG_TRACE (DEBUG::TempoMath, "++ next bar from numbers\n"); - } else { - /* next bar goes where the next meter metric is */ - current = limit; - DEBUG_TRACE (DEBUG::TempoMath, "++ next bar at next metric\n"); - } - - bar++; - beat = 1; - bar_adjusted = true; - } - } - - /* if we're done, then we're done */ - - if (current >= upper) { + if ((*i).frame >= upper) { break; } - - /* i is an iterator that refers to the next metric (or none). - if there is a next metric, move to it, and continue. - */ - - if (next_metric != metrics->end()) { - - if ((t = dynamic_cast(*next_metric)) != 0) { - tempo = t; - } else if ((m = dynamic_cast(*next_metric)) != 0) { - meter = m; - - if (!bar_adjusted) { - /* new MeterSection, beat always returns to 1 */ - beat = 1; - } - } - - if (reset_current_to_metric_section) { - current = (*next_metric)->frame (); - } - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("loop around with current @ %1\n", current)); - - divisions_per_bar = meter->divisions_per_bar (); - frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); - beat_frames = meter->frames_per_division (*tempo, _frame_rate); - - DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 bar = %2 dpb %3 meter %4 tempo %5\n", - beat_frames, frames_per_bar, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo))); - - ++next_metric; - } - - } while (1); - - return points; + points.push_back (*i); + } } const TempoSection& @@ -1847,7 +1601,7 @@ TempoMap::set_state (const XMLNode& node, int /*version*/) MetricSectionSorter cmp; metrics->sort (cmp); - timestamp_metrics (true); + recompute_map (true); } }