rework AudioUnit variable input port count.
[ardour.git] / libs / ardour / audio_unit.cc
index 9e461c63466515cfa63aff5d12384b7b1f8083e0..063055b5e63fc018b22bdee07aea3dda4aceeb98 100644 (file)
@@ -19,6 +19,7 @@
 */
 
 #include <sstream>
+#include <fstream>
 #include <errno.h>
 #include <string.h>
 #include <math.h>
@@ -28,7 +29,7 @@
 #include "pbd/xml++.h"
 #include "pbd/convert.h"
 #include "pbd/whitespace.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
 #include "pbd/locale_guard.h"
 
 #include <glibmm/threads.h>
@@ -71,6 +72,81 @@ AUPluginInfo::CachedInfoMap AUPluginInfo::cached_info;
 static string preset_search_path = "/Library/Audio/Presets:/Network/Library/Audio/Presets";
 static string preset_suffix = ".aupreset";
 static bool preset_search_path_initialized = false;
+FILE * AUPluginInfo::_crashlog_fd = NULL;
+bool AUPluginInfo::_scan_only = true;
+
+
+static void au_blacklist (std::string id)
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
+       FILE * blacklist_fd = NULL;
+       if (! (blacklist_fd = fopen(fn.c_str(), "a"))) {
+               PBD::error << "Cannot append to AU blacklist for '"<< id <<"'\n";
+               return;
+       }
+       assert(id.find("\n") == string::npos);
+       fprintf(blacklist_fd, "%s\n", id.c_str());
+       ::fclose(blacklist_fd);
+}
+
+static void au_unblacklist (std::string id)
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
+       if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
+               PBD::warning << "Expected Blacklist file does not exist.\n";
+               return;
+       }
+
+       std::string bl;
+       {
+               std::ifstream ifs(fn.c_str());
+               bl.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+       }
+
+       ::g_unlink (fn.c_str());
+
+       assert (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS));
+       assert(id.find("\n") == string::npos);
+
+       id += "\n"; // add separator
+       const size_t rpl = bl.find(id);
+       if (rpl != string::npos) {
+               bl.replace(rpl, id.size(), "");
+       }
+       if (bl.empty()) {
+               return;
+       }
+
+       FILE * blacklist_fd = NULL;
+       if (! (blacklist_fd = fopen(fn.c_str(), "w"))) {
+               PBD::error << "Cannot open AU blacklist.\n";
+               return;
+       }
+       fprintf(blacklist_fd, "%s", bl.c_str());
+       ::fclose(blacklist_fd);
+}
+
+static bool is_blacklisted (std::string id)
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
+       if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
+               return false;
+       }
+       std::string bl;
+       std::ifstream ifs(fn.c_str());
+       bl.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+
+       assert(id.find("\n") == string::npos);
+
+       id += "\n"; // add separator
+       const size_t rpl = bl.find(id);
+       if (rpl != string::npos) {
+               return true;
+       }
+       return false;
+}
+
+
 
 static OSStatus
 _render_callback(void *userData,
@@ -347,6 +423,7 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAC
        , input_offset (0)
        , input_buffers (0)
        , frames_processed (0)
+       , audio_input_cnt (0)
        , _parameter_listener (0)
        , _parameter_listener_arg (0)
        , last_transport_rolling (false)
@@ -358,6 +435,7 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAC
                p += preset_search_path;
                preset_search_path = p;
                preset_search_path_initialized = true;
+               DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Preset Path: %1\n", preset_search_path));
        }
 
        init ();
@@ -382,6 +460,9 @@ AUPlugin::AUPlugin (const AUPlugin& other)
 
 {
        init ();
+       for (size_t i = 0; i < descriptors.size(); ++i) {
+               set_parameter (i, other.get_parameter (i));
+       }
 }
 
 AUPlugin::~AUPlugin ()
@@ -432,6 +513,7 @@ AUPlugin::discover_factory_presets ()
 
                string name = CFStringRefToStdString (preset->presetName);
                factory_preset_map[name] = preset->presetNumber;
+               DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Factory Preset: %1 > %2\n", name, preset->presetNumber));
        }
 
        CFRelease (presets);
