fix invalid mapping detection
[ardour.git] / libs / ardour / plugin_insert.cc
index 9e55979a244dd9bc2ab0720afe8d09c53dad2ebc..99893ca0220e6a3b1d8fb77646b050a50c6216bc 100644 (file)
@@ -72,7 +72,7 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug)
        , _no_inplace (false)
        , _strict_io (false)
        , _custom_cfg (false)
-       , _pending_no_inplace (false)
+       , _maps_from_state (false)
 {
        /* the first is the master */
 
@@ -86,6 +86,16 @@ PluginInsert::~PluginInsert ()
 {
 }
 
+void
+PluginInsert::set_strict_io (bool b)
+{
+       bool changed = _strict_io != b;
+       _strict_io = b;
+       if (changed) {
+               PluginConfigChanged (); /* EMIT SIGNAL */
+       }
+}
+
 bool
 PluginInsert::set_count (uint32_t num)
 {
@@ -111,12 +121,14 @@ PluginInsert::set_count (uint32_t num)
                                /* XXX do something */
                        }
                }
+               PluginConfigChanged (); /* EMIT SIGNAL */
 
        } else if (num < _plugins.size()) {
                uint32_t diff = _plugins.size() - num;
                for (uint32_t n= 0; n < diff; ++n) {
                        _plugins.pop_back();
                }
+               PluginConfigChanged (); /* EMIT SIGNAL */
        }
 
        return true;
@@ -126,7 +138,21 @@ PluginInsert::set_count (uint32_t num)
 void
 PluginInsert::set_outputs (const ChanCount& c)
 {
+       bool changed = (_custom_out != c) && _custom_cfg;
        _custom_out = c;
+       if (changed) {
+               PluginConfigChanged (); /* EMIT SIGNAL */
+       }
+}
+
+void
+PluginInsert::set_custom_cfg (bool b)
+{
+       bool changed = _custom_cfg != b;
+       _custom_cfg = b;
+       if (changed) {
+               PluginConfigChanged (); /* EMIT SIGNAL */
+       }
 }
 
 void
@@ -388,31 +414,6 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
        PinMappings in_map (_in_map);
        PinMappings out_map (_out_map);
 
-#if 1
-       // auto-detect if inplace processing is possible
-       // TODO: do this once. during configure_io and every time the
-       // plugin-count or mapping changes.
-       bool inplace_ok = true;
-       for (uint32_t pc = 0; pc < get_count() && inplace_ok ; ++pc) {
-               if (!in_map[pc].is_monotonic ()) {
-                       inplace_ok = false;
-               }
-               if (!out_map[pc].is_monotonic ()) {
-                       inplace_ok = false;
-               }
-       }
-
-       if (_pending_no_inplace != !inplace_ok) {
-#ifndef NDEBUG // this 'cerr' needs to go ASAP.
-               cerr << name () << " automatically set : " << (inplace_ok ? "Use Inplace" : "No Inplace") << "\n"; // XXX
-#endif
-               _pending_no_inplace = !inplace_ok;
-       }
-#endif
-
-       _no_inplace = _pending_no_inplace || _plugins.front()->inplace_broken ();
-
-
 #if 1
        // TODO optimize special case.
        // Currently this never triggers because the in_map for "Split" triggeres no_inplace.
