X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fardour%2Faudio_unit.cc;h=063055b5e63fc018b22bdee07aea3dda4aceeb98;hb=c7b64803d999ff993899dbf1f1449060e8ca9e9c;hp=d9184ff64336f38323bcadfef71e24ca5bb84861;hpb=ee0826c823edd7c3db514425b14e145f8d2d801c;p=ardour.git diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc index d9184ff643..063055b5e6 100644 --- a/libs/ardour/audio_unit.cc +++ b/libs/ardour/audio_unit.cc @@ -73,6 +73,80 @@ static string preset_search_path = "/Library/Audio/Presets:/Network/Library/Audi 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(ifs)), (std::istreambuf_iterator())); + } + + ::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(ifs)), (std::istreambuf_iterator())); + + 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, @@ -349,6 +423,7 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptrpresetName); factory_preset_map[name] = preset->presetNumber; + DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Factory Preset: %1 > %2\n", name, preset->presetNumber)); } CFRelease (presets); @@ -443,6 +523,7 @@ void AUPlugin::init () { OSErr err; + CFStringRef itemName; /* these keep track of *configured* channel set up, not potential set ups. @@ -450,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"); @@ -515,6 +610,9 @@ AUPlugin::init () discover_factory_presets (); // Plugin::setup_controls (); + + au_unblacklist(CFStringRefToStdString(itemName)); + if (itemName != NULL) CFRelease(itemName); } void @@ -915,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)); @@ -1042,6 +1143,32 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out) 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 >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) { int32_t possible_in = i->first; @@ -1092,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 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 */ @@ -1181,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 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 */ @@ -1231,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; } @@ -1899,6 +2012,8 @@ AUPlugin::do_save_preset (string preset_name) 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; } @@ -2087,6 +2202,7 @@ AUPlugin::find_presets () find_files_matching_filter (preset_files, preset_search_path, au_preset_filter, this, true, true, true); if (preset_files.empty()) { + DEBUG_TRACE (DEBUG::AudioUnits, "AU No Preset Files found for given plugin.\n"); return; } @@ -2107,6 +2223,9 @@ 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)); } } @@ -2115,6 +2234,7 @@ AUPlugin::find_presets () 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 */ @@ -2123,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)); } } @@ -2136,6 +2257,7 @@ AUPlugin::has_editor () const AUPluginInfo::AUPluginInfo (boost::shared_ptr d) : descriptor (d) + , version (0) { type = ARDOUR::AudioUnit; } @@ -2180,10 +2302,19 @@ 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 ...)")); } @@ -2278,7 +2409,8 @@ AUPluginInfo::au_start_crashlog () 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\n"; + PBD::error << "Cannot create AU error-log" << fn << "\n"; + cerr << "Cannot create AU error-log" << fn << "\n"; } } @@ -2298,9 +2430,12 @@ AUPluginInfo::au_remove_crashlog () void AUPluginInfo::au_crashlog (std::string msg) { - assert(_crashlog_fd); - fprintf(_crashlog_fd, "AU: %s\n", msg.c_str()); - ::fflush(_crashlog_fd); + 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 @@ -2314,12 +2449,14 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip 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); - CFStringRef itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"), + itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"), compTypeString, compManufacturerString, compSubTypeString); au_crashlog(string_compose("Scanning ID: %1", CFStringRefToStdString(itemName))); if (compTypeString != NULL) @@ -2330,6 +2467,12 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip 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 (new CAComponentDescription(temp)))); @@ -2372,6 +2515,7 @@ 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)); @@ -2388,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 @@ -2423,17 +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, @@ -2457,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; @@ -2470,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) { @@ -2508,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