@@ -441,6 +523,7 @@ void
 AUPlugin::init ()
 {
        OSErr err;
+       CFStringRef itemName;
 
        /* these keep track of *configured* channel set up,
           not potential set ups.
@@ -448,6 +531,20 @@ AUPlugin::init ()
 
        input_channels = -1;
        output_channels = -1;
+       {
+               CAComponentDescription temp;
+               GetComponentInfo (comp.get()->Comp(), &temp, NULL, NULL, NULL);
+               CFStringRef compTypeString = UTCreateStringForOSType(temp.componentType);
+               CFStringRef compSubTypeString = UTCreateStringForOSType(temp.componentSubType);
+               CFStringRef compManufacturerString = UTCreateStringForOSType(temp.componentManufacturer);
+               itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
+                               compTypeString, compManufacturerString, compSubTypeString);
+               if (compTypeString != NULL) CFRelease(compTypeString);
+               if (compSubTypeString != NULL) CFRelease(compSubTypeString);
+               if (compManufacturerString != NULL) CFRelease(compManufacturerString);
+       }
+
+       au_blacklist(CFStringRefToStdString(itemName));
 
        try {
                DEBUG_TRACE (DEBUG::AudioUnits, "opening AudioUnit\n");
@@ -513,6 +610,9 @@ AUPlugin::init ()
        discover_factory_presets ();
 
        // Plugin::setup_controls ();
+
+       au_unblacklist(CFStringRefToStdString(itemName));
+       if (itemName != NULL) CFRelease(itemName);
 }
 
 void
@@ -530,7 +630,7 @@ AUPlugin::discover_parameters ()
 
        for (uint32_t i = 0; i < sizeof (scopes) / sizeof (scopes[0]); ++i) {
 
-               AUParamInfo param_info (unit->AU(), false, false, scopes[i]);
+               AUParamInfo param_info (unit->AU(), false, /* include read only */ true, scopes[i]);
 
                for (uint32_t i = 0; i < param_info.NumParams(); ++i) {
 
@@ -604,24 +704,33 @@ AUPlugin::discover_parameters ()
 
                        d.lower = info.minValue;
                        d.upper = info.maxValue;
-                       d.default_value = info.defaultValue;
+                       d.normal = info.defaultValue;
 
                        d.integer_step = (info.unit == kAudioUnitParameterUnit_Indexed);
                        d.toggled = (info.unit == kAudioUnitParameterUnit_Boolean) ||
                                (d.integer_step && ((d.upper - d.lower) == 1.0));
                        d.sr_dependent = (info.unit == kAudioUnitParameterUnit_SampleFrames);
-                       d.automatable = !d.toggled &&
+                       d.automatable = /* !d.toggled && -- ardour can automate toggles, can AU ? */
                                !(info.flags & kAudioUnitParameterFlag_NonRealTime) &&
                                (info.flags & kAudioUnitParameterFlag_IsWritable);
 
                        d.logarithmic = (info.flags & kAudioUnitParameterFlag_DisplayLogarithmic);
-                       d.unit = info.unit;
+                       d.au_unit = info.unit;
+                       switch (info.unit) {
+                       case kAudioUnitParameterUnit_Decibels:
+                               d.unit = ParameterDescriptor::DB;
+                               break;
+                       case kAudioUnitParameterUnit_MIDINoteNumber:
+                               d.unit = ParameterDescriptor::MIDI_NOTE;
+                               break;
+                       case kAudioUnitParameterUnit_Hertz:
+                               d.unit = ParameterDescriptor::HZ;
+                               break;
+                       }
 
-                       d.step = 1.0;
-                       d.smallstep = 0.1;
-                       d.largestep = 10.0;
                        d.min_unbound = 0; // lower is bound
                        d.max_unbound = 0; // upper is bound
+                       d.update_steps();
 
                        descriptors.push_back (d);
 
@@ -759,7 +868,7 @@ float
 AUPlugin::default_value (uint32_t port)
 {
        if (port < descriptors.size()) {
-               return descriptors[port].default_value;
+               return descriptors[port].normal;
        }
 
        return 0;
@@ -904,8 +1013,11 @@ AUPlugin::configure_io (ChanCount in, ChanCount out)
 {
        AudioStreamBasicDescription streamFormat;
        bool was_initialized = initialized;
-       int32_t audio_in = in.n_audio();
        int32_t audio_out = out.n_audio();
+       if (audio_input_cnt > 0) {
+               in.set (DataType::AUDIO, audio_input_cnt);
+       }
+       int32_t audio_in = in.n_audio();
 
        DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("configure %1 for %2 in %3 out\n", name(), in, out));
 
@@ -966,11 +1078,11 @@ AUPlugin::input_streams() const
 {
        ChanCount c;
 
-       c.set (DataType::AUDIO, 1);
-       c.set (DataType::MIDI, 0);
 
        if (input_channels < 0) {
-               warning << string_compose (_("AUPlugin: %1 input_streams() called without any format set!"), name()) << endmsg;
+               // force PluginIoReConfigure -- see also commit msg e38eb06
+               c.set (DataType::AUDIO, 0);
+               c.set (DataType::MIDI, 0);
        } else {
                c.set (DataType::AUDIO, input_channels);
                c.set (DataType::MIDI, _has_midi_input ? 1 : 0);
@@ -985,11 +1097,10 @@ AUPlugin::output_streams() const
 {
        ChanCount c;
 
-       c.set (DataType::AUDIO, 1);
-       c.set (DataType::MIDI, 0);
-
        if (output_channels < 0) {
-               warning << string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg;
+               // force PluginIoReConfigure - see also commit msg e38eb06
+               c.set (DataType::AUDIO, 0);
+               c.set (DataType::MIDI, 0);
        } else {
                c.set (DataType::AUDIO, output_channels);
                c.set (DataType::MIDI, _has_midi_output ? 1 : 0);
@@ -1026,7 +1137,37 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out)
        //configurations in most cases. so first lets see
        //if there's a configuration that keeps out==in
 
-       audio_out = audio_in;
+       if (in.n_midi() > 0 && audio_in == 0) {
+               audio_out = 2; // prefer stereo version if available.
+       } else {
+               audio_out = audio_in;
+       }
+
+       /* kAudioUnitProperty_SupportedNumChannels
+        * https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html#//apple_ref/doc/uid/TP40003278-CH12-SW20
+        *
+        * - both fields are -1
+        *   e.g. inChannels = -1 outChannels = -1
+        *    This is the default case. Any number of input and output channels, as long as the numbers match
+        *
+        * - one field is -1, the other field is positive
+        *   e.g. inChannels = -1 outChannels = 2
+        *    Any number of input channels, exactly two output channels
+        *
+        * - one field is -1, the other field is -2
+        *   e.g. inChannels = -1 outChannels = -2
+        *    Any number of input channels, any number of output channels
+        *
+        * - both fields have non-negative values
+        *   e.g. inChannels = 2 outChannels = 6
+        *    Exactly two input channels, exactly six output channels
+        *   e.g. inChannels = 0 outChannels = 2
+        *    No input channels, exactly two output channels (such as for an instrument unit with stereo output)
+        *
+        * - both fields have negative values, neither of which is –1 or –2
+        *   e.g. inChannels = -4 outChannels = -8
+        *    Up to four input channels and up to eight output channels
+        */
 
        for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
 
@@ -1078,21 +1219,12 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out)
                                audio_out = 2;
                                found = true;
                        } else if (possible_out < -2) {
-                               /* explicitly variable number of outputs. 
-
-                                   Since Ardour can handle any configuration,
-                                   we have to somehow pick a number. 
-
-                                   We'll use the number of inputs
-                                   to the master bus, or 2 if there
-                                   is no master bus.
-                                */
-                                boost::shared_ptr<Route> master = _session.master_out();
-                                if (master) {
-                                        audio_out = master->input()->n_ports().n_audio();
-                                } else {
-                                        audio_out = 2;
-                                }
+                               /* explicitly variable number of outputs.
+                                *
+                                * We really need to ask the user in this case.
+                                * stereo will be correct in 99.9% of all cases.
+                                */
+                               audio_out = 2;
                                found = true;
                        } else {
                                /* exact number of outputs */
@@ -1167,21 +1299,12 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out)
                                audio_out = 2;
                                found = true;
                        } else if (possible_out < -2) {
-                               /* explicitly variable number of outputs. 
-
-                                   Since Ardour can handle any configuration,
-                                   we have to somehow pick a number. 
-
-                                   We'll use the number of inputs
-                                   to the master bus, or 2 if there
-                                   is no master bus.
-                                */
-                                boost::shared_ptr<Route> master = _session.master_out();
-                                if (master) {
-                                        audio_out = master->input()->n_ports().n_audio();
-                                } else {
-                                        audio_out = 2;
-                                }
+                               /* explicitly variable number of outputs.
+                                *
+                                * We really need to ask the user in this case.
+                                * stereo will be correct in 99.9% of all cases.
+                                */
+                               audio_out = 2;
                                found = true;
                        } else {
                                /* exact number of outputs */
@@ -1217,21 +1340,25 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out)
                }
 
                if (found) {
+                       if (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, -possible_in));
+                       }
                        break;
                }
 
        }
 
        if (found) {
+               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;
        }
 
