Big ol' automation refactor.
authorDavid Robillard <d@drobilla.net>
Wed, 27 Jun 2007 15:51:50 +0000 (15:51 +0000)
committerDavid Robillard <d@drobilla.net>
Wed, 27 Jun 2007 15:51:50 +0000 (15:51 +0000)
Things with automation parameters now inherit from Automatable, which handles serialization, fetching/adding/removing parameters, etc.
Use AutomationList everywhere instead of Curve, make Curve a member of AutomationList instead (towards other types of "Curve" needed for CC, among other things).
Work towards MIDI CC sending "automation" tracks.

git-svn-id: svn://localhost/ardour2/trunk@2069 d708f5d6-7413-0410-9779-e7cbd77b26cf

68 files changed:
gtk2_ardour/SConscript
gtk2_ardour/audio_region_view.cc
gtk2_ardour/audio_time_axis.cc
gtk2_ardour/audio_time_axis.h
gtk2_ardour/automation_gain_line.cc
gtk2_ardour/automation_gain_line.h
gtk2_ardour/automation_line.h
gtk2_ardour/automation_midi_cc_line.cc [new file with mode: 0644]
gtk2_ardour/automation_midi_cc_line.h [new file with mode: 0644]
gtk2_ardour/automation_pan_line.cc
gtk2_ardour/automation_pan_line.h
gtk2_ardour/crossfade_edit.cc
gtk2_ardour/crossfade_edit.h
gtk2_ardour/crossfade_view.cc
gtk2_ardour/curvetest.cc
gtk2_ardour/editor_canvas_events.cc
gtk2_ardour/editor_items.h
gtk2_ardour/editor_mouse.cc
gtk2_ardour/gain_automation_time_axis.cc
gtk2_ardour/gain_automation_time_axis.h
gtk2_ardour/gain_meter.cc
gtk2_ardour/ladspa_pluginui.cc
gtk2_ardour/midi_controller_time_axis.cc [new file with mode: 0644]
gtk2_ardour/midi_controller_time_axis.h [new file with mode: 0644]
gtk2_ardour/midi_time_axis.cc
gtk2_ardour/midi_time_axis.h
gtk2_ardour/mixer_strip.cc
gtk2_ardour/pan_automation_time_axis.cc
gtk2_ardour/plugin_ui.h
gtk2_ardour/redirect_automation_line.cc
gtk2_ardour/redirect_automation_line.h
gtk2_ardour/redirect_automation_time_axis.cc
gtk2_ardour/redirect_automation_time_axis.h
gtk2_ardour/redirect_box.cc
gtk2_ardour/region_gain_line.cc
gtk2_ardour/region_gain_line.h
gtk2_ardour/route_time_axis.cc
gtk2_ardour/route_time_axis.h
libs/ardour/ardour/audioregion.h
libs/ardour/ardour/automatable.h
libs/ardour/ardour/automation_event.h
libs/ardour/ardour/crossfade.h
libs/ardour/ardour/curve.h
libs/ardour/ardour/gain.h
libs/ardour/ardour/io.h
libs/ardour/ardour/ladspa_plugin.h
libs/ardour/ardour/panner.h
libs/ardour/ardour/param_id.h [new file with mode: 0644]
libs/ardour/ardour/plugin.h
libs/ardour/ardour/plugin_insert.h
libs/ardour/ardour/types.h
libs/ardour/audio_track.cc
libs/ardour/audioregion.cc
libs/ardour/automatable.cc
libs/ardour/automation_event.cc
libs/ardour/crossfade.cc
libs/ardour/curve.cc
libs/ardour/enums.cc
libs/ardour/gain.cc
libs/ardour/insert.cc
libs/ardour/io.cc
libs/ardour/ladspa_plugin.cc
libs/ardour/panner.cc
libs/ardour/plugin.cc
libs/ardour/plugin_insert.cc
libs/ardour/route.cc
libs/ardour/send.cc
libs/surfaces/mackie/mackie_control_protocol.cc

index c5e8f1085622176805a4dadeb6f9977bec7b9221..cb5cc41ae2c782378cc3ea1af948f5db4e0629e9 100644 (file)
@@ -96,6 +96,7 @@ audio_clock.cc
 audio_time_axis.cc
 audio_region_editor.cc
 automation_gain_line.cc
+automation_midi_cc_line.cc
 automation_line.cc
 automation_pan_line.cc
 automation_time_axis.cc
@@ -142,6 +143,7 @@ export_session_dialog.cc
 export_region_dialog.cc
 export_range_markers_dialog.cc
 gain_automation_time_axis.cc
+midi_controller_time_axis.cc
 gain_meter.cc
 ghostregion.cc
 gtk-custom-hruler.c
index 991f15075a8cd0047c186b00a3eaed877cd52251..058295949f8de94495d5b264040eb9193c540410 100644 (file)
@@ -534,7 +534,7 @@ AudioRegionView::reset_fade_in_shape_width (nframes_t width)
        fade_in_shape->show();
 
        float curve[npoints];
-       audio_region()->fade_in().get_vector (0, audio_region()->fade_in().back()->when, curve, npoints);
+       audio_region()->fade_in().curve().get_vector (0, audio_region()->fade_in().back()->when, curve, npoints);
 
        points = get_canvas_points ("fade in shape", npoints+3);
 
@@ -620,7 +620,7 @@ AudioRegionView::reset_fade_out_shape_width (nframes_t width)
        fade_out_shape->show();
 
        float curve[npoints];
-       audio_region()->fade_out().get_vector (0, audio_region()->fade_out().back()->when, curve, npoints);
+       audio_region()->fade_out().curve().get_vector (0, audio_region()->fade_out().back()->when, curve, npoints);
 
        if (_height > NAME_HIGHLIGHT_THRESH) {
                h = _height - NAME_HIGHLIGHT_SIZE;
@@ -953,7 +953,7 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
        audio_region()->envelope().add (fx, y);
        
        XMLNode &after = audio_region()->envelope().get_state();
-       trackview.session().add_command (new MementoCommand<Curve>(audio_region()->envelope(), &before, &after));
+       trackview.session().add_command (new MementoCommand<AutomationList>(audio_region()->envelope(), &before, &after));
        trackview.session().commit_reversible_command ();
 }
 
index f1160e26ecfad1bb59d8e659f5de3e5640133838..b259d8723da62c0ad78195488b3285d08e0f96f3 100644 (file)
@@ -83,16 +83,12 @@ AudioTimeAxisView::AudioTimeAxisView (PublicEditor& ed, Session& sess, boost::sh
        assert(!is_track() || is_audio_track());
 
        subplugin_menu.set_name ("ArdourContextMenu");
-       gain_track = 0;
-       pan_track = 0;
        waveform_item = 0;
-       pan_automation_item = 0;
-       gain_automation_item = 0;
 
        _view = new AudioStreamView (*this);
 
-       add_gain_automation_child ();
-       add_pan_automation_child ();
+       create_automation_child (GainAutomation);
+       create_automation_child (PanAutomation);
 
        ignore_toggle = false;
 
@@ -155,81 +151,6 @@ AudioTimeAxisView::hide ()
        TimeAxisView::hide ();
 }
 
-void
-AudioTimeAxisView::set_state (const XMLNode& node)
-{
-       const XMLProperty *prop;
-       
-       TimeAxisView::set_state (node);
-       
-       if ((prop = node.property ("shown_editor")) != 0) {
-               if (prop->value() == "no") {
-                       _marked_for_display = false;
-               } else {
-                       _marked_for_display = true;
-               }
-       } else {
-               _marked_for_display = true;
-       }
-       
-       XMLNodeList nlist = node.children();
-       XMLNodeConstIterator niter;
-       XMLNode *child_node;
-       
-       
-       show_gain_automation = false;
-       show_pan_automation  = false;
-       
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-               child_node = *niter;
-
-               if (child_node->name() == "gain") {
-                       XMLProperty *prop=child_node->property ("shown");
-                       
-                       if (prop != 0) {
-                               if (prop->value() == "yes") {
-                                       show_gain_automation = true;
-                               }
-                       }
-                       continue;
-               }
-               
-               if (child_node->name() == "pan") {
-                       XMLProperty *prop=child_node->property ("shown");
-                       
-                       if (prop != 0) {
-                               if (prop->value() == "yes") {
-                                       show_pan_automation = true;
-                               }                       
-                       }
-                       continue;
-               }
-       }
-}
-
-void
-AudioTimeAxisView::build_automation_action_menu ()
-{
-       using namespace Menu_Helpers;
-
-       RouteTimeAxisView::build_automation_action_menu ();
-
-       MenuList& automation_items = automation_action_menu->items();
-       
-       automation_items.push_back (SeparatorElem());
-
-       automation_items.push_back (CheckMenuElem (_("Fader"), 
-                                                  mem_fun(*this, &AudioTimeAxisView::toggle_gain_track)));
-       gain_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
-       gain_automation_item->set_active(show_gain_automation);
-
-       automation_items.push_back (CheckMenuElem (_("Pan"),
-                                                  mem_fun(*this, &AudioTimeAxisView::toggle_pan_track)));
-       pan_automation_item = static_cast<CheckMenuItem*> (&automation_items.back());
-       pan_automation_item->set_active(show_pan_automation);
-       
-}
-
 void
 AudioTimeAxisView::append_extra_display_menu_items ()
 {
@@ -360,79 +281,45 @@ AudioTimeAxisView::set_waveform_scale (WaveformScale scale)
 }      
 
 void
-AudioTimeAxisView::add_gain_automation_child ()
+AudioTimeAxisView::create_automation_child (ParamID param)
 {
-       XMLProperty* prop;
-       AutomationLine* line;
-
-       gain_track = new GainAutomationTimeAxisView (_session,
-                                                    _route,
-                                                    editor,
-                                                    *this,
-                                                    parent_canvas,
-                                                    _("gain"),
-                                                    _route->gain_automation_curve());
-       
-       line = new AutomationGainLine ("automation gain",
-                                      _session,
-                                      *gain_track,
-                                      *gain_track->canvas_display,
-                                      _route->gain_automation_curve());
+       if (param.type() == GainAutomation) {
+               GainAutomationTimeAxisView* gain_track = new GainAutomationTimeAxisView (_session,
+                               _route,
+                               editor,
+                               *this,
+                               parent_canvas,
+                               _route->describe_parameter(param),
+                               _route->gain_automation());
 
-       line->set_line_color (Config->canvasvar_AutomationLine.get());
-       
+               AutomationLine* line = new AutomationGainLine ("automation gain",
+                               *gain_track,
+                               *gain_track->canvas_display,
+                               _route->gain_automation());
 
-       gain_track->add_line (*line);
+               line->set_line_color (Config->canvasvar_AutomationLine.get());
 
-       add_child (gain_track);
+               gain_track->add_line (*line);
 
-       gain_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::gain_hidden));
+               add_automation_child(ParamID(GainAutomation), gain_track);
 
-       bool hideit = true;
-       
-       XMLNode* node;
-
-       if ((node = gain_track->get_state_node()) != 0) {
-               if  ((prop = node->property ("shown")) != 0) {
-                       if (prop->value() == "yes") {
-                               hideit = false;
-                       }
-               } 
-       }
+       } else if (param.type() == PanAutomation) {
 
-       if (hideit) {
-               gain_track->hide ();
-       }
-}
+               PanAutomationTimeAxisView* pan_track = new PanAutomationTimeAxisView (_session,
+                                _route,
+                                editor,
+                                *this,
+                                parent_canvas,
+                               _route->describe_parameter(param));
 
-void
-AudioTimeAxisView::add_pan_automation_child ()
-{
-       XMLProperty* prop;
-
-       pan_track = new PanAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, _("pan"));
-
-       update_pans ();
-       
-       add_child (pan_track);
-
-       pan_track->Hiding.connect (mem_fun(*this, &AudioTimeAxisView::pan_hidden));
+               ensure_xml_node ();
 
-       ensure_xml_node ();
-       bool hideit = true;
-       
-       XMLNode* node;
-
-       if ((node = pan_track->get_state_node()) != 0) {
-               if ((prop = node->property ("shown")) != 0) {
-                       if (prop->value() == "yes") {
-                               hideit = false;
-                       }
-               } 
-       }
+               add_automation_child(ParamID(PanAutomation), pan_track);
+               
+               update_pans ();
 
-       if (hideit) {
-               pan_track->hide ();
+       } else {
+               error << "AudioTimeAxisView: unknown automation child " << param.to_string() << endmsg;
        }
 }
 
@@ -441,6 +328,14 @@ AudioTimeAxisView::update_pans ()
 {
        Panner::iterator p;
        
+       RouteAutomationNode* ran = automation_track(PanAutomation);
+       if (!ran) {
+               warning << _route << " has no pan automation track" << endmsg;
+               return;
+       }
+
+       AutomationTimeAxisView* pan_track = ran->track;
+       
        pan_track->clear_lines ();
        
        /* we don't draw lines for "greater than stereo" panning.
@@ -454,7 +349,7 @@ AudioTimeAxisView::update_pans ()
 
                AutomationLine* line;
 
-               line = new AutomationPanLine ("automation pan", _session, *pan_track,
+               line = new AutomationPanLine ("automation pan", *pan_track,
                                              *pan_track->canvas_display, 
                                              (*p)->automation());
 
@@ -470,79 +365,6 @@ AudioTimeAxisView::update_pans ()
        }
 }
                
-void
-AudioTimeAxisView::toggle_gain_track ()
-{
-
-       bool showit = gain_automation_item->get_active();
-
-       if (showit != gain_track->marked_for_display()) {
-               if (showit) {
-                       gain_track->set_marked_for_display (true);
-                       gain_track->canvas_display->show();
-                       gain_track->get_state_node()->add_property ("shown", X_("yes"));
-               } else {
-                       gain_track->set_marked_for_display (false);
-                       gain_track->hide ();
-                       gain_track->get_state_node()->add_property ("shown", X_("no"));
-               }
-
-               /* now trigger a redisplay */
-               
-               if (!no_redraw) {
-                        _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
-               }
-       }
-}
-
-void
-AudioTimeAxisView::gain_hidden ()
-{
-       gain_track->get_state_node()->add_property (X_("shown"), X_("no"));
-
-       if (gain_automation_item && !_hidden) {
-               gain_automation_item->set_active (false);
-       }
-
-        _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
-}
-
-void
-AudioTimeAxisView::toggle_pan_track ()
-{
-       bool showit = pan_automation_item->get_active();
-
-       if (showit != pan_track->marked_for_display()) {
-               if (showit) {
-                       pan_track->set_marked_for_display (true);
-                       pan_track->canvas_display->show();
-                       pan_track->get_state_node()->add_property ("shown", X_("yes"));
-               } else {
-                       pan_track->set_marked_for_display (false);
-                       pan_track->hide ();
-                       pan_track->get_state_node()->add_property ("shown", X_("no"));
-               }
-
-               /* now trigger a redisplay */
-               
-               if (!no_redraw) {
-                        _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
-               }
-       }
-}
-
-void
-AudioTimeAxisView::pan_hidden ()
-{
-       pan_track->get_state_node()->add_property ("shown", "no");
-
-       if (pan_automation_item && !_hidden) {
-               pan_automation_item->set_active (false);
-       }
-
-        _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
-}
-
 void
 AudioTimeAxisView::show_all_automation ()
 {
@@ -669,12 +491,6 @@ AudioTimeAxisView::update_control_names ()
        }
 }
 
