2 Copyright (C) 2002-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <pbd/stl_delete.h>
26 #include <pbd/memento_command.h>
27 #include <pbd/stacktrace.h>
29 #include <ardour/automation_list.h>
30 #include <ardour/dB.h>
31 #include <evoral/Curve.hpp>
33 #include "simplerect.h"
34 #include "automation_line.h"
35 #include "control_point.h"
36 #include "rgb_macros.h"
37 #include "ardour_ui.h"
38 #include "public_editor.h"
40 #include "selection.h"
41 #include "time_axis_view.h"
42 #include "point_selection.h"
43 #include "automation_selectable.h"
44 #include "automation_time_axis.h"
45 #include "public_editor.h"
47 #include <ardour/session.h>
53 using namespace ARDOUR;
55 using namespace Editing;
56 using namespace Gnome; // for Canvas
58 AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent, boost::shared_ptr<AutomationList> al)
62 _parent_group (parent)
64 _interpolation = al->interpolation();
65 points_visible = false;
66 update_pending = false;
67 _vc_uses_gain_mapping = false;
70 terminal_points_can_slide = true;
73 group = new ArdourCanvas::Group (parent);
74 group->property_x() = 0.0;
75 group->property_y() = 0.0;
77 line = new ArdourCanvas::Line (*group);
78 line->property_width_pixels() = (guint)1;
79 line->set_data ("line", this);
81 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
83 alist->StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
85 trackview.session().register_with_memento_command_factory(alist->id(), this);
87 if (alist->parameter().type() == GainAutomation)
88 set_verbose_cursor_uses_gain_mapping (true);
90 set_interpolation(alist->interpolation());
93 AutomationLine::~AutomationLine ()
95 vector_delete (&control_points);
100 AutomationLine::event_handler (GdkEvent* event)
102 return PublicEditor::instance().canvas_line_event (event, line, this);
106 AutomationLine::queue_reset ()
108 if (!update_pending) {
109 update_pending = true;
110 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
115 AutomationLine::show ()
117 if (_interpolation != AutomationList::Discrete)
120 if (points_visible) {
121 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
130 AutomationLine::hide ()
133 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
140 AutomationLine::control_point_box_size ()
142 if (_interpolation == AutomationList::Discrete) {
143 return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
147 if (_height > TimeAxisView::hLarger) {
149 } else if (_height > (guint32) TimeAxisView::hNormal) {
156 AutomationLine::set_height (guint32 h)
161 double bsz = control_point_box_size();
163 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
164 (*i)->set_size (bsz);
172 AutomationLine::set_line_color (uint32_t color)
175 line->property_fill_color_rgba() = color;
179 AutomationLine::set_verbose_cursor_uses_gain_mapping (bool yn)
181 if (yn != _vc_uses_gain_mapping) {
182 _vc_uses_gain_mapping = yn;
188 AutomationLine::nth (uint32_t n)
190 if (n < control_points.size()) {
191 return control_points[n];
198 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
201 uint32_t last_movable = UINT_MAX;
202 double x_limit = DBL_MAX;
204 /* this just changes the current view. it does not alter
205 the model in any way at all.
208 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
209 and needs to be converted to a canvas unit distance.
214 y = _height - (y * _height);
216 if (cp.can_slide()) {
218 /* x-coord cannot move beyond adjacent points or the start/end, and is
219 already in frames. it needs to be converted to canvas units.
222 x = trackview.editor.frame_to_unit (x);
224 /* clamp x position using view coordinates */
226 ControlPoint *before;
229 if (cp.view_index()) {
230 before = nth (cp.view_index() - 1);
231 x = max (x, before->get_x()+1.0);
238 if (cp.view_index() < control_points.size() - 1) {
240 after = nth (cp.view_index() + 1);
242 /*if it is a "spike" leave the x alone */
244 if (after->get_x() - before->get_x() < 2) {
248 x = min (x, after->get_x()-1.0);
258 /* find the first point that can't move */
260 for (uint32_t n = cp.view_index() + 1; (after = nth (n)) != 0; ++n) {
261 if (!after->can_slide()) {
262 x_limit = after->get_x() - 1.0;
263 last_movable = after->view_index();
268 delta = x - cp.get_x();
273 /* leave the x-coordinate alone */
275 x = trackview.editor.frame_to_unit ((*cp.model())->when);
281 cp.move_to (x, y, ControlPoint::Full);
282 reset_line_coords (cp);
286 uint32_t limit = min (control_points.size(), (size_t)last_movable);
288 /* move the current point to wherever the user told it to go, subject
292 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
293 reset_line_coords (cp);
295 /* now move all subsequent control points, to reflect the motion.
298 for (uint32_t i = cp.view_index() + 1; i < limit; ++i) {
299 ControlPoint *p = nth (i);
302 if (p->can_slide()) {
303 new_x = min (p->get_x() + delta, x_limit);
304 p->move_to (new_x, p->get_y(), ControlPoint::Full);
305 reset_line_coords (*p);
312 AutomationLine::reset_line_coords (ControlPoint& cp)
314 if (cp.view_index() < line_points.size()) {
315 line_points[cp.view_index()].set_x (cp.get_x());
316 line_points[cp.view_index()].set_y (cp.get_y());
321 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
326 update_pending = true;
328 for (uint32_t i = start; i <= end; ++i) {
330 sync_model_with_view_point (*p, false, 0);
335 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
337 /* part one: find out where the visual control point is.
338 initial results are in canvas units. ask the
339 line to convert them to something relevant.
342 mr.xval = (nframes_t) floor (cp.get_x());
343 mr.yval = 1.0 - (cp.get_y() / _height);
345 /* if xval has not changed, set it directly from the model to avoid rounding errors */
347 if (mr.xval == trackview.editor.frame_to_unit((*cp.model())->when)) {
348 mr.xval = (nframes_t) (*cp.model())->when;
350 mr.xval = trackview.editor.unit_to_frame (mr.xval);
353 /* virtual call: this will do the right thing
354 for whatever particular type of line we are.
357 view_to_model_y (mr.yval);
359 /* part 2: find out where the model point is now
362 mr.xpos = (nframes_t) (*cp.model())->when;
363 mr.ypos = (*cp.model())->value;
365 /* part 3: get the position of the visual control
366 points before and after us.
369 ControlPoint* before;
372 if (cp.view_index()) {
373 before = nth (cp.view_index() - 1);
378 after = nth (cp.view_index() + 1);
381 mr.xmin = (nframes_t) (*before->model())->when;
382 mr.ymin = (*before->model())->value;
383 mr.start = before->model();
388 mr.start = cp.model();
392 mr.end = after->model();
402 AutomationLine::determine_visible_control_points (ALPoints& points)
404 uint32_t view_index, pi, n;
405 AutomationList::iterator model;
407 double last_control_point_x = 0.0;
408 double last_control_point_y = 0.0;
409 uint32_t this_rx = 0;
410 uint32_t prev_rx = 0;
411 uint32_t this_ry = 0;
412 uint32_t prev_ry = 0;
417 /* hide all existing points, and the line */
421 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
428 if (points.empty()) {
432 npoints = points.size();
434 /* compute derivative/slope for the entire line */
436 slope = new double[npoints];
438 for (n = 0; n < npoints - 1; ++n) {
439 double xdelta = points[n+1].x - points[n].x;
440 double ydelta = points[n+1].y - points[n].y;
441 slope[n] = ydelta/xdelta;
444 box_size = (uint32_t) control_point_box_size ();
446 /* read all points and decide which ones to show as control points */
450 for (model = alist->begin(), pi = 0; pi < npoints; ++model, ++pi) {
452 double tx = points[pi].x;
453 double ty = points[pi].y;
455 if (isnan (tx) || isnan (ty)) {
456 warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
461 /* now ensure that the control_points vector reflects the current curve
462 state, but don't plot control points too close together. also, don't
463 plot a series of points all with the same value.
465 always plot the first and last points, of course.
468 if (invalid_point (points, pi)) {
469 /* for some reason, we are supposed to ignore this point,
470 but still keep track of the model index.
475 if (pi > 0 && pi < npoints - 1) {
476 if (slope[pi] == slope[pi-1]) {
478 /* no reason to display this point */
484 /* need to round here. the ultimate coordinates are integer
485 pixels, so tiny deltas in the coords will be eliminated
486 and we end up with "colinear" line segments. since the
487 line rendering code in libart doesn't like this very
488 much, we eliminate them here. don't do this for the first and last
492 this_rx = (uint32_t) rint (tx);
493 this_ry = (uint32_t) rint (ty);
495 if (view_index && pi != npoints && /* not the first, not the last */
496 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
497 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
498 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
502 /* ok, we should display this point */
504 if (view_index >= cpsize) {
506 /* make sure we have enough control points */
508 ControlPoint* ncp = new ControlPoint (*this);
510 ncp->set_size (box_size);
512 control_points.push_back (ncp);
516 ControlPoint::ShapeType shape;
518 if (!terminal_points_can_slide) {
520 control_points[view_index]->set_can_slide(false);
522 shape = ControlPoint::Start;
524 shape = ControlPoint::Full;
526 } else if (pi == npoints - 1) {
527 control_points[view_index]->set_can_slide(false);
528 shape = ControlPoint::End;
530 control_points[view_index]->set_can_slide(true);
531 shape = ControlPoint::Full;
534 control_points[view_index]->set_can_slide(true);
535 shape = ControlPoint::Full;
538 last_control_point_x = tx;
539 last_control_point_y = ty;
541 control_points[view_index]->reset (tx, ty, model, view_index, shape);
546 /* finally, control visibility */
548 if (_visible && points_visible) {
549 control_points[view_index]->show ();
550 control_points[view_index]->set_visible (true);
552 if (!points_visible) {
553 control_points[view_index]->set_visible (false);
560 /* discard extra CP's to avoid confusing ourselves */
562 while (control_points.size() > view_index) {
563 ControlPoint* cp = control_points.back();
564 control_points.pop_back ();
568 if (!terminal_points_can_slide) {
569 control_points.back()->set_can_slide(false);
574 if (view_index > 1) {
576 npoints = view_index;
578 /* reset the line coordinates */
580 while (line_points.size() < npoints) {
581 line_points.push_back (Art::Point (0,0));
584 while (line_points.size() > npoints) {
585 line_points.pop_back ();
588 for (view_index = 0; view_index < npoints; ++view_index) {
589 line_points[view_index].set_x (control_points[view_index]->get_x());
590 line_points[view_index].set_y (control_points[view_index]->get_y());
593 line->property_points() = line_points;
595 if (_visible && _interpolation != AutomationList::Discrete)
600 set_selected_points (trackview.editor.get_selection().points);
605 AutomationLine::get_verbose_cursor_string (double fraction)
609 if (_vc_uses_gain_mapping) {
610 if (fraction == 0.0) {
611 snprintf (buf, sizeof (buf), "-inf dB");
613 snprintf (buf, sizeof (buf), "%.1fdB", coefficient_to_dB (slider_position_to_gain (fraction)));
616 view_to_model_y(fraction);
617 if (EventTypeMap::instance().is_integer(alist->parameter())) {
618 snprintf (buf, sizeof (buf), "%d", (int)fraction);
620 snprintf (buf, sizeof (buf), "%.2f", fraction);
628 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
630 return p[index].x == max_frames && p[index].y == DBL_MAX;
634 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
636 p[index].x = max_frames;
637 p[index].y = DBL_MAX;
641 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
643 if (trackview.editor.current_session() == 0) { /* how? */
650 str = _("automation event move");
652 str = _("automation range drag");
655 trackview.editor.current_session()->begin_reversible_command (str);
656 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
660 first_drag_fraction = fraction;
661 last_drag_fraction = fraction;
667 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
670 drag_distance += (x - drag_x);
672 drag_distance -= (drag_x - x);
677 modify_view_point (cp, x, fraction, with_push);
679 if (line_points.size() > 1) {
680 line->property_points() = line_points;
684 did_push = with_push;
688 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
690 double ydelta = fraction - last_drag_fraction;
692 did_push = with_push;
694 last_drag_fraction = fraction;
701 for (uint32_t i = i1 ; i <= i2; i++) {
703 modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
706 if (line_points.size() > 1) {
707 line->property_points() = line_points;
714 AutomationLine::end_drag (ControlPoint* cp)
723 sync_model_with_view_point (*cp, did_push, drag_distance);
725 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
730 update_pending = false;
732 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
733 trackview.editor.current_session()->commit_reversible_command ();
734 trackview.editor.current_session()->set_dirty ();
739 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
741 ModelRepresentation mr;
744 model_representation (cp, mr);
746 /* how much are we changing the central point by */
748 ydelta = mr.yval - mr.ypos;
751 apply the full change to the central point, and interpolate
752 on both axes to cover all model points represented
753 by the control point.
756 /* change all points before the primary point */
758 for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) {
760 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
761 double y_delta = ydelta * fract;
762 double x_delta = distance * fract;
766 if (y_delta || x_delta) {
767 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
771 /* change the primary point */
773 update_pending = true;
774 alist->modify (cp.model(), mr.xval, mr.yval);
777 /* change later points */
779 AutomationList::iterator i = cp.model();
783 while (i != mr.end) {
785 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
787 /* all later points move by the same distance along the x-axis as the main point */
790 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
798 /* move all points after the range represented by the view by the same distance
799 as the main point moved.
802 alist->slide (mr.end, drag_distance);
808 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
810 ControlPoint *bcp = 0;
811 ControlPoint *acp = 0;
814 /* xval is in frames */
816 unit_xval = trackview.editor.frame_to_unit (xval);
818 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
820 if ((*i)->get_x() <= unit_xval) {
822 if (!bcp || (*i)->get_x() > bcp->get_x()) {
824 before = bcp->view_index();
827 } else if ((*i)->get_x() > unit_xval) {
829 after = acp->view_index();
838 AutomationLine::is_last_point (ControlPoint& cp)
840 ModelRepresentation mr;
842 model_representation (cp, mr);
844 // If the list is not empty, and the point is the last point in the list
846 if (!alist->empty() && mr.end == alist->end()) {
854 AutomationLine::is_first_point (ControlPoint& cp)
856 ModelRepresentation mr;
858 model_representation (cp, mr);
860 // If the list is not empty, and the point is the first point in the list
862 if (!alist->empty() && mr.start == alist->begin()) {
869 // This is copied into AudioRegionGainLine
871 AutomationLine::remove_point (ControlPoint& cp)
873 ModelRepresentation mr;
875 model_representation (cp, mr);
877 trackview.editor.current_session()->begin_reversible_command (_("remove control point"));
878 XMLNode &before = alist->get_state();
880 alist->erase (mr.start, mr.end);
882 trackview.editor.current_session()->add_command(new MementoCommand<AutomationList>(
883 *alist.get(), &before, &alist->get_state()));
884 trackview.editor.current_session()->commit_reversible_command ();
885 trackview.editor.current_session()->set_dirty ();
889 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
890 double botfrac, double topfrac, list<Selectable*>& results)
897 bool collecting = false;
899 /* Curse X11 and its inverted coordinate system! */
901 bot = (1.0 - topfrac) * _height;
902 top = (1.0 - botfrac) * _height;
907 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
909 nframes_t when = (nframes_t) (*(*i)->model())->when;
911 if (when >= start && when <= end) {
913 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
916 (*i)->set_visible(true);
918 nstart = min (nstart, when);
919 nend = max (nend, when);
925 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
935 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
941 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
947 AutomationLine::set_selected_points (PointSelection& points)
952 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
953 (*i)->set_selected(false);
956 if (points.empty()) {
960 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
962 if (&(*r).track != &trackview) {
966 /* Curse X11 and its inverted coordinate system! */
968 bot = (1.0 - (*r).high_fract) * _height;
969 top = (1.0 - (*r).low_fract) * _height;
971 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
975 rstart = trackview.editor.frame_to_unit ((*r).start);
976 rend = trackview.editor.frame_to_unit ((*r).end);
978 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
980 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
982 (*i)->set_selected(true);
990 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
991 (*i)->show_color (false, !points_visible);
996 void AutomationLine::set_colors() {
997 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
998 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
999 (*i)->show_color (false, !points_visible);
1004 AutomationLine::show_selection ()
1006 TimeSelection& time (trackview.editor.get_selection().time);
1008 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1010 (*i)->set_selected(false);
1012 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1013 double rstart, rend;
1015 rstart = trackview.editor.frame_to_unit ((*r).start);
1016 rend = trackview.editor.frame_to_unit ((*r).end);
1018 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1019 (*i)->set_selected(true);
1024 (*i)->show_color (false, !points_visible);
1029 AutomationLine::hide_selection ()
1031 // show_selection ();
1035 AutomationLine::list_changed ()
1041 AutomationLine::reset_callback (const Evoral::ControlList& events)
1043 ALPoints tmp_points;
1044 uint32_t npoints = events.size();
1047 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1050 control_points.clear ();
1055 AutomationList::const_iterator ai;
1057 for (ai = events.begin(); ai != events.end(); ++ai) {
1059 double translated_y = (*ai)->value;
1060 model_to_view_y (translated_y);
1062 add_model_point (tmp_points, (*ai)->when, translated_y);
1065 determine_visible_control_points (tmp_points);
1070 AutomationLine::add_model_point (ALPoints& tmp_points, double frame, double yfract)
1072 tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit (frame),
1073 _height - (yfract * _height)));
1077 AutomationLine::reset ()
1079 update_pending = false;
1085 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1089 AutomationLine::clear ()
1091 /* parent must create command */
1092 XMLNode &before = get_state();
1094 trackview.editor.current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1095 trackview.editor.current_session()->commit_reversible_command ();
1096 trackview.editor.current_session()->set_dirty ();
1100 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1105 AutomationLine::change_model_range (AutomationList::iterator start, AutomationList::iterator end, double xdelta, float ydelta)
1107 alist->move_range (start, end, xdelta, ydelta);
1111 AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
1118 AutomationLine::show_all_control_points ()
1120 points_visible = true;
1122 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1123 (*i)->show_color((_interpolation != AutomationList::Discrete), false);
1125 (*i)->set_visible (true);
1130 AutomationLine::hide_all_but_selected_control_points ()
1132 if (alist->interpolation() == AutomationList::Discrete)
1135 points_visible = false;
1137 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1138 if (!(*i)->selected()) {
1139 (*i)->set_visible (false);
1145 AutomationLine::track_entered()
1147 if (alist->interpolation() != AutomationList::Discrete)
1148 show_all_control_points();
1152 AutomationLine::track_exited()
1154 if (alist->interpolation() != AutomationList::Discrete) {
1155 hide_all_but_selected_control_points();
1160 AutomationLine::get_state (void)
1162 /* function as a proxy for the model */
1163 return alist->get_state();
1167 AutomationLine::set_state (const XMLNode &node)
1169 /* function as a proxy for the model */
1170 return alist->set_state (node);
1174 AutomationLine::view_to_model_y (double& y)
1176 /* TODO: This should be more generic ... */
1177 if (alist->parameter().type() == GainAutomation) {
1178 y = slider_position_to_gain (y);
1181 } else if (alist->parameter().type() == PanAutomation) {
1182 // vertical coordinate axis reversal
1184 } else if (alist->parameter().type() == PluginAutomation) {
1185 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1187 y = (int)(y * alist->parameter().max());
1192 AutomationLine::model_to_view_y (double& y)
1194 /* TODO: This should be more generic ... */
1195 if (alist->parameter().type() == GainAutomation) {
1196 y = gain_to_slider_position (y);
1197 } else if (alist->parameter().type() == PanAutomation) {
1198 // vertical coordinate axis reversal
1200 } else if (alist->parameter().type() == PluginAutomation) {
1201 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
1203 y = y / (double)alist->parameter().max(); /* ... like this */
1209 AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
1211 _interpolation = style;
1213 if (style == AutomationList::Discrete) {
1214 show_all_control_points();
1217 hide_all_but_selected_control_points();