-       out.set (DataType::MIDI, 0);
-       out.set (DataType::AUDIO, audio_out);
-
        return true;
 }
 
@@ -1652,27 +1779,42 @@ AUPlugin::parameter_is_audio (uint32_t) const
 }
 
 bool
-AUPlugin::parameter_is_control (uint32_t) const
+AUPlugin::parameter_is_control (uint32_t param) const
 {
-       return true;
+       assert(param < descriptors.size());
+       if (descriptors[param].automatable) {
+               /* corrently ardour expects all controls to be automatable
+                * IOW ardour GUI elements mandate an Evoral::Parameter
+                * for all input+control ports.
+                */
+               return true;
+       }
+       return false;
 }
 
 bool
-AUPlugin::parameter_is_input (uint32_t) const
+AUPlugin::parameter_is_input (uint32_t param) const
 {
-       return false;
+       /* AU params that are both readable and writeable,
+        * are listed in kAudioUnitScope_Global
+        */
+       return (descriptors[param].scope == kAudioUnitScope_Input || descriptors[param].scope == kAudioUnitScope_Global);
 }
 
 bool
-AUPlugin::parameter_is_output (uint32_t) const
+AUPlugin::parameter_is_output (uint32_t param) const
 {
-       return false;
+       assert(param < descriptors.size());
+       // TODO check if ardour properly handles ports
+       // that report is_input + is_output == true
+       // -> add || descriptors[param].scope == kAudioUnitScope_Global
+       return (descriptors[param].scope == kAudioUnitScope_Output);
 }
 
 void
 AUPlugin::add_state (XMLNode* root) const
 {
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
        CFDataRef xmlData;
        CFPropertyListRef propertyList;
 
@@ -1711,7 +1853,7 @@ AUPlugin::set_state(const XMLNode& node, int version)
 {
        int ret = -1;
        CFPropertyListRef propertyList;
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
 
        if (node.name() != state_node_name()) {
                error << _("Bad node sent to AUPlugin::set_state") << endmsg;
@@ -1868,6 +2010,10 @@ AUPlugin::do_save_preset (string preset_name)
 
        CFRelease(propertyList);
 
+       user_preset_map[preset_name] = user_preset_path;;
+
+       DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Saving Preset to %1\n", user_preset_path));
+
        return string ("file:///") + user_preset_path;
 }
 
@@ -2049,20 +2195,20 @@ AUPlugin::current_preset() const
 void
 AUPlugin::find_presets ()
 {
-       vector<string*>* preset_files;
-       PathScanner scanner;
+       vector<string> preset_files;
 
        user_preset_map.clear ();
 
-       preset_files = scanner (preset_search_path, au_preset_filter, this, true, true, -1, true);
+       find_files_matching_filter (preset_files, preset_search_path, au_preset_filter, this, true, true, true);
 
-       if (!preset_files) {
+       if (preset_files.empty()) {
+               DEBUG_TRACE (DEBUG::AudioUnits, "AU No Preset Files found for given plugin.\n");
                return;
        }
 
-       for (vector<string*>::iterator x = preset_files->begin(); x != preset_files->end(); ++x) {
+       for (vector<string>::iterator x = preset_files.begin(); x != preset_files.end(); ++x) {
 
-               string path = *(*x);
+               string path = *x;
                string preset_name;
 
                /* make an initial guess at the preset name using the path */
@@ -2077,17 +2223,18 @@ AUPlugin::find_presets ()
 
                if (check_and_get_preset_name (get_comp()->Comp(), path, preset_name)) {
                        user_preset_map[preset_name] = path;
+                       DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Preset File: %1 > %2\n", preset_name, path));
+               } else {
+                       DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU INVALID Preset: %1 > %2\n", preset_name, path));
                }
 
-               delete *x;
        }
 
-       delete preset_files;
-
        /* now fill the vector<string> with the names we have */
 
        for (UserPresetMap::iterator i = user_preset_map.begin(); i != user_preset_map.end(); ++i) {
                _presets.insert (make_pair (i->second, Plugin::PresetRecord (i->second, i->first)));
+               DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Adding User Preset: %1 > %2\n", i->first, i->second));
        }
 
        /* add factory presets */
@@ -2096,6 +2243,7 @@ AUPlugin::find_presets ()
                /* XXX: dubious */
                string const uri = string_compose ("%1", _presets.size ());
                _presets.insert (make_pair (uri, Plugin::PresetRecord (uri, i->first, i->second)));
+               DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Adding Factory Preset: %1 > %2\n", i->first, i->second));
        }
 }
 
@@ -2109,6 +2257,7 @@ AUPlugin::has_editor () const
 
 AUPluginInfo::AUPluginInfo (boost::shared_ptr<CAComponentDescription> d)
        : descriptor (d)
+       , version (0)
 {
        type = ARDOUR::AudioUnit;
 }
@@ -2153,13 +2302,24 @@ AUPluginInfo::au_cache_path ()
 }
 
 PluginInfoList*
-AUPluginInfo::discover ()
+AUPluginInfo::discover (bool scan_only)
 {
        XMLTree tree;
 
+       /* AU require a CAComponentDescription pointer provided by the OS.
+        * Ardour only caches port and i/o config. It can't just 'scan' without
+        * 'discovering' (like we do for VST).
+        * 
+        * "Scan Only" means
+        * "Iterate over all plugins. skip the ones where there's no io-cache".
+        */
+       _scan_only = scan_only;
+
        if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS)) {
                ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)"));
        }
