closing in on pin management.
authorRobin Gareus <robin@gareus.org>
Sun, 27 Mar 2016 19:33:40 +0000 (21:33 +0200)
committerRobin Gareus <robin@gareus.org>
Sun, 27 Mar 2016 19:33:40 +0000 (21:33 +0200)
overall the PluginInsert API is complete.
many implementation details remain.

libs/ardour/ardour/chan_count.h
libs/ardour/ardour/plugin_insert.h
libs/ardour/ardour/route.h
libs/ardour/luabindings.cc
libs/ardour/plugin_insert.cc
libs/ardour/route.cc

index b613f67a3c77cb7130a47e263078af9402f5ab9f..fc76734817ce1b6f52fbc34f686bf900ddccdaf1 100644 (file)
@@ -120,6 +120,14 @@ public:
                return ret;
        }
 
                return ret;
        }
 
+       ChanCount operator*(const unsigned int factor) const {
+               ChanCount ret;
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       ret.set(*t, get(*t) * factor );
+               }
+               return ret;
+       }
+
        ChanCount& operator+=(const ChanCount& other) {
                for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                        _counts[*t] += other._counts[*t];
        ChanCount& operator+=(const ChanCount& other) {
                for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                        _counts[*t] += other._counts[*t];
index d9db98dee4ec2d6fb2f8d1299cc38f133a0eb31c..44aa195cc902d7a34b815c1d5ff39ae8e08b800d 100644 (file)
@@ -85,26 +85,35 @@ class LIBARDOUR_API PluginInsert : public Processor
                }
        }
 
                }
        }
 
-#ifndef NDEBUG // prototyping -- this should be done synchroneously in configure_io()
-       void set_input_map (uint32_t num, ChanMapping m) {
-               if (num < _in_map.size()) {
-                       _in_map[num] = m;
-               }
-       }
-       void set_output_map (uint32_t num, ChanMapping m) {
-               if (num < _in_map.size()) {
-                       _out_map[num] = m;
-               }
-       }
-#endif
+       void set_input_map (uint32_t, ChanMapping);
+       void set_output_map (uint32_t, ChanMapping);
 
 
+       // these are ports visible on the outside
        ChanCount output_streams() const;
        ChanCount input_streams() const;
        ChanCount output_streams() const;
        ChanCount input_streams() const;
+
+       // actual ports of all plugins.
+       // n * natural_i/o or result of reconfigurable i/o
+       ChanCount internal_output_streams() const;
+       ChanCount internal_input_streams() const;
+
+       // a single plugin's internal i/o
        ChanCount natural_output_streams() const;
        ChanCount natural_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(); }
+       // allow to override output_streams(), implies "Custom Mode"
+
+       // only the owning route may call these (with process lock held)
+       // route is not a friend class, it owns us
+       bool set_count      (uint32_t num);
+       void set_outputs    (const ChanCount&);
+       void set_strict_io  (bool b) { _strict_io = b; }
+       void set_custom_cfg (bool b) { _custom_cfg = b; }
+       // end C++ class slavery!
+
+       uint32_t get_count  () const { return _plugins.size(); }
+       bool     strict_io  () const { return _strict_io; }
+       bool     custom_cfg () const { return _custom_cfg; }
 
        bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
        bool configure_io (ChanCount in, ChanCount out);
 
        bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
        bool configure_io (ChanCount in, ChanCount out);
@@ -176,26 +185,26 @@ class LIBARDOUR_API PluginInsert : public Processor
                return _pending_no_inplace;
        }
 
                return _pending_no_inplace;
        }
 
-       void set_no_inplace (bool b) {
-               _pending_no_inplace = b;
-       }
-
-       void set_strict_io (bool b) {
-               _strict_io = b;
+       void set_no_inplace (bool b) { // XXX tenative dev API
+               _pending_no_inplace = b; // TODO detect automatically
        }
 
        bool strict_io_configured () const {
        }
 
        bool strict_io_configured () const {
-               return _strict_io_configured;
+               return _match.strict_io;
        }
 
        bool splitting () const {
                return _match.method == Split;
        }
 
        }
 
        bool splitting () const {
                return _match.method == Split;
        }
 
-       void configured_io (ChanCount &in, ChanCount &out) { in = _configured_in; out = _configured_out; }
+       void configured_io (ChanCount &in, ChanCount &out) const {
+               in = _configured_in;
+               out = _configured_out;
+       }
 
        PBD::Signal2<void,BufferSet*, BufferSet*> AnalysisDataGathered;
        PBD::Signal0<void> PluginIoReConfigure;
 
        PBD::Signal2<void,BufferSet*, BufferSet*> AnalysisDataGathered;
        PBD::Signal0<void> PluginIoReConfigure;