@@ -878,6 +879,7 @@ PluginInsert::set_input_map (uint32_t num, ChanMapping m) {
        if (num < _in_map.size()) {
                bool changed = _in_map[num] != m;
                _in_map[num] = m;
+               sanitize_maps ();
                if (changed) {
                        PluginMapChanged (); /* EMIT SIGNAL */
                }
@@ -889,6 +891,7 @@ PluginInsert::set_output_map (uint32_t num, ChanMapping m) {
        if (num < _out_map.size()) {
                bool changed = _out_map[num] != m;
                _out_map[num] = m;
+               sanitize_maps ();
                if (changed) {
                        PluginMapChanged (); /* EMIT SIGNAL */
                }
@@ -942,6 +945,105 @@ PluginInsert::has_midi_bypass () const
        return false;
 }
 
+bool
+PluginInsert::sanitize_maps ()
+{
+       bool changed = false;
+       /* strip dead wood */
+       PinMappings new_ins;
+       PinMappings new_outs;
+       for (uint32_t pc = 0; pc < get_count(); ++pc) {
+               ChanMapping new_in;
+               ChanMapping new_out;
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       for (uint32_t i = 0; i < natural_input_streams().get (*t); ++i) {
+                               bool valid;
+                               uint32_t idx = _in_map[pc].get (*t, i, &valid);
+                               if (valid && idx < _configured_in.get (*t)) {
+                                       new_in.set (*t, i, idx);
+                               }
+                       }
+                       for (uint32_t o = 0; o < natural_output_streams().get (*t); ++o) {
+                               bool valid;
+                               uint32_t idx = _out_map[pc].get (*t, o, &valid);
+                               if (valid && idx < _configured_out.get (*t)) {
+                                       new_out.set (*t, o, idx);
+                               }
+                       }
+               }
+               if (_in_map[pc] != new_in || _out_map[pc] != new_out) {
+                       changed = true;
+               }
+               new_ins[pc] = new_in;
+               new_outs[pc] = new_out;
+       }
+       /* prevent dup output assignments */
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               for (uint32_t o = 0; o < _configured_out.get (*t); ++o) {
+                       bool mapped = false;
+                       for (uint32_t pc = 0; pc < get_count(); ++pc) {
+                               bool valid;
+                               uint32_t idx = new_outs[pc].get_src (*t, o, &valid);
+                               if (valid && mapped) {
+                                       new_outs[pc].unset (*t, idx);
+                               } else if (valid) {
+                                       mapped = true;
+                               }
+                       }
+               }
+       }
+
+       if (_in_map != new_ins || _out_map != new_outs) {
+               changed = true;
+       }
+       _in_map = new_ins;
+       _out_map = new_outs;
+
+       return changed;
+}
+
+bool
+PluginInsert::reset_map (bool emit)
+{
+       uint32_t pc = 0;
+       const PinMappings old_in (_in_map);
+       const PinMappings old_out (_out_map);
+
+       _in_map.clear ();
+       _out_map.clear ();
+       for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
+               if (_match.method == Split) {
+                       _in_map[pc] = ChanMapping ();
+                       /* connect inputs in round-robin fashion */
+                       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                               const uint32_t cend = _configured_in.get (*t);
+                               if (cend == 0) { continue; }
+                               uint32_t c = 0;
+                               for (uint32_t in = 0; in < natural_input_streams().get (*t); ++in) {
+                                       _in_map[pc].set (*t, in, c);
+                                       c = c + 1 % cend;
+                               }
+                       }
+               } else {
+                       _in_map[pc] = ChanMapping (ChanCount::min (natural_input_streams (), _configured_in));
+               }
+               _out_map[pc] = ChanMapping (ChanCount::min (natural_output_streams(), _configured_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));
+               }
+       }
+       sanitize_maps ();
+       if (old_in == _in_map && old_out == _out_map) {
+               return false;
+       }
+       if (emit) {
+               PluginMapChanged (); /* EMIT SIGNAL */
+       }
+       return true;
+}
+
 bool
 PluginInsert::configure_io (ChanCount in, ChanCount out)
 {
@@ -959,8 +1061,13 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
 
        /* get plugin configuration */
        _match = private_can_support_io_configuration (in, out);
-#ifndef NDEBUG // XXX
-       cout << "Match '" << name() << "': " << _match;
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::ChanMapping)) {
+               DEBUG_STR_DECL(a);
+               DEBUG_STR_APPEND(a, string_compose ("Match '%1':",  name()));
+               DEBUG_STR_APPEND(a, _match);
+               DEBUG_TRACE (DEBUG::ChanMapping, DEBUG_STR(a).str());
+       }
 #endif
 
        /* set the matching method and number of plugins that we will use to meet this configuration */