+       // create crash log file
+       au_start_crashlog ();
 
        PluginInfoList* plugs = new PluginInfoList;
 
@@ -2168,6 +2328,9 @@ AUPluginInfo::discover ()
        discover_generators (*plugs);
        discover_instruments (*plugs);
 
+       // all fine if we get here
+       au_remove_crashlog ();
+
        DEBUG_TRACE (DEBUG::PluginManager, string_compose ("AU: discovered %1 plugins\n", plugs->size()));
 
        return plugs;
@@ -2225,16 +2388,90 @@ AUPluginInfo::discover_instruments (PluginInfoList& plugs)
        discover_by_description (plugs, desc);
 }
 
+
+bool
+AUPluginInfo::au_get_crashlog (std::string &msg)
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
+       if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
+               return false;
+       }
+       std::ifstream ifs(fn.c_str());
+       msg.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+       au_remove_crashlog ();
+       return true;
+}
+
+void
+AUPluginInfo::au_start_crashlog ()
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
+       assert(!_crashlog_fd);
+       DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("Creating AU Log: %1\n", fn));
+       if (!(_crashlog_fd = fopen(fn.c_str(), "w"))) {
+               PBD::error << "Cannot create AU error-log" << fn << "\n";
+               cerr << "Cannot create AU error-log" << fn << "\n";
+       }
+}
+
+void
+AUPluginInfo::au_remove_crashlog ()
+{
+       if (_crashlog_fd) {
+               ::fclose(_crashlog_fd);
+               _crashlog_fd = NULL;
+       }
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
+       ::g_unlink(fn.c_str());
+       DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("Remove AU Log: %1\n", fn));
+}
+
+
+void
+AUPluginInfo::au_crashlog (std::string msg)
+{
+       if (!_crashlog_fd) {
+               fprintf(stderr, "AU: %s\n", msg.c_str());
+       } else {
+               fprintf(_crashlog_fd, "AU: %s\n", msg.c_str());
+               ::fflush(_crashlog_fd);
+       }
+}
+
 void
 AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescription& desc)
 {
        Component comp = 0;
+       au_crashlog(string_compose("Start AU discovery for Type: %1", (int)desc.componentType));
 
        comp = FindNextComponent (NULL, &desc);
 
        while (comp != NULL) {
                CAComponentDescription temp;
                GetComponentInfo (comp, &temp, NULL, NULL, NULL);
+               CFStringRef itemName = NULL;
+
+               {
+                       if (itemName != NULL) CFRelease(itemName);
+                       CFStringRef compTypeString = UTCreateStringForOSType(temp.componentType);
+                       CFStringRef compSubTypeString = UTCreateStringForOSType(temp.componentSubType);
+                       CFStringRef compManufacturerString = UTCreateStringForOSType(temp.componentManufacturer);
+                       itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
+                                       compTypeString, compManufacturerString, compSubTypeString);
+                       au_crashlog(string_compose("Scanning ID: %1", CFStringRefToStdString(itemName)));
+                       if (compTypeString != NULL)
+                               CFRelease(compTypeString);
+                       if (compSubTypeString != NULL)
+                               CFRelease(compSubTypeString);
+                       if (compManufacturerString != NULL)
+                               CFRelease(compManufacturerString);
+               }
+
+               if (is_blacklisted(CFStringRefToStdString(itemName))) {
+                       info << string_compose (_("Skipped blacklisted AU plugin %1 "), CFStringRefToStdString(itemName)) << endmsg;
+                       comp = FindNextComponent (comp, &desc);
+                       continue;
+               }
 
                AUPluginInfoPtr info (new AUPluginInfo
                                      (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription(temp))));