+       PBD::Signal0<void> PluginMapChanged;
 
        /** Enumeration of the ways in which we can match our insert's
         *  IO to that of the plugin(s).
 
        /** Enumeration of the ways in which we can match our insert's
         *  IO to that of the plugin(s).
@@ -235,28 +244,36 @@ class LIBARDOUR_API PluginInsert : public Processor
 
        ChanCount _configured_in;
        ChanCount _configured_out;
 
        ChanCount _configured_in;
        ChanCount _configured_out;
+       ChanCount _custom_out;
 
 
+       bool _configured;
        bool _no_inplace;
        bool _no_inplace;
-       bool _pending_no_inplace;
        bool _strict_io;
        bool _strict_io;
-       bool _strict_io_configured;
+       bool _custom_cfg;
+       bool _pending_no_inplace;
 
        /** Description of how we can match our plugin's IO to our own insert IO */
        struct Match {
 
        /** Description of how we can match our plugin's IO to our own insert IO */
        struct Match {
-               Match () : method (Impossible), plugins (0) {}
-               Match (MatchingMethod m, int32_t p, ChanCount h = ChanCount ()) : method (m), plugins (p), hide (h) {}
+               Match () : method (Impossible), plugins (0), strict_io (false), custom_cfg (false) {}
+               Match (MatchingMethod m, int32_t p,
+                               bool strict = false, bool custom = false, ChanCount h = ChanCount ())
+                       : method (m), plugins (p), hide (h), strict_io (strict), custom_cfg (custom) {}
 
                MatchingMethod method; ///< method to employ
                int32_t plugins;       ///< number of copies of the plugin that we need
                ChanCount hide;        ///< number of channels to hide
 
                MatchingMethod method; ///< method to employ
                int32_t plugins;       ///< number of copies of the plugin that we need
                ChanCount hide;        ///< number of channels to hide
+               bool strict_io;        ///< force in == out
+               bool custom_cfg;       ///< custom config (if not strict)
        };
 
        Match private_can_support_io_configuration (ChanCount const &, ChanCount &);
 
        /** details of the match currently being used */
        Match _match;
        };
 
        Match private_can_support_io_configuration (ChanCount const &, ChanCount &);
 
        /** details of the match currently being used */
        Match _match;
-       std::map <uint32_t, ARDOUR::ChanMapping> _in_map;
-       std::map <uint32_t, ARDOUR::ChanMapping> _out_map;
+
+       typedef std::map <uint32_t, ARDOUR::ChanMapping> PinMappings;
+       PinMappings _in_map;
+       PinMappings _out_map;
 
        void automation_run (BufferSet& bufs, framepos_t start, pframes_t nframes);
        void connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now = 0);
 
        void automation_run (BufferSet& bufs, framepos_t start, pframes_t nframes);
        void connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now = 0);
index 990f1670549be95c137070922d74426f7c49b4d4..b103abf45bb408634c2bf5e635184252681927e4 100644 (file)
@@ -276,6 +276,8 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
 
        bool strict_io () const { return _strict_io; }
        bool set_strict_io (bool);
 
        bool strict_io () const { return _strict_io; }
        bool set_strict_io (bool);
+       bool reset_plugin_insert (boost::shared_ptr<Processor> proc);
+       bool customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs);
 
        framecnt_t set_private_port_latencies (bool playback) const;
        void       set_public_port_latencies (framecnt_t, bool playback) const;
 
        framecnt_t set_private_port_latencies (bool playback) const;
        void       set_public_port_latencies (framecnt_t, bool playback) const;
index a52fb26b4e5e263e2bac81fcb8881fec6bdef29b..2cc51f422c9d3c6b5f3b1635b8a2f825787fcf2a 100644 (file)
@@ -245,6 +245,8 @@ LuaBindings::common (lua_State* L)
                .addFunction ("set_comment", &Route::set_comment)
                .addFunction ("strict_io", &Route::strict_io)
                .addFunction ("set_strict_io", &Route::set_strict_io)
                .addFunction ("set_comment", &Route::set_comment)
                .addFunction ("strict_io", &Route::strict_io)
                .addFunction ("set_strict_io", &Route::set_strict_io)
+               .addFunction ("reset_plugin_insert", &Route::reset_plugin_insert)
+               .addFunction ("customize_plugin_insert", &Route::customize_plugin_insert)
                .endClass ()
 
                .deriveWSPtrClass <Track, Route> ("Track")
                .endClass ()
 
                .deriveWSPtrClass <Track, Route> ("Track")
