From f3e5450492109bb4fab898ae824e30ede28bf7b5 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 28 Nov 2006 17:52:09 +0000 Subject: major fixes to automation editing git-svn-id: svn://localhost/ardour2/trunk@1161 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/automation_line.cc | 234 ++++++++++++++++------------------ gtk2_ardour/automation_line.h | 12 +- gtk2_ardour/editor_mouse.cc | 4 +- gtk2_ardour/region_gain_line.cc | 4 +- gtk2_ardour/region_gain_line.h | 2 +- libs/ardour/ardour/automation_event.h | 6 +- libs/ardour/automation_event.cc | 76 +++++++++-- 7 files changed, 191 insertions(+), 147 deletions(-) diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index cdb978fb84..18777def10 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -348,13 +348,6 @@ AutomationLine::nth (uint32_t n) } } -void -AutomationLine::modify_view (ControlPoint& cp, double x, double y, bool with_push) -{ - modify_view_point (cp, x, y, with_push); - update_line (); -} - void AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push) { @@ -476,12 +469,6 @@ AutomationLine::reset_line_coords (ControlPoint& cp) line_points[cp.view_index].set_y (cp.get_y()); } -void -AutomationLine::update_line () -{ - line->property_points() = line_points; -} - void AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end) { @@ -492,10 +479,8 @@ AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end) for (uint32_t i = start; i <= end; ++i) { p = nth(i); - sync_model_with_view_point(*p); + sync_model_with_view_point (*p, false, 0); } - - } void @@ -509,7 +494,6 @@ AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr) mr.xval = (nframes_t) floor (cp.get_x()); mr.yval = 1.0 - (cp.get_y() / _height); - /* if xval has not changed, set it directly from the model to avoid rounding errors */ if (mr.xval == trackview.editor.frame_to_unit((*cp.model)->when)) { @@ -518,7 +502,6 @@ AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr) mr.xval = trackview.editor.unit_to_frame (mr.xval); } - /* virtual call: this will do the right thing for whatever particular type of line we are. */ @@ -567,98 +550,6 @@ AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr) } } -void -AutomationLine::sync_model_from (ControlPoint& cp) -{ - ControlPoint* p; - uint32_t lasti; - - sync_model_with_view_point (cp); - - /* we might have moved all points after `cp' by some amount - if we pressed the with_push modifyer some of the time during the drag - so all subsequent points have to be resynced - */ - - lasti = control_points.size() - 1; - p = nth (lasti); - - update_pending = true; - - while (p != &cp && lasti) { - sync_model_with_view_point (*p); - --lasti; - p = nth (lasti); - } -} - -void -AutomationLine::sync_model_with_view_point (ControlPoint& cp) -{ - ModelRepresentation mr; - double ydelta; - - model_representation (cp, mr); - - /* part 4: how much are we changing the central point by */ - - ydelta = mr.yval - mr.ypos; - - /* IMPORTANT: changing the model when the x-coordinate changes - may invalidate the iterators that we are using. this means that we have - to change the points before+after the one corresponding to the visual CP - first (their x-coordinate doesn't change). then we change the - "main" point. - - apply the full change to the central point, and interpolate - in each direction to cover all model points represented - by the control point. - */ - - /* part 5: change all points before the primary point */ - - for (AutomationList::iterator i = mr.start; i != cp.model; ++i) { - - double delta; - - delta = ydelta * ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin); - - /* x-coordinate (generally time) stays where it is, - y-coordinate moves by a certain amount. - */ - - update_pending = true; - change_model (i, (*i)->when, mr.yval + delta); - } - - /* part 6: change later points */ - - AutomationList::iterator i = cp.model; - - ++i; - - while (i != mr.end) { - - double delta; - - delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos); - - /* x-coordinate (generally time) stays where it is, - y-coordinate moves by a certain amount. - */ - - update_pending = true; - change_model (i, (*i)->when, (*i)->value + delta); - - ++i; - } - - /* part 7: change the primary point */ - - update_pending = true; - change_model (cp.model, mr.xval, mr.yval); -} - void AutomationLine::determine_visible_control_points (ALPoints& points) { @@ -708,11 +599,15 @@ AutomationLine::determine_visible_control_points (ALPoints& points) view_index = 0; + ofstream oout ("orig_coordinates"); + for (model = alist.begin(), pi = 0; pi < npoints; ++model, ++pi) { double tx = points[pi].x; double ty = points[pi].y; + oout << tx << ' ' << ty << endl; + /* now ensure that the control_points vector reflects the current curve state, but don't plot control points too close together. also, don't plot a series of points all with the same value. @@ -812,6 +707,8 @@ AutomationLine::determine_visible_control_points (ALPoints& points) view_index++; } + + oout.close (); /* discard extra CP's to avoid confusing ourselves */ @@ -890,7 +787,7 @@ AutomationLine::invalidate_point (ALPoints& p, uint32_t index) } void -AutomationLine::start_drag (ControlPoint* cp, float fraction) +AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction) { if (trackview.editor.current_session() == 0) { /* how? */ return; @@ -907,22 +804,37 @@ AutomationLine::start_drag (ControlPoint* cp, float fraction) trackview.editor.current_session()->begin_reversible_command (str); trackview.editor.current_session()->add_command (new MementoCommand(alist, &get_state(), 0)); + drag_x = x; + drag_distance = 0; first_drag_fraction = fraction; last_drag_fraction = fraction; drags = 0; + did_push = false; } void AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push) { - modify_view (cp, x, fraction, with_push); + if (x > drag_x) { + drag_distance += (x - drag_x); + } else { + drag_distance -= (drag_x - x); + } + + drag_x = x; + + modify_view_point (cp, x, fraction, with_push); + line->property_points() = line_points; drags++; + did_push = with_push; } void AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push) { double ydelta = fraction - last_drag_fraction; + + did_push = with_push; last_drag_fraction = fraction; @@ -936,7 +848,6 @@ AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_p modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push); } - update_line (); drags++; } @@ -944,20 +855,95 @@ AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_p void AutomationLine::end_drag (ControlPoint* cp) { - if (drags) { + if (!drags) { + return; + } - if (cp) { - sync_model_from (*cp); - } else { - sync_model_with_view_line (line_drag_cp1, line_drag_cp2); + alist.freeze (); + + if (cp) { + sync_model_with_view_point (*cp, did_push, drag_distance); + } else { + sync_model_with_view_line (line_drag_cp1, line_drag_cp2); + } + + alist.thaw (); + + update_pending = false; + + trackview.editor.current_session()->add_command (new MementoCommand(alist, 0, &alist.get_state())); + trackview.editor.current_session()->commit_reversible_command (); + trackview.editor.current_session()->set_dirty (); +} + + +void +AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance) +{ + ModelRepresentation mr; + double ydelta; + + model_representation (cp, mr); + + /* how much are we changing the central point by */ + + ydelta = mr.yval - mr.ypos; + + /* + apply the full change to the central point, and interpolate + on both axes to cover all model points represented + by the control point. + */ + + /* change all points before the primary point */ + + for (AutomationList::iterator i = mr.start; i != cp.model; ++i) { + + double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin); + double y_delta = ydelta * fract; + double x_delta = distance * fract; + + /* interpolate */ + + if (y_delta || x_delta) { + alist.modify (i, (*i)->when + x_delta, mr.ymin + y_delta); + } + } + + /* change the primary point */ + + update_pending = true; + alist.modify (cp.model, mr.xval, mr.yval); + + + /* change later points */ + + AutomationList::iterator i = cp.model; + + ++i; + + while (i != mr.end) { + + double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos); + + /* all later points move by the same distance along the x-axis as the main point */ + + if (delta) { + alist.modify (i, (*i)->when + distance, (*i)->value + delta); } + + ++i; + } + + if (did_push) { - update_pending = false; + /* move all points after the range represented by the view by the same distance + as the main point moved. + */ - trackview.editor.current_session()->add_command (new MementoCommand(alist, 0, &alist.get_state())); - trackview.editor.current_session()->commit_reversible_command (); - trackview.editor.current_session()->set_dirty (); + alist.slide (mr.end, drag_distance); } + } bool @@ -1153,8 +1139,6 @@ AutomationLine::show_selection () { TimeSelection& time (trackview.editor.get_selection().time); - // cerr << "show selection\n"; - for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { (*i)->selected = false; @@ -1178,7 +1162,6 @@ AutomationLine::show_selection () void AutomationLine::hide_selection () { - // cerr << "hide selection\n"; // show_selection (); } @@ -1243,7 +1226,6 @@ AutomationLine::clear () void AutomationLine::change_model (AutomationList::iterator i, double x, double y) { - alist.modify (i, (nframes_t) x, y); } void diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index b73a1c548a..1349efac4b 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -117,7 +117,7 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoin /* dragging API */ - virtual void start_drag (ControlPoint*, float fraction); + virtual void start_drag (ControlPoint*, nframes_t x, float fraction); virtual void point_drag(ControlPoint&, nframes_t x, float, bool with_push); virtual void end_drag (ControlPoint*); virtual void line_drag(uint32_t i1, uint32_t i2, float, bool with_push); @@ -174,7 +174,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoin bool update_pending : 1; bool no_draw : 1; bool points_visible : 1; - + bool did_push; + ArdourCanvas::Group& _parent_group; ArdourCanvas::Group* group; ArdourCanvas::Line* line; /* line */ @@ -193,10 +194,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoin static bool invalid_point (ALPoints&, uint32_t index); void determine_visible_control_points (ALPoints&); - void sync_model_from (ControlPoint&); - void sync_model_with_view_point (ControlPoint&); + void sync_model_with_view_point (ControlPoint&, bool did_push, int64_t distance); void sync_model_with_view_line (uint32_t, uint32_t); - void modify_view (ControlPoint&, double, double, bool with_push); virtual void change_model (ARDOUR::AutomationList::iterator, double x, double y); virtual void change_model_range (ARDOUR::AutomationList::iterator,ARDOUR::AutomationList::iterator, double delta, float ydelta); @@ -212,10 +211,11 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoin double last_drag_fraction; uint32_t line_drag_cp1; uint32_t line_drag_cp2; + int64_t drag_x; + int64_t drag_distance; void modify_view_point(ControlPoint&, double, double, bool with_push); void reset_line_coords (ControlPoint&); - void update_line (); double control_point_box_size (); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 7cf67922b8..3fbd0a19bd 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -2489,7 +2489,7 @@ Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event) start_grab (event, fader_cursor); - control_point->line.start_drag (control_point, 0); + control_point->line.start_drag (control_point, drag_info.grab_frame, 0); float fraction = 1.0 - (control_point->get_y() / control_point->line.height()); set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction), @@ -2622,7 +2622,7 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event) double fraction = 1.0 - (cy / line->height()); - line->start_drag (0, fraction); + line->start_drag (0, drag_info.grab_frame, fraction); set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction), drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20); diff --git a/gtk2_ardour/region_gain_line.cc b/gtk2_ardour/region_gain_line.cc index efc50f91f8..b5ac571163 100644 --- a/gtk2_ardour/region_gain_line.cc +++ b/gtk2_ardour/region_gain_line.cc @@ -44,9 +44,9 @@ AudioRegionGainLine::model_to_view_y (double& y) } void -AudioRegionGainLine::start_drag (ControlPoint* cp, float fraction) +AudioRegionGainLine::start_drag (ControlPoint* cp, nframes_t x, float fraction) { - AutomationLine::start_drag(cp,fraction); + AutomationLine::start_drag (cp, x, fraction); if (!rv.audio_region()->envelope_active()) { trackview.session().add_command(new MementoCommand(*(rv.audio_region().get()), &rv.audio_region()->get_state(), 0)); rv.audio_region()->set_envelope_active(false); diff --git a/gtk2_ardour/region_gain_line.h b/gtk2_ardour/region_gain_line.h index ffd64a6491..86a1f08733 100644 --- a/gtk2_ardour/region_gain_line.h +++ b/gtk2_ardour/region_gain_line.h @@ -21,7 +21,7 @@ class AudioRegionGainLine : public AutomationLine void view_to_model_y (double&); void model_to_view_y (double&); - void start_drag (ControlPoint*, float fraction); + void start_drag (ControlPoint*, nframes_t x, float fraction); void end_drag (ControlPoint*); void remove_point (ControlPoint&); diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index e5c194e683..a2cfb23e61 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -82,7 +82,8 @@ class AutomationList : public PBD::StatefulDestructible void clear (); void x_scale (double factor); bool extend_to (double); - + void slide (iterator before, double distance); + void reposition_for_rt_add (double when); void rt_add (double when, double value); void add (double when, double value); @@ -190,7 +191,7 @@ class AutomationList : public PBD::StatefulDestructible AutomationEventList events; mutable Glib::Mutex lock; - bool _frozen; + int8_t _frozen; bool changed_when_thawed; bool _dirty; @@ -209,6 +210,7 @@ class AutomationList : public PBD::StatefulDestructible double min_yval; double max_yval; double default_value; + bool sort_pending; iterator rt_insertion_point; double rt_pos; diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc index 0fa1f09bb1..6769cc25ec 100644 --- a/libs/ardour/automation_event.cc +++ b/libs/ardour/automation_event.cc @@ -36,6 +36,11 @@ using namespace PBD; sigc::signal AutomationList::AutomationListCreated; +static bool sort_events_by_time (ControlEvent* a, ControlEvent* b) +{ + return a->when < b->when; +} + #if 0 static void dumpit (const AutomationList& al, string prefix = "") { @@ -49,7 +54,7 @@ static void dumpit (const AutomationList& al, string prefix = "") AutomationList::AutomationList (double defval) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _state = Off; _style = Absolute; @@ -62,13 +67,14 @@ AutomationList::AutomationList (double defval) rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); + sort_pending = false; AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _style = other._style; min_yval = other.min_yval; @@ -81,6 +87,7 @@ AutomationList::AutomationList (const AutomationList& other) rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); + sort_pending = false; for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) { /* we have to use other point_factory() because @@ -95,7 +102,7 @@ AutomationList::AutomationList (const AutomationList& other) AutomationList::AutomationList (const AutomationList& other, double start, double end) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _style = other._style; min_yval = other.min_yval; @@ -108,6 +115,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); + sort_pending = false; /* now grab the relevant points, and shift them back if necessary */ @@ -128,7 +136,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl AutomationList::AutomationList (const XMLNode& node) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _touching = false; min_yval = FLT_MIN; @@ -140,6 +148,7 @@ AutomationList::AutomationList (const XMLNode& node) rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); + sort_pending = false; set_state (node); @@ -517,12 +526,37 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double ++start; } + if (!_frozen) { + events.sort (sort_events_by_time); + } else { + sort_pending = true; + } + mark_dirty (); } maybe_signal_changed (); } +void +AutomationList::slide (iterator before, double distance) +{ + { + Glib::Mutex::Lock lm (lock); + + if (before == events.end()) { + return; + } + + while (before != events.end()) { + (*before)->when += distance; + ++before; + } + } + + maybe_signal_changed (); +} + void AutomationList::modify (iterator iter, double when, double val) { @@ -533,14 +567,23 @@ AutomationList::modify (iterator iter, double when, double val) { Glib::Mutex::Lock lm (lock); + (*iter)->when = when; (*iter)->value = val; + if (isnan (val)) { abort (); } + + if (!_frozen) { + events.sort (sort_events_by_time); + } else { + sort_pending = true; + } + mark_dirty (); } - + maybe_signal_changed (); } @@ -581,13 +624,30 @@ AutomationList::control_points_adjacent (double xval) void AutomationList::freeze () { - _frozen = true; + _frozen++; } void AutomationList::thaw () { - _frozen = false; + if (_frozen == 0) { + fatal << string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg; + /*NOTREACHED*/ + } + + if (--_frozen > 0) { + return; + } + + { + Glib::Mutex::Lock lm (lock); + + if (sort_pending) { + events.sort (sort_events_by_time); + sort_pending = false; + } + } + if (changed_when_thawed) { StateChanged(); /* EMIT SIGNAL */ } @@ -1354,7 +1414,7 @@ AutomationList::set_state (const XMLNode& node) deserialize_events (*(*niter)); } } - + return 0; } -- cgit v1.2.3