-XMLNode* 
-AudioTimeAxisView::get_child_xml_node (const string & childname)
-{
-       return RouteUI::get_child_xml_node (childname);
-}
-
 void
 AudioTimeAxisView::set_layer_display (LayerDisplay d)
 {
index ab5ef955c79e50f3cbc442d24d5cca1e9eb44061..a2f331cf6a0e0d0467c339adc5cdfb8560c5d33d 100644 (file)
@@ -83,16 +83,14 @@ class AudioTimeAxisView : public RouteTimeAxisView
        guint32 show_at (double y, int& nth, Gtk::VBox *parent);
        void hide ();
        
-       void set_state (const XMLNode&);
-       XMLNode* get_child_xml_node (const string & childname);
-
+       void create_automation_child (ARDOUR::ParamID param);
+       
   private:
        friend class AudioStreamView;
        friend class AudioRegionView;
        
        void route_active_changed ();
 
-       void build_automation_action_menu ();
        void append_extra_display_menu_items ();
        
        void toggle_show_waveforms ();
@@ -104,26 +102,12 @@ class AudioTimeAxisView : public RouteTimeAxisView
        void show_existing_automation ();
        void hide_all_automation ();
 
-       void add_gain_automation_child ();
-       void add_pan_automation_child ();
-       void add_parameter_automation_child ();
-
-       void toggle_gain_track ();
-       void toggle_pan_track ();
-
        void gain_hidden ();
        void pan_hidden ();
 
        void update_pans ();
        void update_control_names ();
 
-       AutomationTimeAxisView* gain_track;
-       AutomationTimeAxisView* pan_track;
-
-       // Set from XML so context menu automation buttons can be correctly initialized
-       bool show_gain_automation;
-       bool show_pan_automation;
-       
        Gtk::CheckMenuItem* waveform_item;
        Gtk::RadioMenuItem* traditional_item;
        Gtk::RadioMenuItem* rectified_item;
index 006543df18ac15f51aa9d82bfef1b69a0e64ab7f..84488dc2b50742930bde00195405efbda91cc57c 100644 (file)
 #include "automation_gain_line.h"
 #include "utils.h"
 
-#include <ardour/session.h>
-
 using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-AutomationGainLine::AutomationGainLine (const string & name, Session& s, TimeAxisView& tv, ArdourCanvas::Group& parent, Curve& c)
+AutomationGainLine::AutomationGainLine (const string & name, TimeAxisView& tv, ArdourCanvas::Group& parent, AutomationList& l)
 
-       : AutomationLine (name, tv, parent, c),
-         session (s)
+       : AutomationLine (name, tv, parent, l)
 {
        set_verbose_cursor_uses_gain_mapping (true);
 }
index ca90216c8f2b4082f97f740925640a1e94b08614..fe8c9274f2b1c91007da531ba067bb6af3b67556 100644 (file)
 #include "canvas.h"
 #include "automation_line.h"
 
-namespace ARDOUR {
-       class Session;
-}
-
 class TimeAxisView;
 
 class AutomationGainLine : public AutomationLine
 {
   public:
-       AutomationGainLine (const string & name, ARDOUR::Session&, TimeAxisView&, ArdourCanvas::Group& parent, ARDOUR::Curve&);
+       AutomationGainLine (const string & name, TimeAxisView&, ArdourCanvas::Group& parent, ARDOUR::AutomationList&);
        
        void view_to_model_y (double&);
        void model_to_view_y (double&);
-
-  private:
-       ARDOUR::Session& session;
-
 };
 
 
index 2637a0c554dd86cdf194c7bd92418b18ca378e45..41034dbf6e2b8a99f639a348b96f58282536c84b 100644 (file)
@@ -56,7 +56,7 @@ namespace Gnome {
 class ControlPoint 
 {
   public:
-        ControlPoint (AutomationLine& al);
+       ControlPoint (AutomationLine& al);
        ControlPoint (const ControlPoint&, bool dummy_arg_to_force_special_copy_constructor);
        virtual ~ControlPoint ();
 
@@ -98,7 +98,7 @@ class ControlPoint
 class AutomationLine : public sigc::trackable, public PBD::StatefulThingWithGoingAway
 {
   public:
-        AutomationLine (const string & name, TimeAxisView&, ArdourCanvas::Group&, ARDOUR::AutomationList&);
+       AutomationLine (const string & name, TimeAxisView&, ArdourCanvas::Group&, ARDOUR::AutomationList&);
        virtual ~AutomationLine ();
 
        void queue_reset ();
diff --git a/gtk2_ardour/automation_midi_cc_line.cc b/gtk2_ardour/automation_midi_cc_line.cc
new file mode 100644 (file)
index 0000000..4a074dd
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+    Copyright (C) 2000-2007 Paul Davis 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sigc++/signal.h>
+
+#include <ardour/curve.h>
+
+#include "automation_midi_cc_line.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+AutomationMidiCCLine::AutomationMidiCCLine (const string & name, TimeAxisView& tv, ArdourCanvas::Group& parent, AutomationList& l)
+
+       : AutomationLine (name, tv, parent, l)
+{
+       set_verbose_cursor_uses_gain_mapping (true);
+}
+
+void
+AutomationMidiCCLine::view_to_model_y (double& y)
+{
+       assert(y >=     0);
+       assert(y <= 1);
+       
+       y = (int)(y * 127.0);
+       
+       assert(y >=     0);
+       assert(y <= 127);
+}
+
+void
+AutomationMidiCCLine::model_to_view_y (double& y)
+{
+       assert(y >=     0);
+       assert(y <= 127);
+       
+       y = y / 127.0;
+       
+       assert(y >=     0);
+       assert(y <= 1);
+}
+
+string
+AutomationMidiCCLine::get_verbose_cursor_string (float fraction)
+{
+       static const size_t MAX_VAL_LEN = 4; // 4 for "127\0"
+       char buf[MAX_VAL_LEN];
+
+       double cc_val = fraction;
+       view_to_model_y(cc_val); // 0..127
+
+       snprintf (buf, MAX_VAL_LEN, "%u", (unsigned)cc_val);
+
+       return buf;
+}
+
+
+
diff --git a/gtk2_ardour/automation_midi_cc_line.h b/gtk2_ardour/automation_midi_cc_line.h
new file mode 100644 (file)
index 0000000..6ac078a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    Copyright (C) 2000-2007 Paul Davis 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_gtk_automation_midi_cc_line_h__
+#define __ardour_gtk_automation_midi_cc_line_h__
+
+#include <ardour/ardour.h>
+
+#include "canvas.h"
+#include "automation_line.h"
+
+class TimeAxisView;
+
+class AutomationMidiCCLine : public AutomationLine
+{
+  public:
+       AutomationMidiCCLine (const string & name, TimeAxisView&, ArdourCanvas::Group& parent, ARDOUR::AutomationList&);
+       
+       void view_to_model_y (double&);
+       void model_to_view_y (double&);
+       
+       string get_verbose_cursor_string (float);
+};
+
+
+#endif /* __ardour_gtk_automation_midi_cc_line_h__ */
+
+
index 04cfb0a31109bde247112be15b79cd8d400ae515..0e36f4ef885d1f9d4031b770579acc6908bc67ad 100644 (file)
 #include "utils.h"
 #include <cmath>
 
-#include <ardour/session.h>
-
 using namespace ARDOUR;
 using namespace PBD;
 
-AutomationPanLine::AutomationPanLine (const string & name, Session& s, TimeAxisView& tv, ArdourCanvas::Group& parent, Curve& c)
+AutomationPanLine::AutomationPanLine (const string & name, TimeAxisView& tv, ArdourCanvas::Group& parent, AutomationList& l)
 
-       : AutomationLine (name, tv, parent, c),
-         session (s)
+       : AutomationLine (name, tv, parent, l)
 {
 }
 
index c6b32cbc928e90fa59ec76e2084ff9a1cac2c49d..6374c535e0ebdb307274eb91bdb0e1168a1759a2 100644 (file)
 #include "canvas.h"
 #include "automation_line.h"
 
-namespace ARDOUR {
-       class Session;
-}
-
 class TimeAxisView;
 
 class AutomationPanLine : public AutomationLine
 {
   public:
-       AutomationPanLine (const string & name, ARDOUR::Session&, TimeAxisView&, ArdourCanvas::Group& parent, ARDOUR::Curve&);
+       AutomationPanLine (const string & name, TimeAxisView&, ArdourCanvas::Group& parent, ARDOUR::AutomationList&);
        
        void view_to_model_y (double&);
        void model_to_view_y (double&);
 
   private:
-       ARDOUR::Session& session;
        vector<ArdourCanvas::Item*> lines;
 };
 
index 349297f8cda83506b16d5f1a7ac323fc8877ea99..fd677ac4a6abdc5a4efa4ef4e65e8a7007c7fc6e 100644 (file)
@@ -66,8 +66,8 @@ CrossfadeEditor::Presets* CrossfadeEditor::fade_out_presets = 0;
 
 CrossfadeEditor::Half::Half ()
        : line (0), 
-         normative_curve (0.0, 1.0, 1.0, true),
-         gain_curve (0.0, 2.0, 1.0, true)
+         normative_curve (ParamID(GainAutomation), 0.0, 1.0, 1.0), // FIXME: GainAutomation?
+         gain_curve (ParamID(GainAutomation), 0.0, 2.0, 1.0)
 {
 }
 
@@ -327,10 +327,10 @@ CrossfadeEditor::audition_state_changed (bool yn)
 }
 
 void
-CrossfadeEditor::set (const ARDOUR::Curve& curve, WhichFade which)
+CrossfadeEditor::set (const ARDOUR::AutomationList& curve, WhichFade which)
 {
        double firstx, endx;
-       ARDOUR::Curve::const_iterator the_end;
+       ARDOUR::AutomationList::const_iterator the_end;
 
        for (list<Point*>::iterator i = fade[which].points.begin(); i != fade[which].points.end(); ++i) {
                        delete *i;
@@ -350,7 +350,7 @@ CrossfadeEditor::set (const ARDOUR::Curve& curve, WhichFade which)
        firstx = (*curve.const_begin())->when;
        endx = (*the_end)->when;
 
-       for (ARDOUR::Curve::const_iterator i = curve.const_begin(); i != curve.const_end(); ++i) {
+       for (ARDOUR::AutomationList::const_iterator i = curve.const_begin(); i != curve.const_end(); ++i) {
                
                double xfract = ((*i)->when - firstx) / (endx - firstx);
                double yfract = ((*i)->value - miny) / (maxy - miny);
@@ -644,7 +644,7 @@ CrossfadeEditor::redraw ()
        size_t npoints = (size_t) effective_width();
        float vec[npoints];
 
-       fade[current].normative_curve.get_vector (0, 1.0, vec, npoints);
+       fade[current].normative_curve.curve().get_vector (0, 1.0, vec, npoints);
        
        ArdourCanvas::Points pts;
        ArdourCanvas::Points spts;
@@ -760,13 +760,13 @@ CrossfadeEditor::apply ()
 void
 CrossfadeEditor::_apply_to (boost::shared_ptr<Crossfade> xf)
 {
-       ARDOUR::Curve& in (xf->fade_in());
-       ARDOUR::Curve& out (xf->fade_out());
+       ARDOUR::AutomationList& in (xf->fade_in());
+       ARDOUR::AutomationList& out (xf->fade_out());
 
        /* IN */
 
 
-       ARDOUR::Curve::const_iterator the_end = in.const_end();
+       ARDOUR::AutomationList::const_iterator the_end = in.const_end();
        --the_end;
 
        double firstx = (*in.begin())->when;
@@ -813,8 +813,8 @@ CrossfadeEditor::setup (boost::shared_ptr<Crossfade> xfade)
 {
        _apply_to (xfade);
        xfade->set_active (true);
-       xfade->fade_in().solve ();
-       xfade->fade_out().solve ();
+       xfade->fade_in().curve().solve ();
+       xfade->fade_out().curve().solve ();
 }
 
 void
index 715aa1a3607da3f6fe3765d9dd0aa66c4d4af110..e9f2dcf7622c0bdad10e8d65f130f1623387e88f 100644 (file)
@@ -33,7 +33,7 @@
 namespace ARDOUR
 {
        class Session;
-       class Curve;
+       class AutomationList;
        class Crossfade;
 }
 
@@ -105,8 +105,8 @@ class CrossfadeEditor : public ArdourDialog
            ArdourCanvas::Line*     line;
            ArdourCanvas::Polygon*  shading;
            list<Point*>            points;
-           ARDOUR::Curve           normative_curve; /* 0 - 1.0, linear */
-           ARDOUR::Curve           gain_curve;      /* 0 - 2.0, gain mapping */
+           ARDOUR::AutomationList  normative_curve; /* 0 - 1.0, linear */
+           ARDOUR::AutomationList  gain_curve;      /* 0 - 2.0, gain mapping */
            vector<ArdourCanvas::WaveView*>  waves;
            
            Half();
@@ -176,7 +176,7 @@ class CrossfadeEditor : public ArdourDialog
        double x_coordinate (double& xfract) const;
        double y_coordinate (double& yfract) const;
     
-       void set (const ARDOUR::Curve& alist, WhichFade);
+       void set (const ARDOUR::AutomationList& alist, WhichFade);
 
        sigc::connection peaks_ready_connection;
 
index d2fdd94e039828d58e862b3d044ddc9bad14554c..cf19137d988271c55faf64d9c7e8cfe0499147b4 100644 (file)
@@ -189,7 +189,7 @@ CrossfadeView::redraw_curves ()
        points = get_canvas_points ("xfade edit redraw", npoints);
        vec = new float[npoints];
 
-       crossfade->fade_in().get_vector (0, crossfade->length(), vec, npoints);
+       crossfade->fade_in().curve().get_vector (0, crossfade->length(), vec, npoints);
        for (int i = 0, pci = 0; i < npoints; ++i) {
                Art::Point &p = (*points)[pci++];
                p.set_x(i);
@@ -197,7 +197,7 @@ CrossfadeView::redraw_curves ()
        }
        fade_in->property_points() = *points;
 
-       crossfade->fade_out().get_vector (0, crossfade->length(), vec, npoints);
+       crossfade->fade_out().curve().get_vector (0, crossfade->length(), vec, npoints);
        for (int i = 0, pci = 0; i < npoints; ++i) {
                Art::Point &p = (*points)[pci++];
                p.set_x(i);
index 63804bd1b411d8069d11e53e4e90c64cf689fbfa..71f0600f32dd22fa301d62f459cf683c970b49c1 100644 (file)
@@ -33,7 +33,7 @@ curvetest (string filename)
 {
        ifstream in (filename.c_str());
        stringstream line;
-       Curve c (-1.0, +1.0, 0, true);
+       AutomationList al (ParamID(), -1.0, +1.0, 0);
        double minx = DBL_MAX;
        double maxx = DBL_MIN;
 
@@ -55,13 +55,13 @@ curvetest (string filename)
                        maxx = x;
                }
                
-               c.add (x, y);
+               al.add (x, y);
        }
 
 
        float foo[1024];
 
-       c.get_vector (minx, maxx, foo, 1024);
+       al.curve().get_vector (minx, maxx, foo, 1024);
        
        for (int i = 0; i < 1024; ++i) {
                cout << minx + (((double) i / 1024.0) * (maxx - minx)) << ' ' << foo[i] << endl;
index 40bef304c503ffa9ef36e54fd3de47b82b718f01..e1ed2d2fa1439cdcc484430aad2afb7f606b6da9 100644 (file)
@@ -35,6 +35,7 @@
 #include "region_gain_line.h"
 #include "automation_gain_line.h"
 #include "automation_pan_line.h"
+#include "automation_midi_cc_line.h"
 #include "automation_time_axis.h"
 #include "redirect_automation_line.h"
 #include "canvas_impl.h"
@@ -592,6 +593,8 @@ Editor::canvas_control_point_event (GdkEvent *event, ArdourCanvas::Item* item, C
                type = PanAutomationControlPointItem;
        } else if (dynamic_cast<RedirectAutomationLine*> (&cp->line) != 0) {
                type = RedirectAutomationControlPointItem;
+       } else if (dynamic_cast<AutomationMidiCCLine*> (&cp->line) != 0) {
+               type = MidiCCAutomationControlPointItem;
        } else {
                return false;
        }
@@ -612,6 +615,8 @@ Editor::canvas_line_event (GdkEvent *event, ArdourCanvas::Item* item, Automation
                type = PanAutomationLineItem;
        } else if (dynamic_cast<RedirectAutomationLine*> (al) != 0) {
                type = RedirectAutomationLineItem;
+       } else if (dynamic_cast<AutomationMidiCCLine*> (al) != 0) {
+               type = MidiCCAutomationLineItem;
        } else {
                return false;
        }
index ad1d63b5bf1ff7e5aebadd914fc8870e15aa49c4..ddba43350a9b6a0c392c5449472136b27e31a744 100644 (file)
@@ -38,6 +38,8 @@ enum ItemType {
        PanAutomationLineItem,
        RedirectAutomationControlPointItem,
        RedirectAutomationLineItem,
+       MidiCCAutomationControlPointItem,
+       MidiCCAutomationLineItem,
        MeterMarkerItem,
        TempoMarkerItem,
        MeterBarItem,
index 2f671c77ebaac21e52b434e3c6d14e6c01b8745d..773c26f67de7334e6ba5f0acd4cb5b65a5cee4f7 100644 (file)
@@ -350,6 +350,7 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it
        case GainAutomationControlPointItem:
        case PanAutomationControlPointItem:
        case RedirectAutomationControlPointItem:
+       case MidiCCAutomationControlPointItem:
                commit = set_selected_track_from_click (press, op, true);
                if (mouse_mode != MouseRange) {
                        commit |= set_selected_control_point_from_click (op, false);
@@ -539,6 +540,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                case GainAutomationControlPointItem:
                                case PanAutomationControlPointItem:
                                case RedirectAutomationControlPointItem:
+                               case MidiCCAutomationControlPointItem:
                                        start_control_point_grab (item, event);
                                        return true;
                                        break;
@@ -546,6 +548,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                case GainAutomationLineItem:
                                case PanAutomationLineItem:
                                case RedirectAutomationLineItem:
+                               case MidiCCAutomationLineItem:
                                        start_line_grab_from_line (item, event);
                                        return true;
                                        break;
@@ -608,6 +611,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                        case GainAutomationControlPointItem:
                        case PanAutomationControlPointItem:
                        case RedirectAutomationControlPointItem:
+                       case MidiCCAutomationControlPointItem:
                                start_control_point_grab (item, event);
                                return true;
                                break;
@@ -622,12 +626,14 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                        case GainAutomationControlPointItem:
                        case PanAutomationControlPointItem:
                        case RedirectAutomationControlPointItem:
+                       case MidiCCAutomationControlPointItem:
                                start_control_point_grab (item, event);
                                break;
 
                        case GainAutomationLineItem:
                        case PanAutomationLineItem:
                        case RedirectAutomationLineItem:
+                       case MidiCCAutomationLineItem:
                                start_line_grab_from_line (item, event);
                                break;
 
@@ -682,6 +688,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                case GainAutomationControlPointItem:
                                case PanAutomationControlPointItem:
                                case RedirectAutomationControlPointItem:
+                               case MidiCCAutomationControlPointItem:
                                        start_control_point_grab (item, event);
                                        return true;
                                        break;
@@ -896,6 +903,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                case GainAutomationControlPointItem:
                case PanAutomationControlPointItem:
                case RedirectAutomationControlPointItem:
+               case MidiCCAutomationControlPointItem:
                        remove_control_point (item, event);
                        break;
 
@@ -917,6 +925,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                case GainAutomationLineItem:
                case PanAutomationLineItem:
                case RedirectAutomationLineItem:
+               case MidiCCAutomationLineItem:
                case StartSelectionTrimItem:
                case EndSelectionTrimItem:
                        return true;
@@ -1061,6 +1070,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case GainAutomationControlPointItem:
        case PanAutomationControlPointItem:
        case RedirectAutomationControlPointItem:
+       case MidiCCAutomationControlPointItem:
                if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
                        cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
                        cp->set_visible (true);
@@ -1096,6 +1106,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                        
        case GainAutomationLineItem:
        case RedirectAutomationLineItem:
+       case MidiCCAutomationLineItem:
        case PanAutomationLineItem:
                if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
                        {
@@ -1218,11 +1229,13 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case GainLineItem:
        case GainAutomationLineItem:
        case RedirectAutomationLineItem:
+       case MidiCCAutomationLineItem:
        case PanAutomationLineItem:
        case GainControlPointItem:
        case GainAutomationControlPointItem:
        case PanAutomationControlPointItem:
        case RedirectAutomationControlPointItem:
+       case MidiCCAutomationControlPointItem:
                /* these do not affect the current entered track state */
                clear_entered_track = false;
                break;
@@ -1254,6 +1267,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case GainAutomationControlPointItem:
        case PanAutomationControlPointItem:
        case RedirectAutomationControlPointItem:
+       case MidiCCAutomationControlPointItem:
                cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
                if (cp->line.npoints() > 1) {
                        if (!cp->selected) {
@@ -1289,6 +1303,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case GainLineItem:
        case GainAutomationLineItem:
        case RedirectAutomationLineItem:
+       case MidiCCAutomationLineItem:
        case PanAutomationLineItem:
                al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
                {
@@ -1432,6 +1447,7 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        case MarkerItem:
        case GainControlPointItem:
        case RedirectAutomationControlPointItem:
+       case MidiCCAutomationControlPointItem:
        case GainAutomationControlPointItem:
        case PanAutomationControlPointItem:
        case TempoMarkerItem:
@@ -1442,6 +1458,7 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        case SelectionItem:
        case GainLineItem:
        case RedirectAutomationLineItem:
+       case MidiCCAutomationLineItem:
        case GainAutomationLineItem:
        case PanAutomationLineItem:
        case FadeInHandleItem:
index 9890854d4b91e2ec7e42ffd0b4c1e495955285f5..509c941555dde530bfe9f4ed56901c607dc98556 100644 (file)
@@ -17,7 +17,7 @@
 
 */
 
-#include <ardour/curve.h>
+#include <ardour/automation_event.h>
 #include <ardour/route.h>
 #include <pbd/memento_command.h>
 
@@ -33,11 +33,11 @@ using namespace Gtk;
 
 GainAutomationTimeAxisView::GainAutomationTimeAxisView (Session& s, boost::shared_ptr<Route> r, 
                                                        PublicEditor& e, TimeAxisView& parent, 
-                                                       ArdourCanvas::Canvas& canvas, const string & n, ARDOUR::Curve& c)
+                                                       ArdourCanvas::Canvas& canvas, const string & n, ARDOUR::AutomationList& l)
 
        : AxisView (s),
          AutomationTimeAxisView (s, r, e, parent, canvas, n, X_("gain"), ""),
-         curve (c)
+         list (l)
        
 {
 }
@@ -62,10 +62,10 @@ GainAutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkE
        lines.front()->view_to_model_y (y);
 
        _session.begin_reversible_command (_("add gain automation event"));
-        XMLNode& before = curve.get_state();
-       curve.add (when, y);
-        XMLNode& after = curve.get_state();
-       _session.commit_reversible_command (new MementoCommand<ARDOUR::Curve>(curve, &before, &after));
+       XMLNode& before = list.get_state();
+       list.add (when, y);
+       XMLNode& after = list.get_state();
+       _session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(list, &before, &after));
        _session.set_dirty ();
 }
 
@@ -73,6 +73,6 @@ void
 GainAutomationTimeAxisView::set_automation_state (AutoState state)
 {
        if (!ignore_state_request) {
-               route->set_gain_automation_state (state);
+               route->set_parameter_automation_state (ParamID(GainAutomation), state);
        }
 }
index 25a97a1a97d95bccf0c756ede0d06069e39847e4..3f07be4ace4fc94b3825724523535bb276e73c08 100644 (file)
@@ -24,8 +24,7 @@
 #include "automation_time_axis.h"
 
 namespace ARDOUR {
-       class Redirect;
-       class Curve;
+       class AutomationList;
 }
 
 class GainAutomationTimeAxisView : public AutomationTimeAxisView
@@ -37,16 +36,16 @@ class GainAutomationTimeAxisView : public AutomationTimeAxisView
                                    TimeAxisView& parent_axis,
                                    ArdourCanvas::Canvas& canvas,
                                    const string & name,
-                                   ARDOUR::Curve&);
+                                   ARDOUR::AutomationList&);
        
        ~GainAutomationTimeAxisView();
 
        void add_automation_event (ArdourCanvas::Item *item, GdkEvent *event, nframes_t, double);
        
    private:
-       ARDOUR::Curve& curve;
+       ARDOUR::AutomationList& list;
 
-        void automation_changed ();
+       void automation_changed ();
        void set_automation_state (ARDOUR::AutoState);
 };
 
index f67682019ed082627a88f8e3a95c26d4d170b943..a4eea61bfa40b9cdf06ee06f85e2b9adc601e159 100644 (file)
@@ -166,13 +166,17 @@ GainMeter::GainMeter (boost::shared_ptr<IO> io, Session& s)
                using namespace Menu_Helpers;
        
                gain_astate_menu.items().push_back (MenuElem (_("Manual"), 
-                                                     bind (mem_fun (*_io, &IO::set_gain_automation_state), (AutoState) Off)));
+                                                     bind (mem_fun (*_io, &IO::set_parameter_automation_state),
+                                                                 ParamID(GainAutomation), (AutoState) Off)));
                gain_astate_menu.items().push_back (MenuElem (_("Play"),
-                                                     bind (mem_fun (*_io, &IO::set_gain_automation_state), (AutoState) Play)));
+                                                     bind (mem_fun (*_io, &IO::set_parameter_automation_state),
+                                                                 ParamID(GainAutomation), (AutoState) Play)));
                gain_astate_menu.items().push_back (MenuElem (_("Write"),
-                                                     bind (mem_fun (*_io, &IO::set_gain_automation_state), (AutoState) Write)));
+                                                     bind (mem_fun (*_io, &IO::set_parameter_automation_state),
+                                                                 ParamID(GainAutomation), (AutoState) Write)));
                gain_astate_menu.items().push_back (MenuElem (_("Touch"),
-                                                     bind (mem_fun (*_io, &IO::set_gain_automation_state), (AutoState) Touch)));
+                                                     bind (mem_fun (*_io, &IO::set_parameter_automation_state),
+                                                                 ParamID(GainAutomation), (AutoState) Touch)));
        
                gain_astyle_menu.items().push_back (MenuElem (_("Trim")));
                gain_astyle_menu.items().push_back (MenuElem (_("Abs")));
@@ -183,8 +187,8 @@ GainMeter::GainMeter (boost::shared_ptr<IO> io, Session& s)
                gain_automation_style_button.signal_button_press_event().connect (mem_fun(*this, &GainMeter::gain_automation_style_button_event), false);
                gain_automation_state_button.signal_button_press_event().connect (mem_fun(*this, &GainMeter::gain_automation_state_button_event), false);
                
-               r->gain_automation_curve().automation_state_changed.connect (mem_fun(*this, &GainMeter::gain_automation_state_changed));
-               r->gain_automation_curve().automation_style_changed.connect (mem_fun(*this, &GainMeter::gain_automation_style_changed));
+               r->gain_automation().automation_state_changed.connect (mem_fun(*this, &GainMeter::gain_automation_state_changed));
+               r->gain_automation().automation_style_changed.connect (mem_fun(*this, &GainMeter::gain_automation_style_changed));
                fader_vbox->pack_start (gain_automation_state_button, false, false, 0);
 
                gain_automation_state_changed ();
@@ -668,7 +672,7 @@ GainMeter::set_fader_name (const char * name)
 void
 GainMeter::update_gain_sensitive ()
 {
-       static_cast<Gtkmm2ext::SliderController*>(gain_slider)->set_sensitive (!(_io->gain_automation_state() & Play));
+       static_cast<Gtkmm2ext::SliderController*>(gain_slider)->set_sensitive (!(_io->gain_automation().automation_state() & Play));
 }
 
 
@@ -811,14 +815,14 @@ GainMeter::meter_point_clicked ()
 gint
 GainMeter::start_gain_touch (GdkEventButton* ev)
 {
-       _io->start_gain_touch ();
+       _io->gain_automation().start_touch ();
        return FALSE;
 }
 
 gint
 GainMeter::end_gain_touch (GdkEventButton* ev)
 {
-       _io->end_gain_touch ();
+       _io->gain_automation().stop_touch ();
        return FALSE;
 }
 
@@ -922,10 +926,10 @@ GainMeter::gain_automation_style_changed ()
   // Route* _route = dynamic_cast<Route*>(&_io);
        switch (_width) {
        case Wide:
-               gain_automation_style_button.set_label (astyle_string(_io->gain_automation_curve().automation_style()));
+               gain_automation_style_button.set_label (astyle_string(_io->gain_automation().automation_style()));
                break;
        case Narrow:
-               gain_automation_style_button.set_label  (short_astyle_string(_io->gain_automation_curve().automation_style()));
+               gain_automation_style_button.set_label  (short_astyle_string(_io->gain_automation().automation_style()));
                break;
        }
 }
@@ -940,14 +944,14 @@ GainMeter::gain_automation_state_changed ()
 
        switch (_width) {
        case Wide:
-               gain_automation_state_button.set_label (astate_string(_io->gain_automation_curve().automation_state()));
+               gain_automation_state_button.set_label (astate_string(_io->gain_automation().automation_state()));
                break;
        case Narrow:
-               gain_automation_state_button.set_label (short_astate_string(_io->gain_automation_curve().automation_state()));
+               gain_automation_state_button.set_label (short_astate_string(_io->gain_automation().automation_state()));
                break;
        }
 
-       x = (_io->gain_automation_state() != Off);
+       x = (_io->gain_automation().automation_state() != Off);
        
        if (gain_automation_state_button.get_active() != x) {
                ignore_toggle = true;
index cd585d073addcfb835b114cf14ceb5495340d67a..25c497a377dd86134c397442971682ff913cd668 100644 (file)
@@ -177,7 +177,7 @@ LadspaPluginUI::build ()
                        
                        /* Don't show latency control ports */
 
-                       if (plugin->describe_parameter (i) == X_("latency")) {
+                       if (plugin->describe_parameter (ParamID(PluginAutomation, i)) == X_("latency")) {
                                continue;
                        }
 
@@ -324,7 +324,8 @@ LadspaPluginUI::automation_state_changed (ControlUI* cui)
 {
        /* update button label */
 
-       switch (insert->get_port_automation_state (cui->port_index) & (Off|Play|Touch|Write)) {
+       switch (insert->get_parameter_automation_state (ParamID(PluginAutomation, cui->port_index))
+                       & (Off|Play|Touch|Write)) {
        case Off:
                cui->automate_button.set_label (_("Manual"));
                break;
@@ -491,7 +492,7 @@ LadspaPluginUI::build_control_ui (guint32 port_index, PBD::Controllable* mcontro
                automation_state_changed (control_ui);
 
                plugin->ParameterChanged.connect (bind (mem_fun(*this, &LadspaPluginUI::parameter_changed), control_ui));
-               insert->automation_list (port_index).automation_state_changed.connect 
+               insert->automation_list (ParamID(PluginAutomation, port_index))->automation_state_changed.connect 
                        (bind (mem_fun(*this, &LadspaPluginUI::automation_state_changed), control_ui));
 
        } else if (plugin->parameter_is_output (port_index)) {
@@ -548,13 +549,13 @@ LadspaPluginUI::build_control_ui (guint32 port_index, PBD::Controllable* mcontro
 void
 LadspaPluginUI::start_touch (LadspaPluginUI::ControlUI* cui)
 {
-       insert->automation_list (cui->port_index).start_touch ();
+       insert->automation_list (ParamID(PluginAutomation, cui->port_index))->start_touch ();
 }
 
 void
 LadspaPluginUI::stop_touch (LadspaPluginUI::ControlUI* cui)
 {
-       insert->automation_list (cui->port_index).stop_touch ();
+       insert->automation_list (ParamID(PluginAutomation, cui->port_index))->stop_touch ();
 }
 
 void
@@ -585,7 +586,7 @@ LadspaPluginUI::astate_clicked (ControlUI* cui, uint32_t port)
 void
 LadspaPluginUI::set_automation_state (AutoState state, ControlUI* cui)
 {
-       insert->set_port_automation_state (cui->port_index, state);
+       insert->set_parameter_automation_state (ParamID(PluginAutomation, cui->port_index), state);
 }
 
 void
@@ -601,7 +602,7 @@ LadspaPluginUI::control_adjustment_changed (ControlUI* cui)
                value = exp(value);
        }
 
-       insert->set_parameter (cui->port_index, (float) value);
+       insert->set_parameter (ParamID(PluginAutomation, cui->port_index), (float) value);
 }
 
 void
@@ -656,7 +657,7 @@ void
 LadspaPluginUI::control_port_toggled (ControlUI* cui)
 {
        if (!cui->ignore_change) {
-               insert->set_parameter (cui->port_index, cui->button->get_active());
+               insert->set_parameter (ParamID(PluginAutomation, cui->port_index), cui->button->get_active());
        }
 }
 
@@ -666,7 +667,7 @@ LadspaPluginUI::control_combo_changed (ControlUI* cui)
        if (!cui->ignore_change) {
                string value = cui->combo->get_active_text();
                std::map<string,float> mapping = *cui->combo_map;
-               insert->set_parameter (cui->port_index, mapping[value]);
+               insert->set_parameter (ParamID(PluginAutomation, cui->port_index), mapping[value]);
        }
 
 }
diff --git a/gtk2_ardour/midi_controller_time_axis.cc b/gtk2_ardour/midi_controller_time_axis.cc
new file mode 100644 (file)
index 0000000..a3dcb0f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+    Copyright (C) 2003 Paul Davis 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/automation_event.h>
+#include <ardour/route.h>
+#include <pbd/memento_command.h>
+
+#include "midi_controller_time_axis.h"
+#include "automation_line.h"
+#include "canvas.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using namespace Gtk;
+
+MidiControllerTimeAxisView::MidiControllerTimeAxisView (Session& s, boost::shared_ptr<Route> r, 
+                                                       PublicEditor& e, TimeAxisView& parent, 
+                                                       ArdourCanvas::Canvas& canvas, const string & n,
+                                                       ParamID param, ARDOUR::AutomationList& l)
+
+       : AxisView (s),
+         AutomationTimeAxisView (s, r, e, parent, canvas, n, param.to_string(), ""),
+         _list (l),
+         _param (param)
+{
+}
+
+MidiControllerTimeAxisView::~MidiControllerTimeAxisView ()
+{
+}
+
+void
+MidiControllerTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkEvent* event, nframes_t when, double y)
+{
+       double x = 0;
+
+       canvas_display->w2i (x, y);
+
+       /* compute vertical fractional position */
+
+       y = 1.0 - (y / height);
+
+       /* map using line */
+
+       lines.front()->view_to_model_y (y);
+
+       _session.begin_reversible_command (_("add midi controller automation event"));
+       XMLNode& before = _list.get_state();
+       _list.add (when, y);
+       XMLNode& after = _list.get_state();
+       _session.commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(_list, &before, &after));
+       _session.set_dirty ();
+}
+
+void
+MidiControllerTimeAxisView::set_automation_state (AutoState state)
+{
+       if (!ignore_state_request) {
+               cerr << "FIXME: set midi controller automation state" << endl;
+               //route->set_midi_controller_state (state);
+       }
+}
+
diff --git a/gtk2_ardour/midi_controller_time_axis.h b/gtk2_ardour/midi_controller_time_axis.h
new file mode 100644 (file)
index 0000000..78f3c12
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2000-2007 Paul Davis 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_gtk_midi_controller_time_axis_h__
+#define __ardour_gtk_midi_controller_time_axis_h__
+
+#include "canvas.h"
+#include "automation_time_axis.h"
+
+namespace ARDOUR {
+       class AutomationList;
+}
+
+class MidiControllerTimeAxisView : public AutomationTimeAxisView
+{
+  public:
+       MidiControllerTimeAxisView (ARDOUR::Session&,
+                                   boost::shared_ptr<ARDOUR::Route>,
+                                   PublicEditor&,
+                                   TimeAxisView& parent_axis,
+                                   ArdourCanvas::Canvas& canvas,
+                                   const string & name,
+                                       ARDOUR::ParamID param,
+                                   ARDOUR::AutomationList&);
+       
+       ~MidiControllerTimeAxisView();
+
+       void add_automation_event (ArdourCanvas::Item *item, GdkEvent *event, nframes_t, double);
+       
+   private:
+       ARDOUR::AutomationList& _list;
+       ARDOUR::ParamID _param;
+
+       void automation_changed ();
+       void set_automation_state (ARDOUR::AutoState);
+};
+
+#endif /* __ardour_gtk_midi_controller_time_axis_h__ */
index dd008bd9c5ef091cdaaf776652c0350bab849650..960ca36af9296c165244c6bdbc748f83b78c1224 100644 (file)
@@ -48,6 +48,7 @@
 #include "ardour_ui.h"
 #include "midi_time_axis.h"
 #include "automation_time_axis.h"
+#include "automation_midi_cc_line.h"
 #include "canvas_impl.h"
 #include "crossfade_view.h"
 #include "enums.h"
@@ -61,6 +62,7 @@
 #include "public_editor.h"
 #include "redirect_automation_line.h"
 #include "redirect_automation_time_axis.h"
+#include "midi_controller_time_axis.h"
 #include "region_view.h"
 #include "rgb_macros.h"
 #include "selection.h"
@@ -147,30 +149,67 @@ MidiTimeAxisView::hide ()
 }
 
 void
-MidiTimeAxisView::set_state (const XMLNode& node)
+MidiTimeAxisView::build_automation_action_menu ()
 {
-       const XMLProperty *prop;
+       using namespace Menu_Helpers;
+
+       RouteTimeAxisView::build_automation_action_menu ();
+
+       MenuList& automation_items = automation_action_menu->items();
        
-       TimeAxisView::set_state (node);
+       automation_items.push_back (SeparatorElem());
+
+       automation_items.push_back (MenuElem (_("Controller..."), 
+                                                  mem_fun(*this, &MidiTimeAxisView::add_controller_track)));
+}
+
+/** Prompt for a controller with a dialog and add an automation track for it
+ */
+void
+MidiTimeAxisView::add_controller_track()
+{
+       /* TODO: fancy controller selection dialog here... */
+
+       ParamID param(MidiCCAutomation, 7);
+       create_automation_child(param);
+}
+
+void
+MidiTimeAxisView::create_automation_child (ParamID param)
+{
+       if (param.type() == MidiCCAutomation) {
        
-       if ((prop = node.property ("shown_editor")) != 0) {
-               if (prop->value() == "no") {
-                       _marked_for_display = false;
-               } else {
-                       _marked_for_display = true;
-               }
+               /* FIXME: this all probably leaks */
+
+               ARDOUR::AutomationList* al = _route->automation_list(param);
+
+               if (!al)
+                       al = new ARDOUR::AutomationList(param, 0, 127, 64);
+
+               _route->add_automation_parameter(al);
+
+               MidiControllerTimeAxisView* track = new MidiControllerTimeAxisView (_session,
+                               _route,
+                               editor,
+                               *this,
+                               parent_canvas,
+                               _route->describe_parameter(param),
+                               param,
+                               *al);
+
+               AutomationMidiCCLine* line = new AutomationMidiCCLine (param.to_string(),
+                               *track,
+                               *track->canvas_display,
+                               *al);
+
+               line->set_line_color (Config->canvasvar_AutomationLine.get());
+
+               track->add_line(*line);
+
+               add_automation_child(param, track);
+
        } else {
-               _marked_for_display = true;
-       }
-       
-       XMLNodeList nlist = node.children();
-       XMLNodeConstIterator niter;
-       XMLNode *child_node;
-       
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-               child_node = *niter;
-               
-               // uh... do stuff..
+               error << "MidiTimeAxisView: unknown automation child " << param.to_string() << endmsg;
        }
 }
 
@@ -205,9 +244,3 @@ MidiTimeAxisView::route_active_changed ()
        }
 }
 