@@ -2252,6 +2489,7 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip
                case kAudioUnitType_Panner:
                case kAudioUnitType_OfflineEffect:
                case kAudioUnitType_FormatConverter:
+                       comp = FindNextComponent (comp, &desc);
                        continue;
 
                case kAudioUnitType_Output:
@@ -2277,8 +2515,10 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip
                        break;
                }
 
+               au_blacklist(CFStringRefToStdString(itemName));
                AUPluginInfo::get_names (temp, info->name, info->creator);
                ARDOUR::PluginScanMessage(_("AU"), info->name, false);
+               au_crashlog(string_compose("Plugin: %1", info->name));
 
                info->type = ARDOUR::AudioUnit;
                info->unique_id = stringify_descriptor (*info->descriptor);
@@ -2292,8 +2532,9 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip
                        info->version = 0;
                }
 
-               if (cached_io_configuration (info->unique_id, info->version, cacomp, info->cache, info->name)) {
+               const int rv = cached_io_configuration (info->unique_id, info->version, cacomp, info->cache, info->name);
 
+               if (rv == 0) {
                        /* here we have to map apple's wildcard system to a simple pair
                           of values. in ::can_do() we use the whole system, but here
                           we need a single pair of values. XXX probably means we should
@@ -2327,15 +2568,20 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip
 
                        plugs.push_back (info);
 
-               } else {
+               }
+               else if (rv == -1) {
                        error << string_compose (_("Cannot get I/O configuration info for AU %1"), info->name) << endmsg;
                }
 
+               au_unblacklist(CFStringRefToStdString(itemName));
+               au_crashlog("Success.");
                comp = FindNextComponent (comp, &desc);
+               if (itemName != NULL) CFRelease(itemName); itemName = NULL;
        }
+       au_crashlog(string_compose("End AU discovery for Type: %1", (int)desc.componentType));
 }
 
-bool
+int
 AUPluginInfo::cached_io_configuration (const std::string& unique_id,
                                       UInt32 version,
                                       CAComponent& comp,
@@ -2359,7 +2605,12 @@ AUPluginInfo::cached_io_configuration (const std::string& unique_id,
 
        if (cim != cached_info.end()) {
                cinfo = cim->second;
-               return true;
+               return 0;
+       }
+
+       if (_scan_only) {
+               PBD::info << string_compose (_("Skipping AU %1 (not indexed. Discover new plugins to add)"), name) << endmsg;
+               return 1;
        }
 
        CAAudioUnit unit;
@@ -2372,19 +2623,19 @@ AUPluginInfo::cached_io_configuration (const std::string& unique_id,
        try {
 
                if (CAAudioUnit::Open (comp, unit) != noErr) {
-                       return false;
+                       return -1;
                }
 
        } catch (...) {
 
                warning << string_compose (_("Could not load AU plugin %1 - ignored"), name) << endmsg;
-               return false;
+               return -1;
 
        }
 
        DEBUG_TRACE (DEBUG::AudioUnits, "get AU channel info\n");
        if ((ret = unit.GetChannelInfo (&channel_info, cnt)) < 0) {
-               return false;
+               return -1;
        }
 
        if (ret > 0) {
@@ -2410,7 +2661,7 @@ AUPluginInfo::cached_io_configuration (const std::string& unique_id,
        add_cached_info (id, cinfo);
        save_cached_info ();
 
-       return true;
+       return 0;
 }
 
 void