summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2009-01-28 15:09:36 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2009-01-28 15:09:36 +0000
commitd56085060908f50be5f49021b04093596004b777 (patch)
tree857756b48bc94c9c4bf74c3ef72c96bbbe80ffaa /libs
parent7e456076abb8ec0757aa0bee6054303a52e1007a (diff)
AU state handling, including changes to PathScanner so that it can now do recursive searches. Note that AU state handling is not built by default, and public binaries for OS X will not include it. If you build Ardour on OS X yourself, add AU_STATE=1 to your scons arguments
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@4456 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/SConscript3
-rw-r--r--libs/ardour/ardour/audio_unit.h15
-rw-r--r--libs/ardour/ardour/plugin.h3
-rw-r--r--libs/ardour/audio_unit.cc613
-rw-r--r--libs/ardour/plugin.cc2
-rw-r--r--libs/pbd/pathscanner.cc86
-rw-r--r--libs/pbd/pbd/pathscanner.h104
7 files changed, 741 insertions, 85 deletions
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 1610130327..100986b5db 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -23,6 +23,7 @@ ardour.Append(POTFILE = domain + '.pot')
if ardour['IS_OSX']:
ardour.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
+
#
# explicitly reference the control protocol LGPL library for includes
#
@@ -253,6 +254,8 @@ if conf.CheckCHeader('/System/Library/Frameworks/CoreAudio.framework/Headers/Cor
if conf.CheckCHeader('/System/Library/Frameworks/AudioUnit.framework/Headers/AudioUnit.h') and ardour['AUDIOUNITS']:
ardour.Append(CXXFLAGS="-DHAVE_AUDIOUNITS")
ardour.Append(LINKFLAGS="-framework AudioUnit")
+ if ardour['AUSTATE']:
+ ardour.Append(CXXFLAGS="-DAU_STATE_SUPPORT")
extra_sources += audiounit_files
if ardour['COREAUDIO']:
diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h
index 07986782e2..b100ae05ab 100644
--- a/libs/ardour/ardour/audio_unit.h
+++ b/libs/ardour/ardour/audio_unit.h
@@ -28,6 +28,7 @@
#include <set>
#include <string>
#include <vector>
+#include <map>
#include <ardour/plugin.h>
@@ -96,7 +97,8 @@ class AUPlugin : public ARDOUR::Plugin
bool save_preset (string name);
bool load_preset (const string preset_label);
std::vector<std::string> get_presets ();
-
+ std::string current_preset() const;
+
bool has_editor () const;
int32_t can_do (int32_t in, int32_t& out);
@@ -115,13 +117,18 @@ class AUPlugin : public ARDOUR::Plugin
private:
boost::shared_ptr<CAComponent> comp;
boost::shared_ptr<CAAudioUnit> unit;
-
+
bool initialized;
int32_t input_channels;
int32_t output_channels;
std::vector<std::pair<int,int> > io_configs;
AudioBufferList* buffers;
-
+
+ /* XXX this should really be shared across all AUPlugin instances */
+
+ typedef std::map<std::string,std::string> PresetMap;
+ PresetMap preset_map;
+
UInt32 global_elements;
UInt32 output_elements;
UInt32 input_elements;
@@ -167,7 +174,7 @@ class AUPluginInfo : public PluginInfo {
private:
boost::shared_ptr<CAComponentDescription> descriptor;
UInt32 version;
-
+
static void discover_music (PluginInfoList&);
static void discover_fx (PluginInfoList&);
static void discover_by_description (PluginInfoList&, CAComponentDescription&);
diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h
index abf2867dec..c983afd4fe 100644
--- a/libs/ardour/ardour/plugin.h
+++ b/libs/ardour/ardour/plugin.h
@@ -138,6 +138,9 @@ class Plugin : public PBD::StatefulDestructible
virtual bool save_preset(string name) = 0;
virtual bool load_preset (const string preset_label);
virtual std::vector<std::string> get_presets();
+ virtual std::string current_preset() const { return std::string(); }
+
+ static sigc::signal<bool> PresetFileExists;
virtual bool has_editor() const = 0;
diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc
index 5d9a2d82e7..df6e13edf4 100644
--- a/libs/ardour/audio_unit.cc
+++ b/libs/ardour/audio_unit.cc
@@ -18,10 +18,13 @@
*/
#include <sstream>
+#include <errno.h>
+#include <string.h>
#include <pbd/transmitter.h>
#include <pbd/xml++.h>
#include <pbd/whitespace.h>
+#include <pbd/pathscanner.h>
#include <glibmm/thread.h>
#include <glibmm/fileutils.h>
@@ -37,6 +40,7 @@
#include <appleutility/CAAudioUnit.h>
#include <appleutility/CAAUParameter.h>
+#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
@@ -46,8 +50,19 @@ using namespace std;
using namespace PBD;
using namespace ARDOUR;
+#ifndef AU_STATE_SUPPORT
+static bool seen_get_state_message = false;
+static bool seen_set_state_message = false;
+static bool seen_loading_message = false;
+static bool seen_saving_message = false;
+#endif
+
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;
+
static OSStatus
_render_callback(void *userData,
AudioUnitRenderActionFlags *ioActionFlags,
@@ -59,6 +74,212 @@ _render_callback(void *userData,
return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
+static int
+save_property_list (CFPropertyListRef propertyList, Glib::ustring path)
+
+{
+ CFDataRef xmlData;
+ int fd;
+
+ // Convert the property list into XML data.
+
+ xmlData = CFPropertyListCreateXMLData( kCFAllocatorDefault, propertyList);
+
+ if (!xmlData) {
+ error << _("Could not create XML version of property list") << endmsg;
+ return -1;
+ }
+
+ // Write the XML data to the file.
+
+ fd = open (path.c_str(), O_WRONLY|O_CREAT|O_EXCL, 0664);
+ while (fd < 0) {
+ if (errno == EEXIST) {
+ /* tell any UI's that this file already exists and ask them what to do */
+ bool overwrite = Plugin::PresetFileExists(); // EMIT SIGNAL
+ if (overwrite) {
+ fd = open (path.c_str(), O_WRONLY, 0664);
+ continue;
+ } else {
+ return 0;
+ }
+ }
+ error << string_compose (_("Cannot open preset file %1 (%2)"), path, strerror (errno)) << endmsg;
+ CFRelease (xmlData);
+ return -1;
+ }
+
+ size_t cnt = CFDataGetLength (xmlData);
+
+ if (write (fd, CFDataGetBytePtr (xmlData), cnt) != cnt) {
+ CFRelease (xmlData);
+ close (fd);
+ return -1;
+ }
+
+ close (fd);
+ return 0;
+}
+
+
+static CFPropertyListRef
+load_property_list (Glib::ustring path)
+{
+ int fd;
+ CFPropertyListRef propertyList;
+ CFDataRef xmlData;
+ CFStringRef errorString;
+
+ // Read the XML file.
+
+ if ((fd = open (path.c_str(), O_RDONLY)) < 0) {
+ return propertyList;
+
+ }
+
+ off_t len = lseek (fd, 0, SEEK_END);
+ char* buf = new char[len];
+ lseek (fd, 0, SEEK_SET);
+
+ if (read (fd, buf, len) != len) {
+ delete [] buf;
+ close (fd);
+ return propertyList;
+ }
+
+ close (fd);
+
+ xmlData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (UInt8*) buf, len, kCFAllocatorNull);
+
+ // Reconstitute the dictionary using the XML data.
+
+ propertyList = CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
+ xmlData,
+ kCFPropertyListImmutable,
+ &errorString);
+
+ CFRelease (xmlData);
+ delete [] buf;
+
+ return propertyList;
+}
+
+//-----------------------------------------------------------------------------
+static void
+set_preset_name_in_plist (CFPropertyListRef plist, string preset_name)
+{
+ if (!plist) {
+ return;
+ }
+ CFStringRef pn = CFStringCreateWithCString (kCFAllocatorDefault, preset_name.c_str(), kCFStringEncodingUTF8);
+
+ if (CFGetTypeID (plist) == CFDictionaryGetTypeID()) {
+ CFDictionarySetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey), pn);
+ }
+
+ CFRelease (pn);
+}
+
+//-----------------------------------------------------------------------------
+static std::string
+get_preset_name_in_plist (CFPropertyListRef plist)
+{
+ std::string ret;
+
+ if (!plist) {
+ return ret;
+ }
+
+ if (CFGetTypeID (plist) == CFDictionaryGetTypeID()) {
+ const void *p = CFDictionaryGetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey));
+ if (p) {
+ CFStringRef str = (CFStringRef) p;
+ int len = CFStringGetLength(str);
+ len = (len * 2) + 1;
+ char local_buffer[len];
+ if (CFStringGetCString (str, local_buffer, len, kCFStringEncodingUTF8)) {
+ ret = local_buffer;
+ }
+ }
+ }
+ return ret;
+}
+
+//--------------------------------------------------------------------------
+// general implementation for ComponentDescriptionsMatch() and ComponentDescriptionsMatch_Loosely()
+// if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
+Boolean ComponentDescriptionsMatch_General(const ComponentDescription * inComponentDescription1, const ComponentDescription * inComponentDescription2, Boolean inIgnoreType);
+Boolean ComponentDescriptionsMatch_General(const ComponentDescription * inComponentDescription1, const ComponentDescription * inComponentDescription2, Boolean inIgnoreType)
+{
+ if ( (inComponentDescription1 == NULL) || (inComponentDescription2 == NULL) )
+ return FALSE;
+
+ if ( (inComponentDescription1->componentSubType == inComponentDescription2->componentSubType)
+ && (inComponentDescription1->componentManufacturer == inComponentDescription2->componentManufacturer) )
+ {
+ // only sub-type and manufacturer IDs need to be equal
+ if (inIgnoreType)
+ return TRUE;
+ // type, sub-type, and manufacturer IDs all need to be equal in order to call this a match
+ else if (inComponentDescription1->componentType == inComponentDescription2->componentType)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//--------------------------------------------------------------------------
+// general implementation for ComponentAndDescriptionMatch() and ComponentAndDescriptionMatch_Loosely()
+// if inIgnoreType is true, then the type code is ignored in the ComponentDescriptions
+Boolean ComponentAndDescriptionMatch_General(Component inComponent, const ComponentDescription * inComponentDescription, Boolean inIgnoreType);
+Boolean ComponentAndDescriptionMatch_General(Component inComponent, const ComponentDescription * inComponentDescription, Boolean inIgnoreType)
+{
+ OSErr status;
+ ComponentDescription desc;
+
+ if ( (inComponent == NULL) || (inComponentDescription == NULL) )
+ return FALSE;
+
+ // get the ComponentDescription of the input Component
+ status = GetComponentInfo(inComponent, &desc, NULL, NULL, NULL);
+ if (status != noErr)
+ return FALSE;
+
+ // check if the Component's ComponentDescription matches the input ComponentDescription
+ return ComponentDescriptionsMatch_General(&desc, inComponentDescription, inIgnoreType);
+}
+
+//--------------------------------------------------------------------------
+// determine if 2 ComponentDescriptions are basically equal
+// (by that, I mean that the important identifying values are compared,
+// but not the ComponentDescription flags)
+Boolean ComponentDescriptionsMatch(const ComponentDescription * inComponentDescription1, const ComponentDescription * inComponentDescription2)
+{
+ return ComponentDescriptionsMatch_General(inComponentDescription1, inComponentDescription2, FALSE);
+}
+
+//--------------------------------------------------------------------------
+// determine if 2 ComponentDescriptions have matching sub-type and manufacturer codes
+Boolean ComponentDescriptionsMatch_Loose(const ComponentDescription * inComponentDescription1, const ComponentDescription * inComponentDescription2)
+{
+ return ComponentDescriptionsMatch_General(inComponentDescription1, inComponentDescription2, TRUE);
+}
+
+//--------------------------------------------------------------------------
+// determine if a ComponentDescription basically matches that of a particular Component
+Boolean ComponentAndDescriptionMatch(Component inComponent, const ComponentDescription * inComponentDescription)
+{
+ return ComponentAndDescriptionMatch_General(inComponent, inComponentDescription, FALSE);
+}
+
+//--------------------------------------------------------------------------
+// determine if a ComponentDescription matches only the sub-type and manufacturer codes of a particular Component
+Boolean ComponentAndDescriptionMatch_Loosely(Component inComponent, const ComponentDescription * inComponentDescription)
+{
+ return ComponentAndDescriptionMatch_General(inComponent, inComponentDescription, TRUE);
+}
+
+
AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAComponent> _comp)
: Plugin (engine, session),
comp (_comp),
@@ -70,6 +291,14 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAC
current_buffers (0),
frames_processed (0)
{
+ if (!preset_search_path_initialized) {
+ Glib::ustring p = Glib::get_home_dir();
+ p += "/Library/Audio/Presets:";
+ p += preset_search_path;
+ preset_search_path = p;
+ preset_search_path_initialized = true;
+ }
+
init ();
}
@@ -109,7 +338,6 @@ AUPlugin::init ()
err = CAAudioUnit::Open (*(comp.get()), *unit);
} catch (...) {
error << _("Exception thrown during AudioUnit plugin loading - plugin ignored") << endmsg;
- cerr << _("Exception thrown during AudioUnit plugin loading - plugin ignored") << endl;
throw failed_constructor();
}
@@ -762,33 +990,406 @@ AUPlugin::parameter_is_output (uint32_t) const
XMLNode&
AUPlugin::get_state()
{
- XMLNode *root = new XMLNode (state_node_name());
LocaleGuard lg (X_("POSIX"));
+ XMLNode *root = new XMLNode (state_node_name());
+
+#ifdef AU_STATE_SUPPORT
+ CFDataRef xmlData;
+ CFPropertyListRef propertyList;
+
+ if (unit->GetAUPreset (propertyList) != noErr) {
+ return *root;
+ }
+
+ // Convert the property list into XML data.
+
+ xmlData = CFPropertyListCreateXMLData( kCFAllocatorDefault, propertyList);
+
+ if (!xmlData) {
+ error << _("Could not create XML version of property list") << endmsg;
+ return *root;
+ }
+
+ /* re-parse XML bytes to create a libxml++ XMLTree that we can merge into
+ our state node. GACK!
+ */
+
+ XMLTree t;
+
+ if (t.read_buffer (string ((const char*) CFDataGetBytePtr (xmlData), CFDataGetLength (xmlData)))) {
+ if (t.root()) {
+ root->add_child_copy (*t.root());
+ }
+ }
+
+ CFRelease (xmlData);
+ CFRelease (propertyList);
+#else
+ if (!seen_get_state_message) {
+ info << _("Saving AudioUnit settings is not supported in this build of Ardour. Consider paying for a newer version")
+ << endmsg;
+ seen_get_state_message = true;
+ }
+#endif
+
return *root;
}
int
AUPlugin::set_state(const XMLNode& node)
{
- return -1;
+#ifdef AU_STATE_SUPPORT
+ int ret = -1;
+ CFPropertyListRef propertyList;
+ LocaleGuard lg (X_("POSIX"));
+
+ if (node.name() != state_node_name()) {
+ error << _("Bad node sent to AUPlugin::set_state") << endmsg;
+ return -1;
+ }
+
+ if (node.children().empty()) {
+ return -1;
+ }
+
+ XMLNode* top = node.children().front();
+ XMLNode* copy = new XMLNode (*top);
+
+ XMLTree t;
+ t.set_root (copy);
+
+ const string& xml = t.write_buffer ();
+ CFDataRef xmlData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (UInt8*) xml.data(), xml.length(), kCFAllocatorNull);
+ CFStringRef errorString;
+
+ propertyList = CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
+ xmlData,
+ kCFPropertyListImmutable,
+ &errorString);
+
+ CFRelease (xmlData);
+
+ if (propertyList) {
+ if (unit->SetAUPreset (propertyList) == noErr) {
+ ret = 0;
+ }
+ CFRelease (propertyList);
+ }
+
+ return ret;
+#else
+ if (!seen_set_state_message) {
+ info << _("Restoring AudioUnit settings is not supported in this build of Ardour. Consider paying for a newer version")
+ << endmsg;
+ }
+ return 0;
+#endif
}
bool
-AUPlugin::save_preset (string name)
+AUPlugin::load_preset (const string preset_label)
{
- return false;
+#ifdef AU_STATE_SUPPORT
+ bool ret = false;
+ CFPropertyListRef propertyList;
+ Glib::ustring path;
+ PresetMap::iterator x = preset_map.find (preset_label);
+
+ if (x == preset_map.end()) {
+ return false;
+ }
+
+ if ((propertyList = load_property_list (x->second)) != 0) {
+ if (unit->SetAUPreset (propertyList) == noErr) {
+ ret = true;
+ }
+ CFRelease(propertyList);
+ }
+
+ return ret;
+#else
+ if (!seen_loading_message) {
+ info << _("Loading AudioUnit presets is not supported in this build of Ardour. Consider paying for a newer version")
+ << endmsg;
+ seen_loading_message = true;
+ }
+ return true;
+#endif
}
bool
-AUPlugin::load_preset (const string preset_label)
+AUPlugin::save_preset (string preset_name)
{
+#ifdef AU_STATE_SUPPORT
+ CFPropertyListRef propertyList;
+ vector<Glib::ustring> v;
+ Glib::ustring user_preset_path;
+ bool ret = true;
+
+ std::string m = maker();
+ std::string n = name();
+
+ strip_whitespace_edges (m);
+ strip_whitespace_edges (n);
+
+ v.push_back (Glib::get_home_dir());
+ v.push_back ("Library");
+ v.push_back ("Audio");
+ v.push_back ("Presets");
+ v.push_back (m);
+ v.push_back (n);
+
+ user_preset_path = Glib::build_filename (v);
+
+ if (g_mkdir_with_parents (user_preset_path.c_str(), 0775) < 0) {
+ error << string_compose (_("Cannot create user plugin presets folder (%1)"), user_preset_path) << endmsg;
+ return false;
+ }
+
+ if (unit->GetAUPreset (propertyList) != noErr) {
+ return false;
+ }
+
+ // add the actual preset name */
+
+ v.push_back (preset_name + preset_suffix);
+
+ // rebuild
+
+ user_preset_path = Glib::build_filename (v);
+
+ set_preset_name_in_plist (propertyList, preset_name);
+
+ if (save_property_list (propertyList, user_preset_path)) {
+ error << string_compose (_("Saving plugin state to %1 failed"), user_preset_path) << endmsg;
+ ret = false;
+ }
+
+ CFRelease(propertyList);
+
+ return ret;
+#else
+ if (!seen_saving_message) {
+ info << _("Saving AudioUnit presets is not supported in this build of Ardour. Consider paying for a newer version")
+ << endmsg;
+ seen_saving_message = true;
+ }
return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// this is just a little helper function used by GetAUComponentDescriptionFromPresetFile()
+static SInt32
+GetDictionarySInt32Value(CFDictionaryRef inAUStateDictionary, CFStringRef inDictionaryKey, Boolean * outSuccess)
+{
+ CFNumberRef cfNumber;
+ SInt32 numberValue = 0;
+ Boolean dummySuccess;
+
+ if (outSuccess == NULL)
+ outSuccess = &dummySuccess;
+ if ( (inAUStateDictionary == NULL) || (inDictionaryKey == NULL) )
+ {
+ *outSuccess = FALSE;
+ return 0;
+ }
+
+ cfNumber = (CFNumberRef) CFDictionaryGetValue(inAUStateDictionary, inDictionaryKey);
+ if (cfNumber == NULL)
+ {
+ *outSuccess = FALSE;
+ return 0;
+ }
+ *outSuccess = CFNumberGetValue(cfNumber, kCFNumberSInt32Type, &numberValue);
+ if (*outSuccess)
+ return numberValue;
+ else
+ return 0;
+}
+
+static OSStatus
+GetAUComponentDescriptionFromStateData(CFPropertyListRef inAUStateData, ComponentDescription * outComponentDescription)
+{
+ CFDictionaryRef auStateDictionary;
+ ComponentDescription tempDesc = {0};
+ SInt32 versionValue;
+ Boolean gotValue;
+
+ if ( (inAUStateData == NULL) || (outComponentDescription == NULL) )
+ return paramErr;
+
+ // the property list for AU state data must be of the dictionary type
+ if (CFGetTypeID(inAUStateData) != CFDictionaryGetTypeID()) {
+ return kAudioUnitErr_InvalidPropertyValue;
+ }
+
+ auStateDictionary = (CFDictionaryRef)inAUStateData;
+
+ // first check to make sure that the version of the AU state data is one that we know understand
+ // XXX should I really do this? later versions would probably still hold these ID keys, right?
+ versionValue = GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetVersionKey), &gotValue);
+
+ if (!gotValue) {
+ return kAudioUnitErr_InvalidPropertyValue;
+ }
+#define kCurrentSavedStateVersion 0
+ if (versionValue != kCurrentSavedStateVersion) {
+ return kAudioUnitErr_InvalidPropertyValue;
+ }
+
+ // grab the ComponentDescription values from the AU state data
+ tempDesc.componentType = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetTypeKey), NULL);
+ tempDesc.componentSubType = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetSubtypeKey), NULL);
+ tempDesc.componentManufacturer = (OSType) GetDictionarySInt32Value(auStateDictionary, CFSTR(kAUPresetManufacturerKey), NULL);
+ // zero values are illegit for specific ComponentDescriptions, so zero for any value means that there was an error
+ if ( (tempDesc.componentType == 0) || (tempDesc.componentSubType == 0) || (tempDesc.componentManufacturer == 0) )
+ return kAudioUnitErr_InvalidPropertyValue;
+
+ *outComponentDescription = tempDesc;
+ return noErr;
+}
+
+
+static bool au_preset_filter (const string& str, void* arg)
+{
+ /* Not a dotfile, has a prefix before a period, suffix is aupreset */
+
+ bool ret;
+
+ ret = (str[0] != '.' && str.length() > 9 && str.find (preset_suffix) == (str.length() - preset_suffix.length()));
+
+ if (ret && arg) {
+
+ /* check the preset file path name against this plugin
+ ID. The idea is that all preset files for this plugin
+ include "<manufacturer>/<plugin-name>" in their path.
+ */
+
+ Plugin* p = (Plugin *) arg;
+ string match = p->maker();
+ match += '/';
+ match += p->name();
+
+ ret = str.find (match) != string::npos;
+
+ if (ret == false) {
+ string m = p->maker ();
+ string n = p->name ();
+ strip_whitespace_edges (m);
+ strip_whitespace_edges (n);
+ match = m;
+ match += '/';
+ match += n;
+
+ ret = str.find (match) != string::npos;
+ }
+ }
+
+ return ret;
+}
+
+bool
+check_and_get_preset_name (Component component, const string& pathstr, string& preset_name)
+{
+ OSStatus status;
+ CFPropertyListRef plist;
+ ComponentDescription presetDesc;
+ bool ret = false;
+
+ plist = load_property_list (pathstr);
+
+ if (!plist) {
+ return ret;
+ }
+
+ // get the ComponentDescription from the AU preset file
+
+ status = GetAUComponentDescriptionFromStateData(plist, &presetDesc);
+
+ if (status == noErr) {
+ if (ComponentAndDescriptionMatch_Loosely(component, &presetDesc)) {
+
+ /* try to get the preset name from the property list */
+
+ if (CFGetTypeID(plist) == CFDictionaryGetTypeID()) {
+
+ const void* psk = CFDictionaryGetValue ((CFMutableDictionaryRef)plist, CFSTR(kAUPresetNameKey));
+
+ if (psk) {
+
+ const char* p = CFStringGetCStringPtr ((CFStringRef) psk, kCFStringEncodingUTF8);
+
+ if (!p) {
+ char buf[PATH_MAX+1];
+
+ if (CFStringGetCString ((CFStringRef)psk, buf, sizeof (buf), kCFStringEncodingUTF8)) {
+ preset_name = buf;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ CFRelease (plist);
+
+ return true;
+}
+
+std::string
+AUPlugin::current_preset() const
+{
+ string preset_name;
+
+#ifdef AU_STATE_SUPPORT
+ CFPropertyListRef propertyList;
+
+ if (unit->GetAUPreset (propertyList) == noErr) {
+ preset_name = get_preset_name_in_plist (propertyList);
+ CFRelease(propertyList);
+ }
+#endif
+ return preset_name;
}
vector<string>
AUPlugin::get_presets ()
{
+ vector<string*>* preset_files;
vector<string> presets;
+ PathScanner scanner;
+
+ preset_files = scanner (preset_search_path, au_preset_filter, this, true, true, -1, true);
+
+ if (!preset_files) {
+ return presets;
+ }
+
+ for (vector<string*>::iterator x = preset_files->begin(); x != preset_files->end(); ++x) {
+
+ string path = *(*x);
+ string preset_name;
+
+ /* make an initial guess at the preset name using the path */
+
+ preset_name = Glib::path_get_basename (path);
+ preset_name = preset_name.substr (0, preset_name.find_last_of ('.'));
+
+ /* check that this preset file really matches this plugin
+ and potentially get the "real" preset name from
+ within the file.
+ */
+
+ if (check_and_get_preset_name (get_comp()->Comp(), path, preset_name)) {
+ presets.push_back (preset_name);
+ preset_map[preset_name] = path;
+ }
+
+ delete *x;
+ }
+
+ delete preset_files;
return presets;
}
diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc
index 9fd1707176..79f17ff8ca 100644
--- a/libs/ardour/plugin.cc
+++ b/libs/ardour/plugin.cc
@@ -58,6 +58,8 @@
using namespace ARDOUR;
using namespace PBD;
+sigc::signal<bool> Plugin::PresetFileExists;
+
Plugin::Plugin (AudioEngine& e, Session& s)
: _engine (e), _session (s)
{
diff --git a/libs/pbd/pathscanner.cc b/libs/pbd/pathscanner.cc
index 1a9cb985c1..8f37477147 100644
--- a/libs/pbd/pathscanner.cc
+++ b/libs/pbd/pathscanner.cc
@@ -23,6 +23,8 @@
#include <cstring>
#include <vector>
#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <pbd/error.h>
#include <pbd/pathscanner.h>
@@ -33,7 +35,7 @@ using namespace PBD;
vector<string *> *
PathScanner::operator() (const string &dirpath, const string &regexp,
bool match_fullpath, bool return_fullpath,
- long limit)
+ long limit, bool recurse)
{
int err;
@@ -58,7 +60,7 @@ PathScanner::operator() (const string &dirpath, const string &regexp,
0,
match_fullpath,
return_fullpath,
- limit);
+ limit, recurse);
}
vector<string *> *
@@ -67,10 +69,22 @@ PathScanner::run_scan (const string &dirpath,
bool (*filter)(const string &, void *),
void *arg,
bool match_fullpath, bool return_fullpath,
- long limit)
-
+ long limit,
+ bool recurse)
+{
+ return run_scan_internal ((vector<string*>*) 0, dirpath, memberfilter, filter, arg, match_fullpath, return_fullpath, limit, recurse);
+}
+
+vector<string *> *
+PathScanner::run_scan_internal (vector<string *> *result,
+ const string &dirpath,
+ bool (PathScanner::*memberfilter)(const string &),
+ bool (*filter)(const string &, void *),
+ void *arg,
+ bool match_fullpath, bool return_fullpath,
+ long limit,
+ bool recurse)
{
- vector<string *> *result = 0;
DIR *dir;
struct dirent *finfo;
char *pathcopy = strdup (dirpath.c_str());
@@ -86,7 +100,9 @@ PathScanner::run_scan (const string &dirpath,
return 0;
}
- result = new vector<string *>;
+ if (result == 0) {
+ result = new vector<string *>;
+ }
do {
@@ -96,37 +112,51 @@ PathScanner::run_scan (const string &dirpath,
while ((finfo = readdir (dir)) != 0) {
+ if ((finfo->d_name[0] == '.' && finfo->d_name[1] == '\0') ||
+ (finfo->d_name[0] == '.' && finfo->d_name[1] == '.' && finfo->d_name[2] == '\0')) {
+ continue;
+ }
+
snprintf (fullpath, sizeof(fullpath), "%s/%s",
thisdir, finfo->d_name);
- if (match_fullpath) {
- search_str = fullpath;
- } else {
- search_str = finfo->d_name;
+ struct stat statbuf;
+ if (stat (fullpath, &statbuf) < 0) {
+ continue;
}
- /* handle either type of function ptr */
-
- if (memberfilter) {
- if (!(this->*memberfilter)(search_str)) {
- continue;
- }
+ if (statbuf.st_mode & S_IFDIR && recurse) {
+ run_scan_internal (result, fullpath, memberfilter, filter, arg, match_fullpath, return_fullpath, limit, recurse);
} else {
- if (!filter(search_str, arg)) {
- continue;
+
+ if (match_fullpath) {
+ search_str = fullpath;
+ } else {
+ search_str = finfo->d_name;
}
+
+ /* handle either type of function ptr */
+
+ if (memberfilter) {
+ if (!(this->*memberfilter)(search_str)) {
+ continue;
+ }
+ } else {
+ if (!filter(search_str, arg)) {
+ continue;
+ }
+ }
+
+ if (return_fullpath) {
+ newstr = new string (fullpath);
+ } else {
+ newstr = new string (finfo->d_name);
+ }
+
+ result->push_back (newstr);
+ nfound++;
}
-
- if (return_fullpath) {
- newstr = new string (fullpath);
- } else {
- newstr = new string (finfo->d_name);
- }
-
- result->push_back (newstr);
- nfound++;
}
-
closedir (dir);
} while ((limit < 0 || (nfound < limit)) && (thisdir = strtok (0, ":")));
diff --git a/libs/pbd/pbd/pathscanner.h b/libs/pbd/pbd/pathscanner.h
index 0a48d4d949..550aab163a 100644
--- a/libs/pbd/pbd/pathscanner.h
+++ b/libs/pbd/pbd/pathscanner.h
@@ -31,55 +31,65 @@ class PathScanner
{
public:
- vector<string *> *operator() (const string &dirpath,
- bool (*filter)(const string &, void *arg),
- void *arg,
- bool match_fullpath = true,
- bool return_fullpath = true,
- long limit = -1) {
- return run_scan (dirpath,
- (bool (PathScanner::*)(const string &)) 0,
- filter,
- arg,
- match_fullpath,
- return_fullpath,
- limit);
- }
-
- vector<string *> *operator() (const string &dirpath,
- const string &regexp,
- bool match_fullpath = true,
- bool return_fullpath = true,
- long limit = -1);
-
+ vector<string *> *operator() (const string &dirpath,
+ bool (*filter)(const string &, void *arg),
+ void *arg,
+ bool match_fullpath = true,
+ bool return_fullpath = true,
+ long limit = -1,
+ bool recurse = false) {
+ return run_scan (dirpath,
+ (bool (PathScanner::*)(const string &)) 0,
+ filter,
+ arg,
+ match_fullpath,
+ return_fullpath,
+ limit, recurse);
+ }
+
+ vector<string *> *operator() (const string &dirpath,
+ const string &regexp,
+ bool match_fullpath = true,
+ bool return_fullpath = true,
+ long limit = -1,
+ bool recurse = false);
- string *find_first (const string &dirpath,
- const string &regexp,
- bool match_fullpath = true,
- bool return_fullpath = true);
-
- string *find_first (const string &dirpath,
- bool (*filter)(const string &, void *),
- void *arg,
- bool match_fullpath = true,
- bool return_fullpath = true);
-
+ string *find_first (const string &dirpath,
+ const string &regexp,
+ bool match_fullpath = true,
+ bool return_fullpath = true);
+
+ string *find_first (const string &dirpath,
+ bool (*filter)(const string &, void *),
+ void *arg,
+ bool match_fullpath = true,
+ bool return_fullpath = true);
+
private:
- regex_t compiled_pattern;
-
- bool regexp_filter (const string &str) {
- return regexec (&compiled_pattern, str.c_str(), 0, 0, 0) == 0;
- }
-
- vector<string *> *run_scan (const string &dirpath,
- bool (PathScanner::*mfilter) (const string &),
- bool (*filter)(const string &, void *),
- void *arg,
- bool match_fullpath,
- bool return_fullpath,
- long limit);
-
-
+ regex_t compiled_pattern;
+
+ bool regexp_filter (const string &str) {
+ return regexec (&compiled_pattern, str.c_str(), 0, 0, 0) == 0;
+ }
+
+ vector<string *> *run_scan (const string &dirpath,
+ bool (PathScanner::*mfilter) (const string &),
+ bool (*filter)(const string &, void *),
+ void *arg,
+ bool match_fullpath,
+ bool return_fullpath,
+ long limit,
+ bool recurse = false);
+
+ vector<string *> *run_scan_internal (vector<string*>*,
+ const string &dirpath,
+ bool (PathScanner::*mfilter) (const string &),
+ bool (*filter)(const string &, void *),
+ void *arg,
+ bool match_fullpath,
+ bool return_fullpath,
+ long limit,
+ bool recurse = false);
};
#endif // __libmisc_pathscanner_h__