@@ -1014,77 +1121,54 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
                 ) {
                /* If the configuraton has not changed, keep the mapping */
        } else if (_match.custom_cfg && _configured) {
-               /* strip dead wood */
-               for (uint32_t pc = 0; pc < get_count(); ++pc) {
-                       ChanMapping new_in;
-                       ChanMapping new_out;
-                       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-                               for (uint32_t i = 0; i < natural_input_streams().get (*t); ++i) {
-                                       bool valid;
-                                       uint32_t idx = _in_map[pc].get (*t, i, &valid);
-                                       if (valid && idx <= in.get (*t)) {
-                                               new_in.set (*t, i, idx);
-                                       }
-                               }
-                               for (uint32_t o = 0; o < natural_output_streams().get (*t); ++o) {
-                                       bool valid;
-                                       uint32_t idx = _out_map[pc].get (*t, o, &valid);
-                                       if (valid && idx <= out.get (*t)) {
-                                               new_out.set (*t, o, idx);
-                                       }
-                               }
-                       }
-                       if (_in_map[pc] != new_in || _out_map[pc] != new_out) {
-                               mapping_changed = true;
-                       }
-                       _in_map[pc] = new_in;
-                       _out_map[pc] = new_out;
-               }
+               mapping_changed = sanitize_maps ();
        } 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) {
-                               _in_map[pc] = ChanMapping ();
-                               /* connect inputs in round-robin fashion */
-                               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-                                       const uint32_t cend = _configured_in.get (*t);
-                                       if (cend == 0) { continue; }
-                                       uint32_t c = 0;
-                                       for (uint32_t in = 0; in < natural_input_streams().get (*t); ++in) {
-                                               _in_map[pc].set (*t, in, c);
-                                               c = c + 1 % cend;
-                                       }
-                               }
-                       } else {
-                               _in_map[pc] = ChanMapping (ChanCount::min (natural_input_streams (), in));
-                       }
-                       _out_map[pc] = ChanMapping (ChanCount::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));
-                       }
+               if (_maps_from_state) {
+                       _maps_from_state = false;
                        mapping_changed = true;
+                       sanitize_maps ();
+               } else {
+                       /* generate a new mapping */
+                       mapping_changed = reset_map (false);
                }
        }
 
        if (mapping_changed) {
                PluginMapChanged (); /* EMIT SIGNAL */
-#ifndef NDEBUG // XXX
-               uint32_t pc = 0;
-               cout << "----<<----\n";
-               for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
-                       cout << "Channel Map for " << name() << " plugin " << pc << "\n";
-                       cout << " * Inputs:\n" << _in_map[pc];
-                       cout << " * Outputs:\n" << _out_map[pc];
+
+#ifndef NDEBUG
+               if (DEBUG_ENABLED(DEBUG::ChanMapping)) {
+                       uint32_t pc = 0;
+                       DEBUG_STR_DECL(a);
+                       DEBUG_STR_APPEND(a, "\n--------<<--------\n");
+                       for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i, ++pc) {
+                               if (pc > 0) {
+                       DEBUG_STR_APPEND(a, "----><----\n");
+                               }
+                               DEBUG_STR_APPEND(a, string_compose ("Channel Map for %1 plugin %2\n", name(), pc));
+                               DEBUG_STR_APPEND(a, " * Inputs:\n");
+                               DEBUG_STR_APPEND(a, _in_map[pc]);
+                               DEBUG_STR_APPEND(a, " * Outputs:\n");
+                               DEBUG_STR_APPEND(a, _out_map[pc]);
+                       }
+                       DEBUG_STR_APPEND(a, "-------->>--------\n");
+                       DEBUG_TRACE (DEBUG::ChanMapping, DEBUG_STR(a).str());
                }
-               cout << "---->>----\n";
 #endif
        }
 
