diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2008-09-10 15:03:30 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2008-09-10 15:03:30 +0000 |
commit | 68e943265edf04e63a8e8b8f62bab20f99d9c637 (patch) | |
tree | ff8941a59662fc0c4622944b65f7b2d5e3bdd0c3 /libs/ardour/audio_unit.cc | |
parent | e4372df05b7d74a6b80dbbf4b6c00cc2b31c4723 (diff) |
merge from 2.0-ongoing @ 3581
git-svn-id: svn://localhost/ardour2/branches/3.0@3711 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour/audio_unit.cc')
-rw-r--r-- | libs/ardour/audio_unit.cc | 522 |
1 files changed, 446 insertions, 76 deletions
diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc index b941bc10bb..4862ddcd12 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; @@ -396,42 +385,233 @@ AUPlugin::_set_block_size (nframes_t nframes) return 0; } -int32_t -AUPlugin::can_support_input_configuration (int32_t in) -{ - streamFormat.mChannelsPerFrame = in; +int32_t +AUPlugin::configure_io (ChanCount in, ChanCount out) +{ + AudioStreamBasicDescription streamFormat; + + 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; + /* 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.n_audio(); + + if (set_input_format (streamFormat) != 0) { + return -1; + } + + streamFormat.mChannelsPerFrame = out.n_audio(); + + if (set_output_format (streamFormat) != 0) { return -1; } + + return Plugin::configure_io (in, out); +} + +bool +AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out) +{ + int32_t ret = count_for_configuration (in, out); + return ret >= 0; +} + +int32_t +AUPlugin::count_for_configuration(ChanCount cin, ChanCount& out) const +{ + // 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()); + int32_t in = cin.n_audio(); /* XXX handle MIDI one day ??? */ + + 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.set (DataType::AUDIO, in); + plugcnt = 1; + } else if (possible_out == -2) { + /* any configuration possible, pick matching */ + out.set (DataType::AUDIO, in); + plugcnt = 1; + } else if (possible_out < -2) { + /* explicit variable number of outputs, pick maximum */ + out.set (DataType::AUDIO, -possible_out); + plugcnt = 1; + } else { + /* exact number of outputs */ + out.set (DataType::AUDIO, possible_out); + plugcnt = 1; + } + } + + if (possible_in == -1) { + + /* wildcard for input */ + + if (possible_out == -1) { + /* out much match in */ + out.set (DataType::AUDIO, in); + plugcnt = 1; + } else if (possible_out == -2) { + /* any configuration possible, pick matching */ + out.set (DataType::AUDIO, in); + plugcnt = 1; + } else if (possible_out < -2) { + /* explicit variable number of outputs, pick maximum */ + out.set (DataType::AUDIO, -possible_out); + plugcnt = 1; + } else { + /* exact number of outputs */ + out.set (DataType::AUDIO, possible_out); + plugcnt = 1; + } + } + + if (possible_in == -2) { + + if (possible_out == -1) { + /* any configuration possible, pick matching */ + out.set (DataType::AUDIO, 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.set (DataType::AUDIO, -possible_out); + plugcnt = 1; + } else { + /* exact number of outputs */ + out.set (DataType::AUDIO, 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.set (DataType::AUDIO, 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.set (DataType::AUDIO, -possible_out); + plugcnt = 1; + } else { + /* exact number of outputs */ + out.set (DataType::AUDIO, possible_out); + plugcnt = 1; + } + } + + if (possible_in == in) { + + /* exact number of inputs ... must match obviously */ + + if (possible_out == -1) { + /* out must match in */ + out.set (DataType::AUDIO, in); + plugcnt = 1; + } else if (possible_out == -2) { + /* any output configuration, pick matching */ + out.set (DataType::AUDIO, in); + plugcnt = -1; + } else if (possible_out < -2) { + /* explicit variable number of outputs, pick maximum */ + out.set (DataType::AUDIO, -possible_out); + plugcnt = 1; + } else { + /* exact number of outputs */ + out.set (DataType::AUDIO, 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::PortCountChanged (ChanCount (DataType::AUDIO, 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; @@ -439,60 +619,44 @@ 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); +ChanCount +AUPlugin::input_streams() const +{ + ChanCount in; - return streamFormat.mChannelsPerFrame; + if (input_channels < 0) { + warning << string_compose (_("AUPlugin: %1 input_streams() called without any format set!"), name()) << endmsg; + in.set_audio (1); } else { - return -1; + in.set_audio (input_channels); } + + return in; } -uint32_t + +ChanCount AUPlugin::output_streams() const { - if (!(format_set & 0x2)) { + ChanCount out; + + if (output_channels < 0) { warning << string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg; - return 1; + out.set_audio (1); + } else { + out.set_audio (output_channels); } - return streamFormat.mChannelsPerFrame; -} - - -uint32_t -AUPlugin::input_streams() const -{ - if (!(format_set & 0x1)) { - warning << _("AUPlugin: input_streams() called without any format set!") << endmsg; - return 1; - } - return streamFormat.mChannelsPerFrame; + return out; } OSStatus @@ -679,13 +843,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); @@ -839,17 +1015,211 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip info->type = ARDOUR::AudioUnit; info->unique_id = stringify_descriptor (*info->descriptor); - /* mark the plugin as having flexible i/o */ + /* 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; + } - info->n_inputs = -1; - info->n_outputs = -1; + if (cached_io_configuration (info->unique_id, info->version, cacomp, info->cache, info->name)) { + + /* here we have to map apple's wildcard system to a simple pair + of values. + */ + + info->n_inputs = ChanCount (DataType::AUDIO, info->cache.io_configs.front().first); + info->n_outputs = ChanCount (DataType::AUDIO, 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); - plugs.push_back (info); + } else { + error << string_compose (_("Cannot get I/O configuration info for AU %1"), info->name) << endmsg; + } comp = FindNextComponent (comp, &desc); } } +bool +AUPluginInfo::cached_io_configuration (const std::string& unique_id, + UInt32 version, + CAComponent& comp, + AUPluginCachedInfo& cinfo, + const std::string& name) +{ + 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->second; + return true; + } + + CAAudioUnit unit; + AUChannelInfo* channel_info; + 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; + } + + if (ret > 0) { + /* no explicit info available */ + + cinfo.io_configs.push_back (pair<int,int> (-1, -1)); + + } else { + + /* store each configuration */ + + for (uint32_t n = 0; n < cnt; ++n) { + cinfo.io_configs.push_back (pair<int,int> (channel_info[n].inChannels, + channel_info[n].outChannels)); + } + + free (channel_info); + } + + add_cached_info (id, cinfo); + save_cached_info (); + + return true; +} + +void +AUPluginInfo::add_cached_info (const std::string& id, AUPluginCachedInfo& cinfo) +{ + cached_info[id] = 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) { + 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) { + + 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); + } + + } + + 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()); + } +} + +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 AUPluginInfo::get_names (CAComponentDescription& comp_desc, std::string& name, Glib::ustring& maker) { |