-XMLNode* 
-MidiTimeAxisView::get_child_xml_node (const string & childname)
-{
-       return RouteUI::get_child_xml_node (childname);
-}
-
index 17b3d72794be3aebc6811e70e7d1c0e3d574722e..3b06411418bc5dff904cfa26314bd6e728cff8b6 100644 (file)
@@ -61,11 +61,14 @@ class MidiTimeAxisView : public RouteTimeAxisView
        /* overridden from parent to store display state */
        guint32 show_at (double y, int& nth, Gtk::VBox *parent);
        void hide ();
-       
-       void set_state (const XMLNode&);
-       XMLNode* get_child_xml_node (const string & childname);
+
+       void add_controller_track ();
+       void create_automation_child (ARDOUR::ParamID param);
 
   private:
+       
+       void build_automation_action_menu ();
+       
        void route_active_changed ();
 
        void add_insert_to_subplugin_menu (ARDOUR::Insert *);
index 0c99dc74018d562a569b10d21d157b76f3f31db9..cb27ef4e882575de52b69936239e2d7da4554e7b 100644 (file)
@@ -435,8 +435,8 @@ MixerStrip::set_width (Width w, void* owner)
                       ((Gtk::Label*)comment_button.get_child())->set_text (_("*comments*"));
                }
 
-               ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (gpm.astyle_string(_route->gain_automation_curve().automation_style()));
-               ((Gtk::Label*)gpm.gain_automation_state_button.get_child())->set_text (gpm.astate_string(_route->gain_automation_curve().automation_state()));
+               ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (gpm.astyle_string(_route->gain_automation().automation_style()));
+               ((Gtk::Label*)gpm.gain_automation_state_button.get_child())->set_text (gpm.astate_string(_route->gain_automation().automation_state()));
                ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (panners.astyle_string(_route->panner().automation_style()));
                ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (panners.astate_string(_route->panner().automation_state()));
                Gtkmm2ext::set_size_request_to_display_given_text (name_button, "long", 2, 2);
@@ -457,8 +457,8 @@ MixerStrip::set_width (Width w, void* owner)
                       ((Gtk::Label*)comment_button.get_child())->set_text (_("*Cmt*"));
                }
 
-               ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (gpm.short_astyle_string(_route->gain_automation_curve().automation_style()));
-               ((Gtk::Label*)gpm.gain_automation_state_button.get_child())->set_text (gpm.short_astate_string(_route->gain_automation_curve().automation_state()));
+               ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (gpm.short_astyle_string(_route->gain_automation().automation_style()));
+               ((Gtk::Label*)gpm.gain_automation_state_button.get_child())->set_text (gpm.short_astate_string(_route->gain_automation().automation_state()));
                ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (panners.short_astyle_string(_route->panner().automation_style()));
                ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (panners.short_astate_string(_route->panner().automation_state()));
                Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
