From 05184ed52ffcdcad3c071d4c99287f832f42b74b Mon Sep 17 00:00:00 2001 From: David Robillard Date: Wed, 20 Jun 2007 03:39:19 +0000 Subject: Split PortInsert and PluginInsert into their own files. git-svn-id: svn://localhost/ardour2/trunk@2026 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/io_selector.cc | 2 +- gtk2_ardour/ladspa_pluginui.cc | 2 +- gtk2_ardour/plugin_ui.cc | 2 +- gtk2_ardour/redirect_automation_line.cc | 2 +- gtk2_ardour/redirect_box.cc | 3 +- gtk2_ardour/route_params_ui.cc | 3 +- libs/ardour/SConscript | 2 + libs/ardour/ardour/insert.h | 117 ---- libs/ardour/ardour/plugin_insert.h | 125 ++++ libs/ardour/ardour/port_insert.h | 74 +++ libs/ardour/audio_track.cc | 1 + libs/ardour/insert.cc | 1044 +------------------------------ libs/ardour/plugin_insert.cc | 884 ++++++++++++++++++++++++++ libs/ardour/port_insert.cc | 242 +++++++ libs/ardour/redirect.cc | 3 +- libs/ardour/route.cc | 2 + libs/ardour/session.cc | 2 + 17 files changed, 1344 insertions(+), 1166 deletions(-) create mode 100644 libs/ardour/ardour/plugin_insert.h create mode 100644 libs/ardour/ardour/port_insert.h create mode 100644 libs/ardour/plugin_insert.cc create mode 100644 libs/ardour/port_insert.cc diff --git a/gtk2_ardour/io_selector.cc b/gtk2_ardour/io_selector.cc index fde793adc7..f40ae8d8b7 100644 --- a/gtk2_ardour/io_selector.cc +++ b/gtk2_ardour/io_selector.cc @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include diff --git a/gtk2_ardour/ladspa_pluginui.cc b/gtk2_ardour/ladspa_pluginui.cc index 1f7ba8d4c0..3b99297135 100644 --- a/gtk2_ardour/ladspa_pluginui.cc +++ b/gtk2_ardour/ladspa_pluginui.cc @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include diff --git a/gtk2_ardour/plugin_ui.cc b/gtk2_ardour/plugin_ui.cc index 90d19d81b6..d46cea5eeb 100644 --- a/gtk2_ardour/plugin_ui.cc +++ b/gtk2_ardour/plugin_ui.cc @@ -37,7 +37,7 @@ #include #include -#include +#include #include #ifdef VST_SUPPORT #include diff --git a/gtk2_ardour/redirect_automation_line.cc b/gtk2_ardour/redirect_automation_line.cc index b1b19d530f..0714a2bdbb 100644 --- a/gtk2_ardour/redirect_automation_line.cc +++ b/gtk2_ardour/redirect_automation_line.cc @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include "i18n.h" diff --git a/gtk2_ardour/redirect_box.cc b/gtk2_ardour/redirect_box.cc index 30bec50a19..7ce64d0f3a 100644 --- a/gtk2_ardour/redirect_box.cc +++ b/gtk2_ardour/redirect_box.cc @@ -43,7 +43,8 @@ #include #include #include -#include +#include +#include #include #include "ardour_ui.h" diff --git a/gtk2_ardour/route_params_ui.cc b/gtk2_ardour/route_params_ui.cc index 03e0f2ca03..b50c5effe7 100644 --- a/gtk2_ardour/route_params_ui.cc +++ b/gtk2_ardour/route_params_ui.cc @@ -34,7 +34,8 @@ #include #include #include -#include +#include +#include #include "route_params_ui.h" #include "keyboard.h" diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 6b4dab456f..c3025ddf97 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -75,6 +75,8 @@ gdither.cc globals.cc import.cc insert.cc +plugin_insert.cc +port_insert.cc io.cc jack_slave.cc ladspa_plugin.cc diff --git a/libs/ardour/ardour/insert.h b/libs/ardour/ardour/insert.h index 10902a76b0..618b7c6446 100644 --- a/libs/ardour/ardour/insert.h +++ b/libs/ardour/ardour/insert.h @@ -32,15 +32,9 @@ class XMLNode; -namespace MIDI { - class Port; -} - namespace ARDOUR { class Session; -class Route; -class Plugin; class Insert : public Redirect { @@ -64,117 +58,6 @@ protected: ChanCount _configured_input; }; -class PortInsert : public Insert -{ - public: - PortInsert (Session&, Placement); - PortInsert (Session&, const XMLNode&); - PortInsert (const PortInsert&); - ~PortInsert (); - - XMLNode& state(bool full); - XMLNode& get_state(void); - int set_state(const XMLNode&); - - void init (); - - void run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset); - - nframes_t latency(); - - ChanCount output_streams() const; - ChanCount input_streams() const; - - virtual bool can_support_input_configuration (ChanCount in) const; - virtual ChanCount output_for_input_configuration (ChanCount in) const; - virtual bool configure_io (ChanCount in, ChanCount out); - - uint32_t bit_slot() const { return bitslot; } - - private: - uint32_t bitslot; -}; - -class PluginInsert : public Insert -{ - public: - PluginInsert (Session&, boost::shared_ptr, Placement); - PluginInsert (Session&, const XMLNode&); - PluginInsert (const PluginInsert&); - ~PluginInsert (); - - static const string port_automation_node_name; - - XMLNode& state(bool); - XMLNode& get_state(void); - int set_state(const XMLNode&); - - void run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset); - void silence (nframes_t nframes, nframes_t offset); - - void activate (); - void deactivate (); - - void set_block_size (nframes_t nframes); - - ChanCount output_streams() const; - ChanCount input_streams() const; - ChanCount natural_output_streams() const; - ChanCount natural_input_streams() const; - - bool set_count (uint32_t num); - uint32_t get_count () const { return _plugins.size(); } - - virtual bool can_support_input_configuration (ChanCount in) const; - virtual ChanCount output_for_input_configuration (ChanCount in) const; - virtual bool configure_io (ChanCount in, ChanCount out); - - bool is_generator() const; - - void set_parameter (uint32_t port, 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); - - boost::shared_ptr plugin(uint32_t num=0) const { - if (num < _plugins.size()) { - return _plugins[num]; - } else { - return _plugins[0]; // we always have one - } - } - - PluginType type (); - - string describe_parameter (uint32_t); - - nframes_t latency(); - - void transport_stopped (nframes_t now); - void automation_snapshot (nframes_t now); - - private: - - void parameter_changed (uint32_t, float); - - vector > _plugins; - - void automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offset); - void connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now = 0); - - void init (); - void set_automatable (); - void auto_state_changed (uint32_t which); - void automation_list_creation_callback (uint32_t, AutomationList&); - - int32_t count_for_configuration (ChanCount in, ChanCount out) const; - - boost::shared_ptr plugin_factory (boost::shared_ptr); -}; - } // namespace ARDOUR #endif /* __ardour_insert_h__ */ diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h new file mode 100644 index 0000000000..37cf0d49b2 --- /dev/null +++ b/libs/ardour/ardour/plugin_insert.h @@ -0,0 +1,125 @@ +/* + Copyright (C) 2000,2007 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_plugin_insert_h__ +#define __ardour_plugin_insert_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include + +class XMLNode; + +namespace ARDOUR { + +class Session; +class Route; +class Plugin; + +/** Plugin inserts: send data through a plugin + */ +class PluginInsert : public Insert +{ + public: + PluginInsert (Session&, boost::shared_ptr, Placement); + PluginInsert (Session&, const XMLNode&); + PluginInsert (const PluginInsert&); + ~PluginInsert (); + + static const string port_automation_node_name; + + XMLNode& state(bool); + XMLNode& get_state(void); + int set_state(const XMLNode&); + + void run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset); + void silence (nframes_t nframes, nframes_t offset); + + void activate (); + void deactivate (); + + void set_block_size (nframes_t nframes); + + ChanCount output_streams() const; + ChanCount input_streams() const; + ChanCount natural_output_streams() const; + ChanCount natural_input_streams() const; + + bool set_count (uint32_t num); + uint32_t get_count () const { return _plugins.size(); } + + virtual bool can_support_input_configuration (ChanCount in) const; + virtual ChanCount output_for_input_configuration (ChanCount in) const; + virtual bool configure_io (ChanCount in, ChanCount out); + + bool is_generator() const; + + void set_parameter (uint32_t port, 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); + + boost::shared_ptr plugin(uint32_t num=0) const { + if (num < _plugins.size()) { + return _plugins[num]; + } else { + return _plugins[0]; // we always have one + } + } + + PluginType type (); + + string describe_parameter (uint32_t); + + nframes_t latency(); + + void transport_stopped (nframes_t now); + void automation_snapshot (nframes_t now); + + private: + + void parameter_changed (uint32_t, float); + + vector > _plugins; + + void automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offset); + void connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now = 0); + + void init (); + void set_automatable (); + void auto_state_changed (uint32_t which); + void automation_list_creation_callback (uint32_t, AutomationList&); + + int32_t count_for_configuration (ChanCount in, ChanCount out) const; + + boost::shared_ptr plugin_factory (boost::shared_ptr); +}; + +} // namespace ARDOUR + +#endif /* __ardour_plugin_insert_h__ */ diff --git a/libs/ardour/ardour/port_insert.h b/libs/ardour/ardour/port_insert.h new file mode 100644 index 0000000000..4898efc789 --- /dev/null +++ b/libs/ardour/ardour/port_insert.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2000,2007 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_port_insert_h__ +#define __ardour_port_insert_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include + +class XMLNode; + +namespace ARDOUR { + +class Session; + +/** Port inserts: send output to a Jack port, pick up input at a Jack port + */ +class PortInsert : public Insert +{ + public: + PortInsert (Session&, Placement); + PortInsert (Session&, const XMLNode&); + PortInsert (const PortInsert&); + ~PortInsert (); + + XMLNode& state(bool full); + XMLNode& get_state(void); + int set_state(const XMLNode&); + + void init (); + + void run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset); + + nframes_t latency(); + + ChanCount output_streams() const; + ChanCount input_streams() const; + + virtual bool can_support_input_configuration (ChanCount in) const; + virtual ChanCount output_for_input_configuration (ChanCount in) const; + virtual bool configure_io (ChanCount in, ChanCount out); + + uint32_t bit_slot() const { return bitslot; } + + private: + uint32_t bitslot; +}; + +} // namespace ARDOUR + +#endif /* __ardour_port_insert_h__ */ diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index b066f4000e..d0012b6d80 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/ardour/insert.cc b/libs/ardour/insert.cc index 88a5c1a14d..519bf1ad83 100644 --- a/libs/ardour/insert.cc +++ b/libs/ardour/insert.cc @@ -49,1057 +49,17 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -/* ********** FIXME: TYPE ************** - * Inserts are still definitely audio only */ Insert::Insert(Session& s, string name, Placement p) : Redirect (s, name, p) , _configured(false) { + // FIXME: default type? } Insert::Insert(Session& s, string name, Placement p, int imin, int imax, int omin, int omax) : Redirect (s, name, p, imin, imax, omin, omax) , _configured(false) { -} - -/*************************************************************** - Plugin inserts: send data through a plugin - ***************************************************************/ - -const string PluginInsert::port_automation_node_name = "PortAutomation"; - -PluginInsert::PluginInsert (Session& s, boost::shared_ptr plug, Placement placement) - : Insert (s, plug->name(), placement) -{ - /* the first is the master */ - - _plugins.push_back (plug); - - _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed)); - - init (); - - { - Glib::Mutex::Lock em (_session.engine().process_lock()); - IO::MoreChannels (max(input_streams(), output_streams())); - } - - RedirectCreated (this); /* EMIT SIGNAL */ -} - -PluginInsert::PluginInsert (Session& s, const XMLNode& node) - : Insert (s, "will change", PreFader) -{ - if (set_state (node)) { - throw failed_constructor(); - } - - set_automatable (); - - _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed)); - - { - Glib::Mutex::Lock em (_session.engine().process_lock()); - IO::MoreChannels (max(input_streams(), output_streams())); - } -} - -PluginInsert::PluginInsert (const PluginInsert& other) - : Insert (other._session, other.plugin()->name(), other.placement()) -{ - uint32_t count = other._plugins.size(); - - /* make as many copies as requested */ - for (uint32_t n = 0; n < count; ++n) { - _plugins.push_back (plugin_factory (other.plugin (n))); - } - - - _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed)); - - init (); - - RedirectCreated (this); /* EMIT SIGNAL */ -} - -bool -PluginInsert::set_count (uint32_t num) -{ - bool require_state = !_plugins.empty(); - - /* this is a bad idea.... we shouldn't do this while active. - only a route holding their redirect_lock should be calling this - */ - - if (num == 0) { - return false; - } else if (num > _plugins.size()) { - uint32_t diff = num - _plugins.size(); - - for (uint32_t n = 0; n < diff; ++n) { - _plugins.push_back (plugin_factory (_plugins[0])); - - if (require_state) { - /* XXX do something */ - } - } - - } else if (num < _plugins.size()) { - uint32_t diff = _plugins.size() - num; - for (uint32_t n= 0; n < diff; ++n) { - _plugins.pop_back(); - } - } - - return true; -} - -void -PluginInsert::init () -{ - set_automatable (); - - set::iterator s; -} - -PluginInsert::~PluginInsert () -{ - GoingAway (); /* EMIT SIGNAL */ -} - -void -PluginInsert::automation_list_creation_callback (uint32_t 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) -{ - AutomationList& alist (automation_list (which)); - - if (alist.automation_state() != Off) { - _plugins[0]->set_parameter (which, alist.eval (_session.transport_frame())); - } -} - -ChanCount -PluginInsert::output_streams() const -{ - if (_configured) - return output_for_input_configuration(_configured_input); - else - return natural_output_streams(); -} - -ChanCount -PluginInsert::input_streams() const -{ - if (_configured) - return _configured_input; - else - return natural_input_streams(); -} - -ChanCount -PluginInsert::natural_output_streams() const -{ - return _plugins[0]->get_info()->n_outputs; -} - -ChanCount -PluginInsert::natural_input_streams() const -{ - return _plugins[0]->get_info()->n_inputs; -} - -bool -PluginInsert::is_generator() const -{ - /* XXX more finesse is possible here. VST plugins have a - a specific "instrument" flag, for example. - */ - - return _plugins[0]->get_info()->n_inputs.n_audio() == 0; -} - -void -PluginInsert::set_automatable () -{ - set a; - - a = _plugins.front()->automatable (); - - for (set::iterator i = a.begin(); i != a.end(); ++i) { - can_automate (*i); - } -} - -void -PluginInsert::parameter_changed (uint32_t which, float val) -{ - vector >::iterator i = _plugins.begin(); - - /* don't set the first plugin, just all the slaves */ - - if (i != _plugins.end()) { - ++i; - for (; i != _plugins.end(); ++i) { - (*i)->set_parameter (which, val); - } - } -} - -void -PluginInsert::set_block_size (nframes_t nframes) -{ - for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->set_block_size (nframes); - } -} - -void -PluginInsert::activate () -{ - for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->activate (); - } -} - -void -PluginInsert::deactivate () -{ - for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->deactivate (); - } -} - -void -PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now) -{ - uint32_t in_index = 0; - uint32_t out_index = 0; - - /* Note that we've already required that plugins - be able to handle in-place processing. - */ - - if (with_auto) { - - map::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()) { - bool valid; - - float val = alist.rt_safe_eval (now, valid); - - if (valid) { - /* set the first plugin, the others will be set via signals */ - _plugins[0]->set_parameter ((*li).first, val); - } - - } - } - } - - for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->connect_and_run (bufs, in_index, out_index, nframes, offset); - } - - /* leave remaining channel buffers alone */ -} - -void -PluginInsert::automation_snapshot (nframes_t now) -{ - map::iterator li; - - for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) { - - AutomationList *alist = ((*li).second); - if (alist != 0 && alist->automation_write ()) { - - float val = _plugins[0]->get_parameter ((*li).first); - alist->rt_add (now, val); - last_automation_snapshot = now; - } - } -} - -void -PluginInsert::transport_stopped (nframes_t now) -{ - map::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) { - _plugins[0]->set_parameter (li->first, alist.eval (now)); - } - } -} - -void -PluginInsert::silence (nframes_t nframes, nframes_t offset) -{ - uint32_t in_index = 0; - uint32_t out_index = 0; - - if (active()) { - for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->connect_and_run (_session.get_silent_buffers ((*i)->get_info()->n_inputs), in_index, out_index, nframes, offset); - } - } -} - -void -PluginInsert::run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) -{ - if (active()) { - - if (_session.transport_rolling()) { - automation_run (bufs, nframes, offset); - } else { - connect_and_run (bufs, nframes, offset, false); - } - } else { - uint32_t in = _plugins[0]->get_info()->n_inputs.n_audio(); - uint32_t out = _plugins[0]->get_info()->n_outputs.n_audio(); - - if (out > in) { - - /* not active, but something has make up for any channel count increase */ - - for (uint32_t n = out - in; n < out; ++n) { - memcpy (bufs.get_audio(n).data(nframes, offset), bufs.get_audio(in - 1).data(nframes, offset), sizeof (Sample) * nframes); - } - } - - bufs.count().set(_default_type, out); - } -} - -void -PluginInsert::set_parameter (uint32_t port, float val) -{ - /* the others will be set from the event triggered by this */ - - _plugins[0]->set_parameter (port, val); - - if (automation_list (port).automation_write()) { - automation_list (port).add (_session.audible_frame(), val); - } - - _session.set_dirty(); -} - -void -PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offset) -{ - ControlEvent next_event (0, 0.0f); - nframes_t now = _session.transport_frame (); - nframes_t end = now + nframes; - - Glib::Mutex::Lock lm (_automation_lock, Glib::TRY_LOCK); - - if (!lm.locked()) { - connect_and_run (bufs, nframes, offset, false); - return; - } - - if (!find_next_event (now, end, next_event)) { - - /* no events have a time within the relevant range */ - - connect_and_run (bufs, nframes, offset, true, now); - return; - } - - while (nframes) { - - nframes_t cnt = min (((nframes_t) ceil (next_event.when) - now), nframes); - - connect_and_run (bufs, cnt, offset, true, now); - - nframes -= cnt; - offset += cnt; - now += cnt; - - if (!find_next_event (now, end, next_event)) { - break; - } - } - - /* cleanup anything that is left to do */ - - if (nframes) { - connect_and_run (bufs, nframes, offset, true, now); - } -} - -float -PluginInsert::default_parameter_value (uint32_t port) -{ - 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 automated_params; - - what_has_automation (automated_params); - - for (set::iterator i = automated_params.begin(); i != automated_params.end(); ++i) { - - AutomationList& al = automation_list (*i); - - switch (al.automation_state()) { - case Write: - al.set_automation_state (Off); - break; - case Touch: - al.set_automation_state (Play); - break; - default: - break; - } - } -} - -boost::shared_ptr -PluginInsert::plugin_factory (boost::shared_ptr other) -{ - boost::shared_ptr lp; -#ifdef VST_SUPPORT - boost::shared_ptr vp; -#endif -#ifdef HAVE_AUDIOUNITS - boost::shared_ptr ap; -#endif - - if ((lp = boost::dynamic_pointer_cast (other)) != 0) { - return boost::shared_ptr (new LadspaPlugin (*lp)); -#ifdef VST_SUPPORT - } else if ((vp = boost::dynamic_pointer_cast (other)) != 0) { - return boost::shared_ptr (new VSTPlugin (*vp)); -#endif -#ifdef HAVE_AUDIOUNITS - } else if ((ap = boost::dynamic_pointer_cast (other)) != 0) { - return boost::shared_ptr (new AUPlugin (*ap)); -#endif - } - - fatal << string_compose (_("programming error: %1"), - X_("unknown plugin type in PluginInsert::plugin_factory")) - << endmsg; - /*NOTREACHED*/ - return boost::shared_ptr ((Plugin*) 0); -} - -bool -PluginInsert::configure_io (ChanCount in, ChanCount out) -{ - ChanCount matching_out = output_for_input_configuration(out); - if (matching_out != out) { - _configured = false; - return false; - } else { - bool success = set_count (count_for_configuration(in, out)); - if (success) { - _configured = true; - _configured_input = in; - } - return success; - } -} - -bool -PluginInsert::can_support_input_configuration (ChanCount in_count) const -{ - int32_t outputs = _plugins[0]->get_info()->n_outputs.get(_default_type); - int32_t inputs = _plugins[0]->get_info()->n_inputs.get(_default_type); - int32_t in = in_count.get(_default_type); - - /* see output_for_input_configuration below */ - if ((inputs == 0) - || (outputs == 1 && inputs == 1) - || (inputs == in) - || ((inputs < in) && (inputs % in == 0))) { - return true; - } else { - return false; - } -} - -ChanCount -PluginInsert::output_for_input_configuration (ChanCount in) const -{ - ChanCount outputs = _plugins[0]->get_info()->n_outputs; - ChanCount inputs = _plugins[0]->get_info()->n_inputs; - - if (inputs.n_total() == 0) { - /* instrument plugin, always legal, but throws away any existing streams */ - return outputs; - } - - if (inputs.n_total() == 1 && outputs == inputs) { - /* mono plugin, replicate as needed to match in */ - return in; - } - - if (inputs == in) { - /* exact match */ - return outputs; - } - - // FIXME: single type plugins only. can we do this for instruments? - if ((inputs.n_total() == inputs.get(_default_type)) - && ((in.n_total() == in.get(_default_type)) - && (inputs.n_total() < in.n_total()) - && (inputs.n_total() % in.n_total() == 0))) { - - /* number of inputs is a factor of the requested input - configuration, so we can replicate. - */ - - return ChanCount(_default_type, in.n_total() / inputs.n_total()); - } - - /* sorry */ - return ChanCount(); -} - -/* Number of plugin instances required to support a given channel configuration. - * (private helper) - */ -int32_t -PluginInsert::count_for_configuration (ChanCount in, ChanCount out) const -{ - // FIXME: take 'out' into consideration - - ChanCount outputs = _plugins[0]->get_info()->n_outputs; - ChanCount inputs = _plugins[0]->get_info()->n_inputs; - - if (inputs.n_total() == 0) { - /* instrument plugin, always legal, but throws away any existing streams */ - return 1; - } - - if (inputs.n_total() == 1 && outputs == inputs) { - /* mono plugin, replicate as needed to match in */ - return in.n_total(); - } - - if (inputs == in) { - /* exact match */ - return 1; - } - - // FIXME: single type plugins only. can we do this for instruments? - if ((inputs.n_total() == inputs.get(_default_type)) - && ((in.n_total() == in.get(_default_type)) - && (inputs.n_total() < in.n_total()) - && (inputs.n_total() % in.n_total() == 0))) { - - /* number of inputs is a factor of the requested input - configuration, so we can replicate. - */ - - return in.n_total() / inputs.n_total(); - } - - /* sorry */ - return 0; -} - -XMLNode& -PluginInsert::get_state(void) -{ - return state (true); -} - -XMLNode& -PluginInsert::state (bool full) -{ - char buf[256]; - XMLNode *node = new XMLNode("Insert"); - - node->add_child_nocopy (Redirect::state (full)); - - node->add_property ("type", _plugins[0]->state_node_name()); - snprintf(buf, sizeof(buf), "%s", _plugins[0]->name()); - node->add_property("id", string(buf)); - if (_plugins[0]->state_node_name() == "ladspa") { - char buf[32]; - snprintf (buf, sizeof (buf), "%ld", _plugins[0]->get_info()->unique_id); - node->add_property("unique-id", string(buf)); - } - node->add_property("count", string_compose("%1", _plugins.size())); - node->add_child_nocopy (_plugins[0]->get_state()); - - /* add port automation state */ - XMLNode *autonode = new XMLNode(port_automation_node_name); - set automatable = _plugins[0]->automatable(); - - for (set::iterator x = automatable.begin(); x != automatable.end(); ++x) { - - 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); - } - - node->add_child_nocopy (*autonode); - - return *node; -} - -int -PluginInsert::set_state(const XMLNode& node) -{ - XMLNodeList nlist = node.children(); - XMLNodeIterator niter; - XMLPropertyList plist; - const XMLProperty *prop; - long unique = 0; - ARDOUR::PluginType type; - - if ((prop = node.property ("type")) == 0) { - error << _("XML node describing insert is missing the `type' field") << endmsg; - return -1; - } - - if (prop->value() == X_("ladspa") || prop->value() == X_("Ladspa")) { /* handle old school sessions */ - type = ARDOUR::LADSPA; - } else if (prop->value() == X_("vst")) { - type = ARDOUR::VST; - } else { - error << string_compose (_("unknown plugin type %1 in plugin insert state"), - prop->value()) - << endmsg; - return -1; - } - - prop = node.property ("unique-id"); - if (prop != 0) { - unique = atol(prop->value().c_str()); - } - - if ((prop = node.property ("id")) == 0) { - error << _("XML node describing insert is missing the `id' field") << endmsg; - return -1; - } - - boost::shared_ptr plugin; - - if (unique != 0) { - plugin = find_plugin (_session, "", unique, type); - } else { - plugin = find_plugin (_session, prop->value(), 0, type); - } - - if (plugin == 0) { - error << string_compose(_("Found a reference to a plugin (\"%1\") that is unknown.\n" - "Perhaps it was removed or moved since it was last used."), prop->value()) - << endmsg; - return -1; - } - - uint32_t count = 1; - - if ((prop = node.property ("count")) != 0) { - sscanf (prop->value().c_str(), "%u", &count); - } - - if (_plugins.size() != count) { - - _plugins.push_back (plugin); - - for (uint32_t n=1; n < count; ++n) { - _plugins.push_back (plugin_factory (plugin)); - } - } - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == plugin->state_node_name()) { - for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->set_state (**niter); - } - break; - } - } - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == Redirect::state_node_name) { - Redirect::set_state (**niter); - break; - } - } - - if (niter == nlist.end()) { - error << _("XML node describing insert is missing a Redirect node") << endmsg; - return -1; - } - - if (niter == nlist.end()) { - error << string_compose(_("XML node describing a plugin insert is missing the `%1' information"), plugin->state_node_name()) << endmsg; - return -1; - } - - /* look for port automation node */ - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - - if ((*niter)->name() != port_automation_node_name) { - continue; - } - - XMLNodeList cnodes; - XMLProperty *cprop; - XMLNodeConstIterator iter; - XMLNode *child; - const char *port; - uint32_t port_id; - - cnodes = (*niter)->children ("port"); - - for(iter = cnodes.begin(); iter != cnodes.end(); ++iter){ - - child = *iter; - - if ((cprop = child->property("number")) != 0) { - port = cprop->value().c_str(); - } else { - warning << _("PluginInsert: Auto: no ladspa port number") << endmsg; - continue; - } - - sscanf (port, "%" PRIu32, &port_id); - - if (port_id >= _plugins[0]->parameter_count()) { - warning << _("PluginInsert: Auto: port id out of range") << endmsg; - continue; - } - - if (!child->children().empty()) { - automation_list (port_id).set_state (*child->children().front()); - } else { - if ((cprop = child->property("auto")) != 0) { - - /* old school */ - - int x; - sscanf (cprop->value().c_str(), "0x%x", &x); - automation_list (port_id).set_automation_state (AutoState (x)); - - } else { - - /* missing */ - - automation_list (port_id).set_automation_state (Off); - } - } - - } - - /* done */ - - break; - } - - if (niter == nlist.end()) { - warning << string_compose(_("XML node describing a port automation is missing the `%1' information"), port_automation_node_name) << endmsg; - } - - // The name of the PluginInsert comes from the plugin, nothing else - set_name(plugin->get_info()->name,this); - - return 0; -} - -string -PluginInsert::describe_parameter (uint32_t what) -{ - return _plugins[0]->describe_parameter (what); -} - -ARDOUR::nframes_t -PluginInsert::latency() -{ - return _plugins[0]->latency (); -} - -ARDOUR::PluginType -PluginInsert::type () -{ - boost::shared_ptr lp; -#ifdef VST_SUPPORT - boost::shared_ptr vp; -#endif -#ifdef HAVE_AUDIOUNITS - boost::shared_ptr ap; -#endif - - PluginPtr other = plugin (); - - if ((lp = boost::dynamic_pointer_cast (other)) != 0) { - return ARDOUR::LADSPA; -#ifdef VST_SUPPORT - } else if ((vp = boost::dynamic_pointer_cast (other)) != 0) { - return ARDOUR::VST; -#endif -#ifdef HAVE_AUDIOUNITS - } else if ((ap = boost::dynamic_pointer_cast (other)) != 0) { - return ARDOUR::AudioUnit; -#endif - } else { - /* NOT REACHED */ - return (ARDOUR::PluginType) 0; - } -} - -/*************************************************************** - Port inserts: send output to a port, pick up input at a port - ***************************************************************/ - -PortInsert::PortInsert (Session& s, Placement p) - : Insert (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), p, 1, -1, 1, -1) -{ - init (); - RedirectCreated (this); /* EMIT SIGNAL */ -} - -PortInsert::PortInsert (const PortInsert& other) - : Insert (other._session, string_compose (_("insert %1"), (bitslot = other._session.next_insert_id()) + 1), other.placement(), 1, -1, 1, -1) -{ - init (); - RedirectCreated (this); /* EMIT SIGNAL */ -} - -void -PortInsert::init () -{ - if (add_input_port ("", this)) { - error << _("PortInsert: cannot add input port") << endmsg; - throw failed_constructor(); - } - - if (add_output_port ("", this)) { - error << _("PortInsert: cannot add output port") << endmsg; - throw failed_constructor(); - } -} - -PortInsert::PortInsert (Session& s, const XMLNode& node) - : Insert (s, "will change", PreFader) -{ - if (set_state (node)) { - throw failed_constructor(); - } - - RedirectCreated (this); /* EMIT SIGNAL */ -} - -PortInsert::~PortInsert () -{ - GoingAway (); -} - -void -PortInsert::run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) -{ - if (n_outputs().get(_default_type) == 0) { - return; - } - - if (!active()) { - /* deliver silence */ - silence (nframes, offset); - return; - } - - deliver_output(bufs, start_frame, end_frame, nframes, offset); - - collect_input(bufs, nframes, offset); -} - -XMLNode& -PortInsert::get_state(void) -{ - return state (true); -} - -XMLNode& -PortInsert::state (bool full) -{ - XMLNode *node = new XMLNode("Insert"); - char buf[32]; - node->add_child_nocopy (Redirect::state(full)); - node->add_property ("type", "port"); - snprintf (buf, sizeof (buf), "%" PRIu32, bitslot); - node->add_property ("bitslot", buf); - - return *node; -} - -int -PortInsert::set_state(const XMLNode& node) -{ - XMLNodeList nlist = node.children(); - XMLNodeIterator niter; - XMLPropertyList plist; - const XMLProperty *prop; - - if ((prop = node.property ("type")) == 0) { - error << _("XML node describing insert is missing the `type' field") << endmsg; - return -1; - } - - if (prop->value() != "port") { - error << _("non-port insert XML used for port plugin insert") << endmsg; - return -1; - } - - if ((prop = node.property ("bitslot")) == 0) { - bitslot = _session.next_insert_id(); - } else { - sscanf (prop->value().c_str(), "%" PRIu32, &bitslot); - _session.mark_insert_id (bitslot); - } - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == Redirect::state_node_name) { - Redirect::set_state (**niter); - break; - } - } - - if (niter == nlist.end()) { - error << _("XML node describing insert is missing a Redirect node") << endmsg; - return -1; - } - - return 0; -} - -ARDOUR::nframes_t -PortInsert::latency() -{ - /* because we deliver and collect within the same cycle, - all I/O is necessarily delayed by at least frames_per_cycle(). - - if the return port for insert has its own latency, we - need to take that into account too. - */ - - return _session.engine().frames_per_cycle() + input_latency(); -} - -bool -PortInsert::can_support_input_configuration (ChanCount in) const -{ - if (input_maximum() == ChanCount::INFINITE && output_maximum() == ChanCount::INFINITE) { - - /* not configured yet */ - - return true; /* we can support anything the first time we're asked */ - - } else { - - /* the "input" config for a port insert corresponds to how - many output ports it will have. - */ - - if (output_maximum() == in) { - - return true; - } - } - - return false; -} - -ChanCount -PortInsert::output_for_input_configuration (ChanCount in) const -{ - return in; -} - -bool -PortInsert::configure_io (ChanCount in, ChanCount out) -{ - /* do not allow configuration to be changed outside the range of - the last request config. or something like that. - */ - - - /* this is a bit odd: - - the number of inputs we are required to handle corresponds - to the number of output ports we need. - - the number of outputs we are required to have corresponds - to the number of input ports we need. - */ - - set_output_maximum (in); - set_output_minimum (in); - set_input_maximum (out); - set_input_minimum (out); - - return (ensure_io (out, in, false, this) == 0); -} - -ChanCount -PortInsert::output_streams() const -{ - return n_inputs (); -} - -ChanCount -PortInsert::input_streams() const -{ - return n_outputs (); + // FIXME: default type? } diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc new file mode 100644 index 0000000000..eefdc5dff1 --- /dev/null +++ b/libs/ardour/plugin_insert.cc @@ -0,0 +1,884 @@ +/* + Copyright (C) 2000 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef VST_SUPPORT +#include +#endif + +#ifdef HAVE_AUDIOUNITS +#include +#endif + +#include +#include +#include + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; + +const string PluginInsert::port_automation_node_name = "PortAutomation"; + +PluginInsert::PluginInsert (Session& s, boost::shared_ptr plug, Placement placement) + : Insert (s, plug->name(), placement) +{ + /* the first is the master */ + + _plugins.push_back (plug); + + _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed)); + + init (); + + { + Glib::Mutex::Lock em (_session.engine().process_lock()); + IO::MoreChannels (max(input_streams(), output_streams())); + } + + RedirectCreated (this); /* EMIT SIGNAL */ +} + +PluginInsert::PluginInsert (Session& s, const XMLNode& node) + : Insert (s, "will change", PreFader) +{ + if (set_state (node)) { + throw failed_constructor(); + } + + set_automatable (); + + _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed)); + + { + Glib::Mutex::Lock em (_session.engine().process_lock()); + IO::MoreChannels (max(input_streams(), output_streams())); + } +} + +PluginInsert::PluginInsert (const PluginInsert& other) + : Insert (other._session, other.plugin()->name(), other.placement()) +{ + uint32_t count = other._plugins.size(); + + /* make as many copies as requested */ + for (uint32_t n = 0; n < count; ++n) { + _plugins.push_back (plugin_factory (other.plugin (n))); + } + + + _plugins[0]->ParameterChanged.connect (mem_fun (*this, &PluginInsert::parameter_changed)); + + init (); + + RedirectCreated (this); /* EMIT SIGNAL */ +} + +bool +PluginInsert::set_count (uint32_t num) +{ + bool require_state = !_plugins.empty(); + + /* this is a bad idea.... we shouldn't do this while active. + only a route holding their redirect_lock should be calling this + */ + + if (num == 0) { + return false; + } else if (num > _plugins.size()) { + uint32_t diff = num - _plugins.size(); + + for (uint32_t n = 0; n < diff; ++n) { + _plugins.push_back (plugin_factory (_plugins[0])); + + if (require_state) { + /* XXX do something */ + } + } + + } else if (num < _plugins.size()) { + uint32_t diff = _plugins.size() - num; + for (uint32_t n= 0; n < diff; ++n) { + _plugins.pop_back(); + } + } + + return true; +} + +void +PluginInsert::init () +{ + set_automatable (); + + set::iterator s; +} + +PluginInsert::~PluginInsert () +{ + GoingAway (); /* EMIT SIGNAL */ +} + +void +PluginInsert::automation_list_creation_callback (uint32_t 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) +{ + AutomationList& alist (automation_list (which)); + + if (alist.automation_state() != Off) { + _plugins[0]->set_parameter (which, alist.eval (_session.transport_frame())); + } +} + +ChanCount +PluginInsert::output_streams() const +{ + if (_configured) + return output_for_input_configuration(_configured_input); + else + return natural_output_streams(); +} + +ChanCount +PluginInsert::input_streams() const +{ + if (_configured) + return _configured_input; + else + return natural_input_streams(); +} + +ChanCount +PluginInsert::natural_output_streams() const +{ + return _plugins[0]->get_info()->n_outputs; +} + +ChanCount +PluginInsert::natural_input_streams() const +{ + return _plugins[0]->get_info()->n_inputs; +} + +bool +PluginInsert::is_generator() const +{ + /* XXX more finesse is possible here. VST plugins have a + a specific "instrument" flag, for example. + */ + + return _plugins[0]->get_info()->n_inputs.n_audio() == 0; +} + +void +PluginInsert::set_automatable () +{ + set a; + + a = _plugins.front()->automatable (); + + for (set::iterator i = a.begin(); i != a.end(); ++i) { + can_automate (*i); + } +} + +void +PluginInsert::parameter_changed (uint32_t which, float val) +{ + vector >::iterator i = _plugins.begin(); + + /* don't set the first plugin, just all the slaves */ + + if (i != _plugins.end()) { + ++i; + for (; i != _plugins.end(); ++i) { + (*i)->set_parameter (which, val); + } + } +} + +void +PluginInsert::set_block_size (nframes_t nframes) +{ + for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->set_block_size (nframes); + } +} + +void +PluginInsert::activate () +{ + for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->activate (); + } +} + +void +PluginInsert::deactivate () +{ + for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->deactivate (); + } +} + +void +PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now) +{ + uint32_t in_index = 0; + uint32_t out_index = 0; + + /* Note that we've already required that plugins + be able to handle in-place processing. + */ + + if (with_auto) { + + map::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()) { + bool valid; + + float val = alist.rt_safe_eval (now, valid); + + if (valid) { + /* set the first plugin, the others will be set via signals */ + _plugins[0]->set_parameter ((*li).first, val); + } + + } + } + } + + for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->connect_and_run (bufs, in_index, out_index, nframes, offset); + } + + /* leave remaining channel buffers alone */ +} + +void +PluginInsert::automation_snapshot (nframes_t now) +{ + map::iterator li; + + for (li = parameter_automation.begin(); li != parameter_automation.end(); ++li) { + + AutomationList *alist = ((*li).second); + if (alist != 0 && alist->automation_write ()) { + + float val = _plugins[0]->get_parameter ((*li).first); + alist->rt_add (now, val); + last_automation_snapshot = now; + } + } +} + +void +PluginInsert::transport_stopped (nframes_t now) +{ + map::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) { + _plugins[0]->set_parameter (li->first, alist.eval (now)); + } + } +} + +void +PluginInsert::silence (nframes_t nframes, nframes_t offset) +{ + uint32_t in_index = 0; + uint32_t out_index = 0; + + if (active()) { + for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->connect_and_run (_session.get_silent_buffers ((*i)->get_info()->n_inputs), in_index, out_index, nframes, offset); + } + } +} + +void +PluginInsert::run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) +{ + if (active()) { + + if (_session.transport_rolling()) { + automation_run (bufs, nframes, offset); + } else { + connect_and_run (bufs, nframes, offset, false); + } + } else { + uint32_t in = _plugins[0]->get_info()->n_inputs.n_audio(); + uint32_t out = _plugins[0]->get_info()->n_outputs.n_audio(); + + if (out > in) { + + /* not active, but something has make up for any channel count increase */ + + for (uint32_t n = out - in; n < out; ++n) { + memcpy (bufs.get_audio(n).data(nframes, offset), bufs.get_audio(in - 1).data(nframes, offset), sizeof (Sample) * nframes); + } + } + + bufs.count().set(_default_type, out); + } +} + +void +PluginInsert::set_parameter (uint32_t port, float val) +{ + /* the others will be set from the event triggered by this */ + + _plugins[0]->set_parameter (port, val); + + if (automation_list (port).automation_write()) { + automation_list (port).add (_session.audible_frame(), val); + } + + _session.set_dirty(); +} + +void +PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offset) +{ + ControlEvent next_event (0, 0.0f); + nframes_t now = _session.transport_frame (); + nframes_t end = now + nframes; + + Glib::Mutex::Lock lm (_automation_lock, Glib::TRY_LOCK); + + if (!lm.locked()) { + connect_and_run (bufs, nframes, offset, false); + return; + } + + if (!find_next_event (now, end, next_event)) { + + /* no events have a time within the relevant range */ + + connect_and_run (bufs, nframes, offset, true, now); + return; + } + + while (nframes) { + + nframes_t cnt = min (((nframes_t) ceil (next_event.when) - now), nframes); + + connect_and_run (bufs, cnt, offset, true, now); + + nframes -= cnt; + offset += cnt; + now += cnt; + + if (!find_next_event (now, end, next_event)) { + break; + } + } + + /* cleanup anything that is left to do */ + + if (nframes) { + connect_and_run (bufs, nframes, offset, true, now); + } +} + +float +PluginInsert::default_parameter_value (uint32_t port) +{ + 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 automated_params; + + what_has_automation (automated_params); + + for (set::iterator i = automated_params.begin(); i != automated_params.end(); ++i) { + + AutomationList& al = automation_list (*i); + + switch (al.automation_state()) { + case Write: + al.set_automation_state (Off); + break; + case Touch: + al.set_automation_state (Play); + break; + default: + break; + } + } +} + +boost::shared_ptr +PluginInsert::plugin_factory (boost::shared_ptr other) +{ + boost::shared_ptr lp; +#ifdef VST_SUPPORT + boost::shared_ptr vp; +#endif +#ifdef HAVE_AUDIOUNITS + boost::shared_ptr ap; +#endif + + if ((lp = boost::dynamic_pointer_cast (other)) != 0) { + return boost::shared_ptr (new LadspaPlugin (*lp)); +#ifdef VST_SUPPORT + } else if ((vp = boost::dynamic_pointer_cast (other)) != 0) { + return boost::shared_ptr (new VSTPlugin (*vp)); +#endif +#ifdef HAVE_AUDIOUNITS + } else if ((ap = boost::dynamic_pointer_cast (other)) != 0) { + return boost::shared_ptr (new AUPlugin (*ap)); +#endif + } + + fatal << string_compose (_("programming error: %1"), + X_("unknown plugin type in PluginInsert::plugin_factory")) + << endmsg; + /*NOTREACHED*/ + return boost::shared_ptr ((Plugin*) 0); +} + +bool +PluginInsert::configure_io (ChanCount in, ChanCount out) +{ + ChanCount matching_out = output_for_input_configuration(out); + if (matching_out != out) { + _configured = false; + return false; + } else { + bool success = set_count (count_for_configuration(in, out)); + if (success) { + _configured = true; + _configured_input = in; + } + return success; + } +} + +bool +PluginInsert::can_support_input_configuration (ChanCount in_count) const +{ + int32_t outputs = _plugins[0]->get_info()->n_outputs.get(_default_type); + int32_t inputs = _plugins[0]->get_info()->n_inputs.get(_default_type); + int32_t in = in_count.get(_default_type); + + /* see output_for_input_configuration below */ + if ((inputs == 0) + || (outputs == 1 && inputs == 1) + || (inputs == in) + || ((inputs < in) && (inputs % in == 0))) { + return true; + } else { + return false; + } +} + +ChanCount +PluginInsert::output_for_input_configuration (ChanCount in) const +{ + ChanCount outputs = _plugins[0]->get_info()->n_outputs; + ChanCount inputs = _plugins[0]->get_info()->n_inputs; + + if (inputs.n_total() == 0) { + /* instrument plugin, always legal, but throws away any existing streams */ + return outputs; + } + + if (inputs.n_total() == 1 && outputs == inputs) { + /* mono plugin, replicate as needed to match in */ + return in; + } + + if (inputs == in) { + /* exact match */ + return outputs; + } + + // FIXME: single type plugins only. can we do this for instruments? + if ((inputs.n_total() == inputs.get(_default_type)) + && ((in.n_total() == in.get(_default_type)) + && (inputs.n_total() < in.n_total()) + && (inputs.n_total() % in.n_total() == 0))) { + + /* number of inputs is a factor of the requested input + configuration, so we can replicate. + */ + + return ChanCount(_default_type, in.n_total() / inputs.n_total()); + } + + /* sorry */ + return ChanCount(); +} + +/* Number of plugin instances required to support a given channel configuration. + * (private helper) + */ +int32_t +PluginInsert::count_for_configuration (ChanCount in, ChanCount out) const +{ + // FIXME: take 'out' into consideration + + ChanCount outputs = _plugins[0]->get_info()->n_outputs; + ChanCount inputs = _plugins[0]->get_info()->n_inputs; + + if (inputs.n_total() == 0) { + /* instrument plugin, always legal, but throws away any existing streams */ + return 1; + } + + if (inputs.n_total() == 1 && outputs == inputs) { + /* mono plugin, replicate as needed to match in */ + return in.n_total(); + } + + if (inputs == in) { + /* exact match */ + return 1; + } + + // FIXME: single type plugins only. can we do this for instruments? + if ((inputs.n_total() == inputs.get(_default_type)) + && ((in.n_total() == in.get(_default_type)) + && (inputs.n_total() < in.n_total()) + && (inputs.n_total() % in.n_total() == 0))) { + + /* number of inputs is a factor of the requested input + configuration, so we can replicate. + */ + + return in.n_total() / inputs.n_total(); + } + + /* sorry */ + return 0; +} + +XMLNode& +PluginInsert::get_state(void) +{ + return state (true); +} + +XMLNode& +PluginInsert::state (bool full) +{ + char buf[256]; + XMLNode *node = new XMLNode("Insert"); + + node->add_child_nocopy (Redirect::state (full)); + + node->add_property ("type", _plugins[0]->state_node_name()); + snprintf(buf, sizeof(buf), "%s", _plugins[0]->name()); + node->add_property("id", string(buf)); + if (_plugins[0]->state_node_name() == "ladspa") { + char buf[32]; + snprintf (buf, sizeof (buf), "%ld", _plugins[0]->get_info()->unique_id); + node->add_property("unique-id", string(buf)); + } + node->add_property("count", string_compose("%1", _plugins.size())); + node->add_child_nocopy (_plugins[0]->get_state()); + + /* add port automation state */ + XMLNode *autonode = new XMLNode(port_automation_node_name); + set automatable = _plugins[0]->automatable(); + + for (set::iterator x = automatable.begin(); x != automatable.end(); ++x) { + + 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); + } + + node->add_child_nocopy (*autonode); + + return *node; +} + +int +PluginInsert::set_state(const XMLNode& node) +{ + XMLNodeList nlist = node.children(); + XMLNodeIterator niter; + XMLPropertyList plist; + const XMLProperty *prop; + long unique = 0; + ARDOUR::PluginType type; + + if ((prop = node.property ("type")) == 0) { + error << _("XML node describing insert is missing the `type' field") << endmsg; + return -1; + } + + if (prop->value() == X_("ladspa") || prop->value() == X_("Ladspa")) { /* handle old school sessions */ + type = ARDOUR::LADSPA; + } else if (prop->value() == X_("vst")) { + type = ARDOUR::VST; + } else { + error << string_compose (_("unknown plugin type %1 in plugin insert state"), + prop->value()) + << endmsg; + return -1; + } + + prop = node.property ("unique-id"); + if (prop != 0) { + unique = atol(prop->value().c_str()); + } + + if ((prop = node.property ("id")) == 0) { + error << _("XML node describing insert is missing the `id' field") << endmsg; + return -1; + } + + boost::shared_ptr plugin; + + if (unique != 0) { + plugin = find_plugin (_session, "", unique, type); + } else { + plugin = find_plugin (_session, prop->value(), 0, type); + } + + if (plugin == 0) { + error << string_compose(_("Found a reference to a plugin (\"%1\") that is unknown.\n" + "Perhaps it was removed or moved since it was last used."), prop->value()) + << endmsg; + return -1; + } + + uint32_t count = 1; + + if ((prop = node.property ("count")) != 0) { + sscanf (prop->value().c_str(), "%u", &count); + } + + if (_plugins.size() != count) { + + _plugins.push_back (plugin); + + for (uint32_t n=1; n < count; ++n) { + _plugins.push_back (plugin_factory (plugin)); + } + } + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + if ((*niter)->name() == plugin->state_node_name()) { + for (vector >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->set_state (**niter); + } + break; + } + } + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + if ((*niter)->name() == Redirect::state_node_name) { + Redirect::set_state (**niter); + break; + } + } + + if (niter == nlist.end()) { + error << _("XML node describing insert is missing a Redirect node") << endmsg; + return -1; + } + + if (niter == nlist.end()) { + error << string_compose(_("XML node describing a plugin insert is missing the `%1' information"), plugin->state_node_name()) << endmsg; + return -1; + } + + /* look for port automation node */ + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + + if ((*niter)->name() != port_automation_node_name) { + continue; + } + + XMLNodeList cnodes; + XMLProperty *cprop; + XMLNodeConstIterator iter; + XMLNode *child; + const char *port; + uint32_t port_id; + + cnodes = (*niter)->children ("port"); + + for(iter = cnodes.begin(); iter != cnodes.end(); ++iter){ + + child = *iter; + + if ((cprop = child->property("number")) != 0) { + port = cprop->value().c_str(); + } else { + warning << _("PluginInsert: Auto: no ladspa port number") << endmsg; + continue; + } + + sscanf (port, "%" PRIu32, &port_id); + + if (port_id >= _plugins[0]->parameter_count()) { + warning << _("PluginInsert: Auto: port id out of range") << endmsg; + continue; + } + + if (!child->children().empty()) { + automation_list (port_id).set_state (*child->children().front()); + } else { + if ((cprop = child->property("auto")) != 0) { + + /* old school */ + + int x; + sscanf (cprop->value().c_str(), "0x%x", &x); + automation_list (port_id).set_automation_state (AutoState (x)); + + } else { + + /* missing */ + + automation_list (port_id).set_automation_state (Off); + } + } + + } + + /* done */ + + break; + } + + if (niter == nlist.end()) { + warning << string_compose(_("XML node describing a port automation is missing the `%1' information"), port_automation_node_name) << endmsg; + } + + // The name of the PluginInsert comes from the plugin, nothing else + set_name(plugin->get_info()->name,this); + + return 0; +} + +string +PluginInsert::describe_parameter (uint32_t what) +{ + return _plugins[0]->describe_parameter (what); +} + +ARDOUR::nframes_t +PluginInsert::latency() +{ + return _plugins[0]->latency (); +} + +ARDOUR::PluginType +PluginInsert::type () +{ + boost::shared_ptr lp; +#ifdef VST_SUPPORT + boost::shared_ptr vp; +#endif +#ifdef HAVE_AUDIOUNITS + boost::shared_ptr ap; +#endif + + PluginPtr other = plugin (); + + if ((lp = boost::dynamic_pointer_cast (other)) != 0) { + return ARDOUR::LADSPA; +#ifdef VST_SUPPORT + } else if ((vp = boost::dynamic_pointer_cast (other)) != 0) { + return ARDOUR::VST; +#endif +#ifdef HAVE_AUDIOUNITS + } else if ((ap = boost::dynamic_pointer_cast (other)) != 0) { + return ARDOUR::AudioUnit; +#endif + } else { + /* NOT REACHED */ + return (ARDOUR::PluginType) 0; + } +} + + diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc new file mode 100644 index 0000000000..25234dbf77 --- /dev/null +++ b/libs/ardour/port_insert.cc @@ -0,0 +1,242 @@ +/* + Copyright (C) 2000,2007 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; + +PortInsert::PortInsert (Session& s, Placement p) + : Insert (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), p, 1, -1, 1, -1) +{ + init (); + RedirectCreated (this); /* EMIT SIGNAL */ +} + +PortInsert::PortInsert (const PortInsert& other) + : Insert (other._session, string_compose (_("insert %1"), (bitslot = other._session.next_insert_id()) + 1), other.placement(), 1, -1, 1, -1) +{ + init (); + RedirectCreated (this); /* EMIT SIGNAL */ +} + +void +PortInsert::init () +{ + if (add_input_port ("", this)) { + error << _("PortInsert: cannot add input port") << endmsg; + throw failed_constructor(); + } + + if (add_output_port ("", this)) { + error << _("PortInsert: cannot add output port") << endmsg; + throw failed_constructor(); + } +} + +PortInsert::PortInsert (Session& s, const XMLNode& node) + : Insert (s, "will change", PreFader) +{ + if (set_state (node)) { + throw failed_constructor(); + } + + RedirectCreated (this); /* EMIT SIGNAL */ +} + +PortInsert::~PortInsert () +{ + GoingAway (); +} + +void +PortInsert::run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) +{ + if (n_outputs().get(_default_type) == 0) { + return; + } + + if (!active()) { + /* deliver silence */ + silence (nframes, offset); + return; + } + + deliver_output(bufs, start_frame, end_frame, nframes, offset); + + collect_input(bufs, nframes, offset); +} + +XMLNode& +PortInsert::get_state(void) +{ + return state (true); +} + +XMLNode& +PortInsert::state (bool full) +{ + XMLNode *node = new XMLNode("Insert"); + char buf[32]; + node->add_child_nocopy (Redirect::state(full)); + node->add_property ("type", "port"); + snprintf (buf, sizeof (buf), "%" PRIu32, bitslot); + node->add_property ("bitslot", buf); + + return *node; +} + +int +PortInsert::set_state(const XMLNode& node) +{ + XMLNodeList nlist = node.children(); + XMLNodeIterator niter; + XMLPropertyList plist; + const XMLProperty *prop; + + if ((prop = node.property ("type")) == 0) { + error << _("XML node describing insert is missing the `type' field") << endmsg; + return -1; + } + + if (prop->value() != "port") { + error << _("non-port insert XML used for port plugin insert") << endmsg; + return -1; + } + + if ((prop = node.property ("bitslot")) == 0) { + bitslot = _session.next_insert_id(); + } else { + sscanf (prop->value().c_str(), "%" PRIu32, &bitslot); + _session.mark_insert_id (bitslot); + } + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + if ((*niter)->name() == Redirect::state_node_name) { + Redirect::set_state (**niter); + break; + } + } + + if (niter == nlist.end()) { + error << _("XML node describing insert is missing a Redirect node") << endmsg; + return -1; + } + + return 0; +} + +ARDOUR::nframes_t +PortInsert::latency() +{ + /* because we deliver and collect within the same cycle, + all I/O is necessarily delayed by at least frames_per_cycle(). + + if the return port for insert has its own latency, we + need to take that into account too. + */ + + return _session.engine().frames_per_cycle() + input_latency(); +} + +bool +PortInsert::can_support_input_configuration (ChanCount in) const +{ + if (input_maximum() == ChanCount::INFINITE && output_maximum() == ChanCount::INFINITE) { + + /* not configured yet */ + + return true; /* we can support anything the first time we're asked */ + + } else { + + /* the "input" config for a port insert corresponds to how + many output ports it will have. + */ + + if (output_maximum() == in) { + + return true; + } + } + + return false; +} + +ChanCount +PortInsert::output_for_input_configuration (ChanCount in) const +{ + return in; +} + +bool +PortInsert::configure_io (ChanCount in, ChanCount out) +{ + /* do not allow configuration to be changed outside the range of + the last request config. or something like that. + */ + + + /* this is a bit odd: + + the number of inputs we are required to handle corresponds + to the number of output ports we need. + + the number of outputs we are required to have corresponds + to the number of input ports we need. + */ + + set_output_maximum (in); + set_output_minimum (in); + set_input_maximum (out); + set_input_minimum (out); + + return (ensure_io (out, in, false, this) == 0); +} + +ChanCount +PortInsert::output_streams() const +{ + return n_inputs (); +} + +ChanCount +PortInsert::input_streams() const +{ + return n_outputs (); +} + diff --git a/libs/ardour/redirect.cc b/libs/ardour/redirect.cc index bdcc718710..d63618943e 100644 --- a/libs/ardour/redirect.cc +++ b/libs/ardour/redirect.cc @@ -33,7 +33,8 @@ #include #include #include -#include +#include +#include #include "i18n.h" diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index d77dc46b0c..d682dd63b8 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 130ad56a11..310bd155ee 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -59,6 +59,8 @@ #include #include #include +#include +#include #include #include #include -- cgit v1.2.3