significant overhaul of automation region/lines to update during tempo map drags etc

This commit is contained in:
Paul Davis 2022-10-25 11:10:32 -06:00
parent e2c5a0777b
commit a928e35efb
10 changed files with 227 additions and 58 deletions

View File

@ -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<ControlPoint *>::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 ();
}

View File

@ -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 */

View File

@ -103,6 +103,7 @@ AutomationRegionView::create_line (boost::shared_ptr<ARDOUR::AutomationList> 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 ()

View File

@ -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<ARDOUR::AutomationList> list);
bool set_position(Temporal::timepos_t const & pos, void* src, double* ignored);

View File

@ -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;
}

View File

@ -97,6 +97,8 @@ public:
/** @return All AutomationLines associated with this view */
std::list<boost::shared_ptr<AutomationLine> > 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<Selectable *>&, bool within = false);
void get_inverted_selectables (Selection&, std::list<Selectable*>& results);

View File

@ -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

View File

@ -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; }

View File

@ -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<MidiTimeAxisView*> (&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<AutomationTimeAxisView> atav = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*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 ();
}

View File

@ -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 ();