@@ -371,15 +373,13 @@ LuaBindings::common (lua_State* L)
                .addFunction ("activate", &PluginInsert::activate)
                .addFunction ("deactivate", &PluginInsert::deactivate)
                .addFunction ("strict_io_configured", &PluginInsert::strict_io_configured)
                .addFunction ("activate", &PluginInsert::activate)
                .addFunction ("deactivate", &PluginInsert::deactivate)
                .addFunction ("strict_io_configured", &PluginInsert::strict_io_configured)
-               .addFunction ("set_strict_io", &PluginInsert::set_strict_io)
                .addFunction ("no_inplace", &PluginInsert::no_inplace)
                .addFunction ("input_map", &PluginInsert::input_map)
                .addFunction ("output_map", &PluginInsert::output_map)
                .addFunction ("no_inplace", &PluginInsert::no_inplace)
                .addFunction ("input_map", &PluginInsert::input_map)
                .addFunction ("output_map", &PluginInsert::output_map)
-#ifndef NDEBUG // this is not safe, prototyping only
-               .addFunction ("set_no_inplace", &PluginInsert::set_no_inplace)
+               .addFunction ("set_no_inplace", &PluginInsert::set_no_inplace) // XXX
                .addFunction ("set_input_map", &PluginInsert::set_input_map)
                .addFunction ("set_output_map", &PluginInsert::set_output_map)
                .addFunction ("set_input_map", &PluginInsert::set_input_map)
                .addFunction ("set_output_map", &PluginInsert::set_output_map)
-#endif
+
                .endClass ()
 
                .deriveWSPtrClass <AutomationControl, Evoral::Control> ("AutomationControl")
                .endClass ()
 
                .deriveWSPtrClass <AutomationControl, Evoral::Control> ("AutomationControl")
@@ -460,6 +460,7 @@ LuaBindings::common (lua_State* L)
                .endClass ()
 
                .beginClass <ChanCount> ("ChanCount")
                .endClass ()
 
                .beginClass <ChanCount> ("ChanCount")
+               .addConstructor <void (*) (DataType, uint32_t)> ()
                .addFunction ("n_audio", &ChanCount::n_audio)
                .endClass()
 
                .addFunction ("n_audio", &ChanCount::n_audio)
                .endClass()
 
index 7af57ae91d5459864f0272ad0da3a9f9b69e3d88..8aeff963fdb5b5a80c52f9056c98a485c60d5a66 100644 (file)
@@ -68,10 +68,11 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug)
        : Processor (s, (plug ? plug->name() : string ("toBeRenamed")))
        , _signal_analysis_collected_nframes(0)
        , _signal_analysis_collect_nframes_max(0)
        : Processor (s, (plug ? plug->name() : string ("toBeRenamed")))
        , _signal_analysis_collected_nframes(0)
        , _signal_analysis_collect_nframes_max(0)
+       , _configured (false)
        , _no_inplace (false)
        , _no_inplace (false)
-       , _pending_no_inplace (false)
        , _strict_io (false)
        , _strict_io (false)
-       , _strict_io_configured (false)
+       , _custom_cfg (false)
+       , _pending_no_inplace (false)
 {
        /* the first is the master */
 
 {
        /* the first is the master */
 
@@ -81,6 +82,10 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug)
        }
 }
 
        }
 }
 
+PluginInsert::~PluginInsert ()
+{
+}
+
 bool
 PluginInsert::set_count (uint32_t num)
 {
 bool
 PluginInsert::set_count (uint32_t num)
 {
@@ -117,8 +122,11 @@ PluginInsert::set_count (uint32_t num)
        return true;
 }
 
        return true;
 }
 
-PluginInsert::~PluginInsert ()
+
+void
+PluginInsert::set_outputs (const ChanCount& c)
 {
 {
+       _custom_out = c;
 }
 
 void
 }
 
 void
