diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2008-05-15 00:52:54 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2008-05-15 00:52:54 +0000 |
commit | cb78405ec29e8648839a25861179da9102b69c72 (patch) | |
tree | 7691ccd053acda822f3af5921da58a84a07282b1 /libs/ardour/audio_unit.cc | |
parent | 3ecc9b4f75b6201efdf5550421423861191d79ec (diff) |
lots and lots of work to correctly deduce AU IO configurations and related issues
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@3349 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour/audio_unit.cc')
-rw-r--r-- | libs/ardour/audio_unit.cc | 477 |
1 files changed, 367 insertions, 110 deletions
diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc index 2e38f86be1..5544ba2e0e 100644 --- a/libs/ardour/audio_unit.cc +++ b/libs/ardour/audio_unit.cc @@ -24,7 +24,10 @@ #include <pbd/whitespace.h> #include <glibmm/thread.h> +#include <glibmm/fileutils.h> +#include <glibmm/miscutils.h> +#include <ardour/ardour.h> #include <ardour/audioengine.h> #include <ardour/io.h> #include <ardour/audio_unit.h> @@ -43,6 +46,8 @@ using namespace std; using namespace PBD; using namespace ARDOUR; +AUPluginInfo::CachedInfoMap AUPluginInfo::cached_info; + static OSStatus _render_callback(void *userData, AudioUnitRenderActionFlags *ioActionFlags, @@ -120,28 +125,12 @@ AUPlugin::init () unit->GetElementCount (kAudioUnitScope_Input, input_elements); unit->GetElementCount (kAudioUnitScope_Output, output_elements); - // set up the basic stream format. these fields do not change - - streamFormat.mSampleRate = _session.frame_rate(); - streamFormat.mFormatID = kAudioFormatLinearPCM; - streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved; - -#ifdef __LITTLE_ENDIAN__ - /* relax */ -#else - streamFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian; -#endif - - streamFormat.mBitsPerChannel = 32; - streamFormat.mFramesPerPacket = 1; - - // subject to later modification as we discover channel counts - - streamFormat.mBytesPerPacket = 4; - streamFormat.mBytesPerFrame = 4; - streamFormat.mChannelsPerFrame = 1; + /* these keep track of *configured* channel set up, + not potential set ups. + */ - format_set = 0; + input_channels = -1; + output_channels = -1; if (_set_block_size (_session.get_block_size())) { error << _("AUPlugin: cannot set processing block size") << endmsg; @@ -392,59 +381,227 @@ AUPlugin::_set_block_size (nframes_t nframes) return 0; } -int32_t -AUPlugin::can_support_input_configuration (int32_t in) -{ -#ifdef NEWSTUFF - CAChannelHelper ch_in; - CAChannelHelper ch_out; - - ch_in.nNumEls = input_elements; +int32_t +AUPlugin::configure_io (int32_t in, int32_t out) +{ + AudioStreamBasicDescription streamFormat; - for (uint32_t n = 0; n < input_elements; ++n) { - ch_in.mChans[n] = in; - } + streamFormat.mSampleRate = _session.frame_rate(); + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved; - /* don't check output */ +#ifdef __LITTLE_ENDIAN__ + /* relax */ +#else + streamFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian; +#endif - ch_out.nNumEls = 0; + streamFormat.mBitsPerChannel = 32; + streamFormat.mFramesPerPacket = 1; - return unit->CanDo (ch_in, ch_out); - -#endif - streamFormat.mChannelsPerFrame = in; /* apple says that for non-interleaved data, these values always refer to a single channel. */ streamFormat.mBytesPerPacket = 4; streamFormat.mBytesPerFrame = 4; - if (set_input_format () == 0) { - return 1; - } else { + streamFormat.mChannelsPerFrame = in; + + if (set_input_format (streamFormat) != 0) { return -1; } + + streamFormat.mChannelsPerFrame = out; + + if (set_output_format (streamFormat) != 0) { + return -1; + } + + return Plugin::configure_io (in, out); +} + +int32_t +AUPlugin::can_do (int32_t in, int32_t& out) +{ + // XXX as of May 13th 2008, AU plugin support returns a count of either 1 or -1. We never + // attempt to multiply-instantiate plugins to meet io configurations. + + int32_t plugcnt = -1; + AUPluginInfoPtr pinfo = boost::dynamic_pointer_cast<AUPluginInfo>(get_info()); + + out = -1; + + vector<pair<int,int> >& io_configs = pinfo->cache.io_configs; + + for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) { + + int32_t possible_in = i->first; + int32_t possible_out = i->second; + + if (possible_out == 0) { + warning << string_compose (_("AU %1 has zero outputs - configuration ignored"), name()) << endmsg; + continue; + } + + if (possible_in == 0) { + + /* instrument plugin, always legal but throws away inputs ... + */ + + if (possible_out == -1) { + /* out much match in (UNLIKELY!!) */ + out = in; + plugcnt = 1; + } else if (possible_out == -2) { + /* any configuration possible, pick matching */ + out = in; + plugcnt = 1; + } else if (possible_out < -2) { + /* explicit variable number of outputs, pick maximum */ + out = -possible_out; + plugcnt = 1; + } else { + /* exact number of outputs */ + out = possible_out; + plugcnt = 1; + } + } + + if (possible_in == -1) { + + /* wildcard for input */ + + if (possible_out == -1) { + /* out much match in */ + out = in; + plugcnt = 1; + } else if (possible_out == -2) { + /* any configuration possible, pick matching */ + out = in; + plugcnt = 1; + } else if (possible_out < -2) { + /* explicit variable number of outputs, pick maximum */ + out = -possible_out; + plugcnt = 1; + } else { + /* exact number of outputs */ + out = possible_out; + plugcnt = 1; + } + } + + if (possible_in == -2) { + + if (possible_out == -1) { + /* any configuration possible, pick matching */ + out = in; + plugcnt = 1; + } else if (possible_out == -2) { + error << string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name()) + << endmsg; + plugcnt = -1; + } else if (possible_out < -2) { + /* explicit variable number of outputs, pick maximum */ + out = -possible_out; + plugcnt = 1; + } else { + /* exact number of outputs */ + out = possible_out; + plugcnt = 1; + } + } + + if (possible_in < -2) { + + /* explicit variable number of inputs */ + + if (in > -possible_in) { + /* request is too large */ + plugcnt = -1; + } + + if (possible_out == -1) { + /* out must match in */ + out = in; + plugcnt = 1; + } else if (possible_out == -2) { + error << string_compose (_("AU plugin %1 has illegal IO configuration (-2,-2)"), name()) + << endmsg; + plugcnt = -1; + } else if (possible_out < -2) { + /* explicit variable number of outputs, pick maximum */ + out = -possible_out; + plugcnt = 1; + } else { + /* exact number of outputs */ + out = possible_out; + plugcnt = 1; + } + } + + if (possible_in == in) { + + /* exact number of inputs ... must match obviously */ + + if (possible_out == -1) { + /* out must match in */ + out = in; + plugcnt = 1; + } else if (possible_out == -2) { + /* any output configuration, pick matching */ + out = in; + plugcnt = -1; + } else if (possible_out < -2) { + /* explicit variable number of outputs, pick maximum */ + out = -possible_out; + plugcnt = 1; + } else { + /* exact number of outputs */ + out = possible_out; + plugcnt = 1; + } + } + + } + + /* no fit */ + return plugcnt; } int -AUPlugin::set_input_format () +AUPlugin::set_input_format (AudioStreamBasicDescription& fmt) { - return set_stream_format (kAudioUnitScope_Input, input_elements); + return set_stream_format (kAudioUnitScope_Input, input_elements, fmt); } int -AUPlugin::set_output_format () +AUPlugin::set_output_format (AudioStreamBasicDescription& fmt) { - return set_stream_format (kAudioUnitScope_Output, output_elements); + if (set_stream_format (kAudioUnitScope_Output, output_elements, fmt) != 0) { + return -1; + } + + if (buffers) { + free (buffers); + buffers = 0; + } + + buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) + + fmt.mChannelsPerFrame * sizeof(AudioBuffer)); + + Glib::Mutex::Lock em (_session.engine().process_lock()); + IO::MoreOutputs (fmt.mChannelsPerFrame); + + return 0; } int -AUPlugin::set_stream_format (int scope, uint32_t cnt) +AUPlugin::set_stream_format (int scope, uint32_t cnt, AudioStreamBasicDescription& fmt) { OSErr result; for (uint32_t i = 0; i < cnt; ++i) { - if ((result = unit->SetFormat (scope, i, streamFormat)) != 0) { + if ((result = unit->SetFormat (scope, i, fmt)) != 0) { error << string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"), (scope == kAudioUnitScope_Input ? "input" : "output"), i, result) << endmsg; return -1; @@ -452,60 +609,33 @@ AUPlugin::set_stream_format (int scope, uint32_t cnt) } if (scope == kAudioUnitScope_Input) { - format_set |= 0x1; + input_channels = fmt.mChannelsPerFrame; } else { - format_set |= 0x2; + output_channels = fmt.mChannelsPerFrame; } return 0; } -int32_t -AUPlugin::compute_output_streams (int32_t nplugins) -{ - /* we will never replicate AU plugins - either they can do the I/O we need - or not. thus, we can ignore nplugins entirely. - */ - - if (set_output_format() == 0) { - - if (buffers) { - free (buffers); - buffers = 0; - } - - buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) + - streamFormat.mChannelsPerFrame * sizeof(AudioBuffer)); - - Glib::Mutex::Lock em (_session.engine().process_lock()); - IO::MoreOutputs (streamFormat.mChannelsPerFrame); - - return streamFormat.mChannelsPerFrame; - } else { - return -1; - } -} - uint32_t -AUPlugin::output_streams() const +AUPlugin::input_streams() const { - if (!(format_set & 0x2)) { - warning << string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg; + if (input_channels < 0) { + warning << string_compose (_("AUPlugin: %1 input_streams() called without any format set!"), name()) << endmsg; return 1; } - - return streamFormat.mChannelsPerFrame; + return input_channels; } uint32_t -AUPlugin::input_streams() const +AUPlugin::output_streams() const { - if (!(format_set & 0x1)) { - warning << _("AUPlugin: input_streams() called without any format set!") << endmsg; + if (output_channels < 0) { + warning << string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg; return 1; } - return streamFormat.mChannelsPerFrame; + return output_channels; } OSStatus @@ -692,13 +822,25 @@ AUPluginInfo::load (Session& session) } catch (failed_constructor &err) { - return PluginPtr ((Plugin*) 0); + return PluginPtr (); } } +Glib::ustring +AUPluginInfo::au_cache_path () +{ + return Glib::build_filename (ARDOUR::get_user_ardour_path(), "au_cache"); +} + PluginInfoList AUPluginInfo::discover () { + XMLTree tree; + + if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS)) { + ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)")); + } + PluginInfoList plugs; discover_fx (plugs); @@ -851,44 +993,76 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip info->type = ARDOUR::AudioUnit; info->unique_id = stringify_descriptor (*info->descriptor); + + /* XXX not sure of the best way to handle plugin versioning yet + */ + + CAComponent cacomp (*info->descriptor); + + if (cacomp.GetResourceVersion (info->version) != noErr) { + info->version = 0; + } - AUPluginCachedInfo cinfo; + if (cached_io_configuration (info->unique_id, info->version, cacomp, info->cache, info->name)) { - if (cached_io_configuration (info->unique_id(), *info->descriptor, cinfo)) { + /* here we have to map apple's wildcard system to a simple pair + of values. + */ - info->n_inputs = cinfo->in; - info->n_outputs = cinfo->out; + info->n_inputs = info->cache.io_configs.front().first; + info->n_outputs = info->cache.io_configs.front().second; + + if (info->cache.io_configs.size() > 1) { + cerr << "ODD: variable IO config for " << info->unique_id << endl; + } + + plugs.push_back (info); } else { - - /* mark the plugin as having flexible i/o */ - - info->n_inputs = -1; - info->n_outputs = -1; + error << string_compose (_("Cannot get I/O configuration info for AU %1"), info->name) << endmsg; } - - plugs.push_back (info); comp = FindNextComponent (comp, &desc); } } bool -AUPluginInfo::cached_io_configuration (std::string unique_id, CAComponentDescriptor& descriptor, AUPluginCachedInfo& cinfo) +AUPluginInfo::cached_io_configuration (const std::string& unique_id, + UInt32 version, + CAComponent& comp, + AUPluginCachedInfo& cinfo, + const std::string& name) { - CachedInfoMap::iterator cim = cached_info.find (unique_id); + std::string id; + char buf[32]; + + /* concatenate unique ID with version to provide a key for cached info lookup. + this ensures we don't get stale information, or should if plugin developers + follow Apple "guidelines". + */ + + snprintf (buf, sizeof (buf), "%u", version); + id = unique_id; + id += '/'; + id += buf; + + CachedInfoMap::iterator cim = cached_info.find (id); if (cim != cached_info.end()) { - cinfo = *cim; + cinfo = cim->second; return true; } - CAComponent comp (descriptor); - CAAudioUnit unit (comp); + CAAudioUnit unit; AUChannelInfo* channel_info; - uint32_t cnt; + UInt32 cnt; int ret; - + + ARDOUR::BootMessage (string_compose (_("Checking AudioUnit: %1"), name)); + + if (CAAudioUnit::Open (comp, unit) != noErr) { + return false; + } if ((ret = unit.GetChannelInfo (&channel_info, cnt)) < 0) { return false; @@ -911,15 +1085,14 @@ AUPluginInfo::cached_io_configuration (std::string unique_id, CAComponentDescrip free (channel_info); } - - add_cached_info (unique_id, cinfo); + add_cached_info (id, cinfo); save_cached_info (); return true; } void -AUPluginInfo::add_cached_info (std::string id, AUPluginCachedInfo& cinfo) +AUPluginInfo::add_cached_info (const std::string& id, AUPluginCachedInfo& cinfo) { cached_info[id] = cinfo; } @@ -927,19 +1100,103 @@ AUPluginInfo::add_cached_info (std::string id, AUPluginCachedInfo& cinfo) void AUPluginInfo::save_cached_info () { + XMLNode* node; + + node = new XMLNode (X_("AudioUnitPluginCache")); + for (map<string,AUPluginCachedInfo>::iterator i = cached_info.begin(); i != cached_info.end(); ++i) { - cerr << i->first << ' '; + XMLNode* parent = new XMLNode (X_("plugin")); + parent->add_property ("id", i->first); + node->add_child_nocopy (*parent); + for (vector<pair<int, int> >::iterator j = i->second.io_configs.begin(); j != i->second.io_configs.end(); ++j) { - cerr << j->first << ' ' << j->second; + + XMLNode* child = new XMLNode (X_("io")); + char buf[32]; + + snprintf (buf, sizeof (buf), "%d", j->first); + child->add_property (X_("in"), buf); + snprintf (buf, sizeof (buf), "%d", j->second); + child->add_property (X_("out"), buf); + parent->add_child_nocopy (*child); } - cerr << endl; + + } + + Glib::ustring path = au_cache_path (); + XMLTree tree; + + tree.set_root (node); + + if (!tree.write (path)) { + error << string_compose (_("could not save AU cache to %1"), path) << endmsg; + unlink (path.c_str()); } } -void +int AUPluginInfo::load_cached_info () { + Glib::ustring path = au_cache_path (); + XMLTree tree; + + if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) { + return 0; + } + + tree.read (path); + const XMLNode* root (tree.root()); + + if (root->name() != X_("AudioUnitPluginCache")) { + return -1; + } + + cached_info.clear (); + + const XMLNodeList children = root->children(); + + for (XMLNodeConstIterator iter = children.begin(); iter != children.end(); ++iter) { + + const XMLNode* child = *iter; + + if (child->name() == X_("plugin")) { + + const XMLNode* gchild; + const XMLNodeList gchildren = child->children(); + const XMLProperty* prop = child->property (X_("id")); + + if (!prop) { + continue; + } + std::string id = prop->value(); + + for (XMLNodeConstIterator giter = gchildren.begin(); giter != gchildren.end(); giter++) { + + gchild = *giter; + + if (gchild->name() == X_("io")) { + + int in; + int out; + const XMLProperty* iprop; + const XMLProperty* oprop; + + if (((iprop = gchild->property (X_("in"))) != 0) && + ((oprop = gchild->property (X_("out"))) != 0)) { + in = atoi (iprop->value()); + out = atoi (iprop->value()); + + AUPluginCachedInfo cinfo; + cinfo.io_configs.push_back (pair<int,int> (in, out)); + add_cached_info (id, cinfo); + } + } + } + } + } + + return 0; } void |