void set_parameter (uint32_t which, float val);
float get_parameter (uint32_t which) const;
+ PluginOutputConfiguration possible_output () const { return _output_configs; }
+
int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const;
uint32_t nth_parameter (uint32_t which, bool& ok) const;
void activate ();
AudioBufferList* buffers;
bool _has_midi_input;
bool _has_midi_output;
+ PluginOutputConfiguration _output_configs;
/* despite all the cool work that apple did on their AU preset
system, they left factory presets and user presets as two
std::string get_docs () const { return _docs; }
std::string get_parameter_docs (uint32_t) const;
+ PluginOutputConfiguration possible_output () const { return _output_configs; }
+
std::set<Evoral::Parameter> automatable() const;
void activate () { }
ChanCount _configured_in;
ChanCount _configured_out;
+ PluginOutputConfiguration _output_configs;
bool _has_midi_input;
bool _has_midi_output;
int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const;
uint32_t nth_parameter (uint32_t port, bool& ok) const;
- IOPortDescription describe_io_port (DataType dt, bool input, uint32_t id);
+ IOPortDescription describe_io_port (DataType dt, bool input, uint32_t id) const;
const void* extension_data (const char* uri) const;
#include <boost/shared_ptr.hpp>
#include <string>
+#include <set>
#include "pbd/statefuldestructible.h"
#include "pbd/controllable.h"
typedef boost::shared_ptr<Plugin> PluginPtr;
typedef boost::shared_ptr<PluginInfo> PluginInfoPtr;
typedef std::list<PluginInfoPtr> PluginInfoList;
+typedef std::set<uint32_t> PluginOutputConfiguration;
/** A plugin is an external module (usually 3rd party provided) loaded into Ardour
* for the purpose of digital signal processing.
bool is_sidechain;
};
- virtual IOPortDescription describe_io_port (DataType dt, bool input, uint32_t id);
+ virtual IOPortDescription describe_io_port (DataType dt, bool input, uint32_t id) const;
+ virtual PluginOutputConfiguration possible_output () const;
virtual void set_automation_control (uint32_t /*port_index*/, boost::shared_ptr<ARDOUR::AutomationControl>) { }
}
}
- ChanCount required_buffers () const { return _required_buffers; }
+ const ChanCount& required_buffers () const { return _required_buffers; }
+ const ChanCount& preset_out () const { return _preset_out; }
// allow to override output_streams(), implies "Custom Mode"
void set_outputs (const ChanCount&);
void set_strict_io (bool b);
void set_custom_cfg (bool b);
+ bool set_preset_out (const ChanCount&);
bool add_sidechain (uint32_t n_audio = 1);
bool del_sidechain ();
boost::shared_ptr<SideChain> sidechain () const { return _sidechain; }
ChanCount _configured_internal; // with side-chain
ChanCount _configured_out;
ChanCount _custom_out;
+ ChanCount _preset_out;
ChanCount _cached_sidechain_pins;
ChanCount _required_buffers;
bool _mapping_changed;
Match private_can_support_io_configuration (ChanCount const &, ChanCount &) const;
+ Match internal_can_support_io_configuration (ChanCount const &, ChanCount &) const;
Match automatic_can_support_io_configuration (ChanCount const &, ChanCount &) const;
/** details of the match currently being used */
*/
bool customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs);
bool add_remove_sidechain (boost::shared_ptr<Processor> proc, bool);
+ bool plugin_preset_output (boost::shared_ptr<Processor> proc, ChanCount outs);
/* enable sidechain input for a given processor
*
bool
AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out, ChanCount* imprecise)
{
- // Note: We never attempt to multiply-instantiate plugins to meet io configurations.
-
+ _output_configs.clear ();
const int32_t audio_in = in.n_audio();
int32_t audio_out;
AUPluginInfoPtr pinfo = boost::dynamic_pointer_cast<AUPluginInfo>(get_info());
name(), io_configs.size(), in, out));
// preferred setting (provided by plugin_insert)
+ const int preferred_out = out.n_audio ();
audio_out = out.n_audio ();
+ bool found = false;
+ bool exact_match = false;
/* kAudioUnitProperty_SupportedNumChannels
* https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html#//apple_ref/doc/uid/TP40003278-CH12-SW20
int32_t possible_in = i->first;
int32_t possible_out = i->second;
- if ((possible_in == audio_in) && (possible_out == audio_out)) {
+ if ((possible_in == audio_in) && (possible_out == preferred_out)) {
DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tCHOSEN: %1 in %2 out to match in %3 out %4\n",
- possible_in, possible_out,
- in, out));
+ possible_in, possible_out,
+ in, out));
- out.set (DataType::MIDI, 0);
- out.set (DataType::AUDIO, audio_out);
-
- return true;
+ // exact match
+ _output_configs.insert (preferred_out);
+ exact_match = true;
+ found = true;
+ break;
}
}
/* now allow potentially "imprecise" matches */
- audio_out = -1;
- bool found = false;
-
+ int32_t audio_out = -1;
float penalty = 9999;
- const int preferred_out = out.n_audio ();
int used_possible_in = 0;
-#define FOUNDCFG(nch) { \
- float p = fabsf ((float)(nch) - preferred_out); \
- if ((nch) > preferred_out) { p *= 1.1; } \
- if (p < penalty) { \
- used_possible_in = possible_in; \
- audio_out = (nch); \
- penalty = p; \
- found = true; \
- } \
+#define FOUNDCFG(nch) { \
+ float p = fabsf ((float)(nch) - preferred_out); \
+ _output_configs.insert (nch); \
+ if ((nch) > preferred_out) { p *= 1.1; } \
+ if (p < penalty) { \
+ used_possible_in = possible_in; \
+ audio_out = (nch); \
+ penalty = p; \
+ found = true; \
+ } \
+}
+
+#define ANYTHINGGOES \
+ _output_configs.insert (0);
+
+#define UPTO(nch) { \
+ for (int n = 1; n < nch; ++n) { \
+ _output_configs.insert (n); \
+ } \
}
for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
if (possible_out == -1) {
/* any configuration possible, provide stereo output */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out == -2) {
/* invalid, should be (0, -1) */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* variable number of outputs up to -N, */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out);
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
} else if (possible_out == -2) {
/* any configuration possible, pick matching */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* explicitly variable number of outputs, pick maximum */
FOUNDCFG (max (-possible_out, preferred_out));
/* and try min, too, in case the penalty is lower */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out)
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
if (possible_out == -1) {
/* any configuration possible, pick matching */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out == -2) {
/* invalid. interpret as (-1, -1) */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* invalid, interpret as (<-2, <-2)
* variable number of outputs up to -N, */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out)
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
if (audio_in > -possible_in && imprecise == NULL) {
/* request is too large */
} else if (possible_out == -1) {
- /* any output configuration possible, provide stereo out */
+ /* any output configuration possible */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out == -2) {
/* invalid. interpret as (<-2, -1) */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* variable number of outputs up to -N, */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out)
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
if (possible_out == -1) {
/* any output configuration possible */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out == -2) {
- /* plugins shouldn't really use (>0,-2) but might.
- * interpret as (>0,-1):
- */
+ /* plugins shouldn't really use (>0,-2), interpret as (>0,-1) */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* > 0, < -2 is not specified
* interpret as up to -N */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out)
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
}
}
- if (found) {
+ if (!found) {
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tFAIL: no io configs match %1\n", in));
+ return false;
+ }
+
+ if (exact_match) {
+ out.set (DataType::MIDI, 0); // currently always zero
+ out.set (DataType::AUDIO, preferred_out);
+ } else {
if (used_possible_in < -2 && audio_in == 0) {
// input-port count cannot be zero, use as many ports
// as outputs, but at most abs(possible_in)
audio_input_cnt = max (1, min (audio_out, -used_possible_in));
}
-
out.set (DataType::MIDI, 0); /// XXX
out.set (DataType::AUDIO, audio_out);
- DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tCHOSEN: in %1 out %2\n", in, out));
- } else {
- DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tFAIL: no io configs match %1\n", in));
- return false;
}
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("\tCHOSEN: in %1 out %2\n", in, out));
return true;
}
LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, ChanCount* imprecise)
{
// caller must hold process lock (no concurrent calls to interpreter
+ _output_configs.clear ();
if (in.n_midi() > 0 && !_has_midi_input && !imprecise) {
return false;
return false;
}
- luabridge::LuaRef table = luabridge::getGlobal (L, "table"); //lua std lib
- luabridge::LuaRef tablesort = table["sort"];
- assert (tablesort.isFunction ());
-
luabridge::LuaRef *_iotable = NULL; // can't use reference :(
try {
luabridge::LuaRef iotable = ioconfig ();
- tablesort (iotable);
if (iotable.isTable ()) {
_iotable = new luabridge::LuaRef (iotable);
}
return false;
}
+ bool found = false;
+ bool exact_match = false;
const int32_t audio_in = in.n_audio ();
- int32_t audio_out;
int32_t midi_out = 0; // TODO handle _has_midi_output
// preferred setting (provided by plugin_insert)
assert (out.n_audio () > 0);
- audio_out = out.n_audio ();
+ const int preferred_out = out.n_audio ();
for (luabridge::Iterator i (iotable); !i.isNil (); ++i) {
assert (i.value ().type () == LUA_TTABLE);
int possible_out = io["audio_out"];
// exact match
- if ((possible_in == audio_in) && (possible_out == audio_out)) {
- out.set (DataType::MIDI, 0);
- out.set (DataType::AUDIO, audio_out);
- return true;
+ if ((possible_in == audio_in) && (possible_out == preferred_out)) {
+ _output_configs.insert (preferred_out);
+ exact_match = true;
+ found = true;
+ break;
}
}
/* now allow potentially "imprecise" matches */
- audio_out = -1;
- bool found = false;
-
+ int32_t audio_out = -1;
float penalty = 9999;
- const int preferred_out = out.n_audio ();
-#define FOUNDCFG(nch) { \
- float p = fabsf ((float)(nch) - preferred_out); \
- if ((nch) > preferred_out) { p *= 1.1; } \
- if (p < penalty) { \
- audio_out = (nch); \
- penalty = p; \
- found = true; \
- } \
+#define FOUNDCFG(nch) { \
+ float p = fabsf ((float)(nch) - preferred_out); \
+ _output_configs.insert (nch); \
+ if ((nch) > preferred_out) { p *= 1.1; } \
+ if (p < penalty) { \
+ audio_out = (nch); \
+ penalty = p; \
+ found = true; \
+ } \
+}
+
+#define ANYTHINGGOES \
+ _output_configs.insert (0);
+
+#define UPTO(nch) { \
+ for (int n = 1; n < nch; ++n) { \
+ _output_configs.insert (n); \
+ } \
}
for (luabridge::Iterator i (iotable); !i.isNil (); ++i) {
if (possible_out == -1) {
/* any configuration possible, stereo output */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out == -2) {
/* invalid, should be (0, -1) */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* variable number of outputs up to -N, */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out);
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
} else if (possible_out == -2) {
/* any configuration possible, pick matching */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* explicitly variable number of outputs, pick maximum */
FOUNDCFG (max (-possible_out, preferred_out));
/* and try min, too, in case the penalty is lower */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out)
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
}
if (possible_in == -2) {
-
if (possible_out == -1) {
/* any configuration possible, pick matching */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out == -2) {
/* invalid. interpret as (-1, -1) */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* invalid, interpret as (<-2, <-2)
* variable number of outputs up to -N, */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out)
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
} else if (possible_out == -1) {
/* any output configuration possible */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out == -2) {
/* invalid. interpret as (<-2, -1) */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* variable number of outputs up to -N, */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out)
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
if (possible_out == -1) {
/* any output configuration possible */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out == -2) {
/* invalid. interpret as (>0, -1) */
FOUNDCFG (preferred_out);
+ ANYTHINGGOES;
} else if (possible_out < -2) {
/* > 0, < -2 is not specified
* interpret as up to -N */
FOUNDCFG (min (-possible_out, preferred_out));
+ UPTO (-possible_out)
} else {
/* exact number of outputs */
FOUNDCFG (possible_out);
}
}
-
if (!found) {
return false;
}
- out.set (DataType::MIDI, midi_out); // currently always zero
- out.set (DataType::AUDIO, audio_out);
+ if (exact_match) {
+ out.set (DataType::MIDI, midi_out); // currently always zero
+ out.set (DataType::AUDIO, preferred_out);
+ } else {
+ out.set (DataType::MIDI, midi_out); // currently always zero
+ out.set (DataType::AUDIO, audio_out);
+ }
return true;
}
}
Plugin::IOPortDescription
-LV2Plugin::describe_io_port (ARDOUR::DataType dt, bool input, uint32_t id)
+LV2Plugin::describe_io_port (ARDOUR::DataType dt, bool input, uint32_t id) const
{
PortFlags match = 0;
switch (dt) {
}
Plugin::IOPortDescription
-Plugin::describe_io_port (ARDOUR::DataType dt, bool input, uint32_t id)
+Plugin::describe_io_port (ARDOUR::DataType dt, bool input, uint32_t id) const
{
std::stringstream ss;
switch (dt) {
ss << _("Out") << " ";
}
- ss << id;
+ ss << (id + 1);
Plugin::IOPortDescription iod (ss.str());
return iod;
}
+PluginOutputConfiguration
+Plugin::possible_output () const
+{
+ PluginOutputConfiguration oc;
+ if (_info) {
+ oc.insert (_info->n_outputs.n_audio ());
+ }
+ return oc;
+}
+
const Plugin::PresetRecord *
Plugin::preset_by_label (const string& label)
{
}
}
+bool
+PluginInsert::set_preset_out (const ChanCount& c)
+{
+ bool changed = _preset_out != c;
+ _preset_out = c;
+ if (changed && !_custom_cfg) {
+ PluginConfigChanged (); /* EMIT SIGNAL */
+ }
+ return changed;
+}
+
bool
PluginInsert::add_sidechain (uint32_t n_audio)
{
ChanCount dout (in); // hint
if (_custom_cfg) {
dout = _custom_out;
+ } else if (_preset_out.n_audio () > 0) {
+ dout.set (DataType::AUDIO, _preset_out.n_audio ());
} else if (dout.n_midi () > 0 && dout.n_audio () == 0) {
dout.set (DataType::AUDIO, 2);
}
ChanCount useins;
bool const r = _plugins.front()->can_support_io_configuration (in, dout, &useins);
assert (r);
- assert (dout.n_audio() <= out.n_audio()); // sans midi bypass
if (useins.n_audio() == 0) {
useins = in;
}
return private_can_support_io_configuration (in, out).method != Impossible;
}
+PluginInsert::Match
+PluginInsert::private_can_support_io_configuration (ChanCount const& in, ChanCount& out) const
+{
+ if (!_custom_cfg && _preset_out.n_audio () > 0) {
+ // preseed hint (for variable i/o)
+ out.set (DataType::AUDIO, _preset_out.n_audio ());
+ }
+
+ Match rv = internal_can_support_io_configuration (in, out);
+
+ if (!_custom_cfg && _preset_out.n_audio () > 0) {
+ DEBUG_TRACE (DEBUG::ChanMapping, string_compose ("using output preset: %1 %2\n", name(), _preset_out));
+ out.set (DataType::AUDIO, _preset_out.n_audio ());
+ }
+ return rv;
+}
+
/** A private version of can_support_io_configuration which returns the method
* by which the configuration can be matched, rather than just whether or not
* it can be.
*/
PluginInsert::Match
-PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanCount& out) const
+PluginInsert::internal_can_support_io_configuration (ChanCount const & inx, ChanCount& out) const
{
if (_plugins.empty()) {
return Match();
* in case the plugin goes missing) */
node.add_child_nocopy (* _configured_in.state (X_("ConfiguredInput")));
node.add_child_nocopy (* _configured_out.state (X_("ConfiguredOutput")));
+ node.add_child_nocopy (* _preset_out.state (X_("PresetOutput")));
/* save custom i/o config */
node.add_property("custom", _custom_cfg ? "yes" : "no");
if ((*i)->name() == X_("ConfiguredOutput")) {
_custom_out = ChanCount(**i);
}
+ if ((*i)->name() == X_("PresetOutput")) {
+ _preset_out = ChanCount(**i);
+ }
if (strncmp ((*i)->name ().c_str(), X_("InputMap-"), 9) == 0) {
long pc = atol (&((*i)->name().c_str()[9]));
if (pc >=0 && pc <= get_count()) {
return true;
}
+bool
+Route::plugin_preset_output (boost::shared_ptr<Processor> proc, ChanCount outs)
+{
+ boost::shared_ptr<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+ return false;
+ }
+
+ {
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+ ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+ if (i == _processors.end ()) {
+ return false;
+ }
+ }
+
+ {
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+ Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+
+ const ChanCount& old (pi->preset_out ());
+ if (!pi->set_preset_out (outs)) {
+ return true; // no change, OK
+ }
+
+ list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+ if (c.empty()) {
+ /* not possible */
+ pi->set_preset_out (old);
+ return false;
+ }
+ configure_processors_unlocked (0);
+ }
+
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ return true;
+}
+
bool
Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
{