From d7bd270aa10b3a8669223debe4c1b572ae876e2b Mon Sep 17 00:00:00 2001 From: David Robillard Date: Wed, 27 Jun 2007 15:51:50 +0000 Subject: Big ol' automation refactor. 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 --- gtk2_ardour/SConscript | 2 + gtk2_ardour/audio_region_view.cc | 6 +- gtk2_ardour/audio_time_axis.cc | 264 ++---------- gtk2_ardour/audio_time_axis.h | 20 +- gtk2_ardour/automation_gain_line.cc | 7 +- gtk2_ardour/automation_gain_line.h | 10 +- gtk2_ardour/automation_line.h | 4 +- gtk2_ardour/automation_midi_cc_line.cc | 76 ++++ gtk2_ardour/automation_midi_cc_line.h | 44 ++ gtk2_ardour/automation_pan_line.cc | 7 +- gtk2_ardour/automation_pan_line.h | 7 +- gtk2_ardour/crossfade_edit.cc | 22 +- gtk2_ardour/crossfade_edit.h | 8 +- gtk2_ardour/crossfade_view.cc | 4 +- gtk2_ardour/curvetest.cc | 6 +- gtk2_ardour/editor_canvas_events.cc | 5 + gtk2_ardour/editor_items.h | 2 + gtk2_ardour/editor_mouse.cc | 17 + gtk2_ardour/gain_automation_time_axis.cc | 16 +- gtk2_ardour/gain_automation_time_axis.h | 9 +- gtk2_ardour/gain_meter.cc | 32 +- gtk2_ardour/ladspa_pluginui.cc | 19 +- gtk2_ardour/midi_controller_time_axis.cc | 81 ++++ gtk2_ardour/midi_controller_time_axis.h | 54 +++ gtk2_ardour/midi_time_axis.cc | 85 ++-- gtk2_ardour/midi_time_axis.h | 9 +- gtk2_ardour/mixer_strip.cc | 8 +- gtk2_ardour/pan_automation_time_axis.cc | 5 + gtk2_ardour/plugin_ui.h | 4 + gtk2_ardour/redirect_automation_line.cc | 6 +- gtk2_ardour/redirect_automation_line.h | 6 +- gtk2_ardour/redirect_automation_time_axis.cc | 22 +- gtk2_ardour/redirect_automation_time_axis.h | 5 +- gtk2_ardour/redirect_box.cc | 2 +- gtk2_ardour/region_gain_line.cc | 4 +- gtk2_ardour/region_gain_line.h | 2 +- gtk2_ardour/route_time_axis.cc | 232 +++++++++- gtk2_ardour/route_time_axis.h | 42 +- libs/ardour/ardour/audioregion.h | 22 +- libs/ardour/ardour/automatable.h | 48 ++- libs/ardour/ardour/automation_event.h | 170 ++++---- libs/ardour/ardour/crossfade.h | 8 +- libs/ardour/ardour/curve.h | 36 +- libs/ardour/ardour/gain.h | 2 +- libs/ardour/ardour/io.h | 34 +- libs/ardour/ardour/ladspa_plugin.h | 4 +- libs/ardour/ardour/panner.h | 14 +- libs/ardour/ardour/param_id.h | 137 ++++++ libs/ardour/ardour/plugin.h | 7 +- libs/ardour/ardour/plugin_insert.h | 16 +- libs/ardour/ardour/types.h | 10 +- libs/ardour/audio_track.cc | 10 +- libs/ardour/audioregion.cc | 44 +- libs/ardour/automatable.cc | 242 ++++++++--- libs/ardour/automation_event.cc | 536 ++++++++++++------------ libs/ardour/crossfade.cc | 16 +- libs/ardour/curve.cc | 126 +++--- libs/ardour/enums.cc | 4 + libs/ardour/gain.cc | 6 +- libs/ardour/insert.cc | 7 +- libs/ardour/io.cc | 136 +++--- libs/ardour/ladspa_plugin.cc | 14 +- libs/ardour/panner.cc | 6 +- libs/ardour/plugin.cc | 2 +- libs/ardour/plugin_insert.cc | 122 ++---- libs/ardour/route.cc | 37 +- libs/ardour/send.cc | 2 +- libs/surfaces/mackie/mackie_control_protocol.cc | 2 +- 68 files changed, 1764 insertions(+), 1210 deletions(-) create mode 100644 gtk2_ardour/automation_midi_cc_line.cc create mode 100644 gtk2_ardour/automation_midi_cc_line.h create mode 100644 gtk2_ardour/midi_controller_time_axis.cc create mode 100644 gtk2_ardour/midi_controller_time_axis.h create mode 100644 libs/ardour/ardour/param_id.h diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index c5e8f10856..cb5cc41ae2 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -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 diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index 991f15075a..058295949f 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -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(audio_region()->envelope(), &before, &after)); + trackview.session().add_command (new MementoCommand(audio_region()->envelope(), &before, &after)); trackview.session().commit_reversible_command (); } diff --git a/gtk2_ardour/audio_time_axis.cc b/gtk2_ardour/audio_time_axis.cc index f1160e26ec..b259d8723d 100644 --- a/gtk2_ardour/audio_time_axis.cc +++ b/gtk2_ardour/audio_time_axis.cc @@ -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 (&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 (&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) { diff --git a/gtk2_ardour/audio_time_axis.h b/gtk2_ardour/audio_time_axis.h index ab5ef955c7..a2f331cf6a 100644 --- a/gtk2_ardour/audio_time_axis.h +++ b/gtk2_ardour/audio_time_axis.h @@ -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; diff --git a/gtk2_ardour/automation_gain_line.cc b/gtk2_ardour/automation_gain_line.cc index 006543df18..84488dc2b5 100644 --- a/gtk2_ardour/automation_gain_line.cc +++ b/gtk2_ardour/automation_gain_line.cc @@ -26,16 +26,13 @@ #include "automation_gain_line.h" #include "utils.h" -#include - 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); } diff --git a/gtk2_ardour/automation_gain_line.h b/gtk2_ardour/automation_gain_line.h index ca90216c8f..fe8c9274f2 100644 --- a/gtk2_ardour/automation_gain_line.h +++ b/gtk2_ardour/automation_gain_line.h @@ -25,23 +25,15 @@ #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; - }; diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index 2637a0c554..41034dbf6e 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -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 index 0000000000..4a074ddbe9 --- /dev/null +++ b/gtk2_ardour/automation_midi_cc_line.cc @@ -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 + +#include + +#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 index 0000000000..6ac078a911 --- /dev/null +++ b/gtk2_ardour/automation_midi_cc_line.h @@ -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 + +#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__ */ + + diff --git a/gtk2_ardour/automation_pan_line.cc b/gtk2_ardour/automation_pan_line.cc index 04cfb0a311..0e36f4ef88 100644 --- a/gtk2_ardour/automation_pan_line.cc +++ b/gtk2_ardour/automation_pan_line.cc @@ -27,15 +27,12 @@ #include "utils.h" #include -#include - 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) { } diff --git a/gtk2_ardour/automation_pan_line.h b/gtk2_ardour/automation_pan_line.h index c6b32cbc92..6374c535e0 100644 --- a/gtk2_ardour/automation_pan_line.h +++ b/gtk2_ardour/automation_pan_line.h @@ -25,22 +25,17 @@ #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 lines; }; diff --git a/gtk2_ardour/crossfade_edit.cc b/gtk2_ardour/crossfade_edit.cc index 349297f8cd..fd677ac4a6 100644 --- a/gtk2_ardour/crossfade_edit.cc +++ b/gtk2_ardour/crossfade_edit.cc @@ -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::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 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 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 diff --git a/gtk2_ardour/crossfade_edit.h b/gtk2_ardour/crossfade_edit.h index 715aa1a360..e9f2dcf762 100644 --- a/gtk2_ardour/crossfade_edit.h +++ b/gtk2_ardour/crossfade_edit.h @@ -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 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 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; diff --git a/gtk2_ardour/crossfade_view.cc b/gtk2_ardour/crossfade_view.cc index d2fdd94e03..cf19137d98 100644 --- a/gtk2_ardour/crossfade_view.cc +++ b/gtk2_ardour/crossfade_view.cc @@ -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); diff --git a/gtk2_ardour/curvetest.cc b/gtk2_ardour/curvetest.cc index 63804bd1b4..71f0600f32 100644 --- a/gtk2_ardour/curvetest.cc +++ b/gtk2_ardour/curvetest.cc @@ -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; diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index 40bef304c5..e1ed2d2fa1 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -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 (&cp->line) != 0) { type = RedirectAutomationControlPointItem; + } else if (dynamic_cast (&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 (al) != 0) { type = RedirectAutomationLineItem; + } else if (dynamic_cast (al) != 0) { + type = MidiCCAutomationLineItem; } else { return false; } diff --git a/gtk2_ardour/editor_items.h b/gtk2_ardour/editor_items.h index ad1d63b5bf..ddba43350a 100644 --- a/gtk2_ardour/editor_items.h +++ b/gtk2_ardour/editor_items.h @@ -38,6 +38,8 @@ enum ItemType { PanAutomationLineItem, RedirectAutomationControlPointItem, RedirectAutomationLineItem, + MidiCCAutomationControlPointItem, + MidiCCAutomationLineItem, MeterMarkerItem, TempoMarkerItem, MeterBarItem, diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 2f671c77eb..773c26f67d 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -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(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(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 (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: diff --git a/gtk2_ardour/gain_automation_time_axis.cc b/gtk2_ardour/gain_automation_time_axis.cc index 9890854d4b..509c941555 100644 --- a/gtk2_ardour/gain_automation_time_axis.cc +++ b/gtk2_ardour/gain_automation_time_axis.cc @@ -17,7 +17,7 @@ */ -#include +#include #include #include @@ -33,11 +33,11 @@ using namespace Gtk; GainAutomationTimeAxisView::GainAutomationTimeAxisView (Session& s, boost::shared_ptr 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(curve, &before, &after)); + XMLNode& before = list.get_state(); + list.add (when, y); + XMLNode& after = list.get_state(); + _session.commit_reversible_command (new MementoCommand(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); } } diff --git a/gtk2_ardour/gain_automation_time_axis.h b/gtk2_ardour/gain_automation_time_axis.h index 25a97a1a97..3f07be4ace 100644 --- a/gtk2_ardour/gain_automation_time_axis.h +++ b/gtk2_ardour/gain_automation_time_axis.h @@ -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); }; diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc index f67682019e..a4eea61bfa 100644 --- a/gtk2_ardour/gain_meter.cc +++ b/gtk2_ardour/gain_meter.cc @@ -166,13 +166,17 @@ GainMeter::GainMeter (boost::shared_ptr 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, 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(gain_slider)->set_sensitive (!(_io->gain_automation_state() & Play)); + static_cast(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(&_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; diff --git a/gtk2_ardour/ladspa_pluginui.cc b/gtk2_ardour/ladspa_pluginui.cc index cd585d073a..25c497a377 100644 --- a/gtk2_ardour/ladspa_pluginui.cc +++ b/gtk2_ardour/ladspa_pluginui.cc @@ -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 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 index 0000000000..a3dcb0f508 --- /dev/null +++ b/gtk2_ardour/midi_controller_time_axis.cc @@ -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 +#include +#include + +#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 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(_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 index 0000000000..78f3c12c1a --- /dev/null +++ b/gtk2_ardour/midi_controller_time_axis.h @@ -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, + 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__ */ diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index dd008bd9c5..960ca36af9 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -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); -} - diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index 17b3d72794..3b06411418 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -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 *); diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 0c99dc7401..cb27ef4e88 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -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); diff --git a/gtk2_ardour/pan_automation_time_axis.cc b/gtk2_ardour/pan_automation_time_axis.cc index c008a10ff0..1199982b5d 100644 --- a/gtk2_ardour/pan_automation_time_axis.cc +++ b/gtk2_ardour/pan_automation_time_axis.cc @@ -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; } diff --git a/gtk2_ardour/plugin_ui.h b/gtk2_ardour/plugin_ui.h index a505f80576..e583f11dce 100644 --- a/gtk2_ardour/plugin_ui.h +++ b/gtk2_ardour/plugin_ui.h @@ -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; diff --git a/gtk2_ardour/redirect_automation_line.cc b/gtk2_ardour/redirect_automation_line.cc index 89b6fb4f2a..5680a8b98f 100644 --- a/gtk2_ardour/redirect_automation_line.cc +++ b/gtk2_ardour/redirect_automation_line.cc @@ -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; diff --git a/gtk2_ardour/redirect_automation_line.h b/gtk2_ardour/redirect_automation_line.h index 33d411e48e..d0e3aa8897 100644 --- a/gtk2_ardour/redirect_automation_line.h +++ b/gtk2_ardour/redirect_automation_line.h @@ -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; diff --git a/gtk2_ardour/redirect_automation_time_axis.cc b/gtk2_ardour/redirect_automation_time_axis.cc index 9df0bb9e0c..bb7937cdf7 100644 --- a/gtk2_ardour/redirect_automation_time_axis.cc +++ b/gtk2_ardour/redirect_automation_time_axis.cc @@ -34,12 +34,12 @@ using namespace Gtk; RedirectAutomationTimeAxisView::RedirectAutomationTimeAxisView (Session& s, boost::shared_ptr 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(alist, &before, &after)); + XMLNode &before = alist->get_state(); + alist->add (when, y); + XMLNode &after = alist->get_state(); + _session.add_command(new MementoCommand(*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); } } diff --git a/gtk2_ardour/redirect_automation_time_axis.h b/gtk2_ardour/redirect_automation_time_axis.h index eceed7446a..d87e4faca5 100644 --- a/gtk2_ardour/redirect_automation_time_axis.h +++ b/gtk2_ardour/redirect_automation_time_axis.h @@ -24,6 +24,7 @@ #include "canvas.h" #include "automation_time_axis.h" +#include 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(); diff --git a/gtk2_ardour/redirect_box.cc b/gtk2_ardour/redirect_box.cc index 8749ab12db..0402b0e1f6 100644 --- a/gtk2_ardour/redirect_box.cc +++ b/gtk2_ardour/redirect_box.cc @@ -1050,7 +1050,7 @@ RedirectBox::edit_insert (boost::shared_ptr insert) } } - if ((send = boost::dynamic_pointer_cast (send)) == 0) { + if ((send = boost::dynamic_pointer_cast (send)) != 0) { if (!_session.engine().connected()) { return; diff --git a/gtk2_ardour/region_gain_line.cc b/gtk2_ardour/region_gain_line.cc index e935d3adc2..25ba903ca8 100644 --- a/gtk2_ardour/region_gain_line.cc +++ b/gtk2_ardour/region_gain_line.cc @@ -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) { diff --git a/gtk2_ardour/region_gain_line.h b/gtk2_ardour/region_gain_line.h index 801fe09bad..259615aa39 100644 --- a/gtk2_ardour/region_gain_line.h +++ b/gtk2_ardour/region_gain_line.h @@ -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&); diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 900064a763..f113724cf5 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -50,6 +51,7 @@ #include #include #include +#include #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 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::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(&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& 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::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::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::iterator i = insert_automation.begin(); i != insert_automation.end(); ++i) { for (vector::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::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::iterator i = insert_automation.begin(); i != insert_automation.end(); ++i) { for (vector::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, uint32_t what) +RouteTimeAxisView::find_insert_automation_node (boost::shared_ptr insert, ParamID what) { for (list::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, uint32_t what) +RouteTimeAxisView::add_insert_automation_curve (boost::shared_ptr insert, ParamID what) { RedirectAutomationLine* ral; string name; @@ -1538,13 +1717,13 @@ RouteTimeAxisView::add_insert_automation_curve (boost::shared_ptr 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) { - set s; + set s; RedirectAutomationLine *ral; insert->what_has_visible_automation (s); - for (set::iterator i = s.begin(); i != s.end(); ++i) { + for (set::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_ptrHiding.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) { @@ -1605,8 +1817,8 @@ RouteTimeAxisView::add_insert_to_subplugin_menu (boost::shared_ptr inser InsertAutomationInfo *rai; list::iterator x; - const std::set& automatable = insert->what_can_be_automated (); - std::set has_visible_automation; + const std::set& automatable = insert->what_can_be_automated (); + std::set has_visible_automation; insert->what_has_visible_automation(has_visible_automation); @@ -1641,7 +1853,7 @@ RouteTimeAxisView::add_insert_to_subplugin_menu (boost::shared_ptr inser items.clear (); - for (std::set::const_iterator i = automatable.begin(); i != automatable.end(); ++i) { + for (std::set::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, uint32_t what) +RouteTimeAxisView::find_insert_automation_curve (boost::shared_ptr insert, ParamID what) { InsertAutomationNode* ran; diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h index e14d682c83..42c889e16f 100644 --- a/gtk2_ardour/route_time_axis.h +++ b/gtk2_ardour/route_time_axis.h @@ -79,6 +79,7 @@ public: void set_selected_regionviews (RegionSelection&); void get_selectables (nframes_t start, nframes_t end, double top, double bot, list&); void get_inverted_selectables (Selection&, list&); + bool show_automation(ARDOUR::ParamID param); boost::shared_ptr 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); + + 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 i, uint32_t); + find_insert_automation_node (boost::shared_ptr i, ARDOUR::ParamID); RedirectAutomationLine* - find_insert_automation_curve (boost::shared_ptr i, uint32_t); + find_insert_automation_curve (boost::shared_ptr i, ARDOUR::ParamID); - void add_insert_automation_curve (boost::shared_ptr r, uint32_t); + void add_insert_automation_curve (boost::shared_ptr r, ARDOUR::ParamID); void add_existing_insert_automation_curves (boost::shared_ptr); + + 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 insert_automation; + list insert_automation; vector insert_automation_curves; + + // Set from XML so context menu automation buttons can be correctly initialized + set _show_automation; + + map _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__ */ diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index f844d45ef1..db763876b0 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -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 */ diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index c6621b9780..f6d6d86ed0 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -24,6 +24,7 @@ #include #include #include +#include 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&) const; - void what_has_visible_automation(std::set&) const; - const std::set& what_can_be_automated() const { return _can_automate_list; } + void what_has_automation(std::set&) const; + void what_has_visible_automation(std::set&) const; + const std::set& 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 _parameter_automation; - std::set _visible_parameter_automation; - std::set _can_automate_list; + std::map _parameter_automation; + std::set _visible_parameter_automation; + std::set _can_automate_list; nframes_t _last_automation_snapshot; }; diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index f04dcdc112..af1a3cb704 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -32,35 +32,39 @@ #include #include +#include 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 AutomationEventList; - typedef AutomationEventList::iterator iterator; - typedef AutomationEventList::const_iterator const_iterator; + typedef std::list 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 automation_style_changed; void set_automation_style (AutoStyle m); - AutoStyle automation_style() const { return _style; } + AutoStyle automation_style() const { return _style; } sigc::signal 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 control_points_adjacent (double when); template 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 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 range; + std::pair 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 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 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 diff --git a/libs/ardour/ardour/crossfade.h b/libs/ardour/ardour/crossfade.h index 61a30f1c0f..78a137bde3 100644 --- a/libs/ardour/ardour/crossfade.h +++ b/libs/ardour/ardour/crossfade.h @@ -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; diff --git a/libs/ardour/ardour/curve.h b/libs/ardour/ardour/curve.h index 605eda2e4b..b96bb5c78e 100644 --- a/libs/ardour/ardour/curve.h +++ b/libs/ardour/ardour/curve.h @@ -30,50 +30,30 @@ 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 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 diff --git a/libs/ardour/ardour/gain.h b/libs/ardour/ardour/gain.h index 5832f71101..e57cfdc0d7 100644 --- a/libs/ardour/ardour/gain.h +++ b/libs/ardour/ardour/gain.h @@ -25,7 +25,7 @@ namespace ARDOUR { -struct Gain : public Curve { +struct Gain : public AutomationList { Gain(); Gain (const Gain&); diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 3952d14c0e..1592ac7cac 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include #include @@ -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 gain_automation_state_changed; - - virtual void set_gain_automation_style (AutoStyle); - AutoStyle gain_automation_style () const { return _gain_automation_curve.automation_style(); } - //sigc::signal 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 */ diff --git a/libs/ardour/ardour/ladspa_plugin.h b/libs/ardour/ardour/ladspa_plugin.h index f1f1bb8811..5c15632391 100644 --- a/libs/ardour/ardour/ladspa_plugin.h +++ b/libs/ardour/ardour/ladspa_plugin.h @@ -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 automatable() const; + std::set 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; diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 0e5ab68525..af3cda94e2 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -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 Changed; /* for position */ sigc::signal 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 index 0000000000..eb5563b06d --- /dev/null +++ b/libs/ardour/ardour/param_id.h @@ -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 +#include +#include +#include + +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. + */ + 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__ + diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index b1a2823b33..22c0862202 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -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 automatable() const = 0; + virtual std::set 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 ParameterChanged; + sigc::signal ParameterChanged; PBD::Controllable *get_nth_control (uint32_t); diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index 4acacae7f5..df0460af84 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -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(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 > _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; diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index d5f10410db..b2f13def81 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -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 { diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index b08990c2ab..151b434c6b 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -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(); diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 55a1879726..a7a5fca912 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -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 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 afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -98,9 +98,9 @@ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, n /* Basic AudioRegion constructor (one channel) */ AudioRegion::AudioRegion (boost::shared_ptr 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 afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -113,9 +113,9 @@ AudioRegion::AudioRegion (boost::shared_ptr 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 other) AudioRegion::AudioRegion (boost::shared_ptr 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 afs = boost::dynamic_pointer_cast (src); if (afs) { @@ -200,10 +200,10 @@ AudioRegion::AudioRegion (boost::shared_ptr 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) { diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index 2ce553a188..40ab7af769 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -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 tosave; + set 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& s) const +Automatable::what_has_automation (set& s) const { Glib::Mutex::Lock lm (_automation_lock); - map::const_iterator li; + map::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& s) const } void -Automatable::what_has_visible_automation (set& s) const +Automatable::what_has_visible_automation (set& s) const { Glib::Mutex::Lock lm (_automation_lock); - set::const_iterator li; + set::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::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::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::iterator i; + set::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::const_iterator li; + map::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, ¶m) != 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, ¶m) != 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::iterator li; + map::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::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 automated_params; + + what_has_automation (automated_params); + + for (set::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; + } + } +} + diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc index 50688dda2c..82d3e457cb 100644 --- a/libs/ardour/automation_event.cc +++ b/libs/ardour/automation_event.cc @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include #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 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::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 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::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::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::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 range; + pair 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 range; + pair 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) { diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 847741832d..e5770507d5 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -78,8 +78,8 @@ Crossfade::Crossfade (boost::shared_ptr in, boost::shared_ptr