index c008a10ff08903c554615f8a20e3a1cc02834e5b..1199982b5d3f9e49e1caecd0262026260dd33140 100644 (file)
@@ -55,6 +55,11 @@ PanAutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item, GdkEv
 {
        if (lines.empty()) {
                /* no data, possibly caused by no outputs/inputs */
+               Gtkmm2ext::PopUp* msg = new Gtkmm2ext::PopUp (Gtk::WIN_POS_MOUSE, 5000, true);
+
+               msg->set_text (_("Pan automation track has no lines, unable to add point\n(is track pannable?)"));
+               msg->touch ();
+
                return;
        }
 
index a505f80576082f709dc5ed6b87bfed5f87500add..e583f11dce2f3a9fea6efc1e01e4aacd8320f19f 100644 (file)
@@ -136,6 +136,10 @@ class LadspaPluginUI : public PlugUIBase, public Gtk::VBox
        static const int32_t initial_output_rows = 1;
        static const int32_t initial_output_cols = 4;
 
+       /* TODO: pull this out of PluginUI and make it generic.
+        * Sticking this in the track controls of an automation track would
+        * make a handy touch controller for anything.
+        */
        struct ControlUI : public Gtk::HBox {
 
            uint32_t      port_index;
index 89b6fb4f2a3a3bb32bac7c47193591aedf89d700..5680a8b98f22e6d609553d6115260c378bea0fd2 100644 (file)
@@ -33,7 +33,7 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-RedirectAutomationLine::RedirectAutomationLine (const string & name, Insert& i, uint32_t port, Session& s,
+RedirectAutomationLine::RedirectAutomationLine (const string & name, Insert& i, ParamID param, Session& s,
                                                
                                                TimeAxisView& tv, ArdourCanvas::Group& parent,
                                                
@@ -42,7 +42,7 @@ RedirectAutomationLine::RedirectAutomationLine (const string & name, Insert& i,
         : AutomationLine (name, tv, parent, l),
          session (s),
          _insert (i),
-         _port (port)
+         _param (param)
 {
        set_verbose_cursor_uses_gain_mapping (false);
 
@@ -54,7 +54,7 @@ RedirectAutomationLine::RedirectAutomationLine (const string & name, Insert& i,
                /*NOTREACHED*/
        }
 
-       pi->plugin()->get_parameter_descriptor (_port, desc);
+       pi->plugin()->get_parameter_descriptor (_param, desc);
 
        upper = desc.upper;
        lower = desc.lower;
index 33d411e48ec78476548a00c135853c1aa9b528ea..d0e3aa88973c7574e96f713f0042fe8b922dc953 100644 (file)
@@ -34,10 +34,10 @@ class TimeAxisView;
 class RedirectAutomationLine : public AutomationLine
 {
   public:
-       RedirectAutomationLine (const string & name, ARDOUR::Insert&, uint32_t port, ARDOUR::Session&, TimeAxisView&, 
+       RedirectAutomationLine (const string & name, ARDOUR::Insert&, ARDOUR::ParamID param, ARDOUR::Session&, TimeAxisView&, 
                                ArdourCanvas::Group& parent, ARDOUR::AutomationList&);
        
-       uint32_t port() const { return _port; }
+       ARDOUR::ParamID param() const { return _param; }
        ARDOUR::Insert& insert() const { return _insert; }
 
        string get_verbose_cursor_string (float);
@@ -45,7 +45,7 @@ class RedirectAutomationLine : public AutomationLine
   private:
        ARDOUR::Session& session;
        ARDOUR::Insert& _insert;
-       uint32_t _port;
+       ARDOUR::ParamID _param;
        float upper;
        float lower;
        float range;
index 9df0bb9e0c0fedf995750f8ce977e94da76984e5..bb7937cdf72f36c16e12cbe2e3f24014612b7ce0 100644 (file)
@@ -34,12 +34,12 @@ using namespace Gtk;
 
 RedirectAutomationTimeAxisView::RedirectAutomationTimeAxisView (Session& s, boost::shared_ptr<Route> r, 
                                                                PublicEditor& e, TimeAxisView& parent, Canvas& canvas, std::string n,
-                                                               uint32_t prt, Insert& i, string state_name)
+                                                               ParamID p, Insert& i, string state_name)
 
        : AxisView (s),
          AutomationTimeAxisView (s, r, e, parent, canvas, n, state_name, i.name()),
          insert (i),
-         port (prt)
+         param (p)
        
 {
        char buf[32];
@@ -53,7 +53,7 @@ RedirectAutomationTimeAxisView::RedirectAutomationTimeAxisView (Session& s, boos
 
        kids = xml_node->children ();
 
-       snprintf (buf, sizeof(buf), "Port_%" PRIu32, port);
+       snprintf (buf, sizeof(buf), "Port_%" PRIu32, param.id());
                
        for (iter = kids.begin(); iter != kids.end(); ++iter) {
                if ((*iter)->name() == buf) {
@@ -91,17 +91,17 @@ RedirectAutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* item,
        /* map to model space */
 
        if (!lines.empty()) {
-               AutomationList& alist (insert.automation_list(port));
+               AutomationList* alist (insert.automation_list(param, true));
                string description = _("add automation event to ");
-               description += insert.describe_parameter (port);
+               description += insert.describe_parameter (param);
 
                lines.front()->view_to_model_y (y);
                
                _session.begin_reversible_command (description);
-                XMLNode &before = alist.get_state();
-               alist.add (when, y);
-                XMLNode &after = alist.get_state();
-                _session.add_command(new MementoCommand<AutomationList>(alist, &before, &after));
+               XMLNode &before = alist->get_state();
+               alist->add (when, y);
+               XMLNode &after = alist->get_state();
+               _session.add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
                _session.commit_reversible_command ();
                _session.set_dirty ();
        }
@@ -146,7 +146,7 @@ RedirectAutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
        XMLNodeConstIterator i;
        XMLNode * port_node = 0;
 
-       snprintf (buf, sizeof(buf), "Port_%" PRIu32, port);
+       snprintf (buf, sizeof(buf), "Port_%" PRIu32, param.id());
 
        for (i = nlist.begin(); i != nlist.end(); ++i) {
                if ((*i)->name() == buf) {
@@ -168,6 +168,6 @@ void
 RedirectAutomationTimeAxisView::set_automation_state (AutoState state)
 {
        if (!ignore_state_request) {
-               insert.automation_list (port).set_automation_state (state);
+               insert.automation_list (param, true)->set_automation_state (state);
        }
 }
index eceed7446a6f8fd99efb1bceb18e7d26591ab6af..d87e4faca5985d894c82cd24c3bde149a9700acd 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "canvas.h"
 #include "automation_time_axis.h"
+#include <ardour/param_id.h>
 
 namespace ARDOUR {
        class Redirect;
@@ -38,7 +39,7 @@ class RedirectAutomationTimeAxisView : public AutomationTimeAxisView
                                        TimeAxisView& parent,
                                        ArdourCanvas::Canvas& canvas,
                                        std::string name,
-                                       uint32_t port,
+                                       ARDOUR::ParamID param,
                                        ARDOUR::Insert& i,
                                        std::string state_name);
 
@@ -52,7 +53,7 @@ class RedirectAutomationTimeAxisView : public AutomationTimeAxisView
        
    private:
        ARDOUR::Insert& insert;
-        uint32_t port;
+       ARDOUR::ParamID param;
 
        XMLNode *xml_node;
        void ensure_xml_node();
index 8749ab12db1dd97c2dcb20789ac944912d8c8c5e..0402b0e1f60a020c13db692c637d696bd6737455 100644 (file)
@@ -1050,7 +1050,7 @@ RedirectBox::edit_insert (boost::shared_ptr<Insert> insert)
                }
        }
        
-       if ((send = boost::dynamic_pointer_cast<Send> (send)) == 0) {
+       if ((send = boost::dynamic_pointer_cast<Send> (send)) != 0) {
                
                if (!_session.engine().connected()) {
                        return;
index e935d3adc2b72db7178e01a2151b582e37ea4097..25ba903ca8d2dba2c0f7fa53ba94b3235a410066 100644 (file)
@@ -38,8 +38,8 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-AudioRegionGainLine::AudioRegionGainLine (const string & name, Session& s, AudioRegionView& r, ArdourCanvas::Group& parent, Curve& c)
-  : AutomationLine (name, r.get_time_axis_view(), parent, c),
+AudioRegionGainLine::AudioRegionGainLine (const string & name, Session& s, AudioRegionView& r, ArdourCanvas::Group& parent, AutomationList& l)
+  : AutomationLine (name, r.get_time_axis_view(), parent, l),
          session (s),
          rv (r)
 {
index 801fe09bad1414692c3d169cc8b437a3abd73184..259615aa39fe8155b33b221a8adb1ae42f177a9a 100644 (file)
@@ -35,7 +35,7 @@ class AudioRegionView;
 class AudioRegionGainLine : public AutomationLine
 {
   public:
-  AudioRegionGainLine (const string & name, ARDOUR::Session&, AudioRegionView&, ArdourCanvas::Group& parent, ARDOUR::Curve&);
+  AudioRegionGainLine (const string & name, ARDOUR::Session&, AudioRegionView&, ArdourCanvas::Group& parent, ARDOUR::AutomationList&);
        
        void view_to_model_y (double&);
        void model_to_view_y (double&);
index 900064a7639f21fb3d06769600c82bacf0583538..f113724cf56cc8103a28dfced69cc505ce1709c1 100644 (file)
@@ -23,6 +23,7 @@
 #include <algorithm>
 #include <string>
 #include <vector>
+#include <utility>
 
 #include <sigc++/bind.h>
 
@@ -50,6 +51,7 @@
 #include <ardour/session.h>
 #include <ardour/session_playlist.h>
 #include <ardour/utils.h>
+#include <ardour/param_id.h>
 
 #include "ardour_ui.h"
 #include "route_time_axis.h"
@@ -80,6 +82,7 @@ using namespace ARDOUR;
 using namespace PBD;
 using namespace Gtk;
 using namespace Editing;
+using namespace sigc;
 
 
 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
@@ -252,6 +255,58 @@ RouteTimeAxisView::playlist_modified ()
 {
 }
 
+void
+RouteTimeAxisView::set_state (const XMLNode& node)
+{
+       const XMLProperty *prop;
+       
+       TimeAxisView::set_state (node);
+       
+       if ((prop = node.property ("shown_editor")) != 0) {
+               if (prop->value() == "no") {
+                       _marked_for_display = false;
+               } else {
+                       _marked_for_display = true;
+               }
+       } else {
+               _marked_for_display = true;
+       }
+       
+       XMLNodeList nlist = node.children();
+       XMLNodeConstIterator niter;
+       XMLNode *child_node;
+       
+       _show_automation.clear();
+       
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+               child_node = *niter;
+               
+               ParamID param(child_node->name());
+
+               if (param) {
+               
+                       cerr << "RTAV::set_state parameter: " << param.to_string() << endl;
+                       
+                       XMLProperty* prop = child_node->property ("shown");
+                       
+                       if (_automation_tracks.find(param) == _automation_tracks.end())
+                               create_automation_child(param);
+
+                       if (prop != 0 && prop->value() == "yes")
+                               _show_automation.insert(ParamID(GainAutomation));
+
+               } else {
+                       cerr << "RTAV: no parameter " << child_node->name() << endl;
+               }
+       }
+}
+
+XMLNode* 
+RouteTimeAxisView::get_child_xml_node (const string & childname)
+{
+       return RouteUI::get_child_xml_node (childname);
+}
+
 gint
 RouteTimeAxisView::edit_click (GdkEventButton *ev)
 {
@@ -386,6 +441,24 @@ RouteTimeAxisView::build_automation_action_menu ()
                                              mem_fun(*this, &RouteTimeAxisView::hide_all_automation)));
 
        automation_items.push_back (MenuElem (_("Plugins"), subplugin_menu));
+       
+       map<ARDOUR::ParamID, RouteAutomationNode*>::iterator i;
+       for (i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
+
+               automation_items.push_back (SeparatorElem());
+
+               if ( ! i->second->menu_item) {
+                       automation_items.push_back(CheckMenuElem (_route->describe_parameter(i->second->param), 
+                                       bind (mem_fun(*this, &RouteTimeAxisView::toggle_automation_track), i->second->param)));
+
+                       i->second->menu_item = static_cast<Gtk::CheckMenuItem*>(&automation_items.back());
+
+               } else {
+                       automation_items.push_back (*i->second->menu_item);
+               }
+               
+               i->second->menu_item->set_active(show_automation(i->second->param));
+       }
 }
 
 void
@@ -1092,6 +1165,33 @@ RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>&
        return;
 }
 
+bool
+RouteTimeAxisView::show_automation(ParamID param)
+{
+       return (_show_automation.find(param) != _show_automation.end());
+}
+
+/** Retuns NULL if track for \a param doesn't exist.
+ */
+RouteTimeAxisView::RouteAutomationNode*
+RouteTimeAxisView::automation_track(ParamID param)
+{
+       map<ARDOUR::ParamID, RouteAutomationNode*>::iterator i = _automation_tracks.find(param);
+
+       if (i != _automation_tracks.end())
+               return i->second;
+       else
+               return NULL;
+}
+
+/** Shorthand for GainAutomation, etc.
+ */    
+RouteTimeAxisView::RouteAutomationNode*
+RouteTimeAxisView::automation_track(AutomationType type)
+{
+       return automation_track(ParamID(type));
+}
+
 RouteGroup*
 RouteTimeAxisView::edit_group() const
 {
@@ -1379,10 +1479,70 @@ RouteTimeAxisView::color_handler ()
 
 }
 
+void
+RouteTimeAxisView::toggle_automation_track (ParamID param)
+{
+       RouteAutomationNode* node = automation_track(param);
+
+       if (!node)
+               return;
+
+       bool showit = node->menu_item->get_active();
+
+       if (showit != node->track->marked_for_display()) {
+               if (showit) {
+                       node->track->set_marked_for_display (true);
+                       node->track->canvas_display->show();
+                       node->track->get_state_node()->add_property ("shown", X_("yes"));
+               } else {
+                       node->track->set_marked_for_display (false);
+                       node->track->hide ();
+                       node->track->get_state_node()->add_property ("shown", X_("no"));
+               }
+
+               /* now trigger a redisplay */
+               
+               if (!no_redraw) {
+                        _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
+               }
+       }
+}
+
+void
+RouteTimeAxisView::automation_track_hidden (ParamID param)
+{
+       RouteAutomationNode* ran = automation_track(param);
+       if (!ran)
+               return;
+
+       _show_automation.erase(param);
+       ran->track->get_state_node()->add_property (X_("shown"), X_("no"));
+
+       if (ran->menu_item && !_hidden) {
+               ran->menu_item->set_active (false);
+       }
+
+        _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
+}
+
+
 void
 RouteTimeAxisView::show_all_automation ()
 {
        no_redraw = true;
+       
+       /* Show our automation */
+
+       map<ARDOUR::ParamID, RouteAutomationNode*>::iterator i;
+       for (i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
+               i->second->track->set_marked_for_display (true);
+               i->second->track->canvas_display->show();
+               i->second->track->get_state_node()->add_property ("shown", X_("yes"));
+               i->second->menu_item->set_active(true);
+       }
+
+
+       /* Show insert automation */
 
        for (list<InsertAutomationInfo*>::iterator i = insert_automation.begin(); i != insert_automation.end(); ++i) {
                for (vector<InsertAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
@@ -1396,6 +1556,9 @@ RouteTimeAxisView::show_all_automation ()
 
        no_redraw = false;
 
+
+       /* Redraw */
+
         _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
 }
 
@@ -1403,6 +1566,22 @@ void
 RouteTimeAxisView::show_existing_automation ()
 {
        no_redraw = true;
+       
+       /* Show our automation */
+
+       map<ARDOUR::ParamID, RouteAutomationNode*>::iterator i;
+       for (i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
+               // FIXME: only shown if /first/ line has points
+               if (!i->second->track->lines.empty() && i->second->track->lines[0]->npoints() > 0) {
+                       i->second->track->set_marked_for_display (true);
+                       i->second->track->canvas_display->show();
+                       i->second->track->get_state_node()->add_property ("shown", X_("yes"));
+                       i->second->menu_item->set_active(true);
+               }
+       }
+
+
+       /* Show insert automation */
 
        for (list<InsertAutomationInfo*>::iterator i = insert_automation.begin(); i != insert_automation.end(); ++i) {
                for (vector<InsertAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
@@ -1477,7 +1656,7 @@ RouteTimeAxisView::remove_ran (InsertAutomationNode* ran)
 }
 
 RouteTimeAxisView::InsertAutomationNode*
-RouteTimeAxisView::find_insert_automation_node (boost::shared_ptr<Insert> insert, uint32_t what)
+RouteTimeAxisView::find_insert_automation_node (boost::shared_ptr<Insert> insert, ParamID what)
 {
        for (list<InsertAutomationInfo*>::iterator i = insert_automation.begin(); i != insert_automation.end(); ++i) {
 
@@ -1514,7 +1693,7 @@ legalize_for_xml_node (string str)
 
 
 void
-RouteTimeAxisView::add_insert_automation_curve (boost::shared_ptr<Insert> insert, uint32_t what)
+RouteTimeAxisView::add_insert_automation_curve (boost::shared_ptr<Insert> insert, ParamID what)
 {
        RedirectAutomationLine* ral;
        string name;
@@ -1538,13 +1717,13 @@ RouteTimeAxisView::add_insert_automation_curve (boost::shared_ptr<Insert> insert
        /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
 
        char state_name[256];
-       snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (insert->name()).c_str(), what);
+       snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (insert->name()).c_str(), what.id());
 
        ran->view = new RedirectAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, name, what, *insert, state_name);
 
        ral = new RedirectAutomationLine (name, 
                                          *insert, what, _session, *ran->view,
-                                         *ran->view->canvas_display, insert->automation_list (what));
+                                         *ran->view->canvas_display, *insert->automation_list (what, true));
        
        ral->set_line_color (Config->canvasvar_RedirectAutomationLine.get());
        ral->queue_reset ();
@@ -1583,12 +1762,12 @@ RouteTimeAxisView::insert_automation_track_hidden (RouteTimeAxisView::InsertAuto
 void
 RouteTimeAxisView::add_existing_insert_automation_curves (boost::shared_ptr<Insert> insert)
 {
-       set<uint32_t> s;
+       set<ParamID> s;
        RedirectAutomationLine *ral;
 
        insert->what_has_visible_automation (s);
 
-       for (set<uint32_t>::iterator i = s.begin(); i != s.end(); ++i) {
+       for (set<ParamID>::iterator i = s.begin(); i != s.end(); ++i) {
                
                if ((ral = find_insert_automation_curve (insert, *i)) != 0) {
                        ral->queue_reset ();
@@ -1598,6 +1777,39 @@ RouteTimeAxisView::add_existing_insert_automation_curves (boost::shared_ptr<Inse
        }
 }
 
+void
+RouteTimeAxisView::add_automation_child(ParamID param, AutomationTimeAxisView* track)
+{
+       using namespace Menu_Helpers;
+
+       XMLProperty* prop;
+
+       add_child (track);
+
+       track->Hiding.connect (bind (mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
+
+       bool hideit = true;
+       
+       XMLNode* node;
+
+       if ((node = track->get_state_node()) != 0) {
+               if  ((prop = node->property ("shown")) != 0) {
+                       if (prop->value() == "yes") {
+                               hideit = false;
+                       }
+               } 
+       }
+
+       if (hideit) {
+               track->hide ();
+       } else {
+               _show_automation.insert(param);
+       }
+               
+       _automation_tracks.insert(std::make_pair(param, new RouteAutomationNode(param, NULL, track)));
+}
+
+
 void
 RouteTimeAxisView::add_insert_to_subplugin_menu (boost::shared_ptr<Insert> insert)
 {
@@ -1605,8 +1817,8 @@ RouteTimeAxisView::add_insert_to_subplugin_menu (boost::shared_ptr<Insert> inser
        InsertAutomationInfo *rai;
        list<InsertAutomationInfo*>::iterator x;
        
-       const std::set<uint32_t>& automatable = insert->what_can_be_automated ();
-       std::set<uint32_t> has_visible_automation;
+       const std::set<ParamID>& automatable = insert->what_can_be_automated ();
+       std::set<ParamID> has_visible_automation;
 
        insert->what_has_visible_automation(has_visible_automation);
 
@@ -1641,7 +1853,7 @@ RouteTimeAxisView::add_insert_to_subplugin_menu (boost::shared_ptr<Insert> inser
 
        items.clear ();
 
-       for (std::set<uint32_t>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
+       for (std::set<ParamID>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
 
                InsertAutomationNode* ran;
                CheckMenuItem* mitem;
@@ -1755,7 +1967,7 @@ RouteTimeAxisView::inserts_changed ()
 }
 
 RedirectAutomationLine *
-RouteTimeAxisView::find_insert_automation_curve (boost::shared_ptr<Insert> insert, uint32_t what)
+RouteTimeAxisView::find_insert_automation_curve (boost::shared_ptr<Insert> insert, ParamID what)
 {
        InsertAutomationNode* ran;
 
index e14d682c8397debc387699a590e215f8aa4ceb11..42c889e16fd3c49742c59a6fe9c985924544a7ef 100644 (file)
@@ -79,6 +79,7 @@ public:
        void set_selected_regionviews (RegionSelection&);
        void get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable *>&);
        void get_inverted_selectables (Selection&, list<Selectable*>&);
+       bool show_automation(ARDOUR::ParamID param);
                
        boost::shared_ptr<ARDOUR::Region> find_next_region (nframes_t pos, ARDOUR::RegionPoint, int32_t dir);
 
@@ -94,6 +95,8 @@ public:
        void clear_playlist ();
        
        void build_playlist_menu (Gtk::Menu *);
+
+       virtual void create_automation_child (ARDOUR::ParamID param) = 0;
        
        string              name() const;
        StreamView*         view() const { return _view; }
@@ -103,13 +106,22 @@ public:
 protected:
        friend class StreamView;
        
+       struct RouteAutomationNode {
+               ARDOUR::ParamID         param;
+           Gtk::CheckMenuItem*     menu_item;
+           AutomationTimeAxisView* track;
+           
+               RouteAutomationNode (ARDOUR::ParamID par, Gtk::CheckMenuItem* mi, AutomationTimeAxisView* tr)
+                   : param (par), menu_item (mi), track (tr) {}
+       };
+
        struct InsertAutomationNode {
-           uint32_t                what;
+               ARDOUR::ParamID         what;
            Gtk::CheckMenuItem*     menu_item;
            AutomationTimeAxisView* view;
            RouteTimeAxisView&      parent;
 
-           InsertAutomationNode (uint32_t w, Gtk::CheckMenuItem* mitem, RouteTimeAxisView& p)
+           InsertAutomationNode (ARDOUR::ParamID w, Gtk::CheckMenuItem* mitem, RouteTimeAxisView& p)
                    : what (w), menu_item (mitem), view (0), parent (p) {}
 
            ~InsertAutomationNode ();
@@ -143,15 +155,22 @@ protected:
        
        void insert_automation_track_hidden (InsertAutomationNode*,
                                               boost::shared_ptr<ARDOUR::Insert>);
+       
+       void automation_track_hidden (ARDOUR::ParamID param);
+
+       RouteAutomationNode* automation_track(ARDOUR::ParamID param);
+       RouteAutomationNode* automation_track(ARDOUR::AutomationType type);
 
        InsertAutomationNode*
-       find_insert_automation_node (boost::shared_ptr<ARDOUR::Insert> i, uint32_t);
+       find_insert_automation_node (boost::shared_ptr<ARDOUR::Insert> i, ARDOUR::ParamID);
        
        RedirectAutomationLine*
-       find_insert_automation_curve (boost::shared_ptr<ARDOUR::Insert> i, uint32_t);
+       find_insert_automation_curve (boost::shared_ptr<ARDOUR::Insert> i, ARDOUR::ParamID);
 
-       void add_insert_automation_curve (boost::shared_ptr<ARDOUR::Insert> r, uint32_t);
+       void add_insert_automation_curve (boost::shared_ptr<ARDOUR::Insert> r, ARDOUR::ParamID);
        void add_existing_insert_automation_curves (boost::shared_ptr<ARDOUR::Insert>);
+
+       void add_automation_child(ARDOUR::ParamID param, AutomationTimeAxisView* track);
        
        void reset_insert_automation_curves ();
 
@@ -186,6 +205,7 @@ protected:
        void rename_current_playlist ();
        
        void         automation_click ();
+       void         toggle_automation_track (ARDOUR::ParamID param);
        virtual void show_all_automation ();
        virtual void show_existing_automation ();
        virtual void hide_all_automation ();
@@ -204,7 +224,6 @@ protected:
        void region_view_added (RegionView*);
        void add_ghost_to_insert (RegionView*, AutomationTimeAxisView*);
        
-       
        StreamView*           _view;
        ArdourCanvas::Canvas& parent_canvas;
        bool                  no_redraw;
@@ -238,12 +257,21 @@ protected:
        void _set_track_mode (ARDOUR::Track* track, ARDOUR::TrackMode mode, Gtk::RadioMenuItem* reset_item);
        void track_mode_changed ();
 
-       list<InsertAutomationInfo*>   insert_automation;
+       list<InsertAutomationInfo*>     insert_automation;
        vector<RedirectAutomationLine*> insert_automation_curves;
+       
+       // Set from XML so context menu automation buttons can be correctly initialized
+       set<ARDOUR::ParamID> _show_automation;
+
+       map<ARDOUR::ParamID, RouteAutomationNode*> _automation_tracks;
 
        sigc::connection modified_connection;
 
        void post_construct ();
+       
+       void set_state (const XMLNode&);
+       
+       XMLNode* get_child_xml_node (const string & childname);
 };
 
 #endif /* __ardour_route_time_axis_h__ */
index f844d45ef122a0290f96d4dcde36cd37ed0ff0bb..db763876b0dce727de6a7933ae61d5547d513e96 100644 (file)
@@ -67,9 +67,9 @@ class AudioRegion : public Region
        bool fade_in_active ()  const { return _flags & Region::FadeIn; }
        bool fade_out_active () const { return _flags & Region::FadeOut; }
 
-       Curve& fade_in()  { return _fade_in; }
-       Curve& fade_out() { return _fade_out; }
-       Curve& envelope() { return _envelope; }
+       AutomationList& fade_in()  { return _fade_in; }
+       AutomationList& fade_out() { return _fade_out; }
+       AutomationList& envelope() { return _envelope; }
 
        virtual nframes_t read_peaks (PeakData *buf, nframes_t npeaks,
                                      nframes_t offset, nframes_t cnt,
@@ -162,14 +162,14 @@ class AudioRegion : public Region
        void source_offset_changed ();
        void listen_to_my_curves ();
 
-       mutable Curve     _fade_in;
-       FadeShape         _fade_in_shape;
-       mutable Curve     _fade_out;
-       FadeShape         _fade_out_shape;
-       mutable Curve     _envelope;
-       gain_t            _scale_amplitude;
-       uint32_t          _fade_in_disabled;
-       uint32_t          _fade_out_disabled;
+       mutable AutomationList _fade_in;
+       FadeShape              _fade_in_shape;
+       mutable AutomationList _fade_out;
+       FadeShape              _fade_out_shape;
+       mutable AutomationList _envelope;
+       gain_t                 _scale_amplitude;
+       uint32_t               _fade_in_disabled;
+       uint32_t               _fade_out_disabled;
 
   protected:
        /* default constructor for derived (compound) types */
index c6621b97808102deaaf1a775045a244975d286fd..f6d6d86ed065936c9231669775c208ba4b7f2efa 100644 (file)
@@ -24,6 +24,7 @@
 #include <map>
 #include <ardour/session_object.h>
 #include <ardour/automation_event.h>
+#include <ardour/param_id.h>
 
 namespace ARDOUR {
 
@@ -36,28 +37,46 @@ public:
 
        virtual ~Automatable() {}
 
-       virtual AutomationList& automation_list(uint32_t n);
+       // shorthand for gain, pan, etc
+       inline AutomationList* automation_list(AutomationType type, bool create_if_missing=false) {
+               return automation_list(ParamID(type), create_if_missing);
+       }
 
-       virtual void automation_snapshot (nframes_t now) {};
+       virtual AutomationList* automation_list(ParamID id, bool create_if_missing=false);
+       virtual const AutomationList* automation_list(ParamID id) const;
+
+       virtual void add_automation_parameter(AutomationList* al);
+
+       virtual void automation_snapshot(nframes_t now) {};
 
        virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const;
        
-       virtual string describe_parameter(uint32_t which);
-       virtual float default_parameter_value(uint32_t which) { return 1.0f; }
+       virtual string describe_parameter(ParamID param);
+       virtual float  default_parameter_value(ParamID param) { return 1.0f; }
+       
+       virtual void clear_automation();
+
+       AutoState get_parameter_automation_state (ParamID param);
+       virtual void set_parameter_automation_state (ParamID param, AutoState);
+       
+       AutoStyle get_parameter_automation_style (ParamID param);
+       void set_parameter_automation_style (ParamID param, AutoStyle);
+
+       void protect_automation ();
 
-       void what_has_automation(std::set<uint32_t>&) const;
-       void what_has_visible_automation(std::set<uint32_t>&) const;
-       const std::set<uint32_t>& what_can_be_automated() const { return _can_automate_list; }
+       void what_has_automation(std::set<ParamID>&) const;
+       void what_has_visible_automation(std::set<ParamID>&) const;
+       const std::set<ParamID>& what_can_be_automated() const { return _can_automate_list; }
 
-       void mark_automation_visible(uint32_t, bool);
+       void mark_automation_visible(ParamID, bool);
 
 protected:
 
-       void can_automate(uint32_t);
+       void can_automate(ParamID);
 
-       virtual void automation_list_creation_callback(uint32_t, AutomationList&) {}
+       virtual void automation_list_creation_callback(ParamID, AutomationList&) {}
 
-       int set_automation_state(const XMLNode&);
+       int set_automation_state(const XMLNode&, ParamID default_param);
        XMLNode& get_automation_state();
        
        int load_automation (const std::string& path);
@@ -65,10 +84,9 @@ protected:
 
        mutable Glib::Mutex _automation_lock;
 
-       // FIXME:  map with int keys is a bit silly.  this could be O(1)
-       std::map<uint32_t,AutomationList*> _parameter_automation;
-       std::set<uint32_t> _visible_parameter_automation;
-       std::set<uint32_t> _can_automate_list;
+       std::map<ParamID,AutomationList*> _parameter_automation;
+       std::set<ParamID> _visible_parameter_automation;
+       std::set<ParamID> _can_automate_list;
        
        nframes_t _last_automation_snapshot;
 };
index f04dcdc1122938f7c51c00e2e624b08b5607f3a4..af1a3cb70483dcf8e7736850779bbba5cc462ca5 100644 (file)
 #include <pbd/statefuldestructible.h> 
 
 #include <ardour/ardour.h>
+#include <ardour/param_id.h>
 
 namespace ARDOUR {
-       
+
+class Curve;
+
 struct ControlEvent {
-    double when;
-    double value;
-    
+
     ControlEvent (double w, double v)
-           : when (w), value (v) { }
-    ControlEvent (const ControlEvent& other) 
-           : when (other.when), value (other.value) {}
+           : when (w), value (v) { 
+           coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
+       }
 
-    virtual ~ControlEvent() {}
+    ControlEvent (const ControlEvent& other) 
+           : when (other.when), value (other.value) {
+           coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
+       }
     
-//    bool operator==(const ControlEvent& other) {
-//         return value == other.value && when == other.when;
-//    }
-
+       double when;
+    double value;
+       double coeff[4]; ///< Used by Curve
 };
 
+
 class AutomationList : public PBD::StatefulDestructible
 {
   public:
-       typedef std::list<ControlEvent*> AutomationEventList;
-       typedef AutomationEventList::iterator iterator;
-       typedef AutomationEventList::const_iterator const_iterator;
+       typedef std::list<ControlEvent*> EventList;
+       typedef EventList::iterator iterator;
+       typedef EventList::const_iterator const_iterator;
 
-       AutomationList (double default_value);
-       AutomationList (const XMLNode&);
+       AutomationList (ParamID id, double min_val, double max_val, double default_val);
+       AutomationList (const XMLNode&, ParamID id);
        ~AutomationList();
 
        AutomationList (const AutomationList&);
@@ -68,14 +72,17 @@ class AutomationList : public PBD::StatefulDestructible
        AutomationList& operator= (const AutomationList&);
        bool operator== (const AutomationList&);
 
+       ParamID param_id() const         { return _param_id; }
+       void    set_param_id(ParamID id) { _param_id = id; }
+
        void freeze();
        void thaw ();
 
-       AutomationEventList::size_type size() const { return events.size(); }
-       bool empty() const { return events.empty(); }
+       EventList::size_type size() const { return _events.size(); }
+       bool empty() const { return _events.empty(); }
 
        void reset_default (double val) {
-               default_value = val;
+               _default_value = val;
        }
 
        void clear ();
@@ -111,7 +118,7 @@ class AutomationList : public PBD::StatefulDestructible
        sigc::signal<void> automation_style_changed;
 
        void set_automation_style (AutoStyle m);
-        AutoStyle automation_style() const { return _style; }
+       AutoStyle automation_style() const { return _style; }
        sigc::signal<void> automation_state_changed;
 
        bool automation_playback() const {
@@ -126,29 +133,29 @@ class AutomationList : public PBD::StatefulDestructible
        bool touching() const { return _touching; }
 
        void set_yrange (double min, double max) {
-               min_yval = min;
-               max_yval = max;
+               _min_yval = min;
+               _max_yval = max;
        }
 
-       double get_max_y() const { return max_yval; }
-       double get_min_y() const { return min_yval; }
+       double get_max_y() const { return _max_yval; }
+       double get_min_y() const { return _min_yval; }
 
        void truncate_end (double length);
        void truncate_start (double length);
        
-       iterator begin() { return events.begin(); }
-       iterator end() { return events.end(); }
+       iterator begin() { return _events.begin(); }
+       iterator end() { return _events.end(); }
 
-       ControlEvent* back() { return events.back(); }
-       ControlEvent* front() { return events.front(); }
+       ControlEvent* back() { return _events.back(); }
+       ControlEvent* front() { return _events.front(); }
 
-       const_iterator const_begin() const { return events.begin(); }
-       const_iterator const_end() const { return events.end(); }
+       const_iterator const_begin() const { return _events.begin(); }
+       const_iterator const_end() const { return _events.end(); }
 
        std::pair<AutomationList::iterator,AutomationList::iterator> control_points_adjacent (double when);
 
        template<class T> void apply_to_points (T& obj, void (T::*method)(const AutomationList&)) {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
                (obj.*method)(*this);
        }
 
@@ -160,16 +167,16 @@ class AutomationList : public PBD::StatefulDestructible
        XMLNode& serialize_events ();
 
        void set_max_xval (double);
-       double get_max_xval() const { return max_xval; }
+       double get_max_xval() const { return _max_xval; }
 
        double eval (double where) {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
                return unlocked_eval (where);
        }
 
        double rt_safe_eval (double where, bool& ok) {
 
-               Glib::Mutex::Lock lm (lock, Glib::TRY_LOCK);
+               Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK);
 
                if ((ok = lm.locked())) {
                        return unlocked_eval (where);
@@ -183,65 +190,66 @@ class AutomationList : public PBD::StatefulDestructible
                        return a->when < b->when;
                }
        };
-
-        static sigc::signal<void, AutomationList*> AutomationListCreated;
-
-  protected:
-
-       AutomationEventList events;
-       mutable Glib::Mutex lock;
-       int8_t  _frozen;
-       bool    changed_when_thawed;
-       bool   _dirty;
-
+       
        struct LookupCache {
            double left;  /* leftmost x coordinate used when finding "range" */
-           std::pair<AutomationList::iterator,AutomationList::iterator> range;
+           std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
        };
 
-       mutable LookupCache lookup_cache;
-
-       AutoState  _state;
-       AutoStyle  _style;
-       bool  _touching;
-       bool  _new_touch;
-       double max_xval;
-       double min_yval;
-       double max_yval;
-       double default_value;
-       bool   sort_pending;
-
-       iterator rt_insertion_point;
-       double   rt_pos;
-
-       void maybe_signal_changed ();
-       void mark_dirty ();
-       void _x_scale (double factor);
-
-       /* called by type-specific unlocked_eval() to handle
-          common case of 0, 1 or 2 control points.
-       */
+       static sigc::signal<void, AutomationList*> AutomationListCreated;
 
-       double shared_eval (double x);
+       const EventList& events() const { return _events; }
+       double default_value() const { return _default_value; }
 
-       /* called by shared_eval() to handle any case of
-          3 or more control points.
-       */
-
-       virtual double multipoint_eval (double x); 
+       // teeny const violations for Curve
+       mutable sigc::signal<void> Dirty;
+       Glib::Mutex& lock() const { return _lock; }
+       LookupCache& lookup_cache() const { return _lookup_cache; }
+       
+       /** Called by locked entry point and various private
+        * locations where we already hold the lock.
+        * 
+        * FIXME: Should this be private?  Curve needs it..
+        */
+       double unlocked_eval (double x) const;
 
-       /* called by locked entry point and various private
-          locations where we already hold the lock.
-       */
+       Curve&       curve()       { return *_curve; }
+       const Curve& curve() const { return *_curve; }
 
-       virtual double unlocked_eval (double where);
+  protected:
 
-       virtual ControlEvent* point_factory (double,double) const;
-       virtual ControlEvent* point_factory (const ControlEvent&) const;
+       /** Called by unlocked_eval() to handle cases of 3 or more control points.
+        */
+       virtual double multipoint_eval (double x) const; 
 
        AutomationList* cut_copy_clear (double, double, int op);
 
        int deserialize_events (const XMLNode&);
+       
+       void maybe_signal_changed ();
+       void mark_dirty ();
+       void _x_scale (double factor);
+
+       mutable LookupCache _lookup_cache;
+       
+       ParamID             _param_id;
+       EventList           _events;
+       mutable Glib::Mutex _lock;
+       int8_t              _frozen;
+       bool                _changed_when_thawed;
+       AutoState           _state;
+       AutoStyle           _style;
+       bool                _touching;
+       bool                _new_touch;
+       double              _max_xval;
+       double              _min_yval;
+       double              _max_yval;
+       double              _default_value;
+       bool                _sort_pending;
+       iterator            _rt_insertion_point;
+       double              _rt_pos;
+
+       Curve* _curve;
 };
 
 } // namespace
index 61a30f1c0f3c60391e320aeaa819de51a517d099..78a137bde30a2482e0a0f1ca2be74334f45d5e1e 100644 (file)
@@ -124,8 +124,8 @@ class Crossfade : public ARDOUR::AudioRegion
        bool can_follow_overlap() const;
        void set_follow_overlap (bool yn);
 
-       Curve& fade_in() { return _fade_in; } 
-       Curve& fade_out() { return _fade_out; }
+       AutomationList& fade_in() { return _fade_in; } 
+       AutomationList& fade_out() { return _fade_out; }
 
        nframes_t set_length (nframes_t);
 
@@ -157,8 +157,8 @@ class Crossfade : public ARDOUR::AudioRegion
        int32_t               layer_relation;
 
 
-       mutable Curve _fade_in;
-       mutable Curve _fade_out;
+       mutable AutomationList _fade_in;
+       mutable AutomationList _fade_out;
 
        static Sample* crossfade_buffer_out;
        static Sample* crossfade_buffer_in;
index 605eda2e4b7a6be134966fb4dbbcc5680dfc93f0..b96bb5c78e08db8db75619ebd2d30544fca5ebed 100644 (file)
 
 namespace ARDOUR {
 
-struct CurvePoint : public ControlEvent 
-{
-    double coeff[4];
-
-    CurvePoint (double w, double v) 
-           : ControlEvent (w, v) {
-
-           coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
-    }
-
-    ~CurvePoint() {}
-};
-
-class Curve : public AutomationList
+class Curve
 {
   public:
-       Curve (double min_yval, double max_yval, double defaultvalue, bool nostate = false);
+       Curve (const AutomationList& al);
        ~Curve ();
        Curve (const Curve& other);
-       Curve (const Curve& other, double start, double end);
-       Curve (const XMLNode&);
+       //Curve (const Curve& other, double start, double end);
+       /*Curve (const XMLNode&, ParamID id);*/
 
        bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen);
        void get_vector (double x0, double x1, float *arg, int32_t veclen);
 
-       AutomationEventList::iterator closest_control_point_before (double xval);
-       AutomationEventList::iterator closest_control_point_after (double xval);
-
        void solve ();
 
-       static sigc::signal<void, Curve*> CurveCreated;
-               
-  protected:
-       ControlEvent* point_factory (double,double) const;
-       ControlEvent* point_factory (const ControlEvent&) const;
-
   private:
-       AutomationList::iterator last_bound;
-
        double unlocked_eval (double where);
        double multipoint_eval (double x);
 
        void _get_vector (double x0, double x1, float *arg, int32_t veclen);
 
+       const AutomationList& _list;
+
+       void on_list_dirty() { _dirty = true; }
+       bool _dirty;
 };
 
 } // namespace ARDOUR
index 5832f7110104e1595d9eeef4398487f615960574..e57cfdc0d77c49c7ced2a501ee122cda21591677 100644 (file)
@@ -25,7 +25,7 @@
 
 namespace ARDOUR {
 
-struct Gain : public Curve {
+struct Gain : public AutomationList {
 
     Gain();
     Gain (const Gain&);
index 3952d14c0e1bad2575077ff74529c898bfe4edd1..1592ac7cac2ed0ea05c2608b1750b9b7f189cdfc 100644 (file)
@@ -34,7 +34,7 @@
 #include <pbd/controllable.h>
 
 #include <ardour/ardour.h>
-#include <ardour/session_object.h>
+#include <ardour/automatable.h>
 #include <ardour/utils.h>
 #include <ardour/curve.h>
 #include <ardour/types.h>
@@ -64,9 +64,8 @@ class BufferSet;
  * An IO can contain ports of varying types, making routes/inserts/etc with
  * varied combinations of types (eg MIDI and audio) possible.
  */
-class IO : public SessionObject
+class IO : public Automatable
 {
-
   public:
        static const string state_node_name;
 
@@ -227,22 +226,15 @@ class IO : public SessionObject
        }
 
        void clear_automation ();
-
-       virtual void set_gain_automation_state (AutoState);
-       AutoState gain_automation_state() const { return _gain_automation_curve.automation_state(); }
-       //sigc::signal<void> gain_automation_state_changed;
-
-       virtual void set_gain_automation_style (AutoStyle);
-       AutoStyle gain_automation_style () const { return _gain_automation_curve.automation_style(); }
-       //sigc::signal<void> gain_automation_style_changed;
+       
+       void set_parameter_automation_state (ParamID, AutoState);
 
        virtual void transport_stopped (nframes_t now); // interface: matches Insert
        void automation_snapshot (nframes_t now); // interface: matches Automatable
 
-       ARDOUR::Curve& gain_automation_curve () { return _gain_automation_curve; }
-
-       void start_gain_touch ();
-       void end_gain_touch ();
+       // FIXME: these will probably become unsafe in the near future
+       ARDOUR::AutomationList&       gain_automation()       { return *automation_list(GainAutomation); }
+       const ARDOUR::AutomationList& gain_automation() const { return *automation_list(GainAutomation); }
 
        void start_pan_touch (uint32_t which);
        void end_pan_touch (uint32_t which);
@@ -299,16 +291,12 @@ class IO : public SessionObject
        nframes_t last_automation_snapshot;
        static nframes_t _automation_interval;
 
-       AutoState      _gain_automation_state;
-       AutoStyle      _gain_automation_style;
+       /*AutoState      _gain_automation_state;
+       AutoStyle      _gain_automation_style;*/
 
-       bool     apply_gain_automation;
-       Curve    _gain_automation_curve;
+       bool apply_gain_automation;
+       //Curve     _gain_automation_curve;
        
-       Glib::Mutex automation_lock;
-
-       virtual int set_automation_state (const XMLNode&);
-       virtual XMLNode& get_automation_state ();
        virtual int load_automation (std::string path);
 
        /* AudioTrack::deprecated_use_diskstream_connections() needs these */
index f1f1bb8811fe2df08daaaf5130774fd705185d37..5c156323915109193670ef581879d7ffe58829cb 100644 (file)
@@ -63,7 +63,7 @@ class LadspaPlugin : public ARDOUR::Plugin
        void set_parameter (uint32_t port, float val);
        float get_parameter (uint32_t port) const;
        int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const;
-       std::set<uint32_t> automatable() const;
+       std::set<ParamID> automatable() const;
        uint32_t nth_parameter (uint32_t port, bool& ok) const;
        void activate () { 
                if (descriptor->activate) {
@@ -85,7 +85,7 @@ class LadspaPlugin : public ARDOUR::Plugin
        int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset);
        void store_state (ARDOUR::PluginState&);
        void restore_state (ARDOUR::PluginState&);
-       string describe_parameter (uint32_t);
+       string describe_parameter (ParamID);
        string state_node_name() const { return "ladspa"; }
        void print_parameter (uint32_t, char*, uint32_t len) const;
 
index 0e5ab68525581e2e1ed5580eafd0c76c937a3756..af3cda94e2647b2b582849bd04ec2ee80d4f3a71 100644 (file)
@@ -81,8 +81,9 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful
        /* XXX this is wrong. for multi-dimensional panners, there
           must surely be more than 1 automation curve.
        */
+       /* TODO: Panner is-a Automation solves this */
 
-       virtual Curve& automation() = 0;
+       virtual AutomationList& automation() = 0;
 
        sigc::signal<void> Changed;      /* for position */
        sigc::signal<void> StateChanged; /* for mute */
@@ -149,7 +150,8 @@ class BaseStereoPanner : public StreamPanner
        void set_automation_state (AutoState);
        void set_automation_style (AutoStyle);
 
-       Curve& automation() { return _automation; }
+       /* TODO: StreamPanner is-a Automatable? */
+       AutomationList& automation() { return _automation; }
 
        /* old school automation loading */
 
@@ -163,7 +165,7 @@ class BaseStereoPanner : public StreamPanner
        float left_interp;
        float right_interp;
 
-       Curve  _automation;
+       AutomationList _automation;
 };
 
 class EqualPowerStereoPanner : public BaseStereoPanner
@@ -203,8 +205,10 @@ class Multi2dPanner : public StreamPanner
        /* XXX this is wrong. for multi-dimensional panners, there
           must surely be more than 1 automation curve.
        */
+       
+       /* TODO: StreamPanner is-a Automatable? */
 
-       Curve& automation() { return _automation; }
+       AutomationList& automation() { return _automation; }
 
        void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
        void distribute_automated (AudioBuffer& src, BufferSet& obufs,
@@ -222,7 +226,7 @@ class Multi2dPanner : public StreamPanner
        int load (istream&, string path, uint32_t&);
 
   private:
-       Curve _automation;
+       AutomationList _automation;
        void update ();
 };
 
diff --git a/libs/ardour/ardour/param_id.h b/libs/ardour/ardour/param_id.h
new file mode 100644 (file)
index 0000000..eb5563b
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+    Copyright (C) 2007 Paul Davis 
+    
+    This program is free software; you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by the Free
+    Software Foundation; either version 2 of the License, or (at your option)
+    any later version.
+    
+    This program is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+    
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __ardour_param_id_h__
+#define __ardour_param_id_h__
+
+#include <string>
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+
+/** ID of an automatable parameter.
+ *
+ * A given automatable object has a number of automatable parameters.  This is
+ * the unique ID for those parameters.  Anything automatable (AutomationList,
+ * Curve) must have an ID unique with respect to it's Automatable parent.
+ *
+ * A parameter ID has two parts, a type and an int (only used by some types).
+ *
+ * This is a bit more ugly than it could be, due to using the existing/legacy
+ * ARDOUR::AutomationType:  GainAutomation, PanAutomation, SoloAutomation,
+ * and MuteAutomation use only the type(), but PluginAutomation and
+ * MidiCCAutomation use the id() as port number and CC number, respectively.
+ *
+ * Future types may use a string or URI or whatever, as long as these are
+ * comparable anything may be added.  ints are best as these should be fast to
+ * copy and compare with one another.
+ */
+class ParamID
+{
+public:
+       inline ParamID(AutomationType type = NullAutomation, uint32_t id=0) : _type(type), _id(id) {}
+       
+       /** Construct an ParamID from a string returned from ParamID::to_string
+        * (AutomationList automation-id property)
+        */
+       ParamID(const std::string& str) : _type(NullAutomation), _id(0) {
+               if (str == "gain") {
+                       _type = GainAutomation;
+               } else if (str == "pan") {
+                       _type = PanAutomation;
+               } else if (str == "solo") {
+                       _type = SoloAutomation;
+               } else if (str == "mute") {
+                       _type = MuteAutomation;
+               } else if (str == "fadein") {
+                       _type = FadeInAutomation;
+               } else if (str == "fadeout") {
+                       _type = FadeOutAutomation;
+               } else if (str == "envelope") {
+                       _type = EnvelopeAutomation;
+               } else if (str.length() > 10 && str.substr(0, 10) == "parameter-") {
+                       _type = PluginAutomation;
+                       _id = atoi(str.c_str()+10);
+                       PBD::info << "Parameter: " << str << " -> " << _id << endl;
+               } else if (str.length() > 7 && str.substr(0, 7) == "midicc-") {
+                       _type = MidiCCAutomation;
+                       _id = atoi(str.c_str()+7);
+                       PBD::info << "MIDI CC: " << str << " -> " << _id << endl;
+               } else {
+                       PBD::warning << "Unknown ParamID '" << str << "'" << endmsg;
+               }
+       }
+
+       inline AutomationType type() const { return _type; }
+       inline uint32_t       id()   const { return _id; }
+
+       inline bool operator==(const ParamID& id) const
+               { return (_type == id._type && _id == id._id); }
+       
+       /** Arbitrary but fixed ordering, so we're comparable (usable in std::map) */
+       inline bool operator<(const ParamID& id) const {
+               // FIXME: branch a performance problem?  #ifdef DEBUG?
+               if (_type == NullAutomation)
+                       PBD::warning << "Uninitialized ParamID compared." << endmsg;
+               return (_type < id._type || _id < id._id);
+       }
+       
+       inline operator bool() const { return (_type != 0); }
+
+       /** Unique string representation, suitable as an XML property value.
+        * e.g. <AutomationList automation-id="whatthisreturns">
+        */
+       inline std::string to_string() const {
+               if (_type == GainAutomation) {
+                       return "gain";
+               } else if (_type == PanAutomation) {
+                       return "pan";
+               } else if (_type == SoloAutomation) {
+                       return "solo";
+               } else if (_type == MuteAutomation) {
+                       return "mute";
+               } else if (_type == FadeInAutomation) {
+                       return "fadein";
+               } else if (_type == FadeOutAutomation) {
+                       return "fadeout";
+               } else if (_type == EnvelopeAutomation) {
+                       return "envelope";
+               } else if (_type == PluginAutomation) {
+                       return string_compose("parameter-%1", _id);
+               } else if (_type == MidiCCAutomation) {
+                       return string_compose("midicc-%1", _id);
+               } else {
+                       PBD::warning << "Uninitialized ParamID to_string() called." << endmsg;
+                       return "";
+               }
+       }
+
+private:
+       // default copy constructor is ok
+       AutomationType _type;
+       uint32_t       _id;
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_param_id_h__
+
index b1a2823b3363bdb60695c606e17a1c920f7078ac..22c086220247f7612fdaed58b3562aeb5256c9a5 100644 (file)
@@ -31,6 +31,7 @@
 #include <ardour/chan_count.h>
 #include <ardour/plugin_state.h>
 #include <ardour/cycles.h>
+#include <ardour/param_id.h>
 
 #include <vector>
 #include <set>
@@ -121,10 +122,10 @@ class Plugin : public PBD::StatefulDestructible
 
        virtual int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset) = 0;
        
-       virtual std::set<uint32_t> automatable() const = 0;
+       virtual std::set<ParamID> automatable() const = 0;
        virtual void store_state (ARDOUR::PluginState&) = 0;
        virtual void restore_state (ARDOUR::PluginState&) = 0;
-       virtual string describe_parameter (uint32_t) = 0;
+       virtual string describe_parameter (ParamID) = 0;
        virtual string state_node_name() const = 0;
        virtual void print_parameter (uint32_t, char*, uint32_t len) const = 0;
 
@@ -139,7 +140,7 @@ class Plugin : public PBD::StatefulDestructible
 
        virtual bool has_editor() const = 0;
 
-       sigc::signal<void,uint32_t,float> ParameterChanged;
+       sigc::signal<void,ParamID,float> ParameterChanged;
        
        PBD::Controllable *get_nth_control (uint32_t);
 
index 4acacae7f54f3a1afacaa47b4d1ec2734fc7b559..df0460af8415e300abf551e9956b7e20136c7cbb 100644 (file)
@@ -76,13 +76,9 @@ class PluginInsert : public Insert
 
        bool is_generator() const;
 
-       void set_parameter (uint32_t port, float val);
+       void set_parameter (ParamID param, float val);
 
-       AutoState get_port_automation_state (uint32_t port);
-       void set_port_automation_state (uint32_t port, AutoState);
-       void protect_automation ();
-
-       float default_parameter_value (uint32_t which);
+       float default_parameter_value (ParamID param);
 
        boost::shared_ptr<Plugin> plugin(uint32_t num=0) const {
                if (num < _plugins.size()) { 
@@ -94,7 +90,7 @@ class PluginInsert : public Insert
 
        PluginType type ();
 
-       string describe_parameter (uint32_t);
+       string describe_parameter (ParamID param);
 
        nframes_t latency();
 
@@ -103,7 +99,7 @@ class PluginInsert : public Insert
 
   private:
 
-       void parameter_changed (uint32_t, float);
+       void parameter_changed (ParamID, float);
        
        std::vector<boost::shared_ptr<Plugin> > _plugins;
        
@@ -112,8 +108,8 @@ class PluginInsert : public Insert
 
        void init ();
        void set_automatable ();
-       void auto_state_changed (uint32_t which);
-       void automation_list_creation_callback (uint32_t, AutomationList&);
+       void auto_state_changed (ParamID which);
+       void automation_list_creation_callback (ParamID, AutomationList&);
 
        int32_t count_for_configuration (ChanCount in, ChanCount out) const;
 
index d5f10410db8ff2e933e84c7694ac44b8a7ee7bdd..b2f13def812f8d93232c5a441fcaa652a213ab92 100644 (file)
@@ -93,12 +93,20 @@ namespace ARDOUR {
        OverlapType coverage (nframes_t start_a, nframes_t end_a,
                              nframes_t start_b, nframes_t end_b);
 
+       /** See param_id.h
+        * XXX: I don't think/hope these hex values matter anymore.
+        */
        enum AutomationType {
+               NullAutomation = 0x0,
                GainAutomation = 0x1,
                PanAutomation = 0x2,
                PluginAutomation = 0x4,
                SoloAutomation = 0x8,
-               MuteAutomation = 0x10
+               MuteAutomation = 0x10,
+               MidiCCAutomation = 0x20,
+               FadeInAutomation = 0x40,
+               FadeOutAutomation = 0x80,
+               EnvelopeAutomation = 0x100
        };
 
        enum AutoState {
index b08990c2abefd2363a5820e380f710c28338acd6..151b434c6ba62c2c1d2e83d512bf13325777505c 100644 (file)
@@ -599,10 +599,10 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
                /* don't waste time with automation if we're recording or we've just stopped (yes it can happen) */
 
                if (!diskstream->record_enabled() && _session.transport_rolling()) {
-                       Glib::Mutex::Lock am (automation_lock, Glib::TRY_LOCK);
+                       Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK);
                        
-                       if (am.locked() && _gain_automation_curve.automation_playback()) {
-                               apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes);
+                       if (am.locked() && gain_automation().automation_playback()) {
+                               apply_gain_automation = gain_automation().curve().rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes);
                        }
                }
 
@@ -696,9 +696,9 @@ AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes
                }
        }
        
-       if (_gain_automation_curve.automation_state() == Play) {
+       if (IO::gain_automation().automation_state() == Play) {
                
-               _gain_automation_curve.get_vector (start, start + nframes, gain_automation, nframes);
+               IO::gain_automation().curve().get_vector (start, start + nframes, gain_automation, nframes);
 
                for (BufferSet::audio_iterator bi = buffers.audio_begin(); bi != buffers.audio_end(); ++bi) {
                        Sample *b = bi->data();
index 55a187972695758d9a550f37493577749c4c465e..a7a5fca9127c8e4e7b77646d08979d787f45daa6 100644 (file)
@@ -73,9 +73,9 @@ AudioRegion::init ()
 /* constructor for use by derived types only */
 AudioRegion::AudioRegion (nframes_t start, nframes_t length, string name)
        : Region (start, length, name, DataType::AUDIO),
-         _fade_in (0.0, 2.0, 1.0, false),
-         _fade_out (0.0, 2.0, 1.0, false),
-         _envelope (0.0, 2.0, 1.0, false)
+         _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0),
+         _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0),
+         _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0)
 {
        init ();
 }
@@ -83,9 +83,9 @@ AudioRegion::AudioRegion (nframes_t start, nframes_t length, string name)
 /** Basic AudioRegion constructor (one channel) */
 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length)
        : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0,  Region::Flag(Region::DefaultFlags|Region::External)),
-         _fade_in (0.0, 2.0, 1.0, false),
-         _fade_out (0.0, 2.0, 1.0, false),
-         _envelope (0.0, 2.0, 1.0, false)
+         _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0),
+         _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0),
+         _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0)
 {
        boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
        if (afs) {
@@ -98,9 +98,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
 /* Basic AudioRegion constructor (one channel) */
 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
        : Region (src, start, length, name, DataType::AUDIO, layer, flags)
-       , _fade_in (0.0, 2.0, 1.0, false)
-       , _fade_out (0.0, 2.0, 1.0, false)
-       , _envelope (0.0, 2.0, 1.0, false)
+       , _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0)
+       , _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0)
+       , _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0)
 {
        boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
        if (afs) {
@@ -113,9 +113,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
 /* Basic AudioRegion constructor (many channels) */
 AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
        : Region (srcs, start, length, name, DataType::AUDIO, layer, flags)
-       , _fade_in (0.0, 2.0, 1.0, false)
-       , _fade_out (0.0, 2.0, 1.0, false)
-       , _envelope (0.0, 2.0, 1.0, false)
+       , _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0)
+       , _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0)
+       , _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0)
 {
        init ();
 }
@@ -179,9 +179,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
 
 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
        : Region (src, node)
-       , _fade_in (0.0, 2.0, 1.0, false)
-       , _fade_out (0.0, 2.0, 1.0, false)
-       , _envelope (0.0, 2.0, 1.0, false)
+       , _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0)
+       , _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0)
+       , _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0)
 {
        boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
        if (afs) {
@@ -200,10 +200,10 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod
 }
 
 AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
-       : Region (srcs, node),
-         _fade_in (0.0, 2.0, 1.0, false),
-         _fade_out (0.0, 2.0, 1.0, false),
-         _envelope (0.0, 2.0, 1.0, false)
+       : Region (srcs, node)
+       , _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0)
+       , _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0)
+       , _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0)
 {
        set_default_fades ();
        _scale_amplitude = 1.0;
@@ -405,7 +405,7 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
 
                        limit = min (to_read, fade_in_length - internal_offset);
 
-                       _fade_in.get_vector (internal_offset, internal_offset+limit, gain_buffer, limit);
+                       _fade_in.curve().get_vector (internal_offset, internal_offset+limit, gain_buffer, limit);
 
                        for (nframes_t n = 0; n < limit; ++n) {
                                mixdown_buffer[n] *= gain_buffer[n];
@@ -447,7 +447,7 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
                        nframes_t curve_offset = fade_interval_start - (_length-fade_out_length);
                        nframes_t fade_offset = fade_interval_start - internal_offset;
                                                                       
-                       _fade_out.get_vector (curve_offset,curve_offset+limit, gain_buffer, limit);
+                       _fade_out.curve().get_vector (curve_offset,curve_offset+limit, gain_buffer, limit);
 
                        for (nframes_t n = 0, m = fade_offset; n < limit; ++n, ++m) {
                                mixdown_buffer[m] *= gain_buffer[n];
@@ -459,7 +459,7 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
        /* Regular gain curves */
 
        if (envelope_active())  {
-               _envelope.get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
+               _envelope.curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
                
                if (_scale_amplitude != 1.0f) {
                        for (nframes_t n = 0; n < to_read; ++n) {
index 2ce553a188668ff194ac7b33cb3df4c8a235545a..40ab7af769f2d10df2f5d4f9658498b9c6a0fa50 100644 (file)
@@ -62,7 +62,7 @@ Automatable::old_set_automation_state (const XMLNode& node)
                        if (sstr.fail()) {
                                break;
                        }
-                       mark_automation_visible (what, true);
+                       mark_automation_visible (ParamID(PluginAutomation, what), true);
                }
        }
 
@@ -88,7 +88,7 @@ Automatable::load_automation (const string& path)
        }
 
        Glib::Mutex::Lock lm (_automation_lock);
-       set<uint32_t> tosave;
+       set<ParamID> tosave;
        _parameter_automation.clear ();
 
        while (in) {
@@ -100,9 +100,10 @@ Automatable::load_automation (const string& path)
                in >> when;  if (!in) goto bad;
                in >> value; if (!in) goto bad;
                
-               AutomationList& al = automation_list (port);
-               al.add (when, value);
-               tosave.insert (port);
+               /* FIXME: this is legacy and only used for plugin inserts?  I think? */
+               AutomationList* al = automation_list (ParamID(PluginAutomation, port), true);
+               al->add (when, value);
+               tosave.insert (ParamID(PluginAutomation, port));
        }
        
        return 0;
@@ -113,12 +114,27 @@ Automatable::load_automation (const string& path)
        return -1;
 }
 
+void
+Automatable::add_automation_parameter(AutomationList* al)
+{
+       _parameter_automation[al->param_id()] = al;
+       
+       /* let derived classes do whatever they need with this */
+       automation_list_creation_callback (al->param_id(), *al);
+
+       cerr << _name << ": added (visible, can_automate) parameter " << al->param_id().to_string() << ", # params = "
+               << _parameter_automation.size() << endl;
+
+       // FIXME: sane default behaviour?
+       _visible_parameter_automation.insert(al->param_id());
+       _can_automate_list.insert(al->param_id());
+}
 
 void
-Automatable::what_has_automation (set<uint32_t>& s) const
+Automatable::what_has_automation (set<ParamID>& s) const
 {
        Glib::Mutex::Lock lm (_automation_lock);
-       map<uint32_t,AutomationList*>::const_iterator li;
+       map<ParamID,AutomationList*>::const_iterator li;
        
        for (li = _parameter_automation.begin(); li != _parameter_automation.end(); ++li) {
                s.insert  ((*li).first);
@@ -126,49 +142,79 @@ Automatable::what_has_automation (set<uint32_t>& s) const
 }
 
 void
-Automatable::what_has_visible_automation (set<uint32_t>& s) const
+Automatable::what_has_visible_automation (set<ParamID>& s) const
 {
        Glib::Mutex::Lock lm (_automation_lock);
-       set<uint32_t>::const_iterator li;
+       set<ParamID>::const_iterator li;
        
        for (li = _visible_parameter_automation.begin(); li != _visible_parameter_automation.end(); ++li) {
                s.insert  (*li);
        }
 }
-AutomationList&
-Automatable::automation_list (uint32_t parameter)
+
+/** Returns NULL if we don't have an AutomationList for \a parameter.
+ */
+AutomationList*
+Automatable::automation_list (ParamID parameter, bool create_if_missing)
 {
-       AutomationList* al = _parameter_automation[parameter];
+       std::map<ParamID,AutomationList*>::iterator i = _parameter_automation.find(parameter);
+
+       if (i != _parameter_automation.end()) {
+               return i->second;
+
+       } else if (create_if_missing) {
+               AutomationList* al = new AutomationList (parameter, FLT_MIN, FLT_MAX, default_parameter_value (parameter));
+               add_automation_parameter(al);
+               return al;
 
-       if (al == 0) {
-               al = _parameter_automation[parameter] = new AutomationList (default_parameter_value (parameter));
-               /* let derived classes do whatever they need with this */
-               automation_list_creation_callback (parameter, *al);
+       } else {
+               warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg;
+               return NULL;
        }
+}
 
-       return *al;
+const AutomationList*
+Automatable::automation_list (ParamID parameter) const
+{
+       std::map<ParamID,AutomationList*>::const_iterator i = _parameter_automation.find(parameter);
+
+       if (i != _parameter_automation.end()) {
+               return i->second;
+       } else {
+               warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg;
+               return NULL;
+       }
 }
 
+
 string
-Automatable::describe_parameter (uint32_t which)
+Automatable::describe_parameter (ParamID param)
 {
-       /* derived classes will override this */
-       return "";
+       /* derived classes like PluginInsert should override this */
+
+       if (param == ParamID(GainAutomation))
+               return _("Fader");
+       else if (param == ParamID(PanAutomation))
+               return _("Pan");
+       else if (param.type() == MidiCCAutomation)
+               return string_compose("MIDI CC %1", param.id());
+       else
+               return param.to_string();
 }
 
 void
-Automatable::can_automate (uint32_t what)
+Automatable::can_automate (ParamID what)
 {
        _can_automate_list.insert (what);
 }
 
 void
-Automatable::mark_automation_visible (uint32_t what, bool yn)
+Automatable::mark_automation_visible (ParamID what, bool yn)
 {
        if (yn) {
                _visible_parameter_automation.insert (what);
        } else {
-               set<uint32_t>::iterator i;
+               set<ParamID>::iterator i;
 
                if ((i = _visible_parameter_automation.find (what)) != _visible_parameter_automation.end()) {
                        _visible_parameter_automation.erase (i);
@@ -179,7 +225,7 @@ Automatable::mark_automation_visible (uint32_t what, bool yn)
 bool
 Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const
 {
-       map<uint32_t,AutomationList*>::const_iterator li;       
+       map<ParamID,AutomationList*>::const_iterator li;        
        AutomationList::TimeComparator cmp;
 
        next_event.when = max_frames;
@@ -207,36 +253,50 @@ Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_e
        return next_event.when != max_frames;
 }
 
+/** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
+ * had a single automation parameter, with it's type implicit.  Derived objects should
+ * pass that type and it will be used for the untyped AutomationList found.
+ */
 int
-Automatable::set_automation_state (const XMLNode& node)
+Automatable::set_automation_state (const XMLNode& node, ParamID legacy_param)
 {      
        Glib::Mutex::Lock lm (_automation_lock);
 
        _parameter_automation.clear ();
+       _visible_parameter_automation.clear ();
 
        XMLNodeList nlist = node.children();
        XMLNodeIterator niter;
-
+       
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-               uint32_t param;
 
-               if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, &param) != 1) {
-                       error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
-                       continue;
-               }
+               /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, &param) != 1) {
+                 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
+                 continue;
+                 }*/
+
+               if ((*niter)->name() == "AutomationList") {
+
+                       const XMLProperty* id_prop = (*niter)->property("automation-id");
 
-               AutomationList& al = automation_list (param);
-               if (al.set_state (*(*niter)->children().front())) {
-                       goto bad;
+                       ParamID param = (id_prop ? ParamID(id_prop->value()) : legacy_param);
+                       
+                       AutomationList* al = new AutomationList(**niter, param);
+                       
+                       if (!id_prop) {
+                               warning << "AutomationList node without automation-id property, "
+                                       << "using default: " << legacy_param.to_string() << endmsg;
+                               al->set_param_id(legacy_param);
+                       }
+
+                       add_automation_parameter(al);
+
+               } else {
+                       error << "Expected AutomationList node, got '" << (*niter)->name() << endmsg;
                }
        }
 
        return 0;
-
-  bad:
-       error << string_compose(_("%1: cannot load automation data from XML"), _name) << endmsg;
-       _parameter_automation.clear ();
-       return -1;
 }
 
 XMLNode&
@@ -244,24 +304,108 @@ Automatable::get_automation_state ()
 {
        Glib::Mutex::Lock lm (_automation_lock);
        XMLNode* node = new XMLNode (X_("Automation"));
-       string fullpath;
+       
+       cerr << "'" << _name << "'->get_automation_state, # params = " << _parameter_automation.size() << endl;
 
        if (_parameter_automation.empty()) {
                return *node;
        }
 
-       map<uint32_t,AutomationList*>::iterator li;
+       map<ParamID,AutomationList*>::iterator li;
        
        for (li = _parameter_automation.begin(); li != _parameter_automation.end(); ++li) {
-       
-               XMLNode* child;
-               
-               char buf[64];
-               stringstream str;
-               snprintf (buf, sizeof (buf), "parameter-%" PRIu32, li->first);
-               child = new XMLNode (buf);
-               child->add_child_nocopy (li->second->get_state ());
+               node->add_child_nocopy (li->second->get_state ());
        }
 
        return *node;
 }
+
+void
+Automatable::clear_automation ()
+{
+       Glib::Mutex::Lock lm (_automation_lock);
+
+       map<ParamID,AutomationList*>::iterator li;
+
+       for (li = _parameter_automation.begin(); li != _parameter_automation.end(); ++li)
+               li->second->clear();
+}
+       
+void
+Automatable::set_parameter_automation_state (ParamID param, AutoState s)
+{
+       Glib::Mutex::Lock lm (_automation_lock);
+       
+       AutomationList* al = automation_list (param, true);
+
+       if (s != al->automation_state()) {
+               al->set_automation_state (s);
+               _session.set_dirty ();
+       }
+}
+
+AutoState
+Automatable::get_parameter_automation_state (ParamID param)
+{
+       Glib::Mutex::Lock lm (_automation_lock);
+
+       AutomationList* al = automation_list(param);
+
+       if (al) {
+               return al->automation_state();
+       } else {
+               return Off;
+       }
+}
+
+void
+Automatable::set_parameter_automation_style (ParamID param, AutoStyle s)
+{
+       Glib::Mutex::Lock lm (_automation_lock);
+       
+       AutomationList* al = automation_list (param, true);
+
+       if (s != al->automation_style()) {
+               al->set_automation_style (s);
+               _session.set_dirty ();
+       }
+}
+
+AutoStyle
+Automatable::get_parameter_automation_style (ParamID param)
+{
+       Glib::Mutex::Lock lm (_automation_lock);
+
+       AutomationList* al = automation_list(param);
+
+       if (al) {
+               return al->automation_style();
+       } else {
+               return Absolute; // whatever
+       }
+}
+
+void
+Automatable::protect_automation ()
+{
+       set<ParamID> automated_params;
+
+       what_has_automation (automated_params);
+
+       for (set<ParamID>::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
+
+               AutomationList* al = automation_list (*i);
+
+               switch (al->automation_state()) {
+               case Write:
+                       al->set_automation_state (Off);
+                       break;
+               case Touch:
+                       al->set_automation_state (Play);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
index 50688dda2cb11e2f025e18527433b8bea231ce86..82d3e457cbf999c73fddbf5bde6d22e0778062ac 100644 (file)
@@ -24,7 +24,9 @@
 #include <sstream>
 #include <algorithm>
 #include <sigc++/bind.h>
+#include <ardour/param_id.h>
 #include <ardour/automation_event.h>
+#include <ardour/curve.h>
 #include <pbd/stacktrace.h>
 
 #include "i18n.h"
@@ -52,70 +54,72 @@ static void dumpit (const AutomationList& al, string prefix = "")
 }
 #endif
 
-AutomationList::AutomationList (double defval)
+AutomationList::AutomationList (ParamID id, double min_val, double max_val, double default_val)
+       : _param_id(id)
+       , _curve(new Curve(*this))
 {
+       _param_id = id;
        _frozen = 0;
-       changed_when_thawed = false;
+       _changed_when_thawed = false;
        _state = Off;
        _style = Absolute;
+       _min_yval = min_val;
+       _max_yval = max_val;
        _touching = false;
-       min_yval = FLT_MIN;
-       max_yval = FLT_MAX;
-       max_xval = 0; // means "no limit" 
-       default_value = defval;
-       _dirty = false;
-       rt_insertion_point = events.end();
-       lookup_cache.left = -1;
-       lookup_cache.range.first = events.end();
-       sort_pending = false;
-
-        AutomationListCreated(this);
+       _max_xval = 0; // means "no limit" 
+       _default_value = default_val;
+       _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)
+       : _param_id(other._param_id)
+       , _curve(new Curve(*this))
 {
        _frozen = 0;
-       changed_when_thawed = false;
+       _changed_when_thawed = false;
        _style = other._style;
-       min_yval = other.min_yval;
-       max_yval = other.max_yval;
-       max_xval = other.max_xval;
-       default_value = other.default_value;
+       _min_yval = other._min_yval;
+       _max_yval = other._max_yval;
+       _max_xval = other._max_xval;
+       _default_value = other._default_value;
        _state = other._state;
        _touching = other._touching;
-       _dirty = false;
-       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
-                  its virtual and we're in a constructor.
-               */
-               events.push_back (other.point_factory (**i));
+       _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) {
+               _events.push_back (new ControlEvent (**i));
        }
 
        mark_dirty ();
-        AutomationListCreated(this);
+       AutomationListCreated(this);
 }
 
 AutomationList::AutomationList (const AutomationList& other, double start, double end)
+       : _param_id(other._param_id)
+       , _curve(new Curve(*this))
 {
        _frozen = 0;
-       changed_when_thawed = false;
+       _changed_when_thawed = false;
        _style = other._style;
-       min_yval = other.min_yval;
-       max_yval = other.max_yval;
-       max_xval = other.max_xval;
-       default_value = other.default_value;
+       _min_yval = other._min_yval;
+       _max_yval = other._max_yval;
+       _max_xval = other._max_xval;
+       _default_value = other._default_value;
        _state = other._state;
        _touching = other._touching;
-       _dirty = false;
-       rt_insertion_point = events.end();
-       lookup_cache.left = -1;
-       lookup_cache.range.first = events.end();
-       sort_pending = false;
+       _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 */
 
@@ -123,7 +127,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
 
        if (!section->empty()) {
                for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
-                       events.push_back (other.point_factory ((*i)->when, (*i)->value));
+                       _events.push_back (new ControlEvent ((*i)->when, (*i)->value));
                }
        }
 
@@ -134,32 +138,38 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
        AutomationListCreated(this);
 }
 
-AutomationList::AutomationList (const XMLNode& node)
+/** \a id is used for legacy sessions where the type is not present
+ * in or below the <AutomationList> node.  It is used if \a id is non-null.
+ */
+AutomationList::AutomationList (const XMLNode& node, ParamID id)
+       : _curve(new Curve(*this))
 {
        _frozen = 0;
-       changed_when_thawed = false;
+       _changed_when_thawed = false;
        _touching = false;
-       min_yval = FLT_MIN;
-       max_yval = FLT_MAX;
-       max_xval = 0; // means "no limit" 
-       _dirty = false;
+       _min_yval = FLT_MIN;
+       _max_yval = FLT_MAX;
+       _max_xval = 0; // means "no limit" 
        _state = Off;
        _style = Absolute;
-       rt_insertion_point = events.end();
-       lookup_cache.left = -1;
-       lookup_cache.range.first = events.end();
-       sort_pending = false;
+       _rt_insertion_point = _events.end();
+       _lookup_cache.left = -1;
+       _lookup_cache.range.first = _events.end();
+       _sort_pending = false;
        
        set_state (node);
 
-        AutomationListCreated(this);
+       if (id)
+               _param_id = id;
+
+       AutomationListCreated(this);
 }
 
 AutomationList::~AutomationList()
 {
        GoingAway ();
        
-       for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
+       for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
                delete (*x);
        }
 }
@@ -167,7 +177,7 @@ AutomationList::~AutomationList()
 bool
 AutomationList::operator== (const AutomationList& other)
 {
-       return events == other.events;
+       return _events == other._events;
 }
 
 AutomationList&
@@ -175,16 +185,16 @@ AutomationList::operator= (const AutomationList& other)
 {
        if (this != &other) {
                
-               events.clear ();
+               _events.clear ();
                
-               for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
-                       events.push_back (point_factory (**i));
+               for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) {
+                       _events.push_back (new ControlEvent (**i));
                }
                
-               min_yval = other.min_yval;
-               max_yval = other.max_yval;
-               max_xval = other.max_xval;
-               default_value = other.default_value;
+               _min_yval = other._min_yval;
+               _max_yval = other._max_yval;
+               _max_xval = other._max_xval;
+               _default_value = other._default_value;
                
                mark_dirty ();
                maybe_signal_changed ();
@@ -199,7 +209,7 @@ AutomationList::maybe_signal_changed ()
        mark_dirty ();
 
        if (_frozen) {
-               changed_when_thawed = true;
+               _changed_when_thawed = true;
        } else {
                StateChanged ();
        }
@@ -241,8 +251,8 @@ void
 AutomationList::clear ()
 {
        {
-               Glib::Mutex::Lock lm (lock);
-               events.clear ();
+               Glib::Mutex::Lock lm (_lock);
+               _events.clear ();
                mark_dirty ();
        }
 
@@ -252,25 +262,25 @@ AutomationList::clear ()
 void
 AutomationList::x_scale (double factor)
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Mutex::Lock lm (_lock);
        _x_scale (factor);
 }
 
 bool
 AutomationList::extend_to (double when)
 {
-       Glib::Mutex::Lock lm (lock);
-       if (events.empty() || events.back()->when == when) {
+       Glib::Mutex::Lock lm (_lock);
+       if (_events.empty() || _events.back()->when == when) {
                return false;
        }
-       double factor = when / events.back()->when;
+       double factor = when / _events.back()->when;
        _x_scale (factor);
        return true;
 }
 
 void AutomationList::_x_scale (double factor)
 {
-       for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
+       for (AutomationList::iterator i = _events.begin(); i != _events.end(); ++i) {
                (*i)->when = floor ((*i)->when * factor);
        }
 
@@ -280,11 +290,9 @@ void AutomationList::_x_scale (double factor)
 void
 AutomationList::reposition_for_rt_add (double when)
 {
-       rt_insertion_point = events.end();
+       _rt_insertion_point = _events.end();
 }
 
-#define last_rt_insertion_point rt_insertion_point
-
 void
 AutomationList::rt_add (double when, double value)
 {
@@ -297,26 +305,26 @@ AutomationList::rt_add (double when, double value)
        // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
 
                iterator where;
                TimeComparator cmp;
                ControlEvent cp (when, 0.0);
                bool done = false;
 
-               if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
+               if ((_rt_insertion_point != _events.end()) && ((*_rt_insertion_point)->when < when) ) {
 
                        /* we have a previous insertion point, so we should delete
                           everything between it and the position where we are going
                           to insert this point.
                        */
 
-                       iterator after = last_rt_insertion_point;
+                       iterator after = _rt_insertion_point;
 
-                       if (++after != events.end()) {
+                       if (++after != _events.end()) {
                                iterator far = after;
 
-                               while (far != events.end()) {
+                               while (far != _events.end()) {
                                        if ((*far)->when > when) {
                                                break;
                                        }
@@ -325,14 +333,14 @@ AutomationList::rt_add (double when, double value)
 
                                 if(_new_touch) {
                                         where = far;
-                                        last_rt_insertion_point = where;
+                                        _rt_insertion_point = where;
                                                                                              
                                         if((*where)->when == when) {
                                                 (*where)->value = value;
                                                 done = true;
                                         }
                                 } else {
-                                        where = events.erase (after, far);
+                                        where = _events.erase (after, far);
                                 }
 
                        } else {
@@ -341,20 +349,20 @@ AutomationList::rt_add (double when, double value)
 
                        }
                        
-                       iterator previous = last_rt_insertion_point;
-                        --previous;
+                       iterator previous = _rt_insertion_point;
+                       --previous;
                        
-                       if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
-                               (*last_rt_insertion_point)->when = when;
+                       if (_rt_insertion_point != _events.begin() && (*_rt_insertion_point)->value == value && (*previous)->value == value) {
+                               (*_rt_insertion_point)->when = when;
                                done = true;
                                
                        }
                        
                } else {
 
-                       where = lower_bound (events.begin(), events.end(), &cp, cmp);
+                       where = lower_bound (_events.begin(), _events.end(), &cp, cmp);
 
-                       if (where != events.end()) {
+                       if (where != _events.end()) {
                                if ((*where)->when == when) {
                                        (*where)->value = value;
                                        done = true;
@@ -363,7 +371,7 @@ AutomationList::rt_add (double when, double value)
                }
                
                if (!done) {
-                       last_rt_insertion_point = events.insert (where, point_factory (when, value));
+                       _rt_insertion_point = _events.insert (where, new ControlEvent (when, value));
                }
                
                _new_touch = false;
@@ -377,24 +385,22 @@ void
 AutomationList::fast_simple_add (double when, double value)
 {
        /* to be used only for loading pre-sorted data from saved state */
-       events.insert (events.end(), point_factory (when, value));
+       _events.insert (_events.end(), new ControlEvent (when, value));
 }
 
-#undef last_rt_insertion_point
-
 void
 AutomationList::add (double when, double value)
 {
        /* this is for graphical editing */
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
                TimeComparator cmp;
                ControlEvent cp (when, 0.0f);
                bool insert = true;
                iterator insertion_point;
 
-               for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
+               for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, cmp); insertion_point != _events.end(); ++insertion_point) {
 
                        /* only one point allowed per time point */
 
@@ -411,7 +417,7 @@ AutomationList::add (double when, double value)
 
                if (insert) {
                        
-                       events.insert (insertion_point, point_factory (when, value));
+                       _events.insert (insertion_point, new ControlEvent (when, value));
                        reposition_for_rt_add (0);
 
                } 
@@ -426,8 +432,8 @@ void
 AutomationList::erase (AutomationList::iterator i)
 {
        {
-               Glib::Mutex::Lock lm (lock);
-               events.erase (i);
+               Glib::Mutex::Lock lm (_lock);
+               _events.erase (i);
                reposition_for_rt_add (0);
                mark_dirty ();
        }
@@ -438,8 +444,8 @@ void
 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
 {
        {
-               Glib::Mutex::Lock lm (lock);
-               events.erase (start, end);
+               Glib::Mutex::Lock lm (_lock);
+               _events.erase (start, end);
                reposition_for_rt_add (0);
                mark_dirty ();
        }
@@ -452,19 +458,19 @@ AutomationList::reset_range (double start, double endt)
        bool reset = false;
 
        {
-        Glib::Mutex::Lock lm (lock);
+        Glib::Mutex::Lock lm (_lock);
                TimeComparator cmp;
                ControlEvent cp (start, 0.0f);
                iterator s;
                iterator e;
                
-               if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
+               if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) != _events.end()) {
 
                        cp.when = endt;
-                       e = upper_bound (events.begin(), events.end(), &cp, cmp);
+                       e = upper_bound (_events.begin(), _events.end(), &cp, cmp);
 
                        for (iterator i = s; i != e; ++i) {
-                               (*i)->value = default_value;
+                               (*i)->value = _default_value;
                        }
                        
                        reset = true;
@@ -484,16 +490,16 @@ AutomationList::erase_range (double start, double endt)
        bool erased = false;
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
                TimeComparator cmp;
                ControlEvent cp (start, 0.0f);
                iterator s;
                iterator e;
 
-               if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
+               if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) != _events.end()) {
                        cp.when = endt;
-                       e = upper_bound (events.begin(), events.end(), &cp, cmp);
-                       events.erase (s, e);
+                       e = upper_bound (_events.begin(), _events.end(), &cp, cmp);
+                       _events.erase (s, e);
                        reposition_for_rt_add (0);
                        erased = true;
                        mark_dirty ();
@@ -515,7 +521,7 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double
        */
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
 
                while (start != end) {
                        (*start)->when += xdelta;
@@ -527,9 +533,9 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double
                }
 
                if (!_frozen) {
-                       events.sort (sort_events_by_time);
+                       _events.sort (sort_events_by_time);
                } else {
-                       sort_pending = true;
+                       _sort_pending = true;
                }
 
                mark_dirty ();
@@ -542,13 +548,13 @@ void
 AutomationList::slide (iterator before, double distance)
 {
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
 
-               if (before == events.end()) {
+               if (before == _events.end()) {
                        return;
                }
                
-               while (before != events.end()) {
+               while (before != _events.end()) {
                        (*before)->when += distance;
                        ++before;
                }
@@ -566,7 +572,7 @@ AutomationList::modify (iterator iter, double when, double val)
        */
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
 
                (*iter)->when = when;
                (*iter)->value = val;
@@ -576,9 +582,9 @@ AutomationList::modify (iterator iter, double when, double val)
                }
 
                if (!_frozen) {
-                       events.sort (sort_events_by_time);
+                       _events.sort (sort_events_by_time);
                } else {
-                       sort_pending = true;
+                       _sort_pending = true;
                }
 
                mark_dirty ();
@@ -590,20 +596,20 @@ AutomationList::modify (iterator iter, double when, double val)
 std::pair<AutomationList::iterator,AutomationList::iterator>
 AutomationList::control_points_adjacent (double xval)
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Mutex::Lock lm (_lock);
        iterator i;
        TimeComparator cmp;
        ControlEvent cp (xval, 0.0f);
        std::pair<iterator,iterator> ret;
 
-       ret.first = events.end();
-       ret.second = events.end();
+       ret.first = _events.end();
+       ret.second = _events.end();
 
-       for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
+       for (i = lower_bound (_events.begin(), _events.end(), &cp, cmp); i != _events.end(); ++i) {
                
-               if (ret.first == events.end()) {
+               if (ret.first == _events.end()) {
                        if ((*i)->when >= xval) {
-                               if (i != events.begin()) {
+                               if (i != _events.begin()) {
                                        ret.first = i;
                                        --ret.first;
                                } else {
@@ -641,15 +647,15 @@ AutomationList::thaw ()
        }
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
 
-               if (sort_pending) {
-                       events.sort (sort_events_by_time);
-                       sort_pending = false;
+               if (_sort_pending) {
+                       _events.sort (sort_events_by_time);
+                       _sort_pending = false;
                }
        }
 
-       if (changed_when_thawed) {
+       if (_changed_when_thawed) {
                StateChanged(); /* EMIT SIGNAL */
        }
 }
@@ -657,44 +663,44 @@ AutomationList::thaw ()
 void
 AutomationList::set_max_xval (double x)
 {
-       max_xval = x;
+       _max_xval = x;
 }
 
 void 
 AutomationList::mark_dirty ()
 {
-       lookup_cache.left = -1;
-       _dirty = true;
+       _lookup_cache.left = -1;
+       Dirty (); /* EMIT SIGNAL */
 }
 
 void
 AutomationList::truncate_end (double last_coordinate)
 {
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
                ControlEvent cp (last_coordinate, 0);
                list<ControlEvent*>::reverse_iterator i;
                double last_val;
 
-               if (events.empty()) {
+               if (_events.empty()) {
                        return;
                }
 
-               if (last_coordinate == events.back()->when) {
+               if (last_coordinate == _events.back()->when) {
                        return;
                }
 
-               if (last_coordinate > events.back()->when) {
+               if (last_coordinate > _events.back()->when) {
                        
                        /* extending end:
                        */
 
-                       iterator foo = events.begin();
+                       iterator foo = _events.begin();
                        bool lessthantwo;
 
-                       if (foo == events.end()) {
+                       if (foo == _events.end()) {
                                lessthantwo = true;
-                       } else if (++foo == events.end()) {
+                       } else if (++foo == _events.end()) {
                                lessthantwo = true;
                        } else {
                                lessthantwo = false;
@@ -702,7 +708,7 @@ AutomationList::truncate_end (double last_coordinate)
 
                        if (lessthantwo) {
                                /* less than 2 points: add a new point */
-                               events.push_back (point_factory (last_coordinate, events.back()->value));
+                               _events.push_back (new ControlEvent (last_coordinate, _events.back()->value));
                        } else {
 
                                /* more than 2 points: check to see if the last 2 values
@@ -710,14 +716,14 @@ AutomationList::truncate_end (double last_coordinate)
                                   last point. otherwise, add a new point.
                                */
 
-                               iterator penultimate = events.end();
+                               iterator penultimate = _events.end();
                                --penultimate; /* points at last point */
                                --penultimate; /* points at the penultimate point */
                                
-                               if (events.back()->value == (*penultimate)->value) {
-                                       events.back()->when = last_coordinate;
+                               if (_events.back()->value == (*penultimate)->value) {
+                                       _events.back()->when = last_coordinate;
                                } else {
-                                       events.push_back (point_factory (last_coordinate, events.back()->value));
+                                       _events.push_back (new ControlEvent (last_coordinate, _events.back()->value));
                                }
                        }
 
@@ -726,10 +732,10 @@ AutomationList::truncate_end (double last_coordinate)
                        /* shortening end */
 
                        last_val = unlocked_eval (last_coordinate);
-                       last_val = max ((double) min_yval, last_val);
-                       last_val = min ((double) max_yval, last_val);
+                       last_val = max ((double) _min_yval, last_val);
+                       last_val = min ((double) _max_yval, last_val);
                        
-                       i = events.rbegin();
+                       i = _events.rbegin();
                        
                        /* make i point to the last control point */
                        
@@ -739,9 +745,9 @@ AutomationList::truncate_end (double last_coordinate)
                           beyond the new last coordinate.
                        */
 
-                       uint32_t sz = events.size();
+                       uint32_t sz = _events.size();
                        
-                       while (i != events.rend() && sz > 2) {
+                       while (i != _events.rend() && sz > 2) {
                                list<ControlEvent*>::reverse_iterator tmp;
                                
                                tmp = i;
@@ -751,14 +757,14 @@ AutomationList::truncate_end (double last_coordinate)
                                        break;
                                }
                                
-                               events.erase (i.base());
+                               _events.erase (i.base());
                                --sz;
 
                                i = tmp;
                        }
                        
-                       events.back()->when = last_coordinate;
-                       events.back()->value = last_val;
+                       _events.back()->when = last_coordinate;
+                       _events.back()->value = last_val;
                }
 
                reposition_for_rt_add (0);
@@ -772,12 +778,12 @@ void
 AutomationList::truncate_start (double overall_length)
 {
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
                AutomationList::iterator i;
                double first_legal_value;
                double first_legal_coordinate;
 
-               if (events.empty()) {
+               if (_events.empty()) {
                        fatal << _("programming error:")
                              << "AutomationList::truncate_start() called on an empty list"
                              << endmsg;
@@ -785,26 +791,26 @@ AutomationList::truncate_start (double overall_length)
                        return;
                }
                
-               if (overall_length == events.back()->when) {
+               if (overall_length == _events.back()->when) {
                        /* no change in overall length */
                        return;
                }
                
-               if (overall_length > events.back()->when) {
+               if (overall_length > _events.back()->when) {
                        
                        /* growing at front: duplicate first point. shift all others */
 
-                       double shift = overall_length - events.back()->when;
+                       double shift = overall_length - _events.back()->when;
                        uint32_t np;
 
-                       for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
+                       for (np = 0, i = _events.begin(); i != _events.end(); ++i, ++np) {
                                (*i)->when += shift;
                        }
 
                        if (np < 2) {
 
                                /* less than 2 points: add a new point */
-                               events.push_front (point_factory (0, events.front()->value));
+                               _events.push_front (new ControlEvent (0, _events.front()->value));
 
                        } else {
 
@@ -813,15 +819,15 @@ AutomationList::truncate_start (double overall_length)
                                   first point. otherwise, add a new point.
                                */
 
-                               iterator second = events.begin();
+                               iterator second = _events.begin();
                                ++second; /* points at the second point */
                                
-                               if (events.front()->value == (*second)->value) {
+                               if (_events.front()->value == (*second)->value) {
                                        /* first segment is flat, just move start point back to zero */
-                                       events.front()->when = 0;
+                                       _events.front()->when = 0;
                                } else {
                                        /* leave non-flat segment in place, add a new leading point. */
-                                       events.push_front (point_factory (0, events.front()->value));
+                                       _events.push_front (new ControlEvent (0, _events.front()->value));
                                }
                        }
 
@@ -829,16 +835,16 @@ AutomationList::truncate_start (double overall_length)
 
                        /* shrinking at front */
                        
-                       first_legal_coordinate = events.back()->when - overall_length;
+                       first_legal_coordinate = _events.back()->when - overall_length;
                        first_legal_value = unlocked_eval (first_legal_coordinate);
-                       first_legal_value = max (min_yval, first_legal_value);
-                       first_legal_value = min (max_yval, first_legal_value);
+                       first_legal_value = max (_min_yval, first_legal_value);
+                       first_legal_value = min (_max_yval, first_legal_value);
 
                        /* remove all events earlier than the new "front" */
 
-                       i = events.begin();
+                       i = _events.begin();
                        
-                       while (i != events.end() && !events.empty()) {
+                       while (i != _events.end() && !_events.empty()) {
                                list<ControlEvent*>::iterator tmp;
                                
                                tmp = i;
@@ -848,7 +854,7 @@ AutomationList::truncate_start (double overall_length)
                                        break;
                                }
                                
-                               events.erase (i);
+                               _events.erase (i);
                                
                                i = tmp;
                        }
@@ -858,13 +864,13 @@ AutomationList::truncate_start (double overall_length)
                           relative position
                        */
                        
-                       for (i = events.begin(); i != events.end(); ++i) {
+                       for (i = _events.begin(); i != _events.end(); ++i) {
                                (*i)->when -= first_legal_coordinate;
                        }
 
                        /* add a new point for the interpolated new value */
                        
-                       events.push_front (point_factory (0, first_legal_value));
+                       _events.push_front (new ControlEvent (0, first_legal_value));
                }           
 
                reposition_for_rt_add (0);
@@ -876,48 +882,42 @@ AutomationList::truncate_start (double overall_length)
 }
 
 double
-AutomationList::unlocked_eval (double x)
-{
-       return shared_eval (x);
-}
-
-double
-AutomationList::shared_eval (double x) 
+AutomationList::unlocked_eval (double x) const
 {
-       pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
+       pair<EventList::iterator,EventList::iterator> range;
        int32_t npoints;
        double lpos, upos;
        double lval, uval;
        double fraction;
 
-       npoints = events.size();
+       npoints = _events.size();
 
        switch (npoints) {
        case 0:
-               return default_value;
+               return _default_value;
 
        case 1:
-               if (x >= events.front()->when) {
-                       return events.front()->value;
+               if (x >= _events.front()->when) {
+                       return _events.front()->value;
                } else {
-                       // return default_value;
-                       return events.front()->value;
+                       // return _default_value;
+                       return _events.front()->value;
                } 
                
        case 2:
-               if (x >= events.back()->when) {
-                       return events.back()->value;
-               } else if (x == events.front()->when) {
-                       return events.front()->value;
-               } else if (x < events.front()->when) {
-                       // return default_value;
-                       return events.front()->value;
+               if (x >= _events.back()->when) {
+                       return _events.back()->value;
+               } else if (x == _events.front()->when) {
+                       return _events.front()->value;
+               } else if (x < _events.front()->when) {
+                       // return _default_value;
+                       return _events.front()->value;
                }
 
-               lpos = events.front()->when;
-               lval = events.front()->value;
-               upos = events.back()->when;
-               uval = events.back()->value;
+               lpos = _events.front()->when;
+               lval = _events.front()->value;
+               upos = _events.back()->when;
+               uval = _events.back()->value;
                
                /* linear interpolation betweeen the two points
                */
@@ -927,13 +927,13 @@ AutomationList::shared_eval (double x)
 
        default:
 
-               if (x >= events.back()->when) {
-                       return events.back()->value;
-               } else if (x == events.front()->when) {
-                       return events.front()->value;
-               } else if (x < events.front()->when) {
-                       // return default_value;
-                       return events.front()->value;
+               if (x >= _events.back()->when) {
+                       return _events.back()->value;
+               } else if (x == _events.front()->when) {
+                       return _events.front()->value;
+               } else if (x < _events.front()->when) {
+                       // return _default_value;
+                       return _events.front()->value;
                }
 
                return multipoint_eval (x);
@@ -942,9 +942,9 @@ AutomationList::shared_eval (double x)
 }
 
 double
-AutomationList::multipoint_eval (double x) 
+AutomationList::multipoint_eval (double x) const
 {
-       pair<AutomationList::iterator,AutomationList::iterator> range;
+       pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
        double upos, lpos;
        double uval, lval;
        double fraction;
@@ -953,38 +953,38 @@ AutomationList::multipoint_eval (double x)
           this was called (or if the lookup cache has been marked "dirty" (left<0)
        */
 
-       if ((lookup_cache.left < 0) ||
-           ((lookup_cache.left > x) || 
-            (lookup_cache.range.first == events.end()) || 
-            ((*lookup_cache.range.second)->when < x))) {
+       if ((_lookup_cache.left < 0) ||
+           ((_lookup_cache.left > x) || 
+            (_lookup_cache.range.first == _events.end()) || 
+            ((*_lookup_cache.range.second)->when < x))) {
 
                ControlEvent cp (x, 0);
                TimeComparator cmp;
                
-               lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
+               _lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, cmp);
        }
        
-       range = lookup_cache.range;
+       range = _lookup_cache.range;
 
        if (range.first == range.second) {
 
                /* x does not exist within the list as a control point */
 
-               lookup_cache.left = x;
+               _lookup_cache.left = x;
 
-               if (range.first != events.begin()) {
+               if (range.first != _events.begin()) {
                        --range.first;
                        lpos = (*range.first)->when;
                        lval = (*range.first)->value;
                }  else {
                        /* we're before the first point */
-                       // return default_value;
-                       return events.front()->value;
+                       // return _default_value;
+                       return _events.front()->value;
                }
                
-               if (range.second == events.end()) {
+               if (range.second == _events.end()) {
                        /* we're after the last point */
-                       return events.back()->value;
+                       return _events.back()->value;
                }
 
                upos = (*range.second)->when;
@@ -1000,17 +1000,17 @@ AutomationList::multipoint_eval (double x)
        } 
 
        /* x is a control point in the data */
-       lookup_cache.left = -1;
+       _lookup_cache.left = -1;
        return (*range.first)->value;
 }
 
 AutomationList*
 AutomationList::cut (iterator start, iterator end)
 {
-       AutomationList* nal = new AutomationList (default_value);
+       AutomationList* nal = new AutomationList (_param_id, _min_yval, _max_yval, _default_value);
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
 
                for (iterator x = start; x != end; ) {
                        iterator tmp;
@@ -1018,8 +1018,8 @@ AutomationList::cut (iterator start, iterator end)
                        tmp = x;
                        ++tmp;
                        
-                       nal->events.push_back (point_factory (**x));
-                       events.erase (x);
+                       nal->_events.push_back (new ControlEvent (**x));
+                       _events.erase (x);
                        
                        reposition_for_rt_add (0);
 
@@ -1037,24 +1037,24 @@ AutomationList::cut (iterator start, iterator end)
 AutomationList*
 AutomationList::cut_copy_clear (double start, double end, int op)
 {
-       AutomationList* nal = new AutomationList (default_value);
+       AutomationList* nal = new AutomationList (_param_id, _min_yval, _max_yval, _default_value);
        iterator s, e;
        ControlEvent cp (start, 0.0);
        TimeComparator cmp;
        bool changed = false;
        
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
 
-               if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
+               if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) == _events.end()) {
                        return nal;
                }
 
                cp.when = end;
-               e = upper_bound (events.begin(), events.end(), &cp, cmp);
+               e = upper_bound (_events.begin(), _events.end(), &cp, cmp);
 
                if (op != 2 && (*s)->when != start) {
-                       nal->events.push_back (point_factory (0, unlocked_eval (start)));
+                       nal->_events.push_back (new ControlEvent (0, unlocked_eval (start)));
                }
 
                for (iterator x = s; x != e; ) {
@@ -1070,18 +1070,18 @@ AutomationList::cut_copy_clear (double start, double end, int op)
                        */
                        
                        if (op != 2) {
-                               nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
+                               nal->_events.push_back (new ControlEvent ((*x)->when - start, (*x)->value));
                        }
 
                        if (op != 1) {
-                               events.erase (x);
+                               _events.erase (x);
                        }
                        
                        x = tmp;
                }
 
-               if (op != 2 && nal->events.back()->when != end - start) {
-                       nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
+               if (op != 2 && nal->_events.back()->when != end - start) {
+                       nal->_events.push_back (new ControlEvent (end - start, unlocked_eval (end)));
                }
 
                if (changed) {
@@ -1100,10 +1100,10 @@ AutomationList::cut_copy_clear (double start, double end, int op)
 AutomationList*
 AutomationList::copy (iterator start, iterator end)
 {
-       AutomationList* nal = new AutomationList (default_value);
+       AutomationList* nal = new AutomationList (_param_id, _min_yval, _max_yval, _default_value);
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
                
                for (iterator x = start; x != end; ) {
                        iterator tmp;
@@ -1111,7 +1111,7 @@ AutomationList::copy (iterator start, iterator end)
                        tmp = x;
                        ++tmp;
                        
-                       nal->events.push_back (point_factory (**x));
+                       nal->_events.push_back (new ControlEvent (**x));
                        
                        x = tmp;
                }
@@ -1141,22 +1141,22 @@ AutomationList::clear (double start, double end)
 bool
 AutomationList::paste (AutomationList& alist, double pos, float times)
 {
-       if (alist.events.empty()) {
+       if (alist._events.empty()) {
                return false;
        }
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Mutex::Lock lm (_lock);
                iterator where;
                iterator prev;
                double end = 0;
                ControlEvent cp (pos, 0.0);
                TimeComparator cmp;
 
-               where = upper_bound (events.begin(), events.end(), &cp, cmp);
+               where = upper_bound (_events.begin(), _events.end(), &cp, cmp);
 
                for (iterator i = alist.begin();i != alist.end(); ++i) {
-                       events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
+                       _events.insert (where, new ControlEvent( (*i)->when+pos,( *i)->value));
                        end = (*i)->when + pos;
                }
        
@@ -1165,12 +1165,12 @@ AutomationList::paste (AutomationList& alist, double pos, float times)
                   the correct amount.
                */
 
-               while (where != events.end()) {
+               while (where != _events.end()) {
                        iterator tmp;
                        if ((*where)->when <= end) {
                                tmp = where;
                                ++tmp;
-                               events.erase(where);
+                               _events.erase(where);
                                where = tmp;
 
                        } else {
@@ -1186,18 +1186,6 @@ AutomationList::paste (AutomationList& alist, double pos, float times)
        return true;
 }
 
-ControlEvent*
-AutomationList::point_factory (double when, double val) const
-{
-       return new ControlEvent (when, val);
-}
-
-ControlEvent*
-AutomationList::point_factory (const ControlEvent& other) const
-{
-       return new ControlEvent (other);
-}
-
 XMLNode&
 AutomationList::get_state ()
 {
@@ -1207,20 +1195,24 @@ AutomationList::get_state ()
 XMLNode&
 AutomationList::state (bool full)
 {
+       cerr << _param_id.to_string() << "->state()" << endl;
+
        XMLNode* root = new XMLNode (X_("AutomationList"));
        char buf[64];
        LocaleGuard lg (X_("POSIX"));
 
+       root->add_property ("automation-id", _param_id.to_string());
+
        root->add_property ("id", _id.to_s());
 
-       snprintf (buf, sizeof (buf), "%.12g", default_value);
+       snprintf (buf, sizeof (buf), "%.12g", _default_value);
        root->add_property ("default", buf);
-       snprintf (buf, sizeof (buf), "%.12g", min_yval);
-       root->add_property ("min_yval", buf);
-       snprintf (buf, sizeof (buf), "%.12g", max_yval);
-       root->add_property ("max_yval", buf);
-       snprintf (buf, sizeof (buf), "%.12g", max_xval);
-       root->add_property ("max_xval", buf);
+       snprintf (buf, sizeof (buf), "%.12g", _min_yval);
+       root->add_property ("_min_yval", buf);
+       snprintf (buf, sizeof (buf), "%.12g", _max_yval);
+       root->add_property ("_max_yval", buf);
+       snprintf (buf, sizeof (buf), "%.12g", _max_xval);
+       root->add_property ("_max_xval", buf);
 
        if (full) {
                root->add_property ("state", auto_state_to_string (_state));
@@ -1231,7 +1223,7 @@ AutomationList::state (bool full)
 
        root->add_property ("style", auto_style_to_string (_style));
 
-       if (!events.empty()) {
+       if (!_events.empty()) {
                root->add_child_nocopy (serialize_events());
        }
 
@@ -1244,7 +1236,7 @@ AutomationList::serialize_events ()
        XMLNode* node = new XMLNode (X_("events"));
        stringstream str;
 
-       for (iterator xx = events.begin(); xx != events.end(); ++xx) {
+       for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
                str << (double) (*xx)->when;
                str << ' ';
                str <<(double) (*xx)->value;
@@ -1367,17 +1359,25 @@ AutomationList::set_state (const XMLNode& node)
                error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
                return -1;
        }
-       
+
        if ((prop = node.property ("id")) != 0) {
                _id = prop->value ();
                /* update session AL list */
                AutomationListCreated(this);
        }
        
+       if ((prop = node.property (X_("automation-id"))) != 0){ 
+               _param_id = ParamID(prop->value());
+       } else {
+               warning << "Legacy session: automation list has no automation-id property.";
+       }
+       
+       cerr << "Loaded automation " << _param_id.to_string() << endl;
+       
        if ((prop = node.property (X_("default"))) != 0){ 
-               default_value = atof (prop->value());
+               _default_value = atof (prop->value());
        } else {
-               default_value = 0.0;
+               _default_value = 0.0;
        }
 
        if ((prop = node.property (X_("style"))) != 0) {
@@ -1392,22 +1392,22 @@ AutomationList::set_state (const XMLNode& node)
                _state = Off;
        }
 
-       if ((prop = node.property (X_("min_yval"))) != 0) {
-               min_yval = atof (prop->value ());
+       if ((prop = node.property (X_("_min_yval"))) != 0) {
+               _min_yval = atof (prop->value ());
        } else {
-               min_yval = FLT_MIN;
+               _min_yval = FLT_MIN;
        }
 
-       if ((prop = node.property (X_("max_yval"))) != 0) {
-               max_yval = atof (prop->value ());
+       if ((prop = node.property (X_("_max_yval"))) != 0) {
+               _max_yval = atof (prop->value ());
        } else {
-               max_yval = FLT_MAX;
+               _max_yval = FLT_MAX;
        }
 
-       if ((prop = node.property (X_("max_xval"))) != 0) {
-               max_xval = atof (prop->value ());
+       if ((prop = node.property (X_("_max_xval"))) != 0) {
+               _max_xval = atof (prop->value ());
        } else {
-               max_xval = 0; // means "no limit ;
+               _max_xval = 0; // means "no limit ;
        }
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
index 847741832d6045a1cd89bca507f6d22a3e4f0847..e5770507d540991b5258c7b6b2425b892efd800b 100644 (file)
@@ -78,8 +78,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<Audio
                      nframes_t position,
                      AnchorPoint ap)
        : AudioRegion (position, length, "foobar"),
-         _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
-         _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+         _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
+         _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
 
 {
        _in = in;
@@ -96,8 +96,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<Audio
 
 Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model, bool act)
        : AudioRegion (0, 0, "foobar"),
-         _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
-         _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+         _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
+         _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
 {
        _in_update = false;
        _fixed = false;
@@ -115,8 +115,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioR
 
 Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
        : AudioRegion (0, 0, "foobar"),
-         _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
-         _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+         _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
+         _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
 
 {
        boost::shared_ptr<Region> r;
@@ -300,8 +300,8 @@ Crossfade::read_at (Sample *buf, Sample *mixdown_buffer,
        float* fiv = new float[to_write];
        float* fov = new float[to_write];
 
-       _fade_in.get_vector (offset, offset+to_write, fiv, to_write);
-       _fade_out.get_vector (offset, offset+to_write, fov, to_write);
+       _fade_in.curve().get_vector (offset, offset+to_write, fiv, to_write);
+       _fade_out.curve().get_vector (offset, offset+to_write, fov, to_write);
 
        /* note: although we have not explicitly taken into account the return values
           from _out->read_at() or _in->read_at(), the length() function does this
index 5a1dc108f86062181c638dfc8dc80dcfa30c8d25..3aa9de91272c3737758adfb0b9fe1e2e1e701619 100644 (file)
@@ -31,6 +31,7 @@
 #include <sigc++/bind.h>
 
 #include "ardour/curve.h"
+#include "ardour/automation_event.h"
 
 #include "i18n.h"
 
@@ -39,31 +40,35 @@ using namespace ARDOUR;
 using namespace sigc;
 using namespace PBD;
 
-Curve::Curve (double minv, double maxv, double canv, bool nostate)
-       : AutomationList (canv)
+Curve::Curve (const AutomationList& al)
+       : _list (al)
+       , _dirty (true)
 {
-       min_yval = minv;
-       max_yval = maxv;
+       _list.Dirty.connect(mem_fun(*this, &Curve::on_list_dirty));
 }
 
 Curve::Curve (const Curve& other)
-       : AutomationList (other)
+       : _list (other._list)
+       , _dirty (true)
 {
-       min_yval = other.min_yval;
-       max_yval = other.max_yval;
+       _list.Dirty.connect(mem_fun(*this, &Curve::on_list_dirty));
 }
-
+#if 0
 Curve::Curve (const Curve& other, double start, double end)
-       : AutomationList (other, start, end)
+       : _list (other._list)
 {
-       min_yval = other.min_yval;
-       max_yval = other.max_yval;
+       _min_yval = other._min_yval;
+       _max_yval = other._max_yval;
 }
 
-Curve::Curve (const XMLNode& node)
-       : AutomationList (node)
+/** \a id is used for legacy sessions where the type is not present
+ * in or below the <AutomationList> node.  It is used if \a id is non-null.
+ */
+Curve::Curve (const XMLNode& node, ParamID id)
+       : AutomationList (node, id)
 {
 }
+#endif
 
 Curve::~Curve ()
 {
@@ -78,7 +83,7 @@ Curve::solve ()
                return;
        }
        
-       if ((npoints = events.size()) > 2) {
+       if ((npoints = _list.events().size()) > 2) {
                
                /* Compute coefficients needed to efficiently compute a constrained spline
                   curve. See "Constrained Cubic Spline Interpolation" by CJC Kruger
@@ -88,9 +93,9 @@ Curve::solve ()
                double x[npoints];
                double y[npoints];
                uint32_t i;
-               AutomationEventList::iterator xx;
+               AutomationList::EventList::const_iterator xx;
 
-               for (i = 0, xx = events.begin(); xx != events.end(); ++xx, ++i) {
+               for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
                        x[i] = (double) (*xx)->when;
                        y[i] = (double) (*xx)->value;
                }
@@ -108,16 +113,7 @@ Curve::solve ()
 
                double fplast = 0;
 
-               for (i = 0, xx = events.begin(); xx != events.end(); ++xx, ++i) {
-                       
-                       CurvePoint* cp = dynamic_cast<CurvePoint*>(*xx);
-
-                       if (cp == 0) {
-                               fatal  << _("programming error: ")
-                                      << X_("non-CurvePoint event found in event list for a Curve")
-                                      << endmsg;
-                               /*NOTREACHED*/
-                       }
+               for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
                        
                        double xdelta;   /* gcc is wrong about possible uninitialized use */
                        double xdelta2;  /* ditto */
@@ -192,10 +188,10 @@ Curve::solve ()
 
                        /* store */
 
-                       cp->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13);
-                       cp->coeff[1] = b;
-                       cp->coeff[2] = c;
-                       cp->coeff[3] = d;
+                       (*xx)->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13);
+                       (*xx)->coeff[1] = b;
+                       (*xx)->coeff[2] = c;
+                       (*xx)->coeff[3] = d;
 
                        fplast = fpi;
                }
@@ -208,7 +204,7 @@ Curve::solve ()
 bool
 Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen)
 {
-       Glib::Mutex::Lock lm (lock, Glib::TRY_LOCK);
+       Glib::Mutex::Lock lm(_list.lock(), Glib::TRY_LOCK);
 
        if (!lm.locked()) {
                return false;
@@ -221,7 +217,7 @@ Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen)
 void
 Curve::get_vector (double x0, double x1, float *vec, int32_t veclen)
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Mutex::Lock lm(_list.lock());
        _get_vector (x0, x1, vec, veclen);
 }
 
@@ -233,22 +229,22 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
        int32_t original_veclen;
        int32_t npoints;
 
-        if ((npoints = events.size()) == 0) {
-                 for (i = 0; i < veclen; ++i) {
-                         vec[i] = default_value;
-                 }
-                 return;
+       if ((npoints = _list.events().size()) == 0) {
+               for (i = 0; i < veclen; ++i) {
+                       vec[i] = _list.default_value();
+               }
+               return;
        }
 
        /* events is now known not to be empty */
 
-       max_x = events.back()->when;
-       min_x = events.front()->when;
+       max_x = _list.events().back()->when;
+       min_x = _list.events().front()->when;
 
        lx = max (min_x, x0);
 
        if (x1 < 0) {
-               x1 = events.back()->when;
+               x1 = _list.events().back()->when;
        }
 
        hx = min (max_x, x1);
@@ -267,7 +263,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
                subveclen = min (subveclen, veclen);
 
                for (i = 0; i < subveclen; ++i) {
-                       vec[i] = events.front()->value;
+                       vec[i] = _list.events().front()->value;
                }
 
                veclen -= subveclen;
@@ -286,7 +282,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
                
                subveclen = min (subveclen, veclen);
 
-               val = events.back()->value;
+               val = _list.events().back()->value;
 
                i = veclen - subveclen;
 
@@ -304,7 +300,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
        if (npoints == 1 ) {
        
                for (i = 0; i < veclen; ++i) {
-                       vec[i] = events.front()->value;
+                       vec[i] = _list.events().front()->value;
                }
                return;
        }
@@ -325,11 +321,11 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
                        dx = 0; // not used
                }
        
-               double slope = (events.back()->value - events.front()->value)/  
-                       (events.back()->when - events.front()->when);
+               double slope = (_list.events().back()->value - _list.events().front()->value)/  
+                       (_list.events().back()->when - _list.events().front()->when);
                double yfrac = dx*slope;
  
-               vec[0] = events.front()->value + slope * (lx - events.front()->when);
+               vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when);
  
                for (i = 1; i < veclen; ++i) {
                        vec[i] = vec[i-1] + yfrac;
@@ -357,27 +353,31 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
 double
 Curve::unlocked_eval (double x)
 {
+       // I don't see the point of this...
+
        if (_dirty) {
                solve ();
        }
 
-       return shared_eval (x);
+       return _list.unlocked_eval (x);
 }
 
 double
 Curve::multipoint_eval (double x)
 {      
-       pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
+       pair<AutomationList::EventList::const_iterator,AutomationList::EventList::const_iterator> range;
+
+       AutomationList::LookupCache& lookup_cache = _list.lookup_cache();
 
        if ((lookup_cache.left < 0) ||
            ((lookup_cache.left > x) || 
-            (lookup_cache.range.first == events.end()) || 
+            (lookup_cache.range.first == _list.events().end()) || 
             ((*lookup_cache.range.second)->when < x))) {
                
-               TimeComparator cmp;
+               AutomationList::TimeComparator cmp;
                ControlEvent cp (x, 0.0);
 
-               lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
+               lookup_cache.range = equal_range (_list.events().begin(), _list.events().end(), &cp, cmp);
        }
 
        range = lookup_cache.range;
@@ -399,21 +399,21 @@ Curve::multipoint_eval (double x)
                
                lookup_cache.left = x;
 
-               if (range.first == events.begin()) {
+               if (range.first == _list.events().begin()) {
                        /* we're before the first point */
                        // return default_value;
-                       events.front()->value;
+                       _list.events().front()->value;
                }
                
-               if (range.second == events.end()) {
+               if (range.second == _list.events().end()) {
                        /* we're after the last point */
-                       return events.back()->value;
+                       return _list.events().back()->value;
                }
 
                double x2 = x * x;
-               CurvePoint* cp = dynamic_cast<CurvePoint*> (*range.second);
+               ControlEvent* ev = *range.second;
 
-               return cp->coeff[0] + (cp->coeff[1] * x) + (cp->coeff[2] * x2) + (cp->coeff[3] * x2 * x);
+               return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
        } 
 
        /* x is a control point in the data */
@@ -422,18 +422,6 @@ Curve::multipoint_eval (double x)
        return (*range.first)->value;
 }
 
-ControlEvent*
-Curve::point_factory (double when, double val) const
-{
-       return new CurvePoint (when, val);
-}
-
-ControlEvent*
-Curve::point_factory (const ControlEvent& other) const
-{
-       return new CurvePoint (other.when, other.value);
-}
-
 extern "C" {
 
 void 
index 356c0df5c13c49467002eca5502bdaedbdc76c1f..5a65135f758e85548ebe97c69e6021d8a18c95da 100644 (file)
@@ -104,6 +104,10 @@ setup_enum_writer ()
        REGISTER_ENUM (PluginAutomation);
        REGISTER_ENUM (SoloAutomation);
        REGISTER_ENUM (MuteAutomation);
+       REGISTER_ENUM (MidiCCAutomation);
+       REGISTER_ENUM (FadeInAutomation);
+       REGISTER_ENUM (FadeOutAutomation);
+       REGISTER_ENUM (EnvelopeAutomation);
        REGISTER_BITS (_AutomationType);
 
        REGISTER_ENUM (Off);
index 369df7348c43aec94a2bdd5e58b3ec08c1f793dd..b067e6c08dc20851a5a8e49dafe420dd8aa94b5a 100644 (file)
 using namespace ARDOUR;
 
 Gain::Gain ()
-       : Curve (0.0, 2.0, 1.0f)   /* XXX yuck; clamps gain to -inf .. +6db */
+       : AutomationList (ParamID(GainAutomation), 0.0, 2.0, 1.0f)   /* XXX yuck; clamps gain to -inf .. +6db */
 {
 }
 
 Gain::Gain (const Gain& other)
-       : Curve (other)
+       : AutomationList (other)
 {
 }
 
@@ -35,7 +35,7 @@ Gain&
 Gain::operator= (const Gain& other)
 {
        if (this != &other) {
-               Curve::operator= (other);
+               AutomationList::operator= (other);
        }
        return *this;
 }
index bb02cb0925332c9c0de841de7f5cc82bd025aa28..1e73c398c25e69adf633959bb96b86e2b8188ef4 100644 (file)
@@ -157,7 +157,7 @@ Insert::state (bool full_state)
 
                XMLNode& automation = Automatable::get_automation_state(); 
                
-               for (set<uint32_t>::iterator x = _visible_parameter_automation.begin(); x != _visible_parameter_automation.end(); ++x) {
+               for (set<ParamID>::iterator x = _visible_parameter_automation.begin(); x != _visible_parameter_automation.end(); ++x) {
                        if (x != _visible_parameter_automation.begin()) {
                                sstr << ' ';
                        }
@@ -196,7 +196,7 @@ Insert::set_state (const XMLNode& node)
                        if ((prop = (*niter)->property ("path")) != 0) {
                                old_set_automation_state (*(*niter));
                        } else {
-                               set_automation_state (*(*niter));
+                               set_automation_state (*(*niter), ParamID(PluginAutomation));
                        }
 
                        if ((prop = (*niter)->property ("visible")) != 0) {
@@ -211,7 +211,8 @@ Insert::set_state (const XMLNode& node)
                                        if (sstr.fail()) {
                                                break;
                                        }
-                                       mark_automation_visible (what, true);
+                                       // FIXME: other automation types?
+                                       mark_automation_visible (ParamID(PluginAutomation, what), true);
                                }
                        }
 
index 3069123f165ae18a1f9d9e5c54b8c2520994036b..c60628b6031f8e7fd796e47d9423b0e21e3fedb3 100644 (file)
@@ -101,11 +101,10 @@ static double direct_gain_to_control (gain_t gain) {
 IO::IO (Session& s, const string& name,
        int input_min, int input_max, int output_min, int output_max,
        DataType default_type)
-       : SessionObject(s, name),
-      _output_buffers(new BufferSet()),
-         _default_type(default_type),
+       : Automatable (s, name),
+      _output_buffers (new BufferSet()),
+         _default_type (default_type),
          _gain_control (X_("gaincontrol"), *this),
-         _gain_automation_curve (0.0, 2.0, 1.0),
          _input_minimum (ChanCount::ZERO),
          _input_maximum (ChanCount::INFINITE),
          _output_minimum (ChanCount::ZERO),
@@ -136,12 +135,14 @@ IO::IO (Session& s, const string& name,
        _phase_invert = false;
        deferred_state = 0;
 
+       add_automation_parameter(new AutomationList(ParamID(GainAutomation), 0.0, 2.0, 1.0));
+
        apply_gain_automation = false;
        
        last_automation_snapshot = 0;
 
-       _gain_automation_state = Off;
-       _gain_automation_style = Absolute;
+       /*_gain_automation_state = Off;
+       _gain_automation_style = Absolute;*/
 
        {
                // IO::Meter is emitted from another thread so the
@@ -157,11 +158,10 @@ IO::IO (Session& s, const string& name,
 }
 
 IO::IO (Session& s, const XMLNode& node, DataType dt)
-       : SessionObject(s, "unnamed io"),
-      _output_buffers(new BufferSet()),
+       : Automatable (s, "unnamed io"),
+      _output_buffers (new BufferSet()),
          _default_type (dt),
-         _gain_control (X_("gaincontrol"), *this),
-         _gain_automation_curve (0, 0, 0) // all reset in set_state()
+         _gain_control (X_("gaincontrol"), *this)
 {
        _meter = new PeakMeter (_session);
 
@@ -1129,7 +1129,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src)
 gain_t
 IO::effective_gain () const
 {
-       if (_gain_automation_curve.automation_playback()) {
+       if (gain_automation().automation_playback()) {
                return _effective_gain;
        } else {
                return _desired_gain;
@@ -1288,18 +1288,9 @@ IO::state (bool full_state)
        node->add_property ("iolimits", buf);
 
        /* automation */
-
-       if (full_state) {
-
-               XMLNode* autonode = new XMLNode (X_("Automation"));
-               autonode->add_child_nocopy (get_automation_state());
-               node->add_child_nocopy (*autonode);
-
-               snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state());
-       } else {
-               /* never store anything except Off for automation state in a template */
-               snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); 
-       }
+       
+       if (full_state)
+               node->add_child_nocopy (get_automation_state());
 
        return *node;
 }
@@ -1363,7 +1354,7 @@ IO::set_state (const XMLNode& node)
 
                if ((*iter)->name() == X_("Automation")) {
 
-                       set_automation_state (*(*iter)->children().front());
+                       set_automation_state (*(*iter), ParamID(GainAutomation));
                }
 
                if ((*iter)->name() == X_("controllable")) {
@@ -1410,18 +1401,6 @@ IO::set_state (const XMLNode& node)
        return 0;
 }
 
-int
-IO::set_automation_state (const XMLNode& node)
-{
-       return _gain_automation_curve.set_state (node);
-}
-
-XMLNode&
-IO::get_automation_state ()
-{
-       return (_gain_automation_curve.get_state ());
-}
-
 int
 IO::load_automation (string path)
 {
@@ -1479,7 +1458,7 @@ IO::load_automation (string path)
 
                switch (type) {
                case 'g':
-                       _gain_automation_curve.fast_simple_add (when, value);
+                       gain_automation().fast_simple_add (when, value);
                        break;
 
                case 's':
@@ -2198,41 +2177,43 @@ IO::meter ()
 void
 IO::clear_automation ()
 {
-       Glib::Mutex::Lock lm (automation_lock);
-       _gain_automation_curve.clear ();
+       Automatable::clear_automation (); // clears gain automation
        _panner->clear_automation ();
 }
 
 void
-IO::set_gain_automation_state (AutoState state)
+IO::set_parameter_automation_state (ParamID param, AutoState state)
 {
-       bool changed = false;
+       // XXX: would be nice to get rid of this special hack
 
-       {
-               Glib::Mutex::Lock lm (automation_lock);
+       if (param.type() == GainAutomation) {
 
-               if (state != _gain_automation_curve.automation_state()) {
-                       changed = true;
-                       last_automation_snapshot = 0;
-                       _gain_automation_curve.set_automation_state (state);
-                       
-                       if (state != Off) {
-                               set_gain (_gain_automation_curve.eval (_session.transport_frame()), this);
+               bool changed = false;
+
+               { 
+                       Glib::Mutex::Lock lm (_automation_lock);
+
+                       ARDOUR::AutomationList& gain_auto = gain_automation();
+
+                       if (state != gain_auto.automation_state()) {
+                               changed = true;
+                               last_automation_snapshot = 0;
+                               gain_auto.set_automation_state (state);
+
+                               if (state != Off) {
+                                       // FIXME: shouldn't this use Curve?
+                                       set_gain (gain_auto.eval (_session.transport_frame()), this);
+                               }
                        }
                }
-       }
 
-       if (changed) {
-               _session.set_dirty ();
-               //gain_automation_state_changed (); /* EMIT SIGNAL */
-       }
-}
+               if (changed) {
+                       _session.set_dirty ();
+               }
 
-void
-IO::set_gain_automation_style (AutoStyle style)
-{
-       Glib::Mutex::Lock lm (automation_lock);
-       _gain_automation_curve.set_automation_style (style);
+       } else {
+               Automatable::set_parameter_automation_state(param, state);
+       }
 }
 
 void
@@ -2263,26 +2244,16 @@ IO::set_gain (gain_t val, void *src)
        gain_changed (src);
        _gain_control.Changed (); /* EMIT SIGNAL */
        
-       if (_session.transport_stopped() && src != 0 && src != this && _gain_automation_curve.automation_write()) {
-               _gain_automation_curve.add (_session.transport_frame(), val);
+       ARDOUR::AutomationList& gain_auto = gain_automation();
+
+       if (_session.transport_stopped() && src != 0 && src != this && gain_auto.automation_write()) {
+               gain_auto.add (_session.transport_frame(), val);
                
        }
 
        _session.set_dirty();
 }
 
-void
-IO::start_gain_touch ()
-{
-       _gain_automation_curve.start_touch ();
-}
-
-void
-IO::end_gain_touch ()
-{
-       _gain_automation_curve.stop_touch ();
-}
-
 void
 IO::start_pan_touch (uint32_t which)
 {
@@ -2305,8 +2276,10 @@ IO::automation_snapshot (nframes_t now)
 {
        if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
 
-               if (_gain_automation_curve.automation_write()) {
-                       _gain_automation_curve.rt_add (now, gain());
+               ARDOUR::AutomationList& gain_auto = gain_automation();
+
+               if (gain_auto.automation_write()) {
+                       gain_auto.rt_add (now, gain());
                }
                
                _panner->snapshot (now);
@@ -2318,15 +2291,18 @@ IO::automation_snapshot (nframes_t now)
 void
 IO::transport_stopped (nframes_t frame)
 {
-       _gain_automation_curve.reposition_for_rt_add (frame);
+       ARDOUR::AutomationList& gain_auto = gain_automation();
+
+       gain_auto.reposition_for_rt_add (frame);
 
-       if (_gain_automation_curve.automation_state() != Off) {
+       if (gain_auto.automation_state() != Off) {
                
                /* the src=0 condition is a special signal to not propagate 
                   automation gain changes into the mix group when locating.
                */
 
-               set_gain (_gain_automation_curve.eval (frame), 0);
+               // FIXME: shouldn't this use Curve?
+               set_gain (gain_auto.eval (frame), 0);
        }
 
        _panner->transport_stopped (frame);
index 0c2104ca7ae225a0cbf32782566aeb18af7a6ff9..98a65546a903fdfd3896bef55508157899f5aa6f 100644 (file)
@@ -318,7 +318,7 @@ LadspaPlugin::set_parameter (uint32_t which, float val)
 {
        if (which < descriptor->PortCount) {
                shadow_data[which] = (LADSPA_Data) val;
-               ParameterChanged (which, val); /* EMIT SIGNAL */
+               ParameterChanged (ParamID(PluginAutomation, which), val); /* EMIT SIGNAL */
 
                if (which < parameter_count() && controls[which]) {
                        controls[which]->Changed ();
@@ -493,10 +493,10 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des
 
 
 string
-LadspaPlugin::describe_parameter (uint32_t which)
+LadspaPlugin::describe_parameter (ParamID which)
 {
-       if (which < parameter_count()) {
-               return port_names()[which];
+       if (which.type() == PluginAutomation && which.id() < parameter_count()) {
+               return port_names()[which.id()];
        } else {
                return "??";
        }
@@ -512,16 +512,16 @@ LadspaPlugin::latency () const
        }
 }
 
-set<uint32_t>
+set<ParamID>
 LadspaPlugin::automatable () const
 {
-       set<uint32_t> ret;
+       set<ParamID> ret;
 
        for (uint32_t i = 0; i < parameter_count(); ++i){
                if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && 
                    LADSPA_IS_PORT_CONTROL(port_descriptor (i))){
                        
-                       ret.insert (ret.end(), i);
+                       ret.insert (ret.end(), ParamID(PluginAutomation, i));
                }
        }
 
index a56f1d03929701a79212149a2de3bbdf90e2dd72..d5a238e253687a8873f3405b6a57915e4179e7c2 100644 (file)
@@ -190,7 +190,7 @@ StreamPanner::add_state (XMLNode& node)
 /*---------------------------------------------------------------------- */
 
 BaseStereoPanner::BaseStereoPanner (Panner& p)
-       : StreamPanner (p), _automation (0.0, 1.0, 0.5)
+       : StreamPanner (p), _automation (ParamID(PanAutomation), 0.0, 1.0, 0.5)
 {
 }
 
@@ -438,7 +438,7 @@ EqualPowerStereoPanner::distribute_automated (AudioBuffer& srcbuf, BufferSet& ob
 
        /* fetch positional data */
 
-       if (!_automation.rt_safe_get_vector (start, end, buffers[0], nframes)) {
+       if (!_automation.curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
                /* fallback */
                if (!_muted) {
                        distribute (srcbuf, obufs, 1.0, nframes);
@@ -565,7 +565,7 @@ EqualPowerStereoPanner::set_state (const XMLNode& node)
 /*----------------------------------------------------------------------*/
 
 Multi2dPanner::Multi2dPanner (Panner& p)
-       : StreamPanner (p), _automation (0.0, 1.0, 0.5) // XXX useless
+       : StreamPanner (p), _automation (ParamID(PanAutomation), 0.0, 1.0, 0.5) // XXX useless
 {
        update ();
 }
index b1cb97a475c2f2dc1bc3313db6b3b73f89d10669..1b93ecf82b81c92eef78bbfe2a75b24fc16ad8b3 100644 (file)
@@ -94,7 +94,7 @@ Plugin::get_nth_control (uint32_t n)
 
                get_parameter_descriptor (n, desc);
        
-               controls[n] = new PortControllable (describe_parameter (n), *this, n, 
+               controls[n] = new PortControllable (describe_parameter (ParamID(PluginAutomation, n)), *this, n, 
                                                    desc.lower, desc.upper, desc.toggled, desc.logarithmic);
        } 
 
index baf22a68639a252e9c737e68ad7aada829a7fbd0..1b0130fc37d78dea98d98ff3567c556ccc29fd65 100644 (file)
@@ -152,18 +152,21 @@ PluginInsert::~PluginInsert ()
 }
 
 void
-PluginInsert::automation_list_creation_callback (uint32_t which, AutomationList& alist)
+PluginInsert::automation_list_creation_callback (ParamID which, AutomationList& alist)
 {
   alist.automation_state_changed.connect (sigc::bind (mem_fun (*this, &PluginInsert::auto_state_changed), (which)));
 }
 
 void
-PluginInsert::auto_state_changed (uint32_t which)
+PluginInsert::auto_state_changed (ParamID which)
 {
-       AutomationList& alist (automation_list (which));
+       if (which.type() != PluginAutomation)
+               return;
+
+       AutomationList* alist = automation_list (which);
 
-       if (alist.automation_state() != Off) {
-               _plugins[0]->set_parameter (which, alist.eval (_session.transport_frame()));
+       if (alist && alist->automation_state() != Off) {
+               _plugins[0]->set_parameter (which.id(), alist->eval (_session.transport_frame()));
        }
 }
 
@@ -210,18 +213,21 @@ PluginInsert::is_generator() const
 void
 PluginInsert::set_automatable ()
 {
-       set<uint32_t> a;
+       set<ParamID> a;
        
        a = _plugins.front()->automatable ();
 
-       for (set<uint32_t>::iterator i = a.begin(); i != a.end(); ++i) {
+       for (set<ParamID>::iterator i = a.begin(); i != a.end(); ++i) {
                can_automate (*i);
        }
 }
 
 void
-PluginInsert::parameter_changed (uint32_t which, float val)
+PluginInsert::parameter_changed (ParamID which, float val)
 {
+       if (which.type() != PluginAutomation)
+               return;
+
        vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin();
 
        /* don't set the first plugin, just all the slaves */
@@ -270,14 +276,14 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off
 
        if (with_auto) {
 
-               map<uint32_t,AutomationList*>::iterator li;
+               map<ParamID,AutomationList*>::iterator li;
                uint32_t n;
                
                for (n = 0, li = _parameter_automation.begin(); li != _parameter_automation.end(); ++li, ++n) {
                        
                        AutomationList& alist (*((*li).second));
 
-                       if (alist.automation_playback()) {
+                       if (alist.param_id().type() == PluginAutomation && alist.automation_playback()) {
                                bool valid;
 
                                float val = alist.rt_safe_eval (now, valid);                            
@@ -301,12 +307,13 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off
 void
 PluginInsert::automation_snapshot (nframes_t now)
 {
-       map<uint32_t,AutomationList*>::iterator li;
+       map<ParamID,AutomationList*>::iterator li;
        
        for (li =_parameter_automation.begin(); li !=_parameter_automation.end(); ++li) {
                
                AutomationList *alist = ((*li).second);
-               if (alist != 0 && alist->automation_write ()) {
+               if (alist != 0 && alist->param_id().type() == PluginAutomation
+                               && alist->automation_write ()) {
                        
                        float val = _plugins[0]->get_parameter ((*li).first);
                        alist->rt_add (now, val);
@@ -318,13 +325,13 @@ PluginInsert::automation_snapshot (nframes_t now)
 void
 PluginInsert::transport_stopped (nframes_t now)
 {
-       map<uint32_t,AutomationList*>::iterator li;
+       map<ParamID,AutomationList*>::iterator li;
 
        for (li =_parameter_automation.begin(); li !=_parameter_automation.end(); ++li) {
                AutomationList& alist (*(li->second));
                alist.reposition_for_rt_add (now);
 
-               if (alist.automation_state() != Off) {
+               if (alist.param_id().type() == PluginAutomation && alist.automation_state() != Off) {
                        _plugins[0]->set_parameter (li->first, alist.eval (now));
                }
        }
@@ -374,14 +381,17 @@ PluginInsert::run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame,
 }
 
 void
-PluginInsert::set_parameter (uint32_t port, float val)
+PluginInsert::set_parameter (ParamID param, float val)
 {
+       if (param.type() != PluginAutomation)
+               return;
+
        /* the others will be set from the event triggered by this */
 
-       _plugins[0]->set_parameter (port, val);
+       _plugins[0]->set_parameter (param.id(), val);
        
-       if (automation_list (port).automation_write()) {
-               automation_list (port).add (_session.audible_frame(), val);
+       if (automation_list (param) && automation_list (param)->automation_write()) {
+               automation_list (param)->add (_session.audible_frame(), val);
        }
 
        _session.set_dirty();
@@ -432,63 +442,18 @@ PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offs
 }      
 
 float
-PluginInsert::default_parameter_value (uint32_t port)
+PluginInsert::default_parameter_value (ParamID param)
 {
+       if (param.type() != PluginAutomation)
+               return 1.0;
+
        if (_plugins.empty()) {
                fatal << _("programming error: ") << X_("PluginInsert::default_parameter_value() called with no plugin")
                      << endmsg;
                /*NOTREACHED*/
        }
 
-       return _plugins[0]->default_value (port);
-}
-       
-void
-PluginInsert::set_port_automation_state (uint32_t port, AutoState s)
-{
-       if (port < _plugins[0]->parameter_count()) {
-               
-               AutomationList& al = automation_list (port);
-
-               if (s != al.automation_state()) {
-                       al.set_automation_state (s);
-                       _session.set_dirty ();
-               }
-       }
-}
-
-AutoState
-PluginInsert::get_port_automation_state (uint32_t port)
-{
-       if (port < _plugins[0]->parameter_count()) {
-               return automation_list (port).automation_state();
-       } else {
-               return Off;
-       }
-}
-
-void
-PluginInsert::protect_automation ()
-{
-       set<uint32_t> automated_params;
-
-       what_has_automation (automated_params);
-
-       for (set<uint32_t>::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
-
-               AutomationList& al = automation_list (*i);
-
-               switch (al.automation_state()) {
-               case Write:
-                       al.set_automation_state (Off);
-                       break;
-               case Touch:
-                       al.set_automation_state (Play);
-                       break;
-               default:
-                       break;
-               }
-       }
+       return _plugins[0]->default_value (param.id());
 }
 
 boost::shared_ptr<Plugin>
@@ -684,16 +649,18 @@ PluginInsert::state (bool full)
 
        /* add port automation state */
        XMLNode *autonode = new XMLNode(port_automation_node_name);
-       set<uint32_t> automatable = _plugins[0]->automatable();
+       set<ParamID> automatable = _plugins[0]->automatable();
        
-       for (set<uint32_t>::iterator x =  automatable.begin(); x != automatable.end(); ++x) {
+       for (set<ParamID>::iterator x = automatable.begin(); x != automatable.end(); ++x) {
                
-               XMLNode* child = new XMLNode("port");
+               /*XMLNode* child = new XMLNode("port");
                snprintf(buf, sizeof(buf), "%" PRIu32, *x);
                child->add_property("number", string(buf));
                
                child->add_child_nocopy (automation_list (*x).state (full));
                autonode->add_child_nocopy (*child);
+               */
+               autonode->add_child_nocopy (automation_list (*x)->state (full));
        }
 
        node.add_child_nocopy (*autonode);
@@ -824,7 +791,7 @@ PluginInsert::set_state(const XMLNode& node)
                        }
 
                        if (!child->children().empty()) {
-                               automation_list (port_id).set_state (*child->children().front());
+                               automation_list (ParamID(PluginAutomation, port_id), true)->set_state (*child->children().front());
                        } else {
                                if ((cprop = child->property("auto")) != 0) {
                                        
@@ -832,13 +799,13 @@ PluginInsert::set_state(const XMLNode& node)
 
                                        int x;
                                        sscanf (cprop->value().c_str(), "0x%x", &x);
-                                       automation_list (port_id).set_automation_state (AutoState (x));
+                                       automation_list (ParamID(PluginAutomation, port_id), true)->set_automation_state (AutoState (x));
 
                                } else {
                                        
                                        /* missing */
                                        
-                                       automation_list (port_id).set_automation_state (Off);
+                                       automation_list (ParamID(PluginAutomation, port_id), true)->set_automation_state (Off);
                                }
                        }
 
@@ -860,9 +827,12 @@ PluginInsert::set_state(const XMLNode& node)
 }
 
 string
-PluginInsert::describe_parameter (uint32_t what)
+PluginInsert::describe_parameter (ParamID param)
 {
-       return _plugins[0]->describe_parameter (what);
+       if (param.type() != PluginAutomation)
+               return Automatable::describe_parameter(param);
+
+       return _plugins[0]->describe_parameter (param);
 }
 
 ARDOUR::nframes_t 
index 883bc0f6aade2e2f4ab95c03b3b487ab887e48b9..3353149efa1d3ea89502ec008890acc688725d17 100644 (file)
@@ -2380,12 +2380,14 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra
        apply_gain_automation = false;
 
        { 
-               Glib::Mutex::Lock am (automation_lock, Glib::TRY_LOCK);
+               Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK);
                
                if (am.locked() && _session.transport_rolling()) {
                        
-                       if (_gain_automation_curve.automation_playback()) {
-                               apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes);
+                       ARDOUR::AutomationList& gain_auto = gain_automation();
+                       
+                       if (gain_auto.automation_playback()) {
+                               apply_gain_automation = gain_auto.curve().rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes);
                        }
                }
        }
@@ -2562,33 +2564,10 @@ Route::set_block_size (nframes_t nframes)
 void
 Route::protect_automation ()
 {
-       switch (gain_automation_state()) {
-       case Write:
-               set_gain_automation_state (Off);
-       case Touch:
-               set_gain_automation_state (Play);
-               break;
-       default:
-               break;
-       }
-
-       switch (panner().automation_state ()) {
-       case Write:
-               panner().set_automation_state (Off);
-               break;
-       case Touch:
-               panner().set_automation_state (Play);
-               break;
-       default:
-               break;
-       }
+       Automatable::protect_automation();
        
-       for (InsertList::iterator i = _inserts.begin(); i != _inserts.end(); ++i) {
-               boost::shared_ptr<PluginInsert> pi;
-               if ((pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
-                       pi->protect_automation ();
-               }
-       }
+       for (InsertList::iterator i = _inserts.begin(); i != _inserts.end(); ++i)
+               (*i)->protect_automation();
 }
 
 void
index aee4d4004c9fbd6e5e4c3be27b1ca609c5d96ee8..37867a2cfaf1003da99241b294cfe79315672d06 100644 (file)
@@ -103,7 +103,7 @@ Send::set_state(const XMLNode& node)
                if ((*niter)->name() == "Redirect") {
                        insert_node = *niter;
                } else if ((*niter)->name() == X_("Automation")) {
-                       _io->set_automation_state (*(*niter));
+                       _io->set_automation_state (*(*niter), ParamID(GainAutomation));
                }
        }
        
index b19285707953d08f03cfe42591e78353021e62b0..16b35397cfd4bcc0996cff57f0ea675873a02ea0 100644 (file)
@@ -1019,7 +1019,7 @@ void MackieControlProtocol::notify_panner_changed( RouteSignal * route_signal )
 // TODO handle plugin automation polling
 void MackieControlProtocol::update_automation( RouteSignal & rs )
 {
-       ARDOUR::AutoState gain_state = rs.route().gain_automation_state();
+       ARDOUR::AutoState gain_state = rs.route().gain_automation().automation_state();
        if ( gain_state == Touch || gain_state == Play )
        {
                notify_gain_changed( &rs );