@@ -137,13 +145,27 @@ PluginInsert::control_list_automation_state_changed (Evoral::Parameter which, Au
 
 ChanCount
 PluginInsert::output_streams() const
 
 ChanCount
 PluginInsert::output_streams() const
+{
+       assert (_configured);
+       return _configured_out;
+}
+
+ChanCount
+PluginInsert::input_streams() const
+{
+       assert (_configured);
+       return _configured_in;
+}
+
+ChanCount
+PluginInsert::internal_output_streams() const
 {
        assert (!_plugins.empty());
 
        PluginInfoPtr info = _plugins.front()->get_info();
 
 {
        assert (!_plugins.empty());
 
        PluginInfoPtr info = _plugins.front()->get_info();
 
-       if (_strict_io_configured) {
-               return _configured_in; // XXX, check initial configuration
+       if (_match.strict_io) {
+               return _configured_in; // XXX
        }
        else if (info->reconfigurable_io()) {
                ChanCount out = _plugins.front()->output_streams ();
        }
        else if (info->reconfigurable_io()) {
                ChanCount out = _plugins.front()->output_streams ();
@@ -159,7 +181,7 @@ PluginInsert::output_streams() const
 }
 
 ChanCount
 }
 
 ChanCount
-PluginInsert::input_streams() const
+PluginInsert::internal_input_streams() const
 {
        assert (!_plugins.empty());
 
 {
        assert (!_plugins.empty());
 
@@ -366,7 +388,12 @@ PluginInsert::flush ()
 void
 PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now)
 {
 void
 PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now)
 {
-       _no_inplace = _pending_no_inplace;
+       PinMappings in_map (_in_map);
+       PinMappings out_map (_out_map);
+
+       // TODO auto-detect (if PinMappings are not identity maps)
+       _no_inplace = _pending_no_inplace || _plugins.front()->inplace_broken ();
+
        // Calculate if, and how many frames we need to collect for analysis
        framecnt_t collect_signal_nframes = (_signal_analysis_collect_nframes_max -
                                             _signal_analysis_collected_nframes);
        // Calculate if, and how many frames we need to collect for analysis
        framecnt_t collect_signal_nframes = (_signal_analysis_collect_nframes_max -
                                             _signal_analysis_collected_nframes);
@@ -374,26 +401,31 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                collect_signal_nframes = nframes;
        }
 
                collect_signal_nframes = nframes;
        }
 
-       ChanCount const in_streams = input_streams ();
-       ChanCount const out_streams = output_streams ();
 
        if (_match.method == Split) {
 
        if (_match.method == Split) {
-               assert (_in_map.size () == 1);
+
+               assert (in_map.size () == 1);
+               ChanCount const in_streams = internal_input_streams ();
+               // TODO - inplace & analysis only
+               // for no-inplace the buffer is copied anyway..
                /* fix the input mapping so that we have maps for each of the plugin's inputs */
 
                /* copy the first stream's buffer contents to the others */
                /* XXX: audio only */
                bool valid;
                /* fix the input mapping so that we have maps for each of the plugin's inputs */
 
                /* copy the first stream's buffer contents to the others */
                /* XXX: audio only */
                bool valid;
-               uint32_t first_idx = _in_map[0].get (DataType::AUDIO, 0, &valid);
+               uint32_t first_idx = in_map[0].get (DataType::AUDIO, 0, &valid);
                if (valid) {
                        for (uint32_t i = in_streams.n_audio(); i < natural_input_streams().n_audio(); ++i) {
                if (valid) {
                        for (uint32_t i = in_streams.n_audio(); i < natural_input_streams().n_audio(); ++i) {
-                               bufs.get_audio(_in_map[0].get (DataType::AUDIO, i, &valid)).read_from(bufs.get_audio(first_idx), nframes, offset, offset);
+                               uint32_t idx = in_map[0].get (DataType::AUDIO, i, &valid);
+                               if (valid) {
+                                       bufs.get_audio(idx).read_from(bufs.get_audio(first_idx), nframes, offset, offset);
+                               }
                        }
                }
        }
 
                        }
                }
        }
 
-       bufs.set_count(ChanCount::max(bufs.count(), in_streams));
-       bufs.set_count(ChanCount::max(bufs.count(), out_streams));
+       bufs.set_count(ChanCount::max(bufs.count(), _configured_in));
+       bufs.set_count(ChanCount::max(bufs.count(), _configured_out));
 
        /* Note that we've already required that plugins
           be able to handle in-place processing.
 
        /* Note that we've already required that plugins
           be able to handle in-place processing.
@@ -433,12 +465,12 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
        if (collect_signal_nframes > 0) {
                // collect input
                //std::cerr << "collect input, bufs " << bufs.count().n_audio() << " count,  " << bufs.available().n_audio() << " available" << std::endl;
        if (collect_signal_nframes > 0) {
                // collect input
                //std::cerr << "collect input, bufs " << bufs.count().n_audio() << " count,  " << bufs.available().n_audio() << " available" << std::endl;
-               //std::cerr << "               streams " << input_streams().n_audio() << std::endl;
+               //std::cerr << "               streams " << internal_input_streams().n_audio() << std::endl;
                //std::cerr << "filling buffer with " << collect_signal_nframes << " frames at " << _signal_analysis_collected_nframes << std::endl;
 
                //std::cerr << "filling buffer with " << collect_signal_nframes << " frames at " << _signal_analysis_collected_nframes << std::endl;
 
-               _signal_analysis_inputs.set_count(input_streams());
+               _signal_analysis_inputs.set_count(internal_input_streams());
 
 
-               for (uint32_t i = 0; i < input_streams().n_audio(); ++i) {
+               for (uint32_t i = 0; i < internal_input_streams().n_audio(); ++i) {
                        _signal_analysis_inputs.get_audio(i).read_from(
                                bufs.get_audio(i),
                                collect_signal_nframes,
                        _signal_analysis_inputs.get_audio(i).read_from(
                                bufs.get_audio(i),
                                collect_signal_nframes,
@@ -454,8 +486,8 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                uint32_t pc = 0;
                for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
 
                uint32_t pc = 0;
                for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
 
-                       ARDOUR::ChanMapping in_map (natural_input_streams());
-                       ARDOUR::ChanMapping out_map;
+                       ARDOUR::ChanMapping i_in_map (natural_input_streams());
+                       ARDOUR::ChanMapping i_out_map;
                        ARDOUR::ChanCount mapped;
                        ARDOUR::ChanCount backmap;
 
                        ARDOUR::ChanCount mapped;
                        ARDOUR::ChanCount backmap;
 
@@ -463,7 +495,7 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                                for (uint32_t in = 0; in < natural_input_streams().get (*t); ++in) {
                                        bool valid;
                        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                                for (uint32_t in = 0; in < natural_input_streams().get (*t); ++in) {
                                        bool valid;
-                                       uint32_t in_idx = _in_map[pc].get (*t, in, &valid);
+                                       uint32_t in_idx = in_map[pc].get (*t, in, &valid);
                                        uint32_t m = mapped.get (*t);
                                        if (valid) {
                                                inplace_bufs.get (*t, m).read_from (bufs.get (*t, in_idx), nframes, offset, offset);
                                        uint32_t m = mapped.get (*t);
                                        if (valid) {
                                                inplace_bufs.get (*t, m).read_from (bufs.get (*t, in_idx), nframes, offset, offset);
@@ -482,12 +514,12 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                                for (uint32_t out = 0; out < natural_output_streams().get (*t); ++out) {
                                        uint32_t m = mapped.get (*t);
                                        inplace_bufs.get (*t, m).silence (nframes, offset);
                                for (uint32_t out = 0; out < natural_output_streams().get (*t); ++out) {
                                        uint32_t m = mapped.get (*t);
                                        inplace_bufs.get (*t, m).silence (nframes, offset);
-                                       out_map.set (*t, out, m);
+                                       i_out_map.set (*t, out, m);
                                        mapped.set (*t, m + 1);
                                }
                        }
 
                                        mapped.set (*t, m + 1);
                                }
                        }
 
-                       if ((*i)->connect_and_run(inplace_bufs, in_map, out_map, nframes, offset)) {
+                       if ((*i)->connect_and_run(inplace_bufs, i_in_map, i_out_map, nframes, offset)) {
                                deactivate ();
                        }
 
                                deactivate ();
                        }
 
@@ -497,7 +529,7 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                                for (uint32_t out = 0; out < natural_output_streams().get (*t); ++out) {
                                        uint32_t m = backmap.get (*t);
                                        bool valid;
                                for (uint32_t out = 0; out < natural_output_streams().get (*t); ++out) {
                                        uint32_t m = backmap.get (*t);
                                        bool valid;
-                                       uint32_t out_idx = _out_map[pc].get (*t, out, &valid);
+                                       uint32_t out_idx = out_map[pc].get (*t, out, &valid);
                                        if (valid) {
                                                bufs.get (*t, out_idx).read_from (inplace_bufs.get (*t, m), nframes, offset, offset);
                                                used_outputs.set (*t, out_idx, 1); // mark as used
                                        if (valid) {
                                                bufs.get (*t, out_idx).read_from (inplace_bufs.get (*t, m), nframes, offset, offset);
                                                used_outputs.set (*t, out_idx, 1); // mark as used
@@ -524,7 +556,7 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
        } else {
                uint32_t pc = 0;
                for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
        } else {
                uint32_t pc = 0;
                for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
-                       if ((*i)->connect_and_run(bufs, _in_map[pc], _out_map[pc], nframes, offset)) {
+                       if ((*i)->connect_and_run(bufs, in_map[pc], out_map[pc], nframes, offset)) {
                                deactivate ();
                        }
                }
                                deactivate ();
                        }
                }
@@ -533,11 +565,11 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
        if (collect_signal_nframes > 0) {
                // collect output
                //std::cerr << "       output, bufs " << bufs.count().n_audio() << " count,  " << bufs.available().n_audio() << " available" << std::endl;
        if (collect_signal_nframes > 0) {
                // collect output
                //std::cerr << "       output, bufs " << bufs.count().n_audio() << " count,  " << bufs.available().n_audio() << " available" << std::endl;
-               //std::cerr << "               streams " << output_streams().n_audio() << std::endl;
+               //std::cerr << "               streams " << internal_output_streams().n_audio() << std::endl;
 
 
-               _signal_analysis_outputs.set_count(output_streams());
+               _signal_analysis_outputs.set_count(internal_output_streams());
 
 
-               for (uint32_t i = 0; i < output_streams().n_audio(); ++i) {
+               for (uint32_t i = 0; i < internal_output_streams().n_audio(); ++i) {
                        _signal_analysis_outputs.get_audio(i).read_from(
                                bufs.get_audio(i),
                                collect_signal_nframes,
                        _signal_analysis_outputs.get_audio(i).read_from(
                                bufs.get_audio(i),
                                collect_signal_nframes,
@@ -791,12 +823,42 @@ PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
        return boost::shared_ptr<Plugin> ((Plugin*) 0);
 }
 
        return boost::shared_ptr<Plugin> ((Plugin*) 0);
 }
 
+void
+PluginInsert::set_input_map (uint32_t num, ChanMapping m) {
+       if (num < _in_map.size()) {
+               bool changed = _in_map[num] != m;
+               _in_map[num] = m;
+               if (changed) {
+                       PluginMapChanged (); /* EMIT SIGNAL */
+               }
+       }
+}
+
+void
+PluginInsert::set_output_map (uint32_t num, ChanMapping m) {
+       if (num < _out_map.size()) {
+               bool changed = _out_map[num] != m;
+               _out_map[num] = m;
+               if (changed) {
+                       PluginMapChanged (); /* EMIT SIGNAL */
+               }
+       }
+}
+
 bool
 PluginInsert::configure_io (ChanCount in, ChanCount out)
 {
        Match old_match = _match;
 bool
 PluginInsert::configure_io (ChanCount in, ChanCount out)
 {
        Match old_match = _match;
-       ChanCount old_in = input_streams ();
-       ChanCount old_out = output_streams ();
+       ChanCount old_in;
+       ChanCount old_out;
+
+       if (_configured) {
+               old_in = _configured_in;
+               old_out = _configured_out;
+       } else {
+               old_in = internal_input_streams ();
+               old_out = internal_output_streams ();
+       }
 
        _configured_in = in;
        _configured_out = out;
 
        _configured_in = in;
        _configured_out = out;
@@ -811,6 +873,7 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
        _match = private_can_support_io_configuration (in, out);
        if (set_count (_match.plugins) == false) {
                PluginIoReConfigure (); /* EMIT SIGNAL */
        _match = private_can_support_io_configuration (in, out);
        if (set_count (_match.plugins) == false) {
                PluginIoReConfigure (); /* EMIT SIGNAL */
+               _configured = false;
                return false;
        }
 
                return false;
        }
 
@@ -820,6 +883,7 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
        case Hide:
                if (_plugins.front()->configure_io (_plugins.front()->get_info()->n_inputs, out) == false) {
                        PluginIoReConfigure (); /* EMIT SIGNAL */
        case Hide:
                if (_plugins.front()->configure_io (_plugins.front()->get_info()->n_inputs, out) == false) {
                        PluginIoReConfigure (); /* EMIT SIGNAL */
+                       _configured = false;
                        return false;
                }
                break;
                        return false;
                }
                break;
@@ -827,27 +891,42 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
        default:
                if (_plugins.front()->configure_io (in, out) == false) {
                        PluginIoReConfigure (); /* EMIT SIGNAL */
        default:
                if (_plugins.front()->configure_io (in, out) == false) {
                        PluginIoReConfigure (); /* EMIT SIGNAL */
+                       _configured = false;
                        return false;
                }
                break;
        }
 
                        return false;
                }
                break;
        }
 
