diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index 92ca8a329c..44d07d9755 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -91,6 +91,14 @@ AutomationLine::AutomationLine (const string& name, const ParameterDescriptor& desc) : trackview (tv) , _name (name) + , _height (0) + , _visible (Line) + , terminal_points_can_slide (true) + , update_pending (false) + , have_reset_timeout (false) + , have_redisplay_timeout (false) + , no_draw (false) + , _is_boolean (false) , alist (al) , _parent_group (parent) , _offset (0) @@ -98,15 +106,6 @@ AutomationLine::AutomationLine (const string& name, , _fill (false) , _desc (desc) { - _visible = Line; - - update_pending = false; - have_timeout = false; - no_draw = false; - _is_boolean = false; - terminal_points_can_slide = true; - _height = 0; - group = new ArdourCanvas::Container (&parent, ArdourCanvas::Duple(0, 1.5)); CANVAS_DEBUG_NAME (group, "region gain envelope group"); @@ -258,7 +257,7 @@ AutomationLine::set_height (guint32 h) } else { line->set_fill_y1 (0); } - reset (); + redisplay (true, true); } } @@ -411,8 +410,7 @@ AutomationLine::string_to_fraction (string const & s) const default: break; } - model_to_view_coord_y (v); - return v; + return model_to_view_coord_y (v); } /** Start dragging a single point, possibly adding others if the supplied point is selected and there @@ -554,7 +552,7 @@ AutomationLine::ContiguousControlPoints::move (double dx, double dvalue) double view_y = 1.0 - (*i)->get_y() / line.height(); line.view_to_model_coord_y (view_y); line.apply_delta (view_y, dvalue); - line.model_to_view_coord_y (view_y); + view_y = line.model_to_view_coord_y (view_y); view_y = (1.0 - view_y) * line.height(); (*i)->move_to ((*i)->get_x() + dx, view_y, ControlPoint::Full); @@ -782,7 +780,7 @@ AutomationLine::sync_model_with_view_point (ControlPoint& cp) /* convert to absolute time on timeline */ const timepos_t absolute_time = model_time + get_origin(); - /* now convert it back to match the view_x (RegioView pixel pos) */ + /* now convert it back to match the view_x (RegionView pixel pos) */ const double model_x = trackview.editor().time_to_pixel_unrounded (absolute_time.earlier (_offset).earlier (get_origin ())); if (view_x != model_x) { @@ -811,7 +809,7 @@ AutomationLine::sync_model_with_view_point (ControlPoint& cp) alist->modify (cp.model(), model_time, view_y); /* convert back from model to view y for clamping position (for integer/boolean/etc) */ - model_to_view_coord_y (view_y); + view_y = model_to_view_coord_y (view_y); const double point_y = _height - (view_y * _height); if (point_y != cp.get_y()) { cp.move_to (cp.get_x(), point_y, ControlPoint::Full); @@ -968,6 +966,105 @@ AutomationLine::list_changed () } } +void +AutomationLine::tempo_map_changed () +{ + if (alist->time_domain() != Temporal::BeatTime) { + return; + } + + redisplay (true, false); +} + +void +AutomationLine::redisplay (bool view_only, bool with_y) +{ + have_redisplay_timeout = false; + + if (view_only) { + + + for (std::vector::iterator i = control_points.begin(); i != control_points.end(); i++) { + + AutomationList::iterator ai ((*i)->model()); + + /* drop points outside our range */ + + if (((*ai)->when < _offset)) { + continue; + } + + if ((*ai)->when >= _maximum_time) { + break; + } + + + /* we do not need to recompute the y coordinate here */ + + double ty; + timecnt_t tx; + + if (!with_y) { + + /* re-use existing y-coordinate */ + + ty = (*i)->get_y(); + + } else { + + /* convert to absolute position */ + + ty = model_to_view_coord_y ((*ai)->value); + + if (isnan_local (ty)) { + warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""), + _name) << endmsg; + continue; + } + + ty = _height - (ty * _height); + } + + /* tx is currently the distance of this point from + * _offset, which may be either: + * + * a) zero, for an automation line not connected to a + * region + * + * b) some non-zero value, corresponding to the start + * of the region within its source(s). Remember that + * this start is an offset within the source, not a + * position on the timeline. + * + * We need to convert tx to a global position, and to + * do that we need to measure the distance from the + * result of get_origin(), which tells ut the timeline + * position of _offset + */ + + tx = model_to_view_coord_x ((*ai)->when); + + /* convert x-coordinate to a canvas unit coordinate (this takes + * zoom and scroll into account). + */ + + double px = trackview.editor().duration_to_pixels_unrounded (tx); + + (*i)->move_to (px, ty); + + reset_line_coords (**i); + + } + + if (line_points.size() > 1) { + line->set_steps (line_points, is_stepped()); + } + + } else { + reset (); + } +} + void AutomationLine::reset_callback (const Evoral::ControlList& events) { @@ -997,11 +1094,17 @@ AutomationLine::reset_callback (const Evoral::ControlList& events) for (AutomationList::iterator ai = e.begin(); ai != e.end(); ++ai, ++pi) { - double ty = (*ai)->value; + /* drop points outside our range */ - /* convert from model coordinates to canonical view coordinates */ + if (((*ai)->when < _offset)) { + continue; + } - timepos_t tx = model_to_view_coord (**ai, ty); + if ((*ai)->when >= _maximum_time) { + break; + } + + double ty = model_to_view_coord_y ((*ai)->value); if (isnan_local (ty)) { warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""), @@ -1009,21 +1112,22 @@ AutomationLine::reset_callback (const Evoral::ControlList& events) continue; } - if (tx >= timepos_t::max (tx.time_domain()) || tx.is_negative () || tx >= _maximum_time) { - continue; - } + ty = _height - (ty * _height); + + /* convert from model coordinates to canonical view coordinates */ + + timecnt_t tx = model_to_view_coord_x ((*ai)->when); /* convert x-coordinate to a canvas unit coordinate (this takes * zoom and scroll into account). */ - double px = trackview.editor().time_to_pixel_unrounded (tx); + double px = trackview.editor().duration_to_pixels_unrounded (tx); /* convert from canonical view height (0..1.0) to actual * height coordinates (using X11's top-left rooted system) */ - ty = _height - (ty * _height); add_visible_control_point (vp, pi, px, ty, ai, np); @@ -1072,7 +1176,7 @@ AutomationLine::reset () { DEBUG_TRACE (DEBUG::Automation, "\t\tLINE RESET\n"); update_pending = false; - have_timeout = false; + have_reset_timeout = false; if (no_draw) { return; @@ -1097,10 +1201,10 @@ AutomationLine::queue_reset () if (trackview.editor().session()->transport_rolling() && alist->automation_write()) { /* automation write pass ... defer to a timeout */ /* redraw in 1/4 second */ - if (!have_timeout) { + if (!have_reset_timeout) { DEBUG_TRACE (DEBUG::Automation, "\tqueue timeout\n"); Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &AutomationLine::reset), false), 250); - have_timeout = true; + have_reset_timeout = true; } else { DEBUG_TRACE (DEBUG::Automation, "\ttimeout already queued, change ignored\n"); } @@ -1109,6 +1213,26 @@ AutomationLine::queue_reset () } } +void +AutomationLine::queue_redisplay (bool for_height) +{ + /* this must be called from the GUI thread */ + + if (trackview.editor().session()->transport_rolling() && alist->automation_write()) { + /* automation write pass ... defer to a timeout */ + /* redraw in 1/4 second */ + if (!have_redisplay_timeout) { + DEBUG_TRACE (DEBUG::Automation, "\tqueue timeout\n"); + Glib::signal_timeout().connect (sigc::bind_return (sigc::bind (sigc::mem_fun (*this, &AutomationLine::redisplay), true, for_height), true), 250); + have_redisplay_timeout = true; + } else { + DEBUG_TRACE (DEBUG::Automation, "\ttimeout already queued, change ignored\n"); + } + } else { + redisplay (true, for_height); + } +} + void AutomationLine::clear () { @@ -1229,8 +1353,8 @@ AutomationLine::apply_delta (double& val, double delta) const val = _desc.apply_delta (val, delta); } -void -AutomationLine::model_to_view_coord_y (double& y) const +double +AutomationLine::model_to_view_coord_y (double y) const { if (alist->default_interpolation () != alist->interpolation()) { switch (alist->interpolation()) { @@ -1239,8 +1363,7 @@ AutomationLine::model_to_view_coord_y (double& y) const assert (alist->default_interpolation () == AutomationList::Linear); break; case AutomationList::Linear: - y = (y - _desc.lower) / (_desc.upper - _desc.lower); - return; + return (y - _desc.lower) / (_desc.upper - _desc.lower); default: /* types that default to linear, can't be use * Logarithmic or Exponential interpolation. @@ -1250,15 +1373,22 @@ AutomationLine::model_to_view_coord_y (double& y) const break; } } - y = _desc.to_interface (y); + return _desc.to_interface (y); } -timepos_t -AutomationLine::model_to_view_coord (Evoral::ControlEvent const & ev, double& y) const +timecnt_t +AutomationLine::model_to_view_coord_x (timepos_t const & when) const { - Temporal::timepos_t w (ev.when); - model_to_view_coord_y (y); - return (w).earlier (_offset); + /* @param when is a distance (with implicit origin) from the start of the + * source. So we subtract the offset (from the region if this is + * related to a region; zero otherwise) to get the distance (again, + * implicit origin) from the start of the line. + * + * Then we construct a timecnt_t from this duration, and the origin of + * the line on the timeline. + */ + + return timecnt_t (when.earlier (_offset), get_origin()); } /** Called when our list has announced that its interpolation style has changed */ @@ -1374,10 +1504,6 @@ AutomationLine::session_position (timepos_t const & when) const void AutomationLine::set_offset (timepos_t const & off) { - if (_offset == off) { - return; - } - _offset = off; reset (); } diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index 7e1f346202..05e15c7606 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -76,7 +76,9 @@ public: virtual Temporal::timepos_t get_origin () const; + void redisplay (bool view_only, bool with_y); void queue_reset (); + void queue_redisplay (bool for_height); void reset (); void clear (); void set_fill (bool f) { _fill = f; } // owner needs to call set_height @@ -114,6 +116,7 @@ public: void set_height (guint32); bool get_uses_gain_mapping () const; + void tempo_map_changed (); TimeAxisView& trackview; @@ -126,9 +129,11 @@ public: std::string fraction_to_string (double) const; std::string delta_to_string (double) const; double string_to_fraction (std::string const &) const; + void view_to_model_coord_y (double &) const; - Temporal::timepos_t model_to_view_coord (Evoral::ControlEvent const &, double& y) const; - void model_to_view_coord_y (double &) const; + + double model_to_view_coord_y (double) const; + Temporal::timecnt_t model_to_view_coord_x (Temporal::timepos_t const &) const; double compute_delta (double from, double to) const; void apply_delta (double& val, double delta) const; @@ -175,7 +180,8 @@ protected: bool terminal_points_can_slide; bool update_pending; - bool have_timeout; + bool have_reset_timeout; + bool have_redisplay_timeout; bool no_draw; bool _is_boolean; /** true if we did a push at any point during the current drag */ diff --git a/gtk2_ardour/automation_region_view.cc b/gtk2_ardour/automation_region_view.cc index 15e6a48dc6..d2074610f7 100644 --- a/gtk2_ardour/automation_region_view.cc +++ b/gtk2_ardour/automation_region_view.cc @@ -103,6 +103,7 @@ AutomationRegionView::create_line (boost::shared_ptr lis _line->set_height ((uint32_t)rint(trackview.current_height() - 2.5 - NAME_HIGHLIGHT_SIZE)); _line->set_visibility (AutomationLine::VisibleAspects (AutomationLine::Line|AutomationLine::ControlPoints)); _line->set_maximum_time (timepos_t (_region->length())); + std::cerr << "Set line offset to " << _region->start() << std::endl; _line->set_offset (_region->start ()); } @@ -291,11 +292,10 @@ AutomationRegionView::reset_width_dependent_items (double pixel_width) RegionView::reset_width_dependent_items(pixel_width); if (_line) { - _line->reset(); + _line->reset (); } } - void AutomationRegionView::region_resized (const PBD::PropertyChange& what_changed) { @@ -314,6 +314,16 @@ AutomationRegionView::region_resized (const PBD::PropertyChange& what_changed) } } +void +AutomationRegionView::tempo_map_changed () +{ + if (_line) { + _line->tempo_map_changed (); + } + + set_position (_region->position(), 0, 0); + set_duration (_region->length(), 0); +} void AutomationRegionView::entered () diff --git a/gtk2_ardour/automation_region_view.h b/gtk2_ardour/automation_region_view.h index a79e298d57..58cdff6a7d 100644 --- a/gtk2_ardour/automation_region_view.h +++ b/gtk2_ardour/automation_region_view.h @@ -68,6 +68,8 @@ public: void set_height (double); void reset_width_dependent_items(double pixel_width); + void tempo_map_changed (); + protected: void create_line(boost::shared_ptr list); bool set_position(Temporal::timepos_t const & pos, void* src, double* ignored); diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index 363e291a4c..275a3bb854 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -1176,7 +1176,7 @@ AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& sel for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) { timepos_t when = (*x)->when; double val = (*x)->value; - line.model_to_view_coord (**x, val); + line.model_to_view_coord_y (val); (*x)->when = when; (*x)->value = val; } diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h index 82846abd66..5c681c7ea1 100644 --- a/gtk2_ardour/automation_time_axis.h +++ b/gtk2_ardour/automation_time_axis.h @@ -97,6 +97,8 @@ public: /** @return All AutomationLines associated with this view */ std::list > lines () const; + AutomationStreamView* automation_view() const { return _view; } + void set_selected_points (PointSelection&); void get_selectables (Temporal::timepos_t const &, Temporal::timepos_t const &, double top, double bot, std::list&, bool within = false); void get_inverted_selectables (Selection&, std::list& results); diff --git a/gtk2_ardour/control_point.cc b/gtk2_ardour/control_point.cc index 4d3a1bbcc0..d527a7d692 100644 --- a/gtk2_ardour/control_point.cc +++ b/gtk2_ardour/control_point.cc @@ -133,7 +133,13 @@ void ControlPoint::set_size (double sz) { _size = sz; - move_to (_x, _y, _shape); + move_to (_x, _y); +} + +void +ControlPoint::move_to (double x, double y) +{ + move_to (x, y, _shape); } void diff --git a/gtk2_ardour/control_point.h b/gtk2_ardour/control_point.h index ed9c10e6d7..5776c6dadd 100644 --- a/gtk2_ardour/control_point.h +++ b/gtk2_ardour/control_point.h @@ -56,6 +56,7 @@ public: End }; + void move_to (double x, double y); void move_to (double x, double y, ShapeType); void reset (double x, double y, ARDOUR::AutomationList::iterator, uint32_t, ShapeType); double get_x() const { return _x; } diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index 7b84a6f1ca..54373e6209 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -50,6 +50,7 @@ #include "canvas/item.h" #include "canvas/line_set.h" +#include "automation_streamview.h" #include "bbt_marker_dialog.h" #include "editor.h" #include "marker.h" @@ -899,21 +900,32 @@ Editor::mid_tempo_per_track_update (TimeAxisView& tav) { MidiTimeAxisView* mtav = dynamic_cast (&tav); - if (!mtav) { - return; + if (mtav) { + MidiStreamView* msv = mtav->midi_view(); + + if (msv) { + msv->foreach_regionview (sigc::mem_fun (*this, &Editor::mid_tempo_per_region_update)); + } + + TimeAxisView::Children kids (tav.get_child_list()); + + for (TimeAxisView::Children::iterator ct = kids.begin(); ct != kids.end(); ++ct) { + + boost::shared_ptr atav = boost::dynamic_pointer_cast (*ct); + + if (atav) { + AutomationStreamView* asv = atav->automation_view (); + + if (asv) { + asv->foreach_regionview (sigc::mem_fun (*this, &Editor::mid_tempo_per_region_update)); + } + } + } } - - MidiStreamView* msv = mtav->midi_view(); - - if (!msv) { - return; - } - - msv->foreach_regionview (sigc::mem_fun (*this, &Editor::mid_tempo_per_region_update)); } void Editor::mid_tempo_per_region_update (RegionView* rv) { - rv->redisplay (true); + rv->tempo_map_changed (); } diff --git a/gtk2_ardour/region_view.h b/gtk2_ardour/region_view.h index 47c43de3b8..f81a85308b 100644 --- a/gtk2_ardour/region_view.h +++ b/gtk2_ardour/region_view.h @@ -106,6 +106,10 @@ public: _redisplay (view_only); } + virtual void tempo_map_changed () { + _redisplay (true); + } + struct DisplaySuspender { DisplaySuspender (RegionView& rv, bool just_view = false) : region_view (rv), view_only (just_view) { region_view.disable_display ();