AU: use AudioUnit scanner app, overhaul AU support
This commit is contained in:
parent
d603124743
commit
088ccd24f0
|
@ -144,15 +144,14 @@ class LIBARDOUR_API AUPlugin : public ARDOUR::Plugin
|
|||
Float64* outCycleStartBeat,
|
||||
Float64* outCycleEndBeat);
|
||||
|
||||
static std::string maybe_fix_broken_au_id (const std::string&);
|
||||
|
||||
/* this MUST be called from thread in which you want to receive notifications
|
||||
about parameter changes.
|
||||
*/
|
||||
/* this MUST be called from thread in which you want to receive notifications
|
||||
* about parameter changes.
|
||||
*/
|
||||
int create_parameter_listener (AUEventListenerProc callback, void *arg, float interval_secs);
|
||||
/* these can be called from any thread but SHOULD be called from the same thread
|
||||
that will receive parameter change notifications.
|
||||
*/
|
||||
|
||||
/* these can be called from any thread but SHOULD be called from the same thread
|
||||
* that will receive parameter change notifications.
|
||||
*/
|
||||
int listen_to_parameter (uint32_t param_id);
|
||||
int end_listen_to_parameter (uint32_t param_id);
|
||||
|
||||
|
@ -238,14 +237,8 @@ class LIBARDOUR_API AUPlugin : public ARDOUR::Plugin
|
|||
void parameter_change_listener (void* /*arg*/, void* /*src*/, const AudioUnitEvent* event, UInt64 host_time, Float32 new_value);
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<AUPlugin> AUPluginPtr;
|
||||
|
||||
struct LIBARDOUR_API AUPluginCachedInfo {
|
||||
std::vector<std::pair<int,int> > io_configs;
|
||||
};
|
||||
|
||||
class LIBARDOUR_API AUPluginInfo : public PluginInfo {
|
||||
public:
|
||||
public:
|
||||
AUPluginInfo (boost::shared_ptr<CAComponentDescription>);
|
||||
~AUPluginInfo () {}
|
||||
|
||||
|
@ -262,42 +255,17 @@ class LIBARDOUR_API AUPluginInfo : public PluginInfo {
|
|||
bool is_instrument () const;
|
||||
bool is_utility () const;
|
||||
|
||||
AUPluginCachedInfo cache;
|
||||
|
||||
bool reconfigurable_io() const { return true; }
|
||||
uint32_t max_configurable_ouputs () const { return max_outputs; }
|
||||
|
||||
static void clear_cache ();
|
||||
static PluginInfoList* discover (bool scan_only);
|
||||
static bool au_get_crashlog (std::string &msg);
|
||||
static std::string stringify_descriptor (const CAComponentDescription&);
|
||||
|
||||
static int load_cached_info ();
|
||||
|
||||
private:
|
||||
boost::shared_ptr<CAComponentDescription> descriptor;
|
||||
UInt32 version;
|
||||
uint32_t max_outputs;
|
||||
static FILE * _crashlog_fd;
|
||||
static bool _scan_only;
|
||||
std::vector<std::pair<int,int> > io_configs;
|
||||
|
||||
static void au_start_crashlog (void);
|
||||
static void au_remove_crashlog (void);
|
||||
static void au_crashlog (std::string);
|
||||
static std::string convert_old_unique_id (std::string const&);
|
||||
|
||||
static void discover_music (PluginInfoList&);
|
||||
static void discover_fx (PluginInfoList&);
|
||||
static void discover_generators (PluginInfoList&);
|
||||
static void discover_instruments (PluginInfoList&);
|
||||
static void discover_by_description (PluginInfoList&, CAComponentDescription&);
|
||||
static Glib::ustring au_cache_path ();
|
||||
|
||||
typedef std::map<std::string,AUPluginCachedInfo> CachedInfoMap;
|
||||
static CachedInfoMap cached_info;
|
||||
|
||||
static int cached_io_configuration (const std::string&, UInt32, CAComponent&, AUPluginCachedInfo&, const std::string& name);
|
||||
static void add_cached_info (const std::string&, AUPluginCachedInfo&);
|
||||
static void save_cached_info ();
|
||||
private:
|
||||
boost::shared_ptr<CAComponentDescription> descriptor;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<AUPluginInfo> AUPluginInfoPtr;
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
#include "ardour/plugin.h"
|
||||
#include "ardour/plugin_scan_result.h"
|
||||
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
class CAComponentDescription;
|
||||
#endif
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Plugin;
|
||||
|
@ -52,9 +56,16 @@ struct VST3Info;
|
|||
struct VST2Info;
|
||||
#endif
|
||||
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
struct AUv2Info;
|
||||
struct AUv2DescStr;
|
||||
#endif
|
||||
|
||||
|
||||
class LIBARDOUR_API PluginManager : public boost::noncopyable {
|
||||
public:
|
||||
static PluginManager& instance();
|
||||
static std::string auv2_scanner_bin_path;
|
||||
static std::string vst2_scanner_bin_path;
|
||||
static std::string vst3_scanner_bin_path;
|
||||
|
||||
|
@ -295,7 +306,12 @@ private:
|
|||
void add_lxvst_presets ();
|
||||
void add_presets (std::string domain);
|
||||
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
void au_refresh (bool cache_only = false);
|
||||
void auv2_plugin (CAComponentDescription const&, AUv2Info const&);
|
||||
int auv2_discover (AUv2DescStr const&, bool);
|
||||
bool run_auv2_scanner_app (CAComponentDescription const&, AUv2DescStr const&, PSLEPtr) const;
|
||||
#endif
|
||||
|
||||
void lv2_refresh ();
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "pbd/gstdio_compat.h"
|
||||
#include "pbd/transmitter.h"
|
||||
#include "pbd/xml++.h"
|
||||
|
@ -45,6 +47,7 @@
|
|||
#include "ardour/audio_unit.h"
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/audio_buffer.h"
|
||||
#include "ardour/auv2_scan.h"
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/filesystem_paths.h"
|
||||
#include "ardour/io.h"
|
||||
|
@ -81,88 +84,9 @@ using namespace std;
|
|||
using namespace PBD;
|
||||
using namespace ARDOUR;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
#if 1 // remove me -> libs/ardour/auv2_scan.cc
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static OSStatus
|
||||
_render_callback(void *userData,
|
||||
|
@ -574,32 +498,13 @@ AUPlugin::init ()
|
|||
g_atomic_int_set (&_current_latency, UINT_MAX);
|
||||
|
||||
OSErr err;
|
||||
CFStringRef itemName;
|
||||
|
||||
/* these keep track of *configured* channel set up,
|
||||
not potential set ups.
|
||||
*/
|
||||
* not potential set ups.
|
||||
*/
|
||||
|
||||
input_channels = -1;
|
||||
output_channels = -1;
|
||||
{
|
||||
CAComponentDescription temp;
|
||||
#ifdef COREAUDIO105
|
||||
GetComponentInfo (comp.get()->Comp(), &temp, NULL, NULL, NULL);
|
||||
#else
|
||||
AudioComponentGetDescription (comp.get()->Comp(), &temp);
|
||||
#endif
|
||||
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::AudioUnitConfig, "opening AudioUnit\n");
|
||||
|
@ -697,9 +602,6 @@ AUPlugin::init ()
|
|||
discover_factory_presets ();
|
||||
|
||||
// Plugin::setup_controls ();
|
||||
|
||||
au_unblacklist(CFStringRefToStdString(itemName));
|
||||
if (itemName != NULL) CFRelease(itemName);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -826,115 +728,11 @@ AUPlugin::discover_parameters ()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static unsigned int
|
||||
four_ints_to_four_byte_literal (unsigned char n[4])
|
||||
{
|
||||
/* this is actually implementation dependent. sigh. this is what gcc
|
||||
and quite a few others do.
|
||||
*/
|
||||
return ((n[0] << 24) + (n[1] << 16) + (n[2] << 8) + n[3]);
|
||||
}
|
||||
|
||||
std::string
|
||||
AUPlugin::maybe_fix_broken_au_id (const std::string& id)
|
||||
{
|
||||
if (isdigit (id[0])) {
|
||||
return id;
|
||||
}
|
||||
|
||||
/* ID format is xxxx-xxxx-xxxx
|
||||
where x maybe \xNN or a printable character.
|
||||
|
||||
Split at the '-' and and process each part into an integer.
|
||||
Then put it back together.
|
||||
*/
|
||||
|
||||
|
||||
unsigned char nascent[4];
|
||||
const char* cstr = id.c_str();
|
||||
const char* estr = cstr + id.size();
|
||||
uint32_t n[3];
|
||||
int in;
|
||||
int next_int;
|
||||
char short_buf[3];
|
||||
stringstream s;
|
||||
|
||||
in = 0;
|
||||
next_int = 0;
|
||||
short_buf[2] = '\0';
|
||||
|
||||
while (*cstr && next_int < 4) {
|
||||
|
||||
if (*cstr == '\\') {
|
||||
|
||||
if (estr - cstr < 3) {
|
||||
|
||||
/* too close to the end for \xNN parsing: treat as literal characters */
|
||||
|
||||
nascent[in] = *cstr;
|
||||
++cstr;
|
||||
++in;
|
||||
|
||||
} else {
|
||||
|
||||
if (cstr[1] == 'x' && isxdigit (cstr[2]) && isxdigit (cstr[3])) {
|
||||
|
||||
/* parse \xNN */
|
||||
|
||||
memcpy (short_buf, &cstr[2], 2);
|
||||
nascent[in] = strtol (short_buf, NULL, 16);
|
||||
cstr += 4;
|
||||
++in;
|
||||
|
||||
} else {
|
||||
|
||||
/* treat as literal characters */
|
||||
nascent[in] = *cstr;
|
||||
++cstr;
|
||||
++in;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
nascent[in] = *cstr;
|
||||
++cstr;
|
||||
++in;
|
||||
}
|
||||
|
||||
if (in && (in % 4 == 0)) {
|
||||
/* nascent is ready */
|
||||
n[next_int] = four_ints_to_four_byte_literal (nascent);
|
||||
in = 0;
|
||||
next_int++;
|
||||
|
||||
/* swallow space-hyphen-space */
|
||||
|
||||
if (next_int < 3) {
|
||||
++cstr;
|
||||
++cstr;
|
||||
++cstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next_int != 3) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
s << n[0] << '-' << n[1] << '-' << n[2];
|
||||
|
||||
return s.str();
|
||||
|
||||
err:
|
||||
return string();
|
||||
}
|
||||
|
||||
string
|
||||
AUPlugin::unique_id () const
|
||||
{
|
||||
return AUPluginInfo::stringify_descriptor (comp->Desc());
|
||||
assert (_info->unique_id == auv2_stringify_descriptor (comp->Desc()));
|
||||
return auv2_stringify_descriptor (comp->Desc());
|
||||
}
|
||||
|
||||
const char *
|
||||
|
@ -1327,7 +1125,7 @@ AUPlugin::match_variable_io (ChanCount& in, ChanCount& aux_in, ChanCount& out)
|
|||
const int32_t preferred_out = out.n_audio ();
|
||||
|
||||
AUPluginInfoPtr pinfo = boost::dynamic_pointer_cast<AUPluginInfo>(get_info());
|
||||
vector<pair<int,int> > io_configs = pinfo->cache.io_configs;
|
||||
vector<pair<int,int> > io_configs = pinfo->io_configs;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (DEBUG_ENABLED(DEBUG::AudioUnitConfig)) {
|
||||
|
@ -1345,7 +1143,7 @@ AUPlugin::match_variable_io (ChanCount& in, ChanCount& aux_in, ChanCount& out)
|
|||
bool outs_added = false;
|
||||
#endif
|
||||
if (output_elements > 1) {
|
||||
const vector<pair<int,int> >& ioc (pinfo->cache.io_configs);
|
||||
const vector<pair<int,int> >& ioc (pinfo->io_configs);
|
||||
for (vector<pair<int,int> >::const_iterator i = ioc.begin(); i != ioc.end(); ++i) {
|
||||
int32_t possible_in = i->first;
|
||||
int32_t possible_out = i->second;
|
||||
|
@ -2459,75 +2257,6 @@ check_and_get_preset_name (ArdourComponent component, const string& pathstr, str
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if 1 // remove me
|
||||
static void
|
||||
#ifdef COREAUDIO105
|
||||
get_names (CAComponentDescription& comp_desc, std::string& name, std::string& maker)
|
||||
#else
|
||||
get_names (ArdourComponent& comp, std::string& name, std::string& maker)
|
||||
#endif
|
||||
{
|
||||
CFStringRef itemName = NULL;
|
||||
// Marc Poirier-style item name
|
||||
#ifdef COREAUDIO105
|
||||
CAComponent auComponent (comp_desc);
|
||||
if (auComponent.IsValid()) {
|
||||
CAComponentDescription dummydesc;
|
||||
Handle nameHandle = NewHandle(sizeof(void*));
|
||||
if (nameHandle != NULL) {
|
||||
OSErr err = GetComponentInfo(auComponent.Comp(), &dummydesc, nameHandle, NULL, NULL);
|
||||
if (err == noErr) {
|
||||
ConstStr255Param nameString = (ConstStr255Param) (*nameHandle);
|
||||
if (nameString != NULL) {
|
||||
itemName = CFStringCreateWithPascalString(kCFAllocatorDefault, nameString, CFStringGetSystemEncoding());
|
||||
}
|
||||
}
|
||||
DisposeHandle(nameHandle);
|
||||
}
|
||||
}
|
||||
#else
|
||||
assert (comp);
|
||||
AudioComponentCopyName (comp, &itemName);
|
||||
#endif
|
||||
|
||||
// if Marc-style fails, do the original way
|
||||
if (itemName == NULL) {
|
||||
#ifndef COREAUDIO105
|
||||
CAComponentDescription comp_desc;
|
||||
AudioComponentGetDescription (comp, &comp_desc);
|
||||
#endif
|
||||
CFStringRef compTypeString = UTCreateStringForOSType(comp_desc.componentType);
|
||||
CFStringRef compSubTypeString = UTCreateStringForOSType(comp_desc.componentSubType);
|
||||
CFStringRef compManufacturerString = UTCreateStringForOSType(comp_desc.componentManufacturer);
|
||||
|
||||
itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
|
||||
compTypeString, compManufacturerString, compSubTypeString);
|
||||
|
||||
if (compTypeString != NULL)
|
||||
CFRelease(compTypeString);
|
||||
if (compSubTypeString != NULL)
|
||||
CFRelease(compSubTypeString);
|
||||
if (compManufacturerString != NULL)
|
||||
CFRelease(compManufacturerString);
|
||||
}
|
||||
|
||||
string str = CFStringRefToStdString(itemName);
|
||||
string::size_type colon = str.find (':');
|
||||
|
||||
if (colon) {
|
||||
name = str.substr (colon+1);
|
||||
maker = str.substr (0, colon);
|
||||
strip_whitespace_edges (maker);
|
||||
strip_whitespace_edges (name);
|
||||
} else {
|
||||
name = str;
|
||||
maker = "unknown";
|
||||
strip_whitespace_edges (name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string
|
||||
AUPlugin::current_preset() const
|
||||
{
|
||||
|
@ -2608,10 +2337,12 @@ AUPlugin::has_editor () const
|
|||
return true;
|
||||
}
|
||||
|
||||
/* ****************************************************************************/
|
||||
|
||||
AUPluginInfo::AUPluginInfo (boost::shared_ptr<CAComponentDescription> d)
|
||||
: descriptor (d)
|
||||
, version (0)
|
||||
: version (0)
|
||||
, max_outputs (0)
|
||||
, descriptor (d)
|
||||
{
|
||||
type = ARDOUR::AudioUnit;
|
||||
}
|
||||
|
@ -2724,593 +2455,6 @@ AUPluginInfo::get_presets (bool user_only) const
|
|||
return p;
|
||||
}
|
||||
|
||||
Glib::ustring
|
||||
AUPluginInfo::au_cache_path ()
|
||||
{
|
||||
return Glib::build_filename (ARDOUR::user_cache_directory(), "au_cache");
|
||||
}
|
||||
|
||||
PluginInfoList*
|
||||
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 ...)"));
|
||||
// flush RAM cache -- after clear_cache()
|
||||
cached_info.clear();
|
||||
}
|
||||
// create crash log file
|
||||
au_start_crashlog ();
|
||||
|
||||
PluginInfoList* plugs = new PluginInfoList;
|
||||
|
||||
discover_fx (*plugs);
|
||||
discover_music (*plugs);
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
AUPluginInfo::discover_music (PluginInfoList& plugs)
|
||||
{
|
||||
CAComponentDescription desc;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
desc.componentSubType = 0;
|
||||
desc.componentManufacturer = 0;
|
||||
desc.componentType = kAudioUnitType_MusicEffect;
|
||||
|
||||
discover_by_description (plugs, desc);
|
||||
}
|
||||
|
||||
void
|
||||
AUPluginInfo::discover_fx (PluginInfoList& plugs)
|
||||
{
|
||||
CAComponentDescription desc;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
desc.componentSubType = 0;
|
||||
desc.componentManufacturer = 0;
|
||||
desc.componentType = kAudioUnitType_Effect;
|
||||
|
||||
discover_by_description (plugs, desc);
|
||||
}
|
||||
|
||||
void
|
||||
AUPluginInfo::discover_generators (PluginInfoList& plugs)
|
||||
{
|
||||
CAComponentDescription desc;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
desc.componentSubType = 0;
|
||||
desc.componentManufacturer = 0;
|
||||
desc.componentType = kAudioUnitType_Generator;
|
||||
|
||||
discover_by_description (plugs, desc);
|
||||
}
|
||||
|
||||
void
|
||||
AUPluginInfo::discover_instruments (PluginInfoList& plugs)
|
||||
{
|
||||
CAComponentDescription desc;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
desc.componentSubType = 0;
|
||||
desc.componentManufacturer = 0;
|
||||
desc.componentType = kAudioUnitType_MusicDevice;
|
||||
|
||||
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::AudioUnitConfig, 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::AudioUnitConfig, 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)
|
||||
{
|
||||
ArdourComponent comp = 0;
|
||||
au_crashlog(string_compose("Start AU discovery for Type: %1", (int)desc.componentType));
|
||||
|
||||
comp = ArdourFindNext (NULL, &desc);
|
||||
|
||||
while (comp != NULL) {
|
||||
CAComponentDescription temp;
|
||||
#ifdef COREAUDIO105
|
||||
GetComponentInfo (comp, &temp, NULL, NULL, NULL);
|
||||
#else
|
||||
AudioComponentGetDescription (comp, &temp);
|
||||
#endif
|
||||
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;
|
||||
if (itemName != NULL) {
|
||||
CFRelease(itemName);
|
||||
itemName = NULL;
|
||||
}
|
||||
comp = ArdourFindNext (comp, &desc);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool has_midi_in = false;
|
||||
|
||||
AUPluginInfoPtr info (new AUPluginInfo (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription(temp))));
|
||||
|
||||
/* although apple designed the subtype field to be a "category" indicator,
|
||||
its really turned into a plugin ID field for a given manufacturer. Hence
|
||||
there are no categories for AudioUnits. However, to keep the plugins
|
||||
showing up under "categories", we'll use the "type" as a high level
|
||||
selector.
|
||||
|
||||
NOTE: no panners, format converters or i/o AU's for our purposes
|
||||
*/
|
||||
|
||||
switch (info->descriptor->Type()) {
|
||||
case kAudioUnitType_Panner:
|
||||
case kAudioUnitType_OfflineEffect:
|
||||
case kAudioUnitType_FormatConverter:
|
||||
comp = ArdourFindNext (comp, &desc);
|
||||
continue;
|
||||
|
||||
case kAudioUnitType_Output:
|
||||
info->category = _("Output");
|
||||
break;
|
||||
case kAudioUnitType_MusicDevice:
|
||||
info->category = _("Instrument");
|
||||
has_midi_in = true;
|
||||
break;
|
||||
case kAudioUnitType_MusicEffect:
|
||||
info->category = _("Effect");
|
||||
has_midi_in = true;
|
||||
break;
|
||||
case kAudioUnitType_Effect:
|
||||
info->category = _("Effect");
|
||||
break;
|
||||
case kAudioUnitType_Mixer:
|
||||
info->category = _("Mixer");
|
||||
break;
|
||||
case kAudioUnitType_Generator:
|
||||
info->category = _("Generator");
|
||||
break;
|
||||
default:
|
||||
info->category = _("(Unknown)");
|
||||
break;
|
||||
}
|
||||
|
||||
au_blacklist(CFStringRefToStdString(itemName));
|
||||
#ifdef COREAUDIO105
|
||||
get_names (temp, info->name, info->creator);
|
||||
#else
|
||||
get_names (comp, info->name, info->creator);
|
||||
#endif
|
||||
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);
|
||||
|
||||
/* XXX not sure of the best way to handle plugin versioning yet */
|
||||
|
||||
CAComponent cacomp (*info->descriptor);
|
||||
|
||||
#ifdef COREAUDIO105
|
||||
if (cacomp.GetResourceVersion (info->version) != noErr)
|
||||
#else
|
||||
if (cacomp.GetVersion (info->version) != noErr)
|
||||
#endif
|
||||
{
|
||||
info->version = 0;
|
||||
}
|
||||
|
||||
const int rv = cached_io_configuration (info->unique_id, info->version, cacomp, info->cache, info->name);
|
||||
|
||||
info->max_outputs = 0;
|
||||
|
||||
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
|
||||
remove any use of these values.
|
||||
|
||||
for now, if the plugin provides a wildcard, treat it as 1. we really
|
||||
don't care much, because whether we can handle an i/o configuration
|
||||
depends upon ::configure_variable_io(), not these counts.
|
||||
|
||||
they exist because other parts of ardour try to present i/o configuration
|
||||
info to the user, which should perhaps be revisited.
|
||||
*/
|
||||
|
||||
const vector<pair<int,int> >& ioc (info->cache.io_configs);
|
||||
for (vector<pair<int,int> >::const_iterator i = ioc.begin(); i != ioc.end(); ++i) {
|
||||
int32_t possible_out = i->second;
|
||||
if (possible_out < 0) {
|
||||
continue;
|
||||
} else if (possible_out > info->max_outputs) {
|
||||
info->max_outputs = possible_out;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t possible_in = ioc.front().first;
|
||||
int32_t possible_out = ioc.front().second;
|
||||
|
||||
if (possible_in > 0) {
|
||||
info->n_inputs.set (DataType::AUDIO, possible_in);
|
||||
} else {
|
||||
info->n_inputs.set (DataType::AUDIO, 1);
|
||||
}
|
||||
|
||||
info->n_inputs.set (DataType::MIDI, has_midi_in ? 1 : 0);
|
||||
|
||||
if (possible_out > 0) {
|
||||
info->n_outputs.set (DataType::AUDIO, possible_out);
|
||||
} else {
|
||||
info->n_outputs.set (DataType::AUDIO, 1);
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioUnitConfig, string_compose ("detected AU %1 with %2 i/o configurations - %3\n",
|
||||
info->name.c_str(), info->cache.io_configs.size(), info->unique_id));
|
||||
|
||||
plugs.push_back (info);
|
||||
|
||||
}
|
||||
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 = ArdourFindNext (comp, &desc);
|
||||
if (itemName != NULL) CFRelease(itemName); itemName = NULL;
|
||||
}
|
||||
au_crashlog(string_compose("End AU discovery for Type: %1", (int)desc.componentType));
|
||||
}
|
||||
|
||||
int
|
||||
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", (uint32_t) version);
|
||||
id = unique_id;
|
||||
id += '/';
|
||||
id += buf;
|
||||
|
||||
CachedInfoMap::iterator cim = cached_info.find (id);
|
||||
|
||||
if (cim != cached_info.end()) {
|
||||
cinfo = cim->second;
|
||||
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;
|
||||
AUChannelInfo* channel_info;
|
||||
UInt32 cnt;
|
||||
int ret;
|
||||
|
||||
ARDOUR::BootMessage (string_compose (_("Checking AudioUnit: %1"), name));
|
||||
|
||||
try {
|
||||
|
||||
if (CAAudioUnit::Open (comp, unit) != noErr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
|
||||
warning << string_compose (_("Could not load AU plugin %1 - ignored"), name) << endmsg;
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioUnitConfig, "get AU channel info\n");
|
||||
if ((ret = unit.GetChannelInfo (&channel_info, cnt)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
/* AU is expected to deal with same channel valance in and out */
|
||||
cinfo.io_configs.push_back (pair<int,int> (-1, -1));
|
||||
} else {
|
||||
/* CAAudioUnit::GetChannelInfo silently merges bus formats
|
||||
* check if this was the case and if so, add
|
||||
* bus configs as incremental options.
|
||||
*/
|
||||
Boolean* isWritable = 0;
|
||||
UInt32 dataSize = 0;
|
||||
OSStatus result = AudioUnitGetPropertyInfo (unit.AU(),
|
||||
kAudioUnitProperty_SupportedNumChannels,
|
||||
kAudioUnitScope_Global, 0,
|
||||
&dataSize, isWritable);
|
||||
if (result != noErr && (comp.Desc().IsGenerator() || comp.Desc().IsMusicDevice())) {
|
||||
/* incrementally add busses */
|
||||
int in = 0;
|
||||
int out = 0;
|
||||
for (uint32_t n = 0; n < cnt; ++n) {
|
||||
in += channel_info[n].inChannels;
|
||||
out += channel_info[n].outChannels;
|
||||
cinfo.io_configs.push_back (pair<int,int> (in, out));
|
||||
}
|
||||
} 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 0;
|
||||
}
|
||||
|
||||
void
|
||||
AUPluginInfo::clear_cache ()
|
||||
{
|
||||
const string& fn = au_cache_path();
|
||||
if (Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
|
||||
::g_unlink(fn.c_str());
|
||||
}
|
||||
// keep cached_info in RAM until restart or re-scan
|
||||
cached_info.clear();
|
||||
}
|
||||
|
||||
void
|
||||
AUPluginInfo::add_cached_info (const std::string& id, AUPluginCachedInfo& cinfo)
|
||||
{
|
||||
cached_info[id] = cinfo;
|
||||
}
|
||||
|
||||
#define AU_CACHE_VERSION "2.0"
|
||||
|
||||
void
|
||||
AUPluginInfo::save_cached_info ()
|
||||
{
|
||||
XMLNode* node;
|
||||
|
||||
node = new XMLNode (X_("AudioUnitPluginCache"));
|
||||
node->set_property( "version", AU_CACHE_VERSION );
|
||||
|
||||
for (map<string,AUPluginCachedInfo>::iterator i = cached_info.begin(); i != cached_info.end(); ++i) {
|
||||
XMLNode* parent = new XMLNode (X_("plugin"));
|
||||
parent->set_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"));
|
||||
|
||||
child->set_property (X_("in"), j->first);
|
||||
child->set_property (X_("out"), j->second);
|
||||
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;
|
||||
g_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;
|
||||
}
|
||||
|
||||
if ( !tree.read (path) ) {
|
||||
error << "au_cache is not a valid XML file. AU plugins will be re-scanned" << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const XMLNode* root (tree.root());
|
||||
|
||||
if (root->name() != X_("AudioUnitPluginCache")) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//initial version has incorrectly stored i/o info, and/or garbage chars.
|
||||
XMLProperty const * version = root->property(X_("version"));
|
||||
if (! ((version != NULL) && (version->value() == X_(AU_CACHE_VERSION)))) {
|
||||
error << "au_cache is not correct version. AU plugins will be re-scanned" << endmsg;
|
||||
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();
|
||||
|
||||
string id;
|
||||
if (!child->get_property (X_("id"), id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string fixed;
|
||||
string version;
|
||||
|
||||
string::size_type slash = id.find_last_of ('/');
|
||||
|
||||
if (slash == string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
version = id.substr (slash);
|
||||
id = id.substr (0, slash);
|
||||
fixed = AUPlugin::maybe_fix_broken_au_id (id);
|
||||
|
||||
if (fixed.empty()) {
|
||||
error << string_compose (_("Your AudioUnit configuration cache contains an AU plugin whose ID cannot be understood - ignored (%1)"), id) << endmsg;
|
||||
continue;
|
||||
}
|
||||
|
||||
id = fixed;
|
||||
id += version;
|
||||
|
||||
AUPluginCachedInfo cinfo;
|
||||
|
||||
for (XMLNodeConstIterator giter = gchildren.begin(); giter != gchildren.end(); giter++) {
|
||||
|
||||
gchild = *giter;
|
||||
|
||||
if (gchild->name() == X_("io")) {
|
||||
|
||||
int32_t in;
|
||||
int32_t out;
|
||||
|
||||
if (gchild->get_property (X_("in"), in) && gchild->get_property (X_("out"), out)) {
|
||||
cinfo.io_configs.push_back (pair<int,int> (in, out));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cinfo.io_configs.size()) {
|
||||
add_cached_info (id, cinfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if 1 // code dup ? -> auv2_scan
|
||||
std::string
|
||||
AUPluginInfo::stringify_descriptor (const CAComponentDescription& desc)
|
||||
{
|
||||
stringstream s;
|
||||
|
||||
/* note: OSType is a compiler-implemenation-defined value,
|
||||
historically a 32 bit integer created with a multi-character
|
||||
constant such as 'abcd'. It is, fundamentally, an abomination.
|
||||
*/
|
||||
|
||||
s << desc.Type();
|
||||
s << '-';
|
||||
s << desc.SubType();
|
||||
s << '-';
|
||||
s << desc.Manu();
|
||||
|
||||
return s.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
AUPluginInfo::needs_midi_input () const
|
||||
{
|
||||
|
@ -3348,6 +2492,21 @@ AUPluginInfo::is_utility () const
|
|||
// kAudioUnitType_MidiProcessor ..looks like we aren't even scanning for these yet?
|
||||
}
|
||||
|
||||
std::string
|
||||
AUPluginInfo::convert_old_unique_id (std::string const& id)
|
||||
{
|
||||
vector<std::string> p;
|
||||
boost::split (p, id, boost::is_any_of ("-"));
|
||||
if (p.size () == 3) {
|
||||
OSType t (PBD::atoi (p[0]));
|
||||
OSType s (PBD::atoi (p[1]));
|
||||
OSType m (PBD::atoi (p[2]));
|
||||
CAComponentDescription desc (t, s, m);
|
||||
return auv2_stringify_descriptor (desc);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
AUPlugin::set_info (PluginInfoPtr info)
|
||||
{
|
||||
|
|
|
@ -65,10 +65,6 @@
|
|||
#include "ardour/linux_vst_support.h"
|
||||
#endif
|
||||
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
#include "ardour/audio_unit.h"
|
||||
#endif
|
||||
|
||||
#if defined(__SSE__) || defined(USE_XMMINTRIN)
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
@ -615,10 +611,6 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
AUPluginInfo::load_cached_info ();
|
||||
#endif
|
||||
|
||||
setup_hardware_optimization (try_optimization);
|
||||
|
||||
if (Config->get_cpu_dma_latency () >= 0) {
|
||||
|
|
|
@ -104,7 +104,14 @@
|
|||
#endif
|
||||
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
#include "CAAudioUnit.h"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioUnitUtilities.h>
|
||||
|
||||
#include "ardour/audio_unit.h"
|
||||
#include "ardour/auv2_scan.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
#endif
|
||||
|
||||
|
@ -127,6 +134,7 @@ using namespace PBD;
|
|||
using namespace std;
|
||||
|
||||
PluginManager* PluginManager::_instance = 0;
|
||||
std::string PluginManager::auv2_scanner_bin_path = "";
|
||||
std::string PluginManager::vst2_scanner_bin_path = "";
|
||||
std::string PluginManager::vst3_scanner_bin_path = "";
|
||||
|
||||
|
@ -230,6 +238,14 @@ PluginManager::PluginManager ()
|
|||
#endif // VST3_SUPPORT
|
||||
#endif // any VST
|
||||
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
PBD::Searchpath ausp (Glib::build_filename(ARDOUR::ardour_dll_directory(), "auscan"));
|
||||
ausp += ARDOUR::ardour_dll_directory();
|
||||
if (!PBD::find_file (ausp, "ardour-au-scanner" , auv2_scanner_bin_path)) {
|
||||
PBD::warning << "AUv2 scanner app (ardour-au-scanner) not found in path " << ausp.to_string() << endmsg;
|
||||
}
|
||||
#endif
|
||||
|
||||
load_statuses ();
|
||||
load_tags ();
|
||||
load_stats ();
|
||||
|
@ -660,7 +676,12 @@ void
|
|||
PluginManager::clear_au_cache ()
|
||||
{
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
AUPluginInfo::clear_cache ();
|
||||
string dn = Glib::build_filename (ARDOUR::user_cache_directory(), "auv2");
|
||||
vector<string> a2i_files;
|
||||
find_files_matching_regex (a2i_files, dn, "\\.a2i$", false);
|
||||
for (vector<string>::iterator i = a2i_files.begin(); i != a2i_files.end (); ++i) {
|
||||
::g_unlink(i->c_str());
|
||||
}
|
||||
Config->set_plugin_cache_version (0);
|
||||
Config->save_state();
|
||||
#endif
|
||||
|
@ -1003,25 +1024,220 @@ PluginManager::lv2_refresh ()
|
|||
}
|
||||
|
||||
#ifdef AUDIOUNIT_SUPPORT
|
||||
|
||||
static void auv2_scanner_log (std::string msg, PluginScanLogEntry* psle)
|
||||
{
|
||||
psle->msg (PluginScanLogEntry::OK, msg);
|
||||
}
|
||||
|
||||
bool
|
||||
PluginManager::run_auv2_scanner_app (CAComponentDescription const& desc, AUv2DescStr const& d, PSLEPtr psle) const
|
||||
{
|
||||
char **argp= (char**) calloc (7, sizeof (char*));
|
||||
argp[0] = strdup (auv2_scanner_bin_path.c_str ());
|
||||
argp[1] = strdup ("-f");
|
||||
argp[2] = strdup ("--");
|
||||
argp[3] = strdup (d.type.c_str());
|
||||
argp[4] = strdup (d.subt.c_str());
|
||||
argp[5] = strdup (d.manu.c_str());
|
||||
argp[6] = 0;
|
||||
|
||||
ARDOUR::SystemExec scanner (auv2_scanner_bin_path, argp);
|
||||
PBD::ScopedConnection c;
|
||||
scanner.ReadStdout.connect_same_thread (c, boost::bind (&auv2_scanner_log, _1, &(*psle)));
|
||||
|
||||
if (scanner.start (ARDOUR::SystemExec::MergeWithStdin)) {
|
||||
psle->msg (PluginScanLogEntry::Error, string_compose (_("Cannot launch AU scanner app '%1': %2"), auv2_scanner_bin_path, strerror (errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
int timeout = Config->get_vst_scan_timeout(); // deciseconds
|
||||
bool notime = (timeout <= 0);
|
||||
|
||||
while (scanner.is_running () && (notime || timeout > 0)) {
|
||||
if (!notime && no_timeout ()) {
|
||||
notime = true;
|
||||
timeout = -1;
|
||||
}
|
||||
|
||||
ARDOUR::PluginScanTimeout (timeout);
|
||||
--timeout;
|
||||
Glib::usleep (100000);
|
||||
|
||||
if (cancelled () || (!notime && timeout == 0)) {
|
||||
scanner.terminate ();
|
||||
if (cancelled ()) {
|
||||
psle->msg (PluginScanLogEntry::New, "Scan was cancelled.");
|
||||
} else {
|
||||
psle->msg (PluginScanLogEntry::TimeOut, "Scan Timed Out.");
|
||||
}
|
||||
/* may be partially written */
|
||||
g_unlink (auv2_cache_file (desc).c_str ());
|
||||
auv2_whitelist (d.to_s ());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PluginManager::auv2_plugin (CAComponentDescription const& desc, AUv2Info const& nfo)
|
||||
{
|
||||
PSLEPtr psle (scan_log_entry (AudioUnit, auv2_stringify_descriptor (desc)));
|
||||
|
||||
AUPluginInfoPtr info (new AUPluginInfo (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription (desc))));
|
||||
psle->msg (PluginScanLogEntry::OK);
|
||||
|
||||
info->unique_id = nfo.id;
|
||||
info->name = nfo.name;
|
||||
info->creator = nfo.creator;
|
||||
info->category = nfo.category;
|
||||
info->version = nfo.version;
|
||||
info->max_outputs = nfo.max_outputs;
|
||||
info->io_configs = nfo.io_configs;
|
||||
|
||||
_au_plugin_info->push_back (info);
|
||||
|
||||
psle->add (info);
|
||||
}
|
||||
|
||||
int
|
||||
PluginManager::auv2_discover (AUv2DescStr const& d, bool cache_only)
|
||||
{
|
||||
std::string dstr = d.to_s ();
|
||||
DEBUG_TRACE (DEBUG::PluginManager, string_compose ("checking AU plugin at %1\n", dstr));
|
||||
|
||||
PSLEPtr psle (scan_log_entry (AudioUnit, dstr));
|
||||
|
||||
if (auv2_is_blacklisted (dstr)) {
|
||||
psle->msg (PluginScanLogEntry::Blacklisted);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CAComponentDescription desc (d.desc ());
|
||||
|
||||
bool run_scan = false;
|
||||
bool is_new = false;
|
||||
|
||||
string cache_file = auv2_valid_cache_file (desc, false, &is_new);
|
||||
|
||||
if (!cache_only && auv2_scanner_bin_path.empty () && cache_file.empty ()) {
|
||||
/* scan in host context */
|
||||
psle->reset ();
|
||||
auv2_blacklist (dstr);
|
||||
psle->msg (PluginScanLogEntry::OK, "(internal scan)");
|
||||
if (!auv2_scan_and_cache (desc, sigc::mem_fun (*this, &PluginManager::auv2_plugin), false)) {
|
||||
psle->msg (PluginScanLogEntry::Error, "Cannot load AUv2");
|
||||
psle->msg (PluginScanLogEntry::Blacklisted);
|
||||
return -1;
|
||||
}
|
||||
psle->msg (PluginScanLogEntry::OK, string_compose (_("Saved AUV2 plugin cache to %1"), auv2_cache_file (desc)));
|
||||
auv2_whitelist (dstr);
|
||||
return 0;
|
||||
}:
|
||||
|
||||
XMLTree tree;
|
||||
if (cache_file.empty ()) {
|
||||
run_scan = true;
|
||||
} else if (tree.read (cache_file)) {
|
||||
/* valid cache file was found, now check version */
|
||||
int cf_version = 0;
|
||||
if (!tree.root()->get_property ("version", cf_version) || cf_version < 2) {
|
||||
run_scan = true;
|
||||
}
|
||||
} else {
|
||||
/* failed to parse XML */
|
||||
run_scan = true;
|
||||
}
|
||||
|
||||
if (!cache_only && run_scan) {
|
||||
/* re/generate cache file */
|
||||
psle->reset ();
|
||||
auv2_blacklist (dstr);
|
||||
|
||||
if (!run_auv2_scanner_app (desc, d, psle)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cache_file = auv2_cache_file (desc);
|
||||
|
||||
if (cache_file.empty ()) {
|
||||
psle->msg (PluginScanLogEntry::Error, _("Scan Failed."));
|
||||
psle->msg (PluginScanLogEntry::Blacklisted);
|
||||
return -1;
|
||||
}
|
||||
/* re-read cache file */
|
||||
if (!tree.read (cache_file)) {
|
||||
psle->msg (PluginScanLogEntry::Error, string_compose (_("Cannot parse AUv2 cache file '%1' for plugin '%2'"), cache_file, dstr));
|
||||
psle->msg (PluginScanLogEntry::Blacklisted);
|
||||
return -1;
|
||||
}
|
||||
run_scan = false; // mark as scanned
|
||||
}
|
||||
|
||||
if (cache_file.empty () || run_scan) {
|
||||
/* cache file does not exist and cache_only == true,
|
||||
* or cache file is invalid (scan needed)
|
||||
*/
|
||||
psle->msg (is_new ? PluginScanLogEntry::New : PluginScanLogEntry::Updated);
|
||||
return -1;
|
||||
}
|
||||
|
||||
auv2_whitelist (dstr);
|
||||
psle->set_result (PluginScanLogEntry::OK);
|
||||
|
||||
uint32_t discovered = 0;
|
||||
for (XMLNodeConstIterator i = tree.root()->children().begin(); i != tree.root()->children().end(); ++i) {
|
||||
try {
|
||||
AUv2Info nfo (**i);
|
||||
|
||||
if (nfo.id != dstr) {
|
||||
psle->msg (PluginScanLogEntry::Error, string_compose (_("Cache file %1 ID mismatch '%2' vs '%3'"), cache_file, nfo.id, dstr));
|
||||
continue;
|
||||
}
|
||||
|
||||
auv2_plugin (desc, nfo);
|
||||
++discovered;
|
||||
} catch (...) {
|
||||
psle->msg (PluginScanLogEntry::Error, string_compose (_("Corrupt AUv2 cache file '%1'"), cache_file));
|
||||
DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Cannot load AUv2 '%1'\n", dstr));
|
||||
}
|
||||
}
|
||||
|
||||
return discovered;
|
||||
}
|
||||
|
||||
void
|
||||
PluginManager::au_refresh (bool cache_only)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::PluginManager, "AU: refresh\n");
|
||||
delete _au_plugin_info;
|
||||
_au_plugin_info = new ARDOUR::PluginInfoList();
|
||||
|
||||
bool discover_at_start = Config->get_discover_audio_units ();
|
||||
if (discover_at_start) {
|
||||
/* disable automatic discovery in case scanning crashes */
|
||||
Config->set_discover_audio_units (false);
|
||||
Config->save_state();
|
||||
if (!Config->get_discover_audio_units ()) { // TODO rename: enable AU
|
||||
return;
|
||||
}
|
||||
|
||||
delete _au_plugin_info;
|
||||
_au_plugin_info = AUPluginInfo::discover(cache_only && !discover_at_start);
|
||||
ARDOUR::PluginScanMessage(_("AUv2"), _("Indexing"), false);
|
||||
/* disable AU in case indexing crashes */
|
||||
Config->set_discover_audio_units (false);
|
||||
Config->save_state();
|
||||
|
||||
if (discover_at_start) {
|
||||
/* successful scan re-enabled automatic discovery if it was set */
|
||||
Config->set_discover_audio_units (discover_at_start);
|
||||
Config->save_state();
|
||||
string aucrsh = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crash");
|
||||
g_file_set_contents (aucrsh.c_str(), "", -1, NULL);
|
||||
|
||||
std::vector<AUv2DescStr> audesc;
|
||||
auv2_list_plugins (audesc);
|
||||
|
||||
/* successful, re-enabled AU support */
|
||||
Config->set_discover_audio_units (true);
|
||||
Config->save_state();
|
||||
|
||||
::g_unlink (aucrsh.c_str());
|
||||
|
||||
for (std::vector<AUv2DescStr>::const_iterator i = audesc.begin (); i != audesc.end (); ++i) {
|
||||
ARDOUR::PluginScanMessage(_("AUv2"), i->to_s(), !cache_only && !cancelled());
|
||||
auv2_discover (*i, cache_only);
|
||||
}
|
||||
|
||||
for (PluginInfoList::iterator i = _au_plugin_info->begin(); i != _au_plugin_info->end(); ++i) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user