-       // TODO make configurable, ideally use a single in/out map for all replicated
-       // plugins.
-       uint32_t pc = 0;
-       for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
-               if (_match.method == Split) {
-                       /* TODO see PluginInsert::connect_and_run, channel replication */
-                       _in_map[pc] = ChanMapping (natural_input_streams ());
-               } else {
-                       _in_map[pc] = ChanMapping (input_streams ());
-               }
-               _out_map[pc] = ChanMapping (output_streams());
+       if (old_in == in && old_out == out
+                       && old_match.method == _match.method
+                       && _in_map.size() == _out_map.size()
+                       && _in_map.size() == get_count ()
+                ) {
+               /* If the configuraton has not changed AND there is a custom map,
+                * keep the custom map.
+                */
+               // TODO: check if it's valid..
+       } else {
+               /* generate a new mapping */
+               uint32_t pc = 0;
+               _in_map.clear ();
+               _out_map.clear ();
+               for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
+                       if (_match.method == Split) {
+                               /* TODO see PluginInsert::connect_and_run, channel replication */
+                               _in_map[pc] = ChanMapping (max (natural_input_streams (), in));
+                       } else {
+                               _in_map[pc] = ChanMapping (min (natural_input_streams (), in));
+                       }
+                       _out_map[pc] = ChanMapping (min (natural_output_streams(), out));
 
 
-               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-                       _in_map[pc].offset_to(*t, pc * natural_input_streams().get(*t));
-                       _out_map[pc].offset_to(*t, pc * natural_output_streams().get(*t));
+                       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                               _in_map[pc].offset_to(*t, pc * natural_input_streams().get(*t));
+                               _out_map[pc].offset_to(*t, pc * natural_output_streams().get(*t));
+                       }
                }
                }