+       // auto-detect if inplace processing is possible
+       bool inplace_ok = true;
+       for (uint32_t pc = 0; pc < get_count() && inplace_ok ; ++pc) {
+               if (!_in_map[pc].is_monotonic ()) {
+                       inplace_ok = false;
+               }
+               if (!_out_map[pc].is_monotonic ()) {
+                       inplace_ok = false;
+               }
+       }
+       _no_inplace = !inplace_ok || _plugins.front()->inplace_broken ();
+
        if (old_in != in || old_out != out
                        || (old_match.method != _match.method && (old_match.method == Split || _match.method == Split))
                 ) {
@@ -1256,11 +1340,11 @@ PluginInsert::automatic_can_support_io_configuration (ChanCount const & inx, Cha
        ChanCount outputs = info->n_outputs;
 
        if (in.get(DataType::MIDI) == 1 && outputs.get(DataType::MIDI) == 0) {
-               DEBUG_TRACE ( DEBUG::Processors, string_compose ("bypassing midi-data around %1\n", name()));
+               DEBUG_TRACE ( DEBUG::ChanMapping, string_compose ("bypassing midi-data around %1\n", name()));
                midi_bypass.set (DataType::MIDI, 1);
        }
        if (in.get(DataType::MIDI) == 1 && inputs.get(DataType::MIDI) == 0) {
-               DEBUG_TRACE ( DEBUG::Processors, string_compose ("hiding midi-port from plugin %1\n", name()));
+               DEBUG_TRACE ( DEBUG::ChanMapping, string_compose ("hiding midi-port from plugin %1\n", name()));
                in.set(DataType::MIDI, 0);
        }
 
@@ -1400,11 +1484,12 @@ PluginInsert::state (bool full)
 
        /* save custom i/o config */
        node.add_property("custom", _custom_cfg ? "yes" : "no");
-       if (_custom_cfg) {
-               assert (_custom_out == _configured_out); // redundant
-               for (uint32_t pc = 0; pc < get_count(); ++pc) {
-                       // TODO save _in_map[pc], _out_map[pc]
-               }
+       for (uint32_t pc = 0; pc < get_count(); ++pc) {
+               char tmp[128];
+               snprintf (tmp, sizeof(tmp), "InputMap-%d", pc);
+               node.add_child_nocopy (* _in_map[pc].state (tmp));
+               snprintf (tmp, sizeof(tmp), "OutputMap-%d", pc);
+               node.add_child_nocopy (* _out_map[pc].state (tmp));
        }
 
        _plugins[0]->set_insert_id(this->id());
@@ -1657,15 +1742,31 @@ PluginInsert::set_state(const XMLNode& node, int version)
                _custom_cfg = string_is_affirmative (prop->value());
        }
 
+       uint32_t in_maps = 0;
+       uint32_t out_maps = 0;
        XMLNodeList kids = node.children ();
        for (XMLNodeIterator i = kids.begin(); i != kids.end(); ++i) {
                if ((*i)->name() == X_("ConfiguredOutput")) {
                        _custom_out = ChanCount(**i);
                }
-               // TODO restore mappings for all 0 .. count.
+               if (strncmp ((*i)->name().c_str(), X_("InputMap-"), 9) == 0) {
+                       long pc = atol (&((*i)->name().c_str()[9]));
+                       if (pc >=0 && pc <= get_count()) {
+                               _in_map[pc] = ChanMapping (**i);
+                               ++in_maps;
+                       }
+               }
+               if (strncmp ((*i)->name().c_str(), X_("OutputMap-"), 10) == 0) {
+                       long pc = atol (&((*i)->name().c_str()[10]));
+                       if (pc >=0 && pc <= get_count()) {
+                               _out_map[pc] = ChanMapping (**i);
+                               ++out_maps;
+                       }
+               }
+       }
+       if (in_maps == out_maps && out_maps >0 && out_maps == get_count()) {
+               _maps_from_state = true;
        }
-
-
 
        for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
                if (active()) {