/* * Copyright (C) 2010-2011 Carl Hetherington * Copyright (C) 2013-2022 Robin Gareus * Copyright (C) 2016-2017 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "pbd/types_convert.h" #include "pbd/xml++.h" #include "ardour/audio_buffer.h" #include "ardour/sidechain.h" #include "ardour/io.h" #include "ardour/types_convert.h" #include "ardour/unknown_processor.h" #include "pbd/i18n.h" using namespace std; using namespace ARDOUR; static std::string proc_type_map (std::string const& str) { /* compare to PluginInsert::set_state */ if (str == X_("ladspa") || str == X_("Ladspa")) { /* handle old school sessions */ return "LV1"; } else if (str == X_("lv2")) { return "LV2"; } else if (str == X_("windows-vst")) { return "VST2"; } else if (str == X_("lxvst")) { return "VST2"; } else if (str == X_("mac-vst")) { return "VST2"; } else if (str == X_("audiounit")) { return "AU"; } else if (str == X_("luaproc")) { return "Lua"; } else if (str == X_("vst3")) { return "VST3"; } else { return str; } } UnknownProcessor::UnknownProcessor (Session&s, XMLNode const &state, SessionObject* o) : Processor (s, "", Temporal::AudioTime) , _state (state) , have_ioconfig (false) , saved_input (0) , saved_output (0) { set_owner (o); XMLProperty const* pname = state.property (X_("name")); if (pname) { XMLProperty const* ptype = state.property (X_("type")); if (ptype) { set_name (string_compose ("%1 (%2)", pname->value (), proc_type_map (ptype->value ()))); } else { set_name (pname->value ()); } _display_to_user = true; } int have_io = 0; XMLNodeList kids = state.children (); for (XMLNodeIterator i = kids.begin(); i != kids.end(); ++i) { if ((*i)->name() == X_("ConfiguredInput")) { have_io |= 1; saved_input = new ChanCount(**i); } if ((*i)->name() == X_("ConfiguredOutput")) { have_io |= 2; saved_output = new ChanCount(**i); } /* sidechain is a Processor (IO) * add a SC port to retain connections */ if ((*i)->name () == Processor::state_node_name) { add_sidechain_from_xml (**i, Stateful::loading_state_version); } } have_ioconfig = (have_io == 3); } UnknownProcessor::~UnknownProcessor () { delete saved_input;; delete saved_output; } XMLNode & UnknownProcessor::state () { return *(new XMLNode (_state)); } bool UnknownProcessor::can_support_io_configuration (const ChanCount &in, ChanCount & out) { if (have_ioconfig && in == *saved_input) { out = *saved_output; return true; } else if (!have_ioconfig) { /* pass for old sessions. * * session load assumes processor config succeeds. * if initial configuration fails, processors downstream * remain unconfigured and at least the panner will assert/segfault. * * This may still result in impossible setup, however * Route::configure_processors_unlocked() ignores configure_io() return value * in the inner loop and configures all available processors. * * It can still lead to segfaults IFF the track has no inputs and this is a * generator (processor_max_streams will be zero). */ PBD::warning << _("Using plugin-stub with unknown i/o configuration for: ") << name() << endmsg; #if 0 /* No output channels are fine (or should be, there may be edge-cases with e.g sends). * * Discussion needed. * * This is the safer option (no audio output, no possible damage) * and the way to go in the long run. * An even better solution is to disable the route if there are missing plugins * and/or impossible configurations. * * Currently no output channels results in awkward GUI route display and also * breaks semantics in mixbus (which assumes that the route has channels required * for the always present mixer-strip plugin). */ out = ChanCount (); #else out = in; #endif return true; } else { PBD::error << _("Using plugin-stub with mismatching i/o configuration for: ") << name() << endmsg; out = in; } return true; } void UnknownProcessor::run (BufferSet& bufs, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, double /*speed*/, pframes_t nframes, bool) { if (!have_ioconfig) { return; } // silence excess output buffers for (uint32_t i = saved_input->n_audio(); i < saved_output->n_audio(); ++i) { bufs.get_audio (i).silence (nframes); } } void UnknownProcessor::add_sidechain_from_xml (const XMLNode& node, int version) { if (version < 3000) { return; } XMLNodeList nlist = node.children(); if (nlist.size() == 0) { return; } uint32_t n_audio = 0; uint32_t n_midi = 0; XMLNodeConstIterator it = nlist.front()->children().begin(); for ( ; it != nlist.front()->children().end(); ++ it) { if ((*it)->name() == "Port") { DataType type (DataType::NIL); (*it)->get_property ("type", type); if (type == DataType::AUDIO) { ++n_audio; } else if (type == DataType::MIDI) { ++n_midi; } } } _sidechain.reset (new SideChain (_session, "toBeRenamed")); for (uint32_t n = 0; n < n_audio; ++n) { _sidechain->input()->add_port ("", owner(), DataType::AUDIO); // add a port, don't connect. } for (uint32_t n = 0; n < n_midi; ++n) { _sidechain->input()->add_port ("", owner(), DataType::MIDI); // add a port, don't connect. } _sidechain->set_state (node, version); }