+               //TODO if changed:
+               //PluginMapChanged (); /* EMIT SIGNAL */
        }
 
 
        }
 
 
@@ -871,6 +950,7 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
 
        // std::cerr << "set counts to i" << in.n_audio() << "/o" << out.n_audio() << std::endl;
 
 
        // std::cerr << "set counts to i" << in.n_audio() << "/o" << out.n_audio() << std::endl;
 
+       _configured = true;
        return Processor::configure_io (in, out);
 }
 
        return Processor::configure_io (in, out);
 }
 
@@ -895,11 +975,15 @@ PluginInsert::can_support_io_configuration (const ChanCount& in, ChanCount& out)
 PluginInsert::Match
 PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanCount& out)
 {
 PluginInsert::Match
 PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanCount& out)
 {
-       _strict_io_configured = false;
        if (_plugins.empty()) {
                return Match();
        }
 
        if (_plugins.empty()) {
                return Match();
        }
 
+       if (_custom_cfg && !_strict_io) {
+               out = _custom_out;
+               return Match (ExactMatch, get_count(), false, true); // XXX
+       }
+
        PluginInfoPtr info = _plugins.front()->get_info();
        ChanCount in; in += inx;
        midi_bypass.reset();
        PluginInfoPtr info = _plugins.front()->get_info();
        ChanCount in; in += inx;
        midi_bypass.reset();
@@ -979,11 +1063,11 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
                if (in.n_audio () == 0 && in.n_midi () > 0 && outputs.n_audio () > 0) {
                        // TODO limit the audio-channel count to track output
                        out = outputs + midi_bypass;
                if (in.n_audio () == 0 && in.n_midi () > 0 && outputs.n_audio () > 0) {
                        // TODO limit the audio-channel count to track output
                        out = outputs + midi_bypass;
+                       return Match (ExactMatch, 1);
                } else {
                } else {
-                       _strict_io_configured = true;
                        out = in + midi_bypass;
                        out = in + midi_bypass;
+                       return Match (ExactMatch, 1, true);
                }
                }
-               return Match (ExactMatch, 1);
        }
 
        if (no_inputs) {
        }
 
        if (no_inputs) {
@@ -1049,7 +1133,7 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
           plugin inputs?  Let me count the ways ...
        */
 
           plugin inputs?  Let me count the ways ...
        */
 
-       bool can_split = !_strict_io;
+       bool can_split = !_strict_io; // XXX
        for (DataType::iterator t = DataType::begin(); t != DataType::end() && can_split; ++t) {
 
                bool const can_split_type = (in.get (*t) == 1 && inputs.get (*t) > 1);
        for (DataType::iterator t = DataType::begin(); t != DataType::end() && can_split; ++t) {
 
                bool const can_split_type = (in.get (*t) == 1 && inputs.get (*t) > 1);
@@ -1085,13 +1169,13 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
        }
 
        if (could_hide && !cannot_hide) {
        }
 
        if (could_hide && !cannot_hide) {
-               if (_strict_io /*&& inputs.get (DataType::AUDIO) == outputs.get (DataType::AUDIO)*/) {
-                       _strict_io_configured = true;
+               if (_strict_io) {
                        outputs = inputs;
                        outputs = inputs;
+                       return Match (Hide, 1, true, false, hide_channels);
                } else {
                        out = outputs + midi_bypass;
                } else {
                        out = outputs + midi_bypass;
+                       return Match (Hide, 1, false, false, hide_channels);
                }
                }
-               return Match (Hide, 1, hide_channels);
        }
 
        midi_bypass.reset();
        }
 
        midi_bypass.reset();
@@ -1119,6 +1203,8 @@ PluginInsert::state (bool full)
        node.add_child_nocopy (* _configured_in.state (X_("ConfiguredInput")));
        node.add_child_nocopy (* _configured_out.state (X_("ConfiguredOutput")));
 
        node.add_child_nocopy (* _configured_in.state (X_("ConfiguredInput")));
        node.add_child_nocopy (* _configured_out.state (X_("ConfiguredOutput")));
 
+       // TODO save channel-maps,  _custom_cfg & _custom_out
+
        _plugins[0]->set_insert_id(this->id());
        node.add_child_nocopy (_plugins[0]->get_state());
 
        _plugins[0]->set_insert_id(this->id());
        node.add_child_nocopy (_plugins[0]->get_state());
 
@@ -1664,7 +1750,7 @@ PluginInsert::get_impulse_analysis_plugin()
        boost::shared_ptr<Plugin> ret;
        if (_impulseAnalysisPlugin.expired()) {
                ret = plugin_factory(_plugins[0]);
        boost::shared_ptr<Plugin> ret;
        if (_impulseAnalysisPlugin.expired()) {
                ret = plugin_factory(_plugins[0]);
-               ret->configure_io (input_streams (), output_streams ());
+               ret->configure_io (internal_input_streams (), internal_output_streams ());
                _impulseAnalysisPlugin = ret;
        } else {
                ret = _impulseAnalysisPlugin.lock();
                _impulseAnalysisPlugin = ret;
        } else {
                ret = _impulseAnalysisPlugin.lock();
@@ -1678,8 +1764,8 @@ PluginInsert::collect_signal_for_analysis (framecnt_t nframes)
 {
        // called from outside the audio thread, so this should be safe
        // only do audio as analysis is (currently) only for audio plugins
 {
        // called from outside the audio thread, so this should be safe
        // only do audio as analysis is (currently) only for audio plugins
-       _signal_analysis_inputs.ensure_buffers(  DataType::AUDIO, input_streams().n_audio(),  nframes);
-       _signal_analysis_outputs.ensure_buffers( DataType::AUDIO, output_streams().n_audio(), nframes);
+       _signal_analysis_inputs.ensure_buffers(  DataType::AUDIO, internal_input_streams().n_audio(),  nframes);
+       _signal_analysis_outputs.ensure_buffers( DataType::AUDIO, internal_output_streams().n_audio(), nframes);
 
        _signal_analysis_collected_nframes   = 0;
        _signal_analysis_collect_nframes_max = nframes;
 
        _signal_analysis_collected_nframes   = 0;
        _signal_analysis_collect_nframes_max = nframes;
index 0614ab7ef57923df404bdff10493bfb03258b6a4..2ce15a6149db7d3f08fc75bac0e600b85958ffbf 100644 (file)
@@ -1463,7 +1463,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
-                               pi->set_count (1);
+                               pi->set_count (1); // why? configure_processors_unlocked() will re-do this
                                pi->set_strict_io (_strict_io);
                        }
 
                                pi->set_strict_io (_strict_io);
                        }
 
@@ -2167,8 +2167,8 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                         */
                        processor_max_streams = ChanCount::max(processor_max_streams, pi->input_streams());
                        processor_max_streams = ChanCount::max(processor_max_streams, pi->output_streams());
                         */
                        processor_max_streams = ChanCount::max(processor_max_streams, pi->input_streams());
                        processor_max_streams = ChanCount::max(processor_max_streams, pi->output_streams());
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams());
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_output_streams());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams() * pi->get_count());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_output_streams() * pi->get_count());
                }
                out = c->second;
 
                }
                out = c->second;
 
@@ -2401,6 +2401,75 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
        return 0;
 }
 
        return 0;
 }
 
+bool
+Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
+{
+       ChanCount unused;
+       return customize_plugin_insert (proc, 0, unused);
+}
+
+bool
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
+{
+       if (_strict_io) {
+               return false;
+       }
+
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
+
+       {
+               bool found = false;
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
+                       if (*p == proc) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found) {
+                       return false;
+               }
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+
+               assert (!pi->strict_io ());
+               bool      old_cust = pi->custom_cfg ();
+               uint32_t  old_cnt  = pi->get_count ();
+               ChanCount old_chan = pi->output_streams ();
+
+               if (count == 0) {
+                       pi->set_custom_cfg (false);
+               } else {
+                       pi->set_custom_cfg (true);
+                       pi->set_count (count);
+                       pi->set_outputs (outs);
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               if (c.empty()) {
+                       /* not possible */
+
+                       pi->set_count (old_cnt);
+                       pi->set_outputs (old_chan);
+                       pi->set_custom_cfg (old_cust);
+
+                       return false;
+               }
+               configure_processors_unlocked (0);
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
 bool
 Route::set_strict_io (const bool enable)
 {
 bool
 Route::set_strict_io (const bool enable)
 {