diff options
Diffstat (limited to 'libs')
30 files changed, 1011 insertions, 809 deletions
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 <map> #include <ardour/session_object.h> #include <ardour/automation_event.h> +#include <ardour/param_id.h> namespace ARDOUR { @@ -36,28 +37,46 @@ public: virtual ~Automatable() {} - virtual AutomationList& automation_list(uint32_t n); + // shorthand for gain, pan, etc + inline AutomationList* automation_list(AutomationType type, bool create_if_missing=false) { + return automation_list(ParamID(type), create_if_missing); + } - virtual void automation_snapshot (nframes_t now) {}; + virtual AutomationList* automation_list(ParamID id, bool create_if_missing=false); + virtual const AutomationList* automation_list(ParamID id) const; + + virtual void add_automation_parameter(AutomationList* al); + + virtual void automation_snapshot(nframes_t now) {}; virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const; - virtual string describe_parameter(uint32_t which); - virtual float default_parameter_value(uint32_t which) { return 1.0f; } + virtual string describe_parameter(ParamID param); + virtual float default_parameter_value(ParamID param) { return 1.0f; } + + virtual void clear_automation(); + + AutoState get_parameter_automation_state (ParamID param); + virtual void set_parameter_automation_state (ParamID param, AutoState); + + AutoStyle get_parameter_automation_style (ParamID param); + void set_parameter_automation_style (ParamID param, AutoStyle); + + void protect_automation (); - void what_has_automation(std::set<uint32_t>&) const; - void what_has_visible_automation(std::set<uint32_t>&) const; - const std::set<uint32_t>& what_can_be_automated() const { return _can_automate_list; } + void what_has_automation(std::set<ParamID>&) const; + void what_has_visible_automation(std::set<ParamID>&) const; + const std::set<ParamID>& what_can_be_automated() const { return _can_automate_list; } - void mark_automation_visible(uint32_t, bool); + void mark_automation_visible(ParamID, bool); protected: - void can_automate(uint32_t); + void can_automate(ParamID); - virtual void automation_list_creation_callback(uint32_t, AutomationList&) {} + virtual void automation_list_creation_callback(ParamID, AutomationList&) {} - int set_automation_state(const XMLNode&); + int set_automation_state(const XMLNode&, ParamID default_param); XMLNode& get_automation_state(); int load_automation (const std::string& path); @@ -65,10 +84,9 @@ protected: mutable Glib::Mutex _automation_lock; - // FIXME: map with int keys is a bit silly. this could be O(1) - std::map<uint32_t,AutomationList*> _parameter_automation; - std::set<uint32_t> _visible_parameter_automation; - std::set<uint32_t> _can_automate_list; + std::map<ParamID,AutomationList*> _parameter_automation; + std::set<ParamID> _visible_parameter_automation; + std::set<ParamID> _can_automate_list; nframes_t _last_automation_snapshot; }; 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 <pbd/statefuldestructible.h> #include <ardour/ardour.h> +#include <ardour/param_id.h> namespace ARDOUR { - + +class Curve; + struct ControlEvent { - double when; - double value; - + ControlEvent (double w, double v) - : when (w), value (v) { } - ControlEvent (const ControlEvent& other) - : when (other.when), value (other.value) {} + : when (w), value (v) { + coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0; + } - virtual ~ControlEvent() {} + ControlEvent (const ControlEvent& other) + : when (other.when), value (other.value) { + coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0; + } -// bool operator==(const ControlEvent& other) { -// return value == other.value && when == other.when; -// } - + double when; + double value; + double coeff[4]; ///< Used by Curve }; + class AutomationList : public PBD::StatefulDestructible { public: - typedef std::list<ControlEvent*> AutomationEventList; - typedef AutomationEventList::iterator iterator; - typedef AutomationEventList::const_iterator const_iterator; + typedef std::list<ControlEvent*> EventList; + typedef EventList::iterator iterator; + typedef EventList::const_iterator const_iterator; - AutomationList (double default_value); - AutomationList (const XMLNode&); + AutomationList (ParamID id, double min_val, double max_val, double default_val); + AutomationList (const XMLNode&, ParamID id); ~AutomationList(); AutomationList (const AutomationList&); @@ -68,14 +72,17 @@ class AutomationList : public PBD::StatefulDestructible AutomationList& operator= (const AutomationList&); bool operator== (const AutomationList&); + ParamID param_id() const { return _param_id; } + void set_param_id(ParamID id) { _param_id = id; } + void freeze(); void thaw (); - AutomationEventList::size_type size() const { return events.size(); } - bool empty() const { return events.empty(); } + EventList::size_type size() const { return _events.size(); } + bool empty() const { return _events.empty(); } void reset_default (double val) { - default_value = val; + _default_value = val; } void clear (); @@ -111,7 +118,7 @@ class AutomationList : public PBD::StatefulDestructible sigc::signal<void> automation_style_changed; void set_automation_style (AutoStyle m); - AutoStyle automation_style() const { return _style; } + AutoStyle automation_style() const { return _style; } sigc::signal<void> automation_state_changed; bool automation_playback() const { @@ -126,29 +133,29 @@ class AutomationList : public PBD::StatefulDestructible bool touching() const { return _touching; } void set_yrange (double min, double max) { - min_yval = min; - max_yval = max; + _min_yval = min; + _max_yval = max; } - double get_max_y() const { return max_yval; } - double get_min_y() const { return min_yval; } + double get_max_y() const { return _max_yval; } + double get_min_y() const { return _min_yval; } void truncate_end (double length); void truncate_start (double length); - iterator begin() { return events.begin(); } - iterator end() { return events.end(); } + iterator begin() { return _events.begin(); } + iterator end() { return _events.end(); } - ControlEvent* back() { return events.back(); } - ControlEvent* front() { return events.front(); } + ControlEvent* back() { return _events.back(); } + ControlEvent* front() { return _events.front(); } - const_iterator const_begin() const { return events.begin(); } - const_iterator const_end() const { return events.end(); } + const_iterator const_begin() const { return _events.begin(); } + const_iterator const_end() const { return _events.end(); } std::pair<AutomationList::iterator,AutomationList::iterator> control_points_adjacent (double when); template<class T> void apply_to_points (T& obj, void (T::*method)(const AutomationList&)) { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); (obj.*method)(*this); } @@ -160,16 +167,16 @@ class AutomationList : public PBD::StatefulDestructible XMLNode& serialize_events (); void set_max_xval (double); - double get_max_xval() const { return max_xval; } + double get_max_xval() const { return _max_xval; } double eval (double where) { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); return unlocked_eval (where); } double rt_safe_eval (double where, bool& ok) { - Glib::Mutex::Lock lm (lock, Glib::TRY_LOCK); + Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK); if ((ok = lm.locked())) { return unlocked_eval (where); @@ -183,65 +190,66 @@ class AutomationList : public PBD::StatefulDestructible return a->when < b->when; } }; - - static sigc::signal<void, AutomationList*> AutomationListCreated; - - protected: - - AutomationEventList events; - mutable Glib::Mutex lock; - int8_t _frozen; - bool changed_when_thawed; - bool _dirty; - + struct LookupCache { double left; /* leftmost x coordinate used when finding "range" */ - std::pair<AutomationList::iterator,AutomationList::iterator> range; + std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range; }; - mutable LookupCache lookup_cache; - - AutoState _state; - AutoStyle _style; - bool _touching; - bool _new_touch; - double max_xval; - double min_yval; - double max_yval; - double default_value; - bool sort_pending; - - iterator rt_insertion_point; - double rt_pos; - - void maybe_signal_changed (); - void mark_dirty (); - void _x_scale (double factor); - - /* called by type-specific unlocked_eval() to handle - common case of 0, 1 or 2 control points. - */ + static sigc::signal<void, AutomationList*> AutomationListCreated; - double shared_eval (double x); + const EventList& events() const { return _events; } + double default_value() const { return _default_value; } - /* called by shared_eval() to handle any case of - 3 or more control points. - */ - - virtual double multipoint_eval (double x); + // teeny const violations for Curve + mutable sigc::signal<void> Dirty; + Glib::Mutex& lock() const { return _lock; } + LookupCache& lookup_cache() const { return _lookup_cache; } + + /** Called by locked entry point and various private + * locations where we already hold the lock. + * + * FIXME: Should this be private? Curve needs it.. + */ + double unlocked_eval (double x) const; - /* called by locked entry point and various private - locations where we already hold the lock. - */ + Curve& curve() { return *_curve; } + const Curve& curve() const { return *_curve; } - virtual double unlocked_eval (double where); + protected: - virtual ControlEvent* point_factory (double,double) const; - virtual ControlEvent* point_factory (const ControlEvent&) const; + /** Called by unlocked_eval() to handle cases of 3 or more control points. + */ + virtual double multipoint_eval (double x) const; AutomationList* cut_copy_clear (double, double, int op); int deserialize_events (const XMLNode&); + + void maybe_signal_changed (); + void mark_dirty (); + void _x_scale (double factor); + + mutable LookupCache _lookup_cache; + + ParamID _param_id; + EventList _events; + mutable Glib::Mutex _lock; + int8_t _frozen; + bool _changed_when_thawed; + AutoState _state; + AutoStyle _style; + bool _touching; + bool _new_touch; + double _max_xval; + double _min_yval; + double _max_yval; + double _default_value; + bool _sort_pending; + iterator _rt_insertion_point; + double _rt_pos; + + Curve* _curve; }; } // namespace 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<void, Curve*> CurveCreated; - - protected: - ControlEvent* point_factory (double,double) const; - ControlEvent* point_factory (const ControlEvent&) const; - private: - AutomationList::iterator last_bound; - double unlocked_eval (double where); double multipoint_eval (double x); void _get_vector (double x0, double x1, float *arg, int32_t veclen); + const AutomationList& _list; + + void on_list_dirty() { _dirty = true; } + bool _dirty; }; } // namespace ARDOUR 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 <pbd/controllable.h> #include <ardour/ardour.h> -#include <ardour/session_object.h> +#include <ardour/automatable.h> #include <ardour/utils.h> #include <ardour/curve.h> #include <ardour/types.h> @@ -64,9 +64,8 @@ class BufferSet; * An IO can contain ports of varying types, making routes/inserts/etc with * varied combinations of types (eg MIDI and audio) possible. */ -class IO : public SessionObject +class IO : public Automatable { - public: static const string state_node_name; @@ -227,22 +226,15 @@ class IO : public SessionObject } void clear_automation (); - - virtual void set_gain_automation_state (AutoState); - AutoState gain_automation_state() const { return _gain_automation_curve.automation_state(); } - //sigc::signal<void> gain_automation_state_changed; - - virtual void set_gain_automation_style (AutoStyle); - AutoStyle gain_automation_style () const { return _gain_automation_curve.automation_style(); } - //sigc::signal<void> gain_automation_style_changed; + + void set_parameter_automation_state (ParamID, AutoState); virtual void transport_stopped (nframes_t now); // interface: matches Insert void automation_snapshot (nframes_t now); // interface: matches Automatable - ARDOUR::Curve& gain_automation_curve () { return _gain_automation_curve; } - - void start_gain_touch (); - void end_gain_touch (); + // FIXME: these will probably become unsafe in the near future + ARDOUR::AutomationList& gain_automation() { return *automation_list(GainAutomation); } + const ARDOUR::AutomationList& gain_automation() const { return *automation_list(GainAutomation); } void start_pan_touch (uint32_t which); void end_pan_touch (uint32_t which); @@ -299,16 +291,12 @@ class IO : public SessionObject nframes_t last_automation_snapshot; static nframes_t _automation_interval; - AutoState _gain_automation_state; - AutoStyle _gain_automation_style; + /*AutoState _gain_automation_state; + AutoStyle _gain_automation_style;*/ - bool apply_gain_automation; - Curve _gain_automation_curve; + bool apply_gain_automation; + //Curve _gain_automation_curve; - Glib::Mutex automation_lock; - - virtual int set_automation_state (const XMLNode&); - virtual XMLNode& get_automation_state (); virtual int load_automation (std::string path); /* AudioTrack::deprecated_use_diskstream_connections() needs these */ 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<uint32_t> automatable() const; + std::set<ParamID> automatable() const; uint32_t nth_parameter (uint32_t port, bool& ok) const; void activate () { if (descriptor->activate) { @@ -85,7 +85,7 @@ class LadspaPlugin : public ARDOUR::Plugin int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset); void store_state (ARDOUR::PluginState&); void restore_state (ARDOUR::PluginState&); - string describe_parameter (uint32_t); + string describe_parameter (ParamID); string state_node_name() const { return "ladspa"; } void print_parameter (uint32_t, char*, uint32_t len) const; 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<void> Changed; /* for position */ sigc::signal<void> StateChanged; /* for mute */ @@ -149,7 +150,8 @@ class BaseStereoPanner : public StreamPanner void set_automation_state (AutoState); void set_automation_style (AutoStyle); - Curve& automation() { return _automation; } + /* TODO: StreamPanner is-a Automatable? */ + AutomationList& automation() { return _automation; } /* old school automation loading */ @@ -163,7 +165,7 @@ class BaseStereoPanner : public StreamPanner float left_interp; float right_interp; - Curve _automation; + AutomationList _automation; }; class EqualPowerStereoPanner : public BaseStereoPanner @@ -203,8 +205,10 @@ class Multi2dPanner : public StreamPanner /* XXX this is wrong. for multi-dimensional panners, there must surely be more than 1 automation curve. */ + + /* TODO: StreamPanner is-a Automatable? */ - Curve& automation() { return _automation; } + AutomationList& automation() { return _automation; } void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes); void distribute_automated (AudioBuffer& src, BufferSet& obufs, @@ -222,7 +226,7 @@ class Multi2dPanner : public StreamPanner int load (istream&, string path, uint32_t&); private: - Curve _automation; + AutomationList _automation; void update (); }; diff --git a/libs/ardour/ardour/param_id.h b/libs/ardour/ardour/param_id.h new file mode 100644 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 <string> +#include <pbd/compose.h> +#include <pbd/error.h> +#include <ardour/types.h> + +namespace ARDOUR { + + +/** ID of an automatable parameter. + * + * A given automatable object has a number of automatable parameters. This is + * the unique ID for those parameters. Anything automatable (AutomationList, + * Curve) must have an ID unique with respect to it's Automatable parent. + * + * A parameter ID has two parts, a type and an int (only used by some types). + * + * This is a bit more ugly than it could be, due to using the existing/legacy + * ARDOUR::AutomationType: GainAutomation, PanAutomation, SoloAutomation, + * and MuteAutomation use only the type(), but PluginAutomation and + * MidiCCAutomation use the id() as port number and CC number, respectively. + * + * Future types may use a string or URI or whatever, as long as these are + * comparable anything may be added. ints are best as these should be fast to + * copy and compare with one another. + */ +class ParamID +{ +public: + inline ParamID(AutomationType type = NullAutomation, uint32_t id=0) : _type(type), _id(id) {} + + /** Construct an ParamID from a string returned from ParamID::to_string + * (AutomationList automation-id property) + */ + ParamID(const std::string& str) : _type(NullAutomation), _id(0) { + if (str == "gain") { + _type = GainAutomation; + } else if (str == "pan") { + _type = PanAutomation; + } else if (str == "solo") { + _type = SoloAutomation; + } else if (str == "mute") { + _type = MuteAutomation; + } else if (str == "fadein") { + _type = FadeInAutomation; + } else if (str == "fadeout") { + _type = FadeOutAutomation; + } else if (str == "envelope") { + _type = EnvelopeAutomation; + } else if (str.length() > 10 && str.substr(0, 10) == "parameter-") { + _type = PluginAutomation; + _id = atoi(str.c_str()+10); + PBD::info << "Parameter: " << str << " -> " << _id << endl; + } else if (str.length() > 7 && str.substr(0, 7) == "midicc-") { + _type = MidiCCAutomation; + _id = atoi(str.c_str()+7); + PBD::info << "MIDI CC: " << str << " -> " << _id << endl; + } else { + PBD::warning << "Unknown ParamID '" << str << "'" << endmsg; + } + } + + inline AutomationType type() const { return _type; } + inline uint32_t id() const { return _id; } + + inline bool operator==(const ParamID& id) const + { return (_type == id._type && _id == id._id); } + + /** Arbitrary but fixed ordering, so we're comparable (usable in std::map) */ + inline bool operator<(const ParamID& id) const { + // FIXME: branch a performance problem? #ifdef DEBUG? + if (_type == NullAutomation) + PBD::warning << "Uninitialized ParamID compared." << endmsg; + return (_type < id._type || _id < id._id); + } + + inline operator bool() const { return (_type != 0); } + + /** Unique string representation, suitable as an XML property value. + * e.g. <AutomationList automation-id="whatthisreturns"> + */ + inline std::string to_string() const { + if (_type == GainAutomation) { + return "gain"; + } else if (_type == PanAutomation) { + return "pan"; + } else if (_type == SoloAutomation) { + return "solo"; + } else if (_type == MuteAutomation) { + return "mute"; + } else if (_type == FadeInAutomation) { + return "fadein"; + } else if (_type == FadeOutAutomation) { + return "fadeout"; + } else if (_type == EnvelopeAutomation) { + return "envelope"; + } else if (_type == PluginAutomation) { + return string_compose("parameter-%1", _id); + } else if (_type == MidiCCAutomation) { + return string_compose("midicc-%1", _id); + } else { + PBD::warning << "Uninitialized ParamID to_string() called." << endmsg; + return ""; + } + } + +private: + // default copy constructor is ok + AutomationType _type; + uint32_t _id; +}; + + +} // namespace ARDOUR + +#endif // __ardour_param_id_h__ + 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 <ardour/chan_count.h> #include <ardour/plugin_state.h> #include <ardour/cycles.h> +#include <ardour/param_id.h> #include <vector> #include <set> @@ -121,10 +122,10 @@ class Plugin : public PBD::StatefulDestructible virtual int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset) = 0; - virtual std::set<uint32_t> automatable() const = 0; + virtual std::set<ParamID> automatable() const = 0; virtual void store_state (ARDOUR::PluginState&) = 0; virtual void restore_state (ARDOUR::PluginState&) = 0; - virtual string describe_parameter (uint32_t) = 0; + virtual string describe_parameter (ParamID) = 0; virtual string state_node_name() const = 0; virtual void print_parameter (uint32_t, char*, uint32_t len) const = 0; @@ -139,7 +140,7 @@ class Plugin : public PBD::StatefulDestructible virtual bool has_editor() const = 0; - sigc::signal<void,uint32_t,float> ParameterChanged; + sigc::signal<void,ParamID,float> ParameterChanged; PBD::Controllable *get_nth_control (uint32_t); 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> plugin(uint32_t num=0) const { if (num < _plugins.size()) { @@ -94,7 +90,7 @@ class PluginInsert : public Insert PluginType type (); - string describe_parameter (uint32_t); + string describe_parameter (ParamID param); nframes_t latency(); @@ -103,7 +99,7 @@ class PluginInsert : public Insert private: - void parameter_changed (uint32_t, float); + void parameter_changed (ParamID, float); std::vector<boost::shared_ptr<Plugin> > _plugins; @@ -112,8 +108,8 @@ class PluginInsert : public Insert void init (); void set_automatable (); - void auto_state_changed (uint32_t which); - void automation_list_creation_callback (uint32_t, AutomationList&); + void auto_state_changed (ParamID which); + void automation_list_creation_callback (ParamID, AutomationList&); int32_t count_for_configuration (ChanCount in, ChanCount out) const; 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<AudioSource> src, nframes_t start, nframes_t length) : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0, Region::Flag(Region::DefaultFlags|Region::External)), - _fade_in (0.0, 2.0, 1.0, false), - _fade_out (0.0, 2.0, 1.0, false), - _envelope (0.0, 2.0, 1.0, false) + _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0), + _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0), + _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0) { boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src); if (afs) { @@ -98,9 +98,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n /* Basic AudioRegion constructor (one channel) */ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (src, start, length, name, DataType::AUDIO, layer, flags) - , _fade_in (0.0, 2.0, 1.0, false) - , _fade_out (0.0, 2.0, 1.0, false) - , _envelope (0.0, 2.0, 1.0, false) + , _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0) + , _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) + , _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0) { boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src); if (afs) { @@ -113,9 +113,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n /* Basic AudioRegion constructor (many channels) */ AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (srcs, start, length, name, DataType::AUDIO, layer, flags) - , _fade_in (0.0, 2.0, 1.0, false) - , _fade_out (0.0, 2.0, 1.0, false) - , _envelope (0.0, 2.0, 1.0, false) + , _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0) + , _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) + , _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0) { init (); } @@ -179,9 +179,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other) AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node) : Region (src, node) - , _fade_in (0.0, 2.0, 1.0, false) - , _fade_out (0.0, 2.0, 1.0, false) - , _envelope (0.0, 2.0, 1.0, false) + , _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0) + , _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) + , _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0) { boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src); if (afs) { @@ -200,10 +200,10 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod } AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node) - : Region (srcs, node), - _fade_in (0.0, 2.0, 1.0, false), - _fade_out (0.0, 2.0, 1.0, false), - _envelope (0.0, 2.0, 1.0, false) + : Region (srcs, node) + , _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0) + , _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) + , _envelope (ParamID(EnvelopeAutomation), 0.0, 2.0, 1.0) { set_default_fades (); _scale_amplitude = 1.0; @@ -405,7 +405,7 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff limit = min (to_read, fade_in_length - internal_offset); - _fade_in.get_vector (internal_offset, internal_offset+limit, gain_buffer, limit); + _fade_in.curve().get_vector (internal_offset, internal_offset+limit, gain_buffer, limit); for (nframes_t n = 0; n < limit; ++n) { mixdown_buffer[n] *= gain_buffer[n]; @@ -447,7 +447,7 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff nframes_t curve_offset = fade_interval_start - (_length-fade_out_length); nframes_t fade_offset = fade_interval_start - internal_offset; - _fade_out.get_vector (curve_offset,curve_offset+limit, gain_buffer, limit); + _fade_out.curve().get_vector (curve_offset,curve_offset+limit, gain_buffer, limit); for (nframes_t n = 0, m = fade_offset; n < limit; ++n, ++m) { mixdown_buffer[m] *= gain_buffer[n]; @@ -459,7 +459,7 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff /* Regular gain curves */ if (envelope_active()) { - _envelope.get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read); + _envelope.curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read); if (_scale_amplitude != 1.0f) { for (nframes_t n = 0; n < to_read; ++n) { 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<uint32_t> tosave; + set<ParamID> tosave; _parameter_automation.clear (); while (in) { @@ -100,9 +100,10 @@ Automatable::load_automation (const string& path) in >> when; if (!in) goto bad; in >> value; if (!in) goto bad; - AutomationList& al = automation_list (port); - al.add (when, value); - tosave.insert (port); + /* FIXME: this is legacy and only used for plugin inserts? I think? */ + AutomationList* al = automation_list (ParamID(PluginAutomation, port), true); + al->add (when, value); + tosave.insert (ParamID(PluginAutomation, port)); } return 0; @@ -113,12 +114,27 @@ Automatable::load_automation (const string& path) return -1; } +void +Automatable::add_automation_parameter(AutomationList* al) +{ + _parameter_automation[al->param_id()] = al; + + /* let derived classes do whatever they need with this */ + automation_list_creation_callback (al->param_id(), *al); + + cerr << _name << ": added (visible, can_automate) parameter " << al->param_id().to_string() << ", # params = " + << _parameter_automation.size() << endl; + + // FIXME: sane default behaviour? + _visible_parameter_automation.insert(al->param_id()); + _can_automate_list.insert(al->param_id()); +} void -Automatable::what_has_automation (set<uint32_t>& s) const +Automatable::what_has_automation (set<ParamID>& s) const { Glib::Mutex::Lock lm (_automation_lock); - map<uint32_t,AutomationList*>::const_iterator li; + map<ParamID,AutomationList*>::const_iterator li; for (li = _parameter_automation.begin(); li != _parameter_automation.end(); ++li) { s.insert ((*li).first); @@ -126,49 +142,79 @@ Automatable::what_has_automation (set<uint32_t>& s) const } void -Automatable::what_has_visible_automation (set<uint32_t>& s) const +Automatable::what_has_visible_automation (set<ParamID>& s) const { Glib::Mutex::Lock lm (_automation_lock); - set<uint32_t>::const_iterator li; + set<ParamID>::const_iterator li; for (li = _visible_parameter_automation.begin(); li != _visible_parameter_automation.end(); ++li) { s.insert (*li); } } -AutomationList& -Automatable::automation_list (uint32_t parameter) + +/** Returns NULL if we don't have an AutomationList for \a parameter. + */ +AutomationList* +Automatable::automation_list (ParamID parameter, bool create_if_missing) { - AutomationList* al = _parameter_automation[parameter]; + std::map<ParamID,AutomationList*>::iterator i = _parameter_automation.find(parameter); + + if (i != _parameter_automation.end()) { + return i->second; + + } else if (create_if_missing) { + AutomationList* al = new AutomationList (parameter, FLT_MIN, FLT_MAX, default_parameter_value (parameter)); + add_automation_parameter(al); + return al; - if (al == 0) { - al = _parameter_automation[parameter] = new AutomationList (default_parameter_value (parameter)); - /* let derived classes do whatever they need with this */ - automation_list_creation_callback (parameter, *al); + } else { + warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg; + return NULL; } +} - return *al; +const AutomationList* +Automatable::automation_list (ParamID parameter) const +{ + std::map<ParamID,AutomationList*>::const_iterator i = _parameter_automation.find(parameter); + + if (i != _parameter_automation.end()) { + return i->second; + } else { + warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg; + return NULL; + } } + string -Automatable::describe_parameter (uint32_t which) +Automatable::describe_parameter (ParamID param) { - /* derived classes will override this */ - return ""; + /* derived classes like PluginInsert should override this */ + + if (param == ParamID(GainAutomation)) + return _("Fader"); + else if (param == ParamID(PanAutomation)) + return _("Pan"); + else if (param.type() == MidiCCAutomation) + return string_compose("MIDI CC %1", param.id()); + else + return param.to_string(); } void -Automatable::can_automate (uint32_t what) +Automatable::can_automate (ParamID what) { _can_automate_list.insert (what); } void -Automatable::mark_automation_visible (uint32_t what, bool yn) +Automatable::mark_automation_visible (ParamID what, bool yn) { if (yn) { _visible_parameter_automation.insert (what); } else { - set<uint32_t>::iterator i; + set<ParamID>::iterator i; if ((i = _visible_parameter_automation.find (what)) != _visible_parameter_automation.end()) { _visible_parameter_automation.erase (i); @@ -179,7 +225,7 @@ Automatable::mark_automation_visible (uint32_t what, bool yn) bool Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const { - map<uint32_t,AutomationList*>::const_iterator li; + map<ParamID,AutomationList*>::const_iterator li; AutomationList::TimeComparator cmp; next_event.when = max_frames; @@ -207,36 +253,50 @@ Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_e return next_event.when != max_frames; } +/** \a legacy_param is used for loading legacy sessions where an object (IO, Panner) + * had a single automation parameter, with it's type implicit. Derived objects should + * pass that type and it will be used for the untyped AutomationList found. + */ int -Automatable::set_automation_state (const XMLNode& node) +Automatable::set_automation_state (const XMLNode& node, ParamID legacy_param) { Glib::Mutex::Lock lm (_automation_lock); _parameter_automation.clear (); + _visible_parameter_automation.clear (); XMLNodeList nlist = node.children(); XMLNodeIterator niter; - + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - uint32_t param; - if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶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<uint32_t,AutomationList*>::iterator li; + map<ParamID,AutomationList*>::iterator li; for (li = _parameter_automation.begin(); li != _parameter_automation.end(); ++li) { - - XMLNode* child; - - char buf[64]; - stringstream str; - snprintf (buf, sizeof (buf), "parameter-%" PRIu32, li->first); - child = new XMLNode (buf); - child->add_child_nocopy (li->second->get_state ()); + node->add_child_nocopy (li->second->get_state ()); } return *node; } + +void +Automatable::clear_automation () +{ + Glib::Mutex::Lock lm (_automation_lock); + + map<ParamID,AutomationList*>::iterator li; + + for (li = _parameter_automation.begin(); li != _parameter_automation.end(); ++li) + li->second->clear(); +} + +void +Automatable::set_parameter_automation_state (ParamID param, AutoState s) +{ + Glib::Mutex::Lock lm (_automation_lock); + + AutomationList* al = automation_list (param, true); + + if (s != al->automation_state()) { + al->set_automation_state (s); + _session.set_dirty (); + } +} + +AutoState +Automatable::get_parameter_automation_state (ParamID param) +{ + Glib::Mutex::Lock lm (_automation_lock); + + AutomationList* al = automation_list(param); + + if (al) { + return al->automation_state(); + } else { + return Off; + } +} + +void +Automatable::set_parameter_automation_style (ParamID param, AutoStyle s) +{ + Glib::Mutex::Lock lm (_automation_lock); + + AutomationList* al = automation_list (param, true); + + if (s != al->automation_style()) { + al->set_automation_style (s); + _session.set_dirty (); + } +} + +AutoStyle +Automatable::get_parameter_automation_style (ParamID param) +{ + Glib::Mutex::Lock lm (_automation_lock); + + AutomationList* al = automation_list(param); + + if (al) { + return al->automation_style(); + } else { + return Absolute; // whatever + } +} + +void +Automatable::protect_automation () +{ + set<ParamID> automated_params; + + what_has_automation (automated_params); + + for (set<ParamID>::iterator i = automated_params.begin(); i != automated_params.end(); ++i) { + + AutomationList* al = automation_list (*i); + + switch (al->automation_state()) { + case Write: + al->set_automation_state (Off); + break; + case Touch: + al->set_automation_state (Play); + break; + default: + break; + } + } +} + 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 <sstream> #include <algorithm> #include <sigc++/bind.h> +#include <ardour/param_id.h> #include <ardour/automation_event.h> +#include <ardour/curve.h> #include <pbd/stacktrace.h> #include "i18n.h" @@ -52,70 +54,72 @@ static void dumpit (const AutomationList& al, string prefix = "") } #endif -AutomationList::AutomationList (double defval) +AutomationList::AutomationList (ParamID id, double min_val, double max_val, double default_val) + : _param_id(id) + , _curve(new Curve(*this)) { + _param_id = id; _frozen = 0; - changed_when_thawed = false; + _changed_when_thawed = false; _state = Off; _style = Absolute; + _min_yval = min_val; + _max_yval = max_val; _touching = false; - min_yval = FLT_MIN; - max_yval = FLT_MAX; - max_xval = 0; // means "no limit" - default_value = defval; - _dirty = false; - rt_insertion_point = events.end(); - lookup_cache.left = -1; - lookup_cache.range.first = events.end(); - sort_pending = false; - - AutomationListCreated(this); + _max_xval = 0; // means "no limit" + _default_value = default_val; + _rt_insertion_point = _events.end(); + _lookup_cache.left = -1; + _lookup_cache.range.first = _events.end(); + _sort_pending = false; + + + AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other) + : _param_id(other._param_id) + , _curve(new Curve(*this)) { _frozen = 0; - changed_when_thawed = false; + _changed_when_thawed = false; _style = other._style; - min_yval = other.min_yval; - max_yval = other.max_yval; - max_xval = other.max_xval; - default_value = other.default_value; + _min_yval = other._min_yval; + _max_yval = other._max_yval; + _max_xval = other._max_xval; + _default_value = other._default_value; _state = other._state; _touching = other._touching; - _dirty = false; - rt_insertion_point = events.end(); - lookup_cache.left = -1; - lookup_cache.range.first = events.end(); - sort_pending = false; - - for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) { - /* we have to use other point_factory() because - its virtual and we're in a constructor. - */ - events.push_back (other.point_factory (**i)); + _rt_insertion_point = _events.end(); + _lookup_cache.left = -1; + _lookup_cache.range.first = _events.end(); + _sort_pending = false; + + for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) { + _events.push_back (new ControlEvent (**i)); } mark_dirty (); - AutomationListCreated(this); + AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other, double start, double end) + : _param_id(other._param_id) + , _curve(new Curve(*this)) { _frozen = 0; - changed_when_thawed = false; + _changed_when_thawed = false; _style = other._style; - min_yval = other.min_yval; - max_yval = other.max_yval; - max_xval = other.max_xval; - default_value = other.default_value; + _min_yval = other._min_yval; + _max_yval = other._max_yval; + _max_xval = other._max_xval; + _default_value = other._default_value; _state = other._state; _touching = other._touching; - _dirty = false; - rt_insertion_point = events.end(); - lookup_cache.left = -1; - lookup_cache.range.first = events.end(); - sort_pending = false; + _rt_insertion_point = _events.end(); + _lookup_cache.left = -1; + _lookup_cache.range.first = _events.end(); + _sort_pending = false; /* now grab the relevant points, and shift them back if necessary */ @@ -123,7 +127,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl if (!section->empty()) { for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) { - events.push_back (other.point_factory ((*i)->when, (*i)->value)); + _events.push_back (new ControlEvent ((*i)->when, (*i)->value)); } } @@ -134,32 +138,38 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl AutomationListCreated(this); } -AutomationList::AutomationList (const XMLNode& node) +/** \a id is used for legacy sessions where the type is not present + * in or below the <AutomationList> node. It is used if \a id is non-null. + */ +AutomationList::AutomationList (const XMLNode& node, ParamID id) + : _curve(new Curve(*this)) { _frozen = 0; - changed_when_thawed = false; + _changed_when_thawed = false; _touching = false; - min_yval = FLT_MIN; - max_yval = FLT_MAX; - max_xval = 0; // means "no limit" - _dirty = false; + _min_yval = FLT_MIN; + _max_yval = FLT_MAX; + _max_xval = 0; // means "no limit" _state = Off; _style = Absolute; - rt_insertion_point = events.end(); - lookup_cache.left = -1; - lookup_cache.range.first = events.end(); - sort_pending = false; + _rt_insertion_point = _events.end(); + _lookup_cache.left = -1; + _lookup_cache.range.first = _events.end(); + _sort_pending = false; set_state (node); - AutomationListCreated(this); + if (id) + _param_id = id; + + AutomationListCreated(this); } AutomationList::~AutomationList() { GoingAway (); - for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) { + for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) { delete (*x); } } @@ -167,7 +177,7 @@ AutomationList::~AutomationList() bool AutomationList::operator== (const AutomationList& other) { - return events == other.events; + return _events == other._events; } AutomationList& @@ -175,16 +185,16 @@ AutomationList::operator= (const AutomationList& other) { if (this != &other) { - events.clear (); + _events.clear (); - for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) { - events.push_back (point_factory (**i)); + for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) { + _events.push_back (new ControlEvent (**i)); } - min_yval = other.min_yval; - max_yval = other.max_yval; - max_xval = other.max_xval; - default_value = other.default_value; + _min_yval = other._min_yval; + _max_yval = other._max_yval; + _max_xval = other._max_xval; + _default_value = other._default_value; mark_dirty (); maybe_signal_changed (); @@ -199,7 +209,7 @@ AutomationList::maybe_signal_changed () mark_dirty (); if (_frozen) { - changed_when_thawed = true; + _changed_when_thawed = true; } else { StateChanged (); } @@ -241,8 +251,8 @@ void AutomationList::clear () { { - Glib::Mutex::Lock lm (lock); - events.clear (); + Glib::Mutex::Lock lm (_lock); + _events.clear (); mark_dirty (); } @@ -252,25 +262,25 @@ AutomationList::clear () void AutomationList::x_scale (double factor) { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); _x_scale (factor); } bool AutomationList::extend_to (double when) { - Glib::Mutex::Lock lm (lock); - if (events.empty() || events.back()->when == when) { + Glib::Mutex::Lock lm (_lock); + if (_events.empty() || _events.back()->when == when) { return false; } - double factor = when / events.back()->when; + double factor = when / _events.back()->when; _x_scale (factor); return true; } void AutomationList::_x_scale (double factor) { - for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) { + for (AutomationList::iterator i = _events.begin(); i != _events.end(); ++i) { (*i)->when = floor ((*i)->when * factor); } @@ -280,11 +290,9 @@ void AutomationList::_x_scale (double factor) void AutomationList::reposition_for_rt_add (double when) { - rt_insertion_point = events.end(); + _rt_insertion_point = _events.end(); } -#define last_rt_insertion_point rt_insertion_point - void AutomationList::rt_add (double when, double value) { @@ -297,26 +305,26 @@ AutomationList::rt_add (double when, double value) // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl; { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); iterator where; TimeComparator cmp; ControlEvent cp (when, 0.0); bool done = false; - if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) { + if ((_rt_insertion_point != _events.end()) && ((*_rt_insertion_point)->when < when) ) { /* we have a previous insertion point, so we should delete everything between it and the position where we are going to insert this point. */ - iterator after = last_rt_insertion_point; + iterator after = _rt_insertion_point; - if (++after != events.end()) { + if (++after != _events.end()) { iterator far = after; - while (far != events.end()) { + while (far != _events.end()) { if ((*far)->when > when) { break; } @@ -325,14 +333,14 @@ AutomationList::rt_add (double when, double value) if(_new_touch) { where = far; - last_rt_insertion_point = where; + _rt_insertion_point = where; if((*where)->when == when) { (*where)->value = value; done = true; } } else { - where = events.erase (after, far); + where = _events.erase (after, far); } } else { @@ -341,20 +349,20 @@ AutomationList::rt_add (double when, double value) } - iterator previous = last_rt_insertion_point; - --previous; + iterator previous = _rt_insertion_point; + --previous; - if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) { - (*last_rt_insertion_point)->when = when; + if (_rt_insertion_point != _events.begin() && (*_rt_insertion_point)->value == value && (*previous)->value == value) { + (*_rt_insertion_point)->when = when; done = true; } } else { - where = lower_bound (events.begin(), events.end(), &cp, cmp); + where = lower_bound (_events.begin(), _events.end(), &cp, cmp); - if (where != events.end()) { + if (where != _events.end()) { if ((*where)->when == when) { (*where)->value = value; done = true; @@ -363,7 +371,7 @@ AutomationList::rt_add (double when, double value) } if (!done) { - last_rt_insertion_point = events.insert (where, point_factory (when, value)); + _rt_insertion_point = _events.insert (where, new ControlEvent (when, value)); } _new_touch = false; @@ -377,24 +385,22 @@ void AutomationList::fast_simple_add (double when, double value) { /* to be used only for loading pre-sorted data from saved state */ - events.insert (events.end(), point_factory (when, value)); + _events.insert (_events.end(), new ControlEvent (when, value)); } -#undef last_rt_insertion_point - void AutomationList::add (double when, double value) { /* this is for graphical editing */ { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); TimeComparator cmp; ControlEvent cp (when, 0.0f); bool insert = true; iterator insertion_point; - for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) { + for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, cmp); insertion_point != _events.end(); ++insertion_point) { /* only one point allowed per time point */ @@ -411,7 +417,7 @@ AutomationList::add (double when, double value) if (insert) { - events.insert (insertion_point, point_factory (when, value)); + _events.insert (insertion_point, new ControlEvent (when, value)); reposition_for_rt_add (0); } @@ -426,8 +432,8 @@ void AutomationList::erase (AutomationList::iterator i) { { - Glib::Mutex::Lock lm (lock); - events.erase (i); + Glib::Mutex::Lock lm (_lock); + _events.erase (i); reposition_for_rt_add (0); mark_dirty (); } @@ -438,8 +444,8 @@ void AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end) { { - Glib::Mutex::Lock lm (lock); - events.erase (start, end); + Glib::Mutex::Lock lm (_lock); + _events.erase (start, end); reposition_for_rt_add (0); mark_dirty (); } @@ -452,19 +458,19 @@ AutomationList::reset_range (double start, double endt) bool reset = false; { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); TimeComparator cmp; ControlEvent cp (start, 0.0f); iterator s; iterator e; - if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) { + if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) != _events.end()) { cp.when = endt; - e = upper_bound (events.begin(), events.end(), &cp, cmp); + e = upper_bound (_events.begin(), _events.end(), &cp, cmp); for (iterator i = s; i != e; ++i) { - (*i)->value = default_value; + (*i)->value = _default_value; } reset = true; @@ -484,16 +490,16 @@ AutomationList::erase_range (double start, double endt) bool erased = false; { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); TimeComparator cmp; ControlEvent cp (start, 0.0f); iterator s; iterator e; - if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) { + if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) != _events.end()) { cp.when = endt; - e = upper_bound (events.begin(), events.end(), &cp, cmp); - events.erase (s, e); + e = upper_bound (_events.begin(), _events.end(), &cp, cmp); + _events.erase (s, e); reposition_for_rt_add (0); erased = true; mark_dirty (); @@ -515,7 +521,7 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double */ { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); while (start != end) { (*start)->when += xdelta; @@ -527,9 +533,9 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double } if (!_frozen) { - events.sort (sort_events_by_time); + _events.sort (sort_events_by_time); } else { - sort_pending = true; + _sort_pending = true; } mark_dirty (); @@ -542,13 +548,13 @@ void AutomationList::slide (iterator before, double distance) { { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); - if (before == events.end()) { + if (before == _events.end()) { return; } - while (before != events.end()) { + while (before != _events.end()) { (*before)->when += distance; ++before; } @@ -566,7 +572,7 @@ AutomationList::modify (iterator iter, double when, double val) */ { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); (*iter)->when = when; (*iter)->value = val; @@ -576,9 +582,9 @@ AutomationList::modify (iterator iter, double when, double val) } if (!_frozen) { - events.sort (sort_events_by_time); + _events.sort (sort_events_by_time); } else { - sort_pending = true; + _sort_pending = true; } mark_dirty (); @@ -590,20 +596,20 @@ AutomationList::modify (iterator iter, double when, double val) std::pair<AutomationList::iterator,AutomationList::iterator> AutomationList::control_points_adjacent (double xval) { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); iterator i; TimeComparator cmp; ControlEvent cp (xval, 0.0f); std::pair<iterator,iterator> ret; - ret.first = events.end(); - ret.second = events.end(); + ret.first = _events.end(); + ret.second = _events.end(); - for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) { + for (i = lower_bound (_events.begin(), _events.end(), &cp, cmp); i != _events.end(); ++i) { - if (ret.first == events.end()) { + if (ret.first == _events.end()) { if ((*i)->when >= xval) { - if (i != events.begin()) { + if (i != _events.begin()) { ret.first = i; --ret.first; } else { @@ -641,15 +647,15 @@ AutomationList::thaw () } { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); - if (sort_pending) { - events.sort (sort_events_by_time); - sort_pending = false; + if (_sort_pending) { + _events.sort (sort_events_by_time); + _sort_pending = false; } } - if (changed_when_thawed) { + if (_changed_when_thawed) { StateChanged(); /* EMIT SIGNAL */ } } @@ -657,44 +663,44 @@ AutomationList::thaw () void AutomationList::set_max_xval (double x) { - max_xval = x; + _max_xval = x; } void AutomationList::mark_dirty () { - lookup_cache.left = -1; - _dirty = true; + _lookup_cache.left = -1; + Dirty (); /* EMIT SIGNAL */ } void AutomationList::truncate_end (double last_coordinate) { { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); ControlEvent cp (last_coordinate, 0); list<ControlEvent*>::reverse_iterator i; double last_val; - if (events.empty()) { + if (_events.empty()) { return; } - if (last_coordinate == events.back()->when) { + if (last_coordinate == _events.back()->when) { return; } - if (last_coordinate > events.back()->when) { + if (last_coordinate > _events.back()->when) { /* extending end: */ - iterator foo = events.begin(); + iterator foo = _events.begin(); bool lessthantwo; - if (foo == events.end()) { + if (foo == _events.end()) { lessthantwo = true; - } else if (++foo == events.end()) { + } else if (++foo == _events.end()) { lessthantwo = true; } else { lessthantwo = false; @@ -702,7 +708,7 @@ AutomationList::truncate_end (double last_coordinate) if (lessthantwo) { /* less than 2 points: add a new point */ - events.push_back (point_factory (last_coordinate, events.back()->value)); + _events.push_back (new ControlEvent (last_coordinate, _events.back()->value)); } else { /* more than 2 points: check to see if the last 2 values @@ -710,14 +716,14 @@ AutomationList::truncate_end (double last_coordinate) last point. otherwise, add a new point. */ - iterator penultimate = events.end(); + iterator penultimate = _events.end(); --penultimate; /* points at last point */ --penultimate; /* points at the penultimate point */ - if (events.back()->value == (*penultimate)->value) { - events.back()->when = last_coordinate; + if (_events.back()->value == (*penultimate)->value) { + _events.back()->when = last_coordinate; } else { - events.push_back (point_factory (last_coordinate, events.back()->value)); + _events.push_back (new ControlEvent (last_coordinate, _events.back()->value)); } } @@ -726,10 +732,10 @@ AutomationList::truncate_end (double last_coordinate) /* shortening end */ last_val = unlocked_eval (last_coordinate); - last_val = max ((double) min_yval, last_val); - last_val = min ((double) max_yval, last_val); + last_val = max ((double) _min_yval, last_val); + last_val = min ((double) _max_yval, last_val); - i = events.rbegin(); + i = _events.rbegin(); /* make i point to the last control point */ @@ -739,9 +745,9 @@ AutomationList::truncate_end (double last_coordinate) beyond the new last coordinate. */ - uint32_t sz = events.size(); + uint32_t sz = _events.size(); - while (i != events.rend() && sz > 2) { + while (i != _events.rend() && sz > 2) { list<ControlEvent*>::reverse_iterator tmp; tmp = i; @@ -751,14 +757,14 @@ AutomationList::truncate_end (double last_coordinate) break; } - events.erase (i.base()); + _events.erase (i.base()); --sz; i = tmp; } - events.back()->when = last_coordinate; - events.back()->value = last_val; + _events.back()->when = last_coordinate; + _events.back()->value = last_val; } reposition_for_rt_add (0); @@ -772,12 +778,12 @@ void AutomationList::truncate_start (double overall_length) { { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); AutomationList::iterator i; double first_legal_value; double first_legal_coordinate; - if (events.empty()) { + if (_events.empty()) { fatal << _("programming error:") << "AutomationList::truncate_start() called on an empty list" << endmsg; @@ -785,26 +791,26 @@ AutomationList::truncate_start (double overall_length) return; } - if (overall_length == events.back()->when) { + if (overall_length == _events.back()->when) { /* no change in overall length */ return; } - if (overall_length > events.back()->when) { + if (overall_length > _events.back()->when) { /* growing at front: duplicate first point. shift all others */ - double shift = overall_length - events.back()->when; + double shift = overall_length - _events.back()->when; uint32_t np; - for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) { + for (np = 0, i = _events.begin(); i != _events.end(); ++i, ++np) { (*i)->when += shift; } if (np < 2) { /* less than 2 points: add a new point */ - events.push_front (point_factory (0, events.front()->value)); + _events.push_front (new ControlEvent (0, _events.front()->value)); } else { @@ -813,15 +819,15 @@ AutomationList::truncate_start (double overall_length) first point. otherwise, add a new point. */ - iterator second = events.begin(); + iterator second = _events.begin(); ++second; /* points at the second point */ - if (events.front()->value == (*second)->value) { + if (_events.front()->value == (*second)->value) { /* first segment is flat, just move start point back to zero */ - events.front()->when = 0; + _events.front()->when = 0; } else { /* leave non-flat segment in place, add a new leading point. */ - events.push_front (point_factory (0, events.front()->value)); + _events.push_front (new ControlEvent (0, _events.front()->value)); } } @@ -829,16 +835,16 @@ AutomationList::truncate_start (double overall_length) /* shrinking at front */ - first_legal_coordinate = events.back()->when - overall_length; + first_legal_coordinate = _events.back()->when - overall_length; first_legal_value = unlocked_eval (first_legal_coordinate); - first_legal_value = max (min_yval, first_legal_value); - first_legal_value = min (max_yval, first_legal_value); + first_legal_value = max (_min_yval, first_legal_value); + first_legal_value = min (_max_yval, first_legal_value); /* remove all events earlier than the new "front" */ - i = events.begin(); + i = _events.begin(); - while (i != events.end() && !events.empty()) { + while (i != _events.end() && !_events.empty()) { list<ControlEvent*>::iterator tmp; tmp = i; @@ -848,7 +854,7 @@ AutomationList::truncate_start (double overall_length) break; } - events.erase (i); + _events.erase (i); i = tmp; } @@ -858,13 +864,13 @@ AutomationList::truncate_start (double overall_length) relative position */ - for (i = events.begin(); i != events.end(); ++i) { + for (i = _events.begin(); i != _events.end(); ++i) { (*i)->when -= first_legal_coordinate; } /* add a new point for the interpolated new value */ - events.push_front (point_factory (0, first_legal_value)); + _events.push_front (new ControlEvent (0, first_legal_value)); } reposition_for_rt_add (0); @@ -876,48 +882,42 @@ AutomationList::truncate_start (double overall_length) } double -AutomationList::unlocked_eval (double x) -{ - return shared_eval (x); -} - -double -AutomationList::shared_eval (double x) +AutomationList::unlocked_eval (double x) const { - pair<AutomationEventList::iterator,AutomationEventList::iterator> range; + pair<EventList::iterator,EventList::iterator> range; int32_t npoints; double lpos, upos; double lval, uval; double fraction; - npoints = events.size(); + npoints = _events.size(); switch (npoints) { case 0: - return default_value; + return _default_value; case 1: - if (x >= events.front()->when) { - return events.front()->value; + if (x >= _events.front()->when) { + return _events.front()->value; } else { - // return default_value; - return events.front()->value; + // return _default_value; + return _events.front()->value; } case 2: - if (x >= events.back()->when) { - return events.back()->value; - } else if (x == events.front()->when) { - return events.front()->value; - } else if (x < events.front()->when) { - // return default_value; - return events.front()->value; + if (x >= _events.back()->when) { + return _events.back()->value; + } else if (x == _events.front()->when) { + return _events.front()->value; + } else if (x < _events.front()->when) { + // return _default_value; + return _events.front()->value; } - lpos = events.front()->when; - lval = events.front()->value; - upos = events.back()->when; - uval = events.back()->value; + lpos = _events.front()->when; + lval = _events.front()->value; + upos = _events.back()->when; + uval = _events.back()->value; /* linear interpolation betweeen the two points */ @@ -927,13 +927,13 @@ AutomationList::shared_eval (double x) default: - if (x >= events.back()->when) { - return events.back()->value; - } else if (x == events.front()->when) { - return events.front()->value; - } else if (x < events.front()->when) { - // return default_value; - return events.front()->value; + if (x >= _events.back()->when) { + return _events.back()->value; + } else if (x == _events.front()->when) { + return _events.front()->value; + } else if (x < _events.front()->when) { + // return _default_value; + return _events.front()->value; } return multipoint_eval (x); @@ -942,9 +942,9 @@ AutomationList::shared_eval (double x) } double -AutomationList::multipoint_eval (double x) +AutomationList::multipoint_eval (double x) const { - pair<AutomationList::iterator,AutomationList::iterator> range; + pair<AutomationList::const_iterator,AutomationList::const_iterator> range; double upos, lpos; double uval, lval; double fraction; @@ -953,38 +953,38 @@ AutomationList::multipoint_eval (double x) this was called (or if the lookup cache has been marked "dirty" (left<0) */ - if ((lookup_cache.left < 0) || - ((lookup_cache.left > x) || - (lookup_cache.range.first == events.end()) || - ((*lookup_cache.range.second)->when < x))) { + if ((_lookup_cache.left < 0) || + ((_lookup_cache.left > x) || + (_lookup_cache.range.first == _events.end()) || + ((*_lookup_cache.range.second)->when < x))) { ControlEvent cp (x, 0); TimeComparator cmp; - lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp); + _lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, cmp); } - range = lookup_cache.range; + range = _lookup_cache.range; if (range.first == range.second) { /* x does not exist within the list as a control point */ - lookup_cache.left = x; + _lookup_cache.left = x; - if (range.first != events.begin()) { + if (range.first != _events.begin()) { --range.first; lpos = (*range.first)->when; lval = (*range.first)->value; } else { /* we're before the first point */ - // return default_value; - return events.front()->value; + // return _default_value; + return _events.front()->value; } - if (range.second == events.end()) { + if (range.second == _events.end()) { /* we're after the last point */ - return events.back()->value; + return _events.back()->value; } upos = (*range.second)->when; @@ -1000,17 +1000,17 @@ AutomationList::multipoint_eval (double x) } /* x is a control point in the data */ - lookup_cache.left = -1; + _lookup_cache.left = -1; return (*range.first)->value; } AutomationList* AutomationList::cut (iterator start, iterator end) { - AutomationList* nal = new AutomationList (default_value); + AutomationList* nal = new AutomationList (_param_id, _min_yval, _max_yval, _default_value); { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); for (iterator x = start; x != end; ) { iterator tmp; @@ -1018,8 +1018,8 @@ AutomationList::cut (iterator start, iterator end) tmp = x; ++tmp; - nal->events.push_back (point_factory (**x)); - events.erase (x); + nal->_events.push_back (new ControlEvent (**x)); + _events.erase (x); reposition_for_rt_add (0); @@ -1037,24 +1037,24 @@ AutomationList::cut (iterator start, iterator end) AutomationList* AutomationList::cut_copy_clear (double start, double end, int op) { - AutomationList* nal = new AutomationList (default_value); + AutomationList* nal = new AutomationList (_param_id, _min_yval, _max_yval, _default_value); iterator s, e; ControlEvent cp (start, 0.0); TimeComparator cmp; bool changed = false; { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); - if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) { + if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) == _events.end()) { return nal; } cp.when = end; - e = upper_bound (events.begin(), events.end(), &cp, cmp); + e = upper_bound (_events.begin(), _events.end(), &cp, cmp); if (op != 2 && (*s)->when != start) { - nal->events.push_back (point_factory (0, unlocked_eval (start))); + nal->_events.push_back (new ControlEvent (0, unlocked_eval (start))); } for (iterator x = s; x != e; ) { @@ -1070,18 +1070,18 @@ AutomationList::cut_copy_clear (double start, double end, int op) */ if (op != 2) { - nal->events.push_back (point_factory ((*x)->when - start, (*x)->value)); + nal->_events.push_back (new ControlEvent ((*x)->when - start, (*x)->value)); } if (op != 1) { - events.erase (x); + _events.erase (x); } x = tmp; } - if (op != 2 && nal->events.back()->when != end - start) { - nal->events.push_back (point_factory (end - start, unlocked_eval (end))); + if (op != 2 && nal->_events.back()->when != end - start) { + nal->_events.push_back (new ControlEvent (end - start, unlocked_eval (end))); } if (changed) { @@ -1100,10 +1100,10 @@ AutomationList::cut_copy_clear (double start, double end, int op) AutomationList* AutomationList::copy (iterator start, iterator end) { - AutomationList* nal = new AutomationList (default_value); + AutomationList* nal = new AutomationList (_param_id, _min_yval, _max_yval, _default_value); { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); for (iterator x = start; x != end; ) { iterator tmp; @@ -1111,7 +1111,7 @@ AutomationList::copy (iterator start, iterator end) tmp = x; ++tmp; - nal->events.push_back (point_factory (**x)); + nal->_events.push_back (new ControlEvent (**x)); x = tmp; } @@ -1141,22 +1141,22 @@ AutomationList::clear (double start, double end) bool AutomationList::paste (AutomationList& alist, double pos, float times) { - if (alist.events.empty()) { + if (alist._events.empty()) { return false; } { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm (_lock); iterator where; iterator prev; double end = 0; ControlEvent cp (pos, 0.0); TimeComparator cmp; - where = upper_bound (events.begin(), events.end(), &cp, cmp); + where = upper_bound (_events.begin(), _events.end(), &cp, cmp); for (iterator i = alist.begin();i != alist.end(); ++i) { - events.insert (where, point_factory( (*i)->when+pos,( *i)->value)); + _events.insert (where, new ControlEvent( (*i)->when+pos,( *i)->value)); end = (*i)->when + pos; } @@ -1165,12 +1165,12 @@ AutomationList::paste (AutomationList& alist, double pos, float times) the correct amount. */ - while (where != events.end()) { + while (where != _events.end()) { iterator tmp; if ((*where)->when <= end) { tmp = where; ++tmp; - events.erase(where); + _events.erase(where); where = tmp; } else { @@ -1186,18 +1186,6 @@ AutomationList::paste (AutomationList& alist, double pos, float times) return true; } -ControlEvent* -AutomationList::point_factory (double when, double val) const -{ - return new ControlEvent (when, val); -} - -ControlEvent* -AutomationList::point_factory (const ControlEvent& other) const -{ - return new ControlEvent (other); -} - XMLNode& AutomationList::get_state () { @@ -1207,20 +1195,24 @@ AutomationList::get_state () XMLNode& AutomationList::state (bool full) { + cerr << _param_id.to_string() << "->state()" << endl; + XMLNode* root = new XMLNode (X_("AutomationList")); char buf[64]; LocaleGuard lg (X_("POSIX")); + root->add_property ("automation-id", _param_id.to_string()); + root->add_property ("id", _id.to_s()); - snprintf (buf, sizeof (buf), "%.12g", default_value); + snprintf (buf, sizeof (buf), "%.12g", _default_value); root->add_property ("default", buf); - snprintf (buf, sizeof (buf), "%.12g", min_yval); - root->add_property ("min_yval", buf); - snprintf (buf, sizeof (buf), "%.12g", max_yval); - root->add_property ("max_yval", buf); - snprintf (buf, sizeof (buf), "%.12g", max_xval); - root->add_property ("max_xval", buf); + snprintf (buf, sizeof (buf), "%.12g", _min_yval); + root->add_property ("_min_yval", buf); + snprintf (buf, sizeof (buf), "%.12g", _max_yval); + root->add_property ("_max_yval", buf); + snprintf (buf, sizeof (buf), "%.12g", _max_xval); + root->add_property ("_max_xval", buf); if (full) { root->add_property ("state", auto_state_to_string (_state)); @@ -1231,7 +1223,7 @@ AutomationList::state (bool full) root->add_property ("style", auto_style_to_string (_style)); - if (!events.empty()) { + if (!_events.empty()) { root->add_child_nocopy (serialize_events()); } @@ -1244,7 +1236,7 @@ AutomationList::serialize_events () XMLNode* node = new XMLNode (X_("events")); stringstream str; - for (iterator xx = events.begin(); xx != events.end(); ++xx) { + for (iterator xx = _events.begin(); xx != _events.end(); ++xx) { str << (double) (*xx)->when; str << ' '; str <<(double) (*xx)->value; @@ -1367,17 +1359,25 @@ AutomationList::set_state (const XMLNode& node) error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg; return -1; } - + if ((prop = node.property ("id")) != 0) { _id = prop->value (); /* update session AL list */ AutomationListCreated(this); } + if ((prop = node.property (X_("automation-id"))) != 0){ + _param_id = ParamID(prop->value()); + } else { + warning << "Legacy session: automation list has no automation-id property."; + } + + cerr << "Loaded automation " << _param_id.to_string() << endl; + if ((prop = node.property (X_("default"))) != 0){ - default_value = atof (prop->value()); + _default_value = atof (prop->value()); } else { - default_value = 0.0; + _default_value = 0.0; } if ((prop = node.property (X_("style"))) != 0) { @@ -1392,22 +1392,22 @@ AutomationList::set_state (const XMLNode& node) _state = Off; } - if ((prop = node.property (X_("min_yval"))) != 0) { - min_yval = atof (prop->value ()); + if ((prop = node.property (X_("_min_yval"))) != 0) { + _min_yval = atof (prop->value ()); } else { - min_yval = FLT_MIN; + _min_yval = FLT_MIN; } - if ((prop = node.property (X_("max_yval"))) != 0) { - max_yval = atof (prop->value ()); + if ((prop = node.property (X_("_max_yval"))) != 0) { + _max_yval = atof (prop->value ()); } else { - max_yval = FLT_MAX; + _max_yval = FLT_MAX; } - if ((prop = node.property (X_("max_xval"))) != 0) { - max_xval = atof (prop->value ()); + if ((prop = node.property (X_("_max_xval"))) != 0) { + _max_xval = atof (prop->value ()); } else { - max_xval = 0; // means "no limit ; + _max_xval = 0; // means "no limit ; } for (niter = nlist.begin(); niter != nlist.end(); ++niter) { 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<AudioRegion> in, boost::shared_ptr<Audio nframes_t position, AnchorPoint ap) : AudioRegion (position, length, "foobar"), - _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB - _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB + _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB + _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB { _in = in; @@ -96,8 +96,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<Audio Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model, bool act) : AudioRegion (0, 0, "foobar"), - _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB - _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB + _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB + _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB { _in_update = false; _fixed = false; @@ -115,8 +115,8 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioR Crossfade::Crossfade (const Playlist& playlist, XMLNode& node) : AudioRegion (0, 0, "foobar"), - _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB - _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB + _fade_in (ParamID(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB + _fade_out (ParamID(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB { boost::shared_ptr<Region> r; @@ -300,8 +300,8 @@ Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, float* fiv = new float[to_write]; float* fov = new float[to_write]; - _fade_in.get_vector (offset, offset+to_write, fiv, to_write); - _fade_out.get_vector (offset, offset+to_write, fov, to_write); + _fade_in.curve().get_vector (offset, offset+to_write, fiv, to_write); + _fade_out.curve().get_vector (offset, offset+to_write, fov, to_write); /* note: although we have not explicitly taken into account the return values from _out->read_at() or _in->read_at(), the length() function does this diff --git a/libs/ardour/curve.cc b/libs/ardour/curve.cc index 5a1dc108f8..3aa9de9127 100644 --- a/libs/ardour/curve.cc +++ b/libs/ardour/curve.cc @@ -31,6 +31,7 @@ #include <sigc++/bind.h> #include "ardour/curve.h" +#include "ardour/automation_event.h" #include "i18n.h" @@ -39,31 +40,35 @@ using namespace ARDOUR; using namespace sigc; using namespace PBD; -Curve::Curve (double minv, double maxv, double canv, bool nostate) - : AutomationList (canv) +Curve::Curve (const AutomationList& al) + : _list (al) + , _dirty (true) { - min_yval = minv; - max_yval = maxv; + _list.Dirty.connect(mem_fun(*this, &Curve::on_list_dirty)); } Curve::Curve (const Curve& other) - : AutomationList (other) + : _list (other._list) + , _dirty (true) { - min_yval = other.min_yval; - max_yval = other.max_yval; + _list.Dirty.connect(mem_fun(*this, &Curve::on_list_dirty)); } - +#if 0 Curve::Curve (const Curve& other, double start, double end) - : AutomationList (other, start, end) + : _list (other._list) { - min_yval = other.min_yval; - max_yval = other.max_yval; + _min_yval = other._min_yval; + _max_yval = other._max_yval; } -Curve::Curve (const XMLNode& node) - : AutomationList (node) +/** \a id is used for legacy sessions where the type is not present + * in or below the <AutomationList> node. It is used if \a id is non-null. + */ +Curve::Curve (const XMLNode& node, ParamID id) + : AutomationList (node, id) { } +#endif Curve::~Curve () { @@ -78,7 +83,7 @@ Curve::solve () return; } - if ((npoints = events.size()) > 2) { + if ((npoints = _list.events().size()) > 2) { /* Compute coefficients needed to efficiently compute a constrained spline curve. See "Constrained Cubic Spline Interpolation" by CJC Kruger @@ -88,9 +93,9 @@ Curve::solve () double x[npoints]; double y[npoints]; uint32_t i; - AutomationEventList::iterator xx; + AutomationList::EventList::const_iterator xx; - for (i = 0, xx = events.begin(); xx != events.end(); ++xx, ++i) { + for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) { x[i] = (double) (*xx)->when; y[i] = (double) (*xx)->value; } @@ -108,16 +113,7 @@ Curve::solve () double fplast = 0; - for (i = 0, xx = events.begin(); xx != events.end(); ++xx, ++i) { - - CurvePoint* cp = dynamic_cast<CurvePoint*>(*xx); - - if (cp == 0) { - fatal << _("programming error: ") - << X_("non-CurvePoint event found in event list for a Curve") - << endmsg; - /*NOTREACHED*/ - } + for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) { double xdelta; /* gcc is wrong about possible uninitialized use */ double xdelta2; /* ditto */ @@ -192,10 +188,10 @@ Curve::solve () /* store */ - cp->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13); - cp->coeff[1] = b; - cp->coeff[2] = c; - cp->coeff[3] = d; + (*xx)->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13); + (*xx)->coeff[1] = b; + (*xx)->coeff[2] = c; + (*xx)->coeff[3] = d; fplast = fpi; } @@ -208,7 +204,7 @@ Curve::solve () bool Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen) { - Glib::Mutex::Lock lm (lock, Glib::TRY_LOCK); + Glib::Mutex::Lock lm(_list.lock(), Glib::TRY_LOCK); if (!lm.locked()) { return false; @@ -221,7 +217,7 @@ Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen) void Curve::get_vector (double x0, double x1, float *vec, int32_t veclen) { - Glib::Mutex::Lock lm (lock); + Glib::Mutex::Lock lm(_list.lock()); _get_vector (x0, x1, vec, veclen); } @@ -233,22 +229,22 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) int32_t original_veclen; int32_t npoints; - if ((npoints = events.size()) == 0) { - for (i = 0; i < veclen; ++i) { - vec[i] = default_value; - } - return; + if ((npoints = _list.events().size()) == 0) { + for (i = 0; i < veclen; ++i) { + vec[i] = _list.default_value(); + } + return; } /* events is now known not to be empty */ - max_x = events.back()->when; - min_x = events.front()->when; + max_x = _list.events().back()->when; + min_x = _list.events().front()->when; lx = max (min_x, x0); if (x1 < 0) { - x1 = events.back()->when; + x1 = _list.events().back()->when; } hx = min (max_x, x1); @@ -267,7 +263,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) subveclen = min (subveclen, veclen); for (i = 0; i < subveclen; ++i) { - vec[i] = events.front()->value; + vec[i] = _list.events().front()->value; } veclen -= subveclen; @@ -286,7 +282,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) subveclen = min (subveclen, veclen); - val = events.back()->value; + val = _list.events().back()->value; i = veclen - subveclen; @@ -304,7 +300,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) if (npoints == 1 ) { for (i = 0; i < veclen; ++i) { - vec[i] = events.front()->value; + vec[i] = _list.events().front()->value; } return; } @@ -325,11 +321,11 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) dx = 0; // not used } - double slope = (events.back()->value - events.front()->value)/ - (events.back()->when - events.front()->when); + double slope = (_list.events().back()->value - _list.events().front()->value)/ + (_list.events().back()->when - _list.events().front()->when); double yfrac = dx*slope; - vec[0] = events.front()->value + slope * (lx - events.front()->when); + vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when); for (i = 1; i < veclen; ++i) { vec[i] = vec[i-1] + yfrac; @@ -357,27 +353,31 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) double Curve::unlocked_eval (double x) { + // I don't see the point of this... + if (_dirty) { solve (); } - return shared_eval (x); + return _list.unlocked_eval (x); } double Curve::multipoint_eval (double x) { - pair<AutomationEventList::iterator,AutomationEventList::iterator> range; + pair<AutomationList::EventList::const_iterator,AutomationList::EventList::const_iterator> range; + + AutomationList::LookupCache& lookup_cache = _list.lookup_cache(); if ((lookup_cache.left < 0) || ((lookup_cache.left > x) || - (lookup_cache.range.first == events.end()) || + (lookup_cache.range.first == _list.events().end()) || ((*lookup_cache.range.second)->when < x))) { - TimeComparator cmp; + AutomationList::TimeComparator cmp; ControlEvent cp (x, 0.0); - lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp); + lookup_cache.range = equal_range (_list.events().begin(), _list.events().end(), &cp, cmp); } range = lookup_cache.range; @@ -399,21 +399,21 @@ Curve::multipoint_eval (double x) lookup_cache.left = x; - if (range.first == events.begin()) { + if (range.first == _list.events().begin()) { /* we're before the first point */ // return default_value; - events.front()->value; + _list.events().front()->value; } - if (range.second == events.end()) { + if (range.second == _list.events().end()) { /* we're after the last point */ - return events.back()->value; + return _list.events().back()->value; } double x2 = x * x; - CurvePoint* cp = dynamic_cast<CurvePoint*> (*range.second); + ControlEvent* ev = *range.second; - return cp->coeff[0] + (cp->coeff[1] * x) + (cp->coeff[2] * x2) + (cp->coeff[3] * x2 * x); + return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x); } /* x is a control point in the data */ @@ -422,18 +422,6 @@ Curve::multipoint_eval (double x) return (*range.first)->value; } -ControlEvent* -Curve::point_factory (double when, double val) const -{ - return new CurvePoint (when, val); -} - -ControlEvent* -Curve::point_factory (const ControlEvent& other) const -{ - return new CurvePoint (other.when, other.value); -} - extern "C" { void diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 356c0df5c1..5a65135f75 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -104,6 +104,10 @@ setup_enum_writer () REGISTER_ENUM (PluginAutomation); REGISTER_ENUM (SoloAutomation); REGISTER_ENUM (MuteAutomation); + REGISTER_ENUM (MidiCCAutomation); + REGISTER_ENUM (FadeInAutomation); + REGISTER_ENUM (FadeOutAutomation); + REGISTER_ENUM (EnvelopeAutomation); REGISTER_BITS (_AutomationType); REGISTER_ENUM (Off); diff --git a/libs/ardour/gain.cc b/libs/ardour/gain.cc index 369df7348c..b067e6c08d 100644 --- a/libs/ardour/gain.cc +++ b/libs/ardour/gain.cc @@ -22,12 +22,12 @@ using namespace ARDOUR; Gain::Gain () - : Curve (0.0, 2.0, 1.0f) /* XXX yuck; clamps gain to -inf .. +6db */ + : AutomationList (ParamID(GainAutomation), 0.0, 2.0, 1.0f) /* XXX yuck; clamps gain to -inf .. +6db */ { } Gain::Gain (const Gain& other) - : Curve (other) + : AutomationList (other) { } @@ -35,7 +35,7 @@ Gain& Gain::operator= (const Gain& other) { if (this != &other) { - Curve::operator= (other); + AutomationList::operator= (other); } return *this; } diff --git a/libs/ardour/insert.cc b/libs/ardour/insert.cc index bb02cb0925..1e73c398c2 100644 --- a/libs/ardour/insert.cc +++ b/libs/ardour/insert.cc @@ -157,7 +157,7 @@ Insert::state (bool full_state) XMLNode& automation = Automatable::get_automation_state(); - for (set<uint32_t>::iterator x = _visible_parameter_automation.begin(); x != _visible_parameter_automation.end(); ++x) { + for (set<ParamID>::iterator x = _visible_parameter_automation.begin(); x != _visible_parameter_automation.end(); ++x) { if (x != _visible_parameter_automation.begin()) { sstr << ' '; } @@ -196,7 +196,7 @@ Insert::set_state (const XMLNode& node) if ((prop = (*niter)->property ("path")) != 0) { old_set_automation_state (*(*niter)); } else { - set_automation_state (*(*niter)); + set_automation_state (*(*niter), ParamID(PluginAutomation)); } if ((prop = (*niter)->property ("visible")) != 0) { @@ -211,7 +211,8 @@ Insert::set_state (const XMLNode& node) if (sstr.fail()) { break; } - mark_automation_visible (what, true); + // FIXME: other automation types? + mark_automation_visible (ParamID(PluginAutomation, what), true); } } diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 3069123f16..c60628b603 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -101,11 +101,10 @@ static double direct_gain_to_control (gain_t gain) { IO::IO (Session& s, const string& name, int input_min, int input_max, int output_min, int output_max, DataType default_type) - : SessionObject(s, name), - _output_buffers(new BufferSet()), - _default_type(default_type), + : Automatable (s, name), + _output_buffers (new BufferSet()), + _default_type (default_type), _gain_control (X_("gaincontrol"), *this), - _gain_automation_curve (0.0, 2.0, 1.0), _input_minimum (ChanCount::ZERO), _input_maximum (ChanCount::INFINITE), _output_minimum (ChanCount::ZERO), @@ -136,12 +135,14 @@ IO::IO (Session& s, const string& name, _phase_invert = false; deferred_state = 0; + add_automation_parameter(new AutomationList(ParamID(GainAutomation), 0.0, 2.0, 1.0)); + apply_gain_automation = false; last_automation_snapshot = 0; - _gain_automation_state = Off; - _gain_automation_style = Absolute; + /*_gain_automation_state = Off; + _gain_automation_style = Absolute;*/ { // IO::Meter is emitted from another thread so the @@ -157,11 +158,10 @@ IO::IO (Session& s, const string& name, } IO::IO (Session& s, const XMLNode& node, DataType dt) - : SessionObject(s, "unnamed io"), - _output_buffers(new BufferSet()), + : Automatable (s, "unnamed io"), + _output_buffers (new BufferSet()), _default_type (dt), - _gain_control (X_("gaincontrol"), *this), - _gain_automation_curve (0, 0, 0) // all reset in set_state() + _gain_control (X_("gaincontrol"), *this) { _meter = new PeakMeter (_session); @@ -1129,7 +1129,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) gain_t IO::effective_gain () const { - if (_gain_automation_curve.automation_playback()) { + if (gain_automation().automation_playback()) { return _effective_gain; } else { return _desired_gain; @@ -1288,18 +1288,9 @@ IO::state (bool full_state) node->add_property ("iolimits", buf); /* automation */ - - if (full_state) { - - XMLNode* autonode = new XMLNode (X_("Automation")); - autonode->add_child_nocopy (get_automation_state()); - node->add_child_nocopy (*autonode); - - snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state()); - } else { - /* never store anything except Off for automation state in a template */ - snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); - } + + if (full_state) + node->add_child_nocopy (get_automation_state()); return *node; } @@ -1363,7 +1354,7 @@ IO::set_state (const XMLNode& node) if ((*iter)->name() == X_("Automation")) { - set_automation_state (*(*iter)->children().front()); + set_automation_state (*(*iter), ParamID(GainAutomation)); } if ((*iter)->name() == X_("controllable")) { @@ -1411,18 +1402,6 @@ IO::set_state (const XMLNode& node) } int -IO::set_automation_state (const XMLNode& node) -{ - return _gain_automation_curve.set_state (node); -} - -XMLNode& -IO::get_automation_state () -{ - return (_gain_automation_curve.get_state ()); -} - -int IO::load_automation (string path) { string fullpath; @@ -1479,7 +1458,7 @@ IO::load_automation (string path) switch (type) { case 'g': - _gain_automation_curve.fast_simple_add (when, value); + gain_automation().fast_simple_add (when, value); break; case 's': @@ -2198,41 +2177,43 @@ IO::meter () void IO::clear_automation () { - Glib::Mutex::Lock lm (automation_lock); - _gain_automation_curve.clear (); + Automatable::clear_automation (); // clears gain automation _panner->clear_automation (); } void -IO::set_gain_automation_state (AutoState state) +IO::set_parameter_automation_state (ParamID param, AutoState state) { - bool changed = false; + // XXX: would be nice to get rid of this special hack - { - Glib::Mutex::Lock lm (automation_lock); + if (param.type() == GainAutomation) { - if (state != _gain_automation_curve.automation_state()) { - changed = true; - last_automation_snapshot = 0; - _gain_automation_curve.set_automation_state (state); - - if (state != Off) { - set_gain (_gain_automation_curve.eval (_session.transport_frame()), this); + bool changed = false; + + { + Glib::Mutex::Lock lm (_automation_lock); + + ARDOUR::AutomationList& gain_auto = gain_automation(); + + if (state != gain_auto.automation_state()) { + changed = true; + last_automation_snapshot = 0; + gain_auto.set_automation_state (state); + + if (state != Off) { + // FIXME: shouldn't this use Curve? + set_gain (gain_auto.eval (_session.transport_frame()), this); + } } } - } - if (changed) { - _session.set_dirty (); - //gain_automation_state_changed (); /* EMIT SIGNAL */ - } -} + if (changed) { + _session.set_dirty (); + } -void -IO::set_gain_automation_style (AutoStyle style) -{ - Glib::Mutex::Lock lm (automation_lock); - _gain_automation_curve.set_automation_style (style); + } else { + Automatable::set_parameter_automation_state(param, state); + } } void @@ -2263,8 +2244,10 @@ IO::set_gain (gain_t val, void *src) gain_changed (src); _gain_control.Changed (); /* EMIT SIGNAL */ - if (_session.transport_stopped() && src != 0 && src != this && _gain_automation_curve.automation_write()) { - _gain_automation_curve.add (_session.transport_frame(), val); + ARDOUR::AutomationList& gain_auto = gain_automation(); + + if (_session.transport_stopped() && src != 0 && src != this && gain_auto.automation_write()) { + gain_auto.add (_session.transport_frame(), val); } @@ -2272,18 +2255,6 @@ IO::set_gain (gain_t val, void *src) } void -IO::start_gain_touch () -{ - _gain_automation_curve.start_touch (); -} - -void -IO::end_gain_touch () -{ - _gain_automation_curve.stop_touch (); -} - -void IO::start_pan_touch (uint32_t which) { if (which < _panner->size()) { @@ -2305,8 +2276,10 @@ IO::automation_snapshot (nframes_t now) { if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) { - if (_gain_automation_curve.automation_write()) { - _gain_automation_curve.rt_add (now, gain()); + ARDOUR::AutomationList& gain_auto = gain_automation(); + + if (gain_auto.automation_write()) { + gain_auto.rt_add (now, gain()); } _panner->snapshot (now); @@ -2318,15 +2291,18 @@ IO::automation_snapshot (nframes_t now) void IO::transport_stopped (nframes_t frame) { - _gain_automation_curve.reposition_for_rt_add (frame); + ARDOUR::AutomationList& gain_auto = gain_automation(); + + gain_auto.reposition_for_rt_add (frame); - if (_gain_automation_curve.automation_state() != Off) { + if (gain_auto.automation_state() != Off) { /* the src=0 condition is a special signal to not propagate automation gain changes into the mix group when locating. */ - set_gain (_gain_automation_curve.eval (frame), 0); + // FIXME: shouldn't this use Curve? + set_gain (gain_auto.eval (frame), 0); } _panner->transport_stopped (frame); diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index 0c2104ca7a..98a65546a9 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -318,7 +318,7 @@ LadspaPlugin::set_parameter (uint32_t which, float val) { if (which < descriptor->PortCount) { shadow_data[which] = (LADSPA_Data) val; - ParameterChanged (which, val); /* EMIT SIGNAL */ + ParameterChanged (ParamID(PluginAutomation, which), val); /* EMIT SIGNAL */ if (which < parameter_count() && controls[which]) { controls[which]->Changed (); @@ -493,10 +493,10 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des string -LadspaPlugin::describe_parameter (uint32_t which) +LadspaPlugin::describe_parameter (ParamID which) { - if (which < parameter_count()) { - return port_names()[which]; + if (which.type() == PluginAutomation && which.id() < parameter_count()) { + return port_names()[which.id()]; } else { return "??"; } @@ -512,16 +512,16 @@ LadspaPlugin::latency () const } } -set<uint32_t> +set<ParamID> LadspaPlugin::automatable () const { - set<uint32_t> ret; + set<ParamID> ret; for (uint32_t i = 0; i < parameter_count(); ++i){ if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && LADSPA_IS_PORT_CONTROL(port_descriptor (i))){ - ret.insert (ret.end(), i); + ret.insert (ret.end(), ParamID(PluginAutomation, i)); } } diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index a56f1d0392..d5a238e253 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -190,7 +190,7 @@ StreamPanner::add_state (XMLNode& node) /*---------------------------------------------------------------------- */ BaseStereoPanner::BaseStereoPanner (Panner& p) - : StreamPanner (p), _automation (0.0, 1.0, 0.5) + : StreamPanner (p), _automation (ParamID(PanAutomation), 0.0, 1.0, 0.5) { } @@ -438,7 +438,7 @@ EqualPowerStereoPanner::distribute_automated (AudioBuffer& srcbuf, BufferSet& ob /* fetch positional data */ - if (!_automation.rt_safe_get_vector (start, end, buffers[0], nframes)) { + if (!_automation.curve().rt_safe_get_vector (start, end, buffers[0], nframes)) { /* fallback */ if (!_muted) { distribute (srcbuf, obufs, 1.0, nframes); @@ -565,7 +565,7 @@ EqualPowerStereoPanner::set_state (const XMLNode& node) /*----------------------------------------------------------------------*/ Multi2dPanner::Multi2dPanner (Panner& p) - : StreamPanner (p), _automation (0.0, 1.0, 0.5) // XXX useless + : StreamPanner (p), _automation (ParamID(PanAutomation), 0.0, 1.0, 0.5) // XXX useless { update (); } diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc index b1cb97a475..1b93ecf82b 100644 --- a/libs/ardour/plugin.cc +++ b/libs/ardour/plugin.cc @@ -94,7 +94,7 @@ Plugin::get_nth_control (uint32_t n) get_parameter_descriptor (n, desc); - controls[n] = new PortControllable (describe_parameter (n), *this, n, + controls[n] = new PortControllable (describe_parameter (ParamID(PluginAutomation, n)), *this, n, desc.lower, desc.upper, desc.toggled, desc.logarithmic); } diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index baf22a6863..1b0130fc37 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -152,18 +152,21 @@ PluginInsert::~PluginInsert () } void -PluginInsert::automation_list_creation_callback (uint32_t which, AutomationList& alist) +PluginInsert::automation_list_creation_callback (ParamID which, AutomationList& alist) { alist.automation_state_changed.connect (sigc::bind (mem_fun (*this, &PluginInsert::auto_state_changed), (which))); } void -PluginInsert::auto_state_changed (uint32_t which) +PluginInsert::auto_state_changed (ParamID which) { - AutomationList& alist (automation_list (which)); + if (which.type() != PluginAutomation) + return; + + AutomationList* alist = automation_list (which); - if (alist.automation_state() != Off) { - _plugins[0]->set_parameter (which, alist.eval (_session.transport_frame())); + if (alist && alist->automation_state() != Off) { + _plugins[0]->set_parameter (which.id(), alist->eval (_session.transport_frame())); } } @@ -210,18 +213,21 @@ PluginInsert::is_generator() const void PluginInsert::set_automatable () { - set<uint32_t> a; + set<ParamID> a; a = _plugins.front()->automatable (); - for (set<uint32_t>::iterator i = a.begin(); i != a.end(); ++i) { + for (set<ParamID>::iterator i = a.begin(); i != a.end(); ++i) { can_automate (*i); } } void -PluginInsert::parameter_changed (uint32_t which, float val) +PluginInsert::parameter_changed (ParamID which, float val) { + if (which.type() != PluginAutomation) + return; + vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); /* don't set the first plugin, just all the slaves */ @@ -270,14 +276,14 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off if (with_auto) { - map<uint32_t,AutomationList*>::iterator li; + map<ParamID,AutomationList*>::iterator li; uint32_t n; for (n = 0, li = _parameter_automation.begin(); li != _parameter_automation.end(); ++li, ++n) { AutomationList& alist (*((*li).second)); - if (alist.automation_playback()) { + if (alist.param_id().type() == PluginAutomation && alist.automation_playback()) { bool valid; float val = alist.rt_safe_eval (now, valid); @@ -301,12 +307,13 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off void PluginInsert::automation_snapshot (nframes_t now) { - map<uint32_t,AutomationList*>::iterator li; + map<ParamID,AutomationList*>::iterator li; for (li =_parameter_automation.begin(); li !=_parameter_automation.end(); ++li) { AutomationList *alist = ((*li).second); - if (alist != 0 && alist->automation_write ()) { + if (alist != 0 && alist->param_id().type() == PluginAutomation + && alist->automation_write ()) { float val = _plugins[0]->get_parameter ((*li).first); alist->rt_add (now, val); @@ -318,13 +325,13 @@ PluginInsert::automation_snapshot (nframes_t now) void PluginInsert::transport_stopped (nframes_t now) { - map<uint32_t,AutomationList*>::iterator li; + map<ParamID,AutomationList*>::iterator li; for (li =_parameter_automation.begin(); li !=_parameter_automation.end(); ++li) { AutomationList& alist (*(li->second)); alist.reposition_for_rt_add (now); - if (alist.automation_state() != Off) { + if (alist.param_id().type() == PluginAutomation && alist.automation_state() != Off) { _plugins[0]->set_parameter (li->first, alist.eval (now)); } } @@ -374,14 +381,17 @@ PluginInsert::run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, } void -PluginInsert::set_parameter (uint32_t port, float val) +PluginInsert::set_parameter (ParamID param, float val) { + if (param.type() != PluginAutomation) + return; + /* the others will be set from the event triggered by this */ - _plugins[0]->set_parameter (port, val); + _plugins[0]->set_parameter (param.id(), val); - if (automation_list (port).automation_write()) { - automation_list (port).add (_session.audible_frame(), val); + if (automation_list (param) && automation_list (param)->automation_write()) { + automation_list (param)->add (_session.audible_frame(), val); } _session.set_dirty(); @@ -432,63 +442,18 @@ PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offs } float -PluginInsert::default_parameter_value (uint32_t port) +PluginInsert::default_parameter_value (ParamID param) { + if (param.type() != PluginAutomation) + return 1.0; + if (_plugins.empty()) { fatal << _("programming error: ") << X_("PluginInsert::default_parameter_value() called with no plugin") << endmsg; /*NOTREACHED*/ } - return _plugins[0]->default_value (port); -} - -void -PluginInsert::set_port_automation_state (uint32_t port, AutoState s) -{ - if (port < _plugins[0]->parameter_count()) { - - AutomationList& al = automation_list (port); - - if (s != al.automation_state()) { - al.set_automation_state (s); - _session.set_dirty (); - } - } -} - -AutoState -PluginInsert::get_port_automation_state (uint32_t port) -{ - if (port < _plugins[0]->parameter_count()) { - return automation_list (port).automation_state(); - } else { - return Off; - } -} - -void -PluginInsert::protect_automation () -{ - set<uint32_t> automated_params; - - what_has_automation (automated_params); - - for (set<uint32_t>::iterator i = automated_params.begin(); i != automated_params.end(); ++i) { - - AutomationList& al = automation_list (*i); - - switch (al.automation_state()) { - case Write: - al.set_automation_state (Off); - break; - case Touch: - al.set_automation_state (Play); - break; - default: - break; - } - } + return _plugins[0]->default_value (param.id()); } boost::shared_ptr<Plugin> @@ -684,16 +649,18 @@ PluginInsert::state (bool full) /* add port automation state */ XMLNode *autonode = new XMLNode(port_automation_node_name); - set<uint32_t> automatable = _plugins[0]->automatable(); + set<ParamID> automatable = _plugins[0]->automatable(); - for (set<uint32_t>::iterator x = automatable.begin(); x != automatable.end(); ++x) { + for (set<ParamID>::iterator x = automatable.begin(); x != automatable.end(); ++x) { - XMLNode* child = new XMLNode("port"); + /*XMLNode* child = new XMLNode("port"); snprintf(buf, sizeof(buf), "%" PRIu32, *x); child->add_property("number", string(buf)); child->add_child_nocopy (automation_list (*x).state (full)); autonode->add_child_nocopy (*child); + */ + autonode->add_child_nocopy (automation_list (*x)->state (full)); } node.add_child_nocopy (*autonode); @@ -824,7 +791,7 @@ PluginInsert::set_state(const XMLNode& node) } if (!child->children().empty()) { - automation_list (port_id).set_state (*child->children().front()); + automation_list (ParamID(PluginAutomation, port_id), true)->set_state (*child->children().front()); } else { if ((cprop = child->property("auto")) != 0) { @@ -832,13 +799,13 @@ PluginInsert::set_state(const XMLNode& node) int x; sscanf (cprop->value().c_str(), "0x%x", &x); - automation_list (port_id).set_automation_state (AutoState (x)); + automation_list (ParamID(PluginAutomation, port_id), true)->set_automation_state (AutoState (x)); } else { /* missing */ - automation_list (port_id).set_automation_state (Off); + automation_list (ParamID(PluginAutomation, port_id), true)->set_automation_state (Off); } } @@ -860,9 +827,12 @@ PluginInsert::set_state(const XMLNode& node) } string -PluginInsert::describe_parameter (uint32_t what) +PluginInsert::describe_parameter (ParamID param) { - return _plugins[0]->describe_parameter (what); + if (param.type() != PluginAutomation) + return Automatable::describe_parameter(param); + + return _plugins[0]->describe_parameter (param); } ARDOUR::nframes_t diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 883bc0f6aa..3353149efa 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -2380,12 +2380,14 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra apply_gain_automation = false; { - Glib::Mutex::Lock am (automation_lock, Glib::TRY_LOCK); + Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK); if (am.locked() && _session.transport_rolling()) { - if (_gain_automation_curve.automation_playback()) { - apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); + ARDOUR::AutomationList& gain_auto = gain_automation(); + + if (gain_auto.automation_playback()) { + apply_gain_automation = gain_auto.curve().rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); } } } @@ -2562,33 +2564,10 @@ Route::set_block_size (nframes_t nframes) void Route::protect_automation () { - switch (gain_automation_state()) { - case Write: - set_gain_automation_state (Off); - case Touch: - set_gain_automation_state (Play); - break; - default: - break; - } - - switch (panner().automation_state ()) { - case Write: - panner().set_automation_state (Off); - break; - case Touch: - panner().set_automation_state (Play); - break; - default: - break; - } + Automatable::protect_automation(); - for (InsertList::iterator i = _inserts.begin(); i != _inserts.end(); ++i) { - boost::shared_ptr<PluginInsert> pi; - if ((pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) { - pi->protect_automation (); - } - } + for (InsertList::iterator i = _inserts.begin(); i != _inserts.end(); ++i) + (*i)->protect_automation(); } void diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc index aee4d4004c..37867a2cfa 100644 --- a/libs/ardour/send.cc +++ b/libs/ardour/send.cc @@ -103,7 +103,7 @@ Send::set_state(const XMLNode& node) if ((*niter)->name() == "Redirect") { insert_node = *niter; } else if ((*niter)->name() == X_("Automation")) { - _io->set_automation_state (*(*niter)); + _io->set_automation_state (*(*niter), ParamID(GainAutomation)); } } diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index b192857079..16b35397cf 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -1019,7 +1019,7 @@ void MackieControlProtocol::notify_panner_changed( RouteSignal * route_signal ) // TODO handle plugin automation polling void MackieControlProtocol::update_automation( RouteSignal & rs ) { - ARDOUR::AutoState gain_state = rs.route().gain_automation_state(); + ARDOUR::AutoState gain_state = rs.route().gain_automation().automation_state(); if ( gain_state == Touch || gain_state == Play ) { notify_gain_changed( &rs ); |