summaryrefslogtreecommitdiff
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
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
-rw-r--r--SConstruct9
-rw-r--r--gtk2_ardour/ardour_ui.cc42
-rw-r--r--gtk2_ardour/ardour_ui.h2
-rw-r--r--gtk2_ardour/au_pluginui.mm3
-rw-r--r--gtk2_ardour/plugin_ui.cc31
-rw-r--r--gtk2_ardour/plugin_ui.h3
-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
-rwxr-xr-xtools/osx_packaging/osx_build4
14 files changed, 821 insertions, 99 deletions
diff --git a/SConstruct b/SConstruct
index e7df3c76b6..1565768c0b 100644
--- a/SConstruct
+++ b/SConstruct
@@ -59,8 +59,8 @@ opts.AddOptions(
BoolOption('GPROFILE', 'Compile with support for gprofile (Developers only)', 0),
BoolOption('FREEDESKTOP', 'Install MIME type, icons and .desktop file as per the freedesktop.org spec (requires xdg-utils and shared-mime-info). "scons uninstall" removes associations in desktop database', 0),
BoolOption('TRANZPORT', 'Compile with support for Frontier Designs (if libusb is available)', 1),
- BoolOption('AUBIO', "Use Paul Brossier's aubio library for feature detection (if available)", 1)
-
+ BoolOption('AUBIO', "Use Paul Brossier's aubio library for feature detection (if available)", 1),
+ BoolOption('AUSTATE', "Build with support for AU settings & presets saving/loading", 0)
)
#----------------------------------------------------------------------
@@ -851,10 +851,7 @@ def prep_libcheck(topenv, libinfo):
GTKROOT = os.path.expanduser ('~/gtk/inst')
libinfo.Append(CPPPATH= GTKROOT + "/include", LIBPATH= GTKROOT + "/lib")
libinfo.Append(CXXFLAGS="-I" + GTKROOT + "/include", LINKFLAGS="-L" + GTKROOT + "/lib")
-
-
-
-
+
prep_libcheck(env, env)
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index 32088800df..90b3d65735 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -58,6 +58,7 @@
#include <ardour/audioengine.h>
#include <ardour/playlist.h>
#include <ardour/utils.h>
+#include <ardour/plugin.h>
#include <ardour/audio_diskstream.h>
#include <ardour/audiofilesource.h>
#include <ardour/recent_sessions.h>
@@ -225,6 +226,8 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
ARDOUR::Diskstream::DiskOverrun.connect (mem_fun(*this, &ARDOUR_UI::disk_overrun_handler));
ARDOUR::Diskstream::DiskUnderrun.connect (mem_fun(*this, &ARDOUR_UI::disk_underrun_handler));
+ ARDOUR::Plugin::PresetFileExists.connect (mem_fun(*this, &ARDOUR_UI::preset_file_exists_handler));
+
/* handle dialog requests */
ARDOUR::Session::Dialog.connect (mem_fun(*this, &ARDOUR_UI::session_dialog));
@@ -2961,6 +2964,45 @@ ARDOUR_UI::xrun_handler(nframes_t where)
}
}
+bool
+ARDOUR_UI::preset_file_exists_handler ()
+{
+ /* if driven from another thread, say "do not overwrite" and show the user nothing.
+ */
+
+ if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) { \
+ return false;
+ }
+
+ HBox* hbox = new HBox();
+ Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG);
+ Gtk::Dialog dialog (_("Preset Exists"), true, false);
+ Label message (_("\
+A preset with this name already exists for this plugin.\n\
+\n\
+What you would like to do?\n"));
+ image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
+ hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
+ hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
+ dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
+ dialog.add_button (_("Overwrite the existing preset"), RESPONSE_ACCEPT);
+ dialog.add_button (_("Leave the existing preset alone"), RESPONSE_REJECT);
+ dialog.set_default_response (RESPONSE_ACCEPT);
+ dialog.set_position (WIN_POS_MOUSE);
+ dialog.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY); // need to make it float above the preset name dialog
+
+ message.show();
+ image->show();
+ hbox->show();
+
+ switch (dialog.run ()) {
+ case RESPONSE_ACCEPT:
+ return true;
+ default:
+ return false;
+ }
+}
+
void
ARDOUR_UI::disk_overrun_handler ()
{
diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h
index 204e52c34c..a0c84a18d8 100644
--- a/gtk2_ardour/ardour_ui.h
+++ b/gtk2_ardour/ardour_ui.h
@@ -659,6 +659,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI
void disk_speed_dialog_gone (int ignored_response, Gtk::MessageDialog*);
void disk_overrun_handler ();
void disk_underrun_handler ();
+
+ bool preset_file_exists_handler ();
void session_dialog (std::string);
int pending_state_dialog ();
diff --git a/gtk2_ardour/au_pluginui.mm b/gtk2_ardour/au_pluginui.mm
index a46021f66f..205364b343 100644
--- a/gtk2_ardour/au_pluginui.mm
+++ b/gtk2_ardour/au_pluginui.mm
@@ -67,7 +67,8 @@ AUPluginUI::AUPluginUI (boost::shared_ptr<PluginInsert> insert)
HBox* smaller_hbox = manage (new HBox);
- smaller_hbox->pack_start (preset_label, false, false, 10);
+ smaller_hbox->set_spacing (6);
+ smaller_hbox->pack_start (preset_label, false, false, 4);
smaller_hbox->pack_start (preset_combo, false, false);
smaller_hbox->pack_start (save_button, false, false);
smaller_hbox->pack_start (automation_mode_label, false, false);
diff --git a/gtk2_ardour/plugin_ui.cc b/gtk2_ardour/plugin_ui.cc
index a833ef9c48..e04d360052 100644
--- a/gtk2_ardour/plugin_ui.cc
+++ b/gtk2_ardour/plugin_ui.cc
@@ -325,14 +325,15 @@ PluginUIWindow::plugin_going_away ()
PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
: insert (pi),
plugin (insert->plugin()),
- save_button(_("Add")),
+ save_button(_("Save")),
bypass_button (_("Bypass"))
{
//preset_combo.set_use_arrows_always(true);
- set_popdown_strings (preset_combo, plugin->get_presets());
preset_combo.set_size_request (100, -1);
- preset_combo.set_active_text ("");
+ update_presets ();
+
preset_combo.signal_changed().connect(mem_fun(*this, &PlugUIBase::setting_selected));
+ no_load_preset = false;
save_button.set_name ("PluginSaveButton");
save_button.signal_clicked().connect(mem_fun(*this, &PlugUIBase::save_plugin_setting));
@@ -367,6 +368,10 @@ PlugUIBase::redirect_active_changed (Redirect* r, void* src)
void
PlugUIBase::setting_selected()
{
+ if (no_load_preset) {
+ return;
+ }
+
if (preset_combo.get_active_text().length() > 0) {
if (!plugin->load_preset(preset_combo.get_active_text())) {
warning << string_compose(_("Plugin preset %1 not found"), preset_combo.get_active_text()) << endmsg;
@@ -393,9 +398,16 @@ PlugUIBase::save_plugin_setting ()
prompter.get_result(name);
if (name.length()) {
- if(plugin->save_preset(name)){
+ if (plugin->save_preset(name)) {
+
+ /* a rather inefficient way to add the newly saved preset
+ to the list.
+ */
+
set_popdown_strings (preset_combo, plugin->get_presets());
+ no_load_preset = true;
preset_combo.set_active_text (name);
+ no_load_preset = false;
}
}
break;
@@ -435,5 +447,16 @@ PlugUIBase::focus_toggled (GdkEventButton* ev)
void
PlugUIBase::update_presets ()
{
+ vector<string> presets = plugin->get_presets();
set_popdown_strings (preset_combo, plugin->get_presets());
+
+ string current_preset = plugin->current_preset();
+
+ if (!current_preset.empty()) {
+ for (vector<string>::iterator p = presets.begin(); p != presets.end(); ++p) {
+ if (*p == current_preset) {
+ preset_combo.set_active_text (current_preset);
+ }
+ }
+ }
}
diff --git a/gtk2_ardour/plugin_ui.h b/gtk2_ardour/plugin_ui.h
index c308a54095..95110a9d61 100644
--- a/gtk2_ardour/plugin_ui.h
+++ b/gtk2_ardour/plugin_ui.h
@@ -87,9 +87,10 @@ class PlugUIBase : public virtual sigc::trackable
Gtk::Button save_button;
Gtk::ToggleButton bypass_button;
Gtk::EventBox focus_button;
-
+
Gtk::Image* focus_out_image;
Gtk::Image* focus_in_image;
+ bool no_load_preset;
void setting_selected();
void save_plugin_setting (void);
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__
diff --git a/tools/osx_packaging/osx_build b/tools/osx_packaging/osx_build
index 3e77194e6c..1e5cd9bcfc 100755
--- a/tools/osx_packaging/osx_build
+++ b/tools/osx_packaging/osx_build
@@ -322,7 +322,7 @@ done
echo "Fixing up library names ..."
# now do the same for all the libraries we include
-for dylib in $Frameworks/*.dylib $Frameworks/modules/*.so $Surfaces/*.dylib ; do
+for dylib in $Frameworks/*.so $Frameworks/*.dylib $Frameworks/modules/*.so $Surfaces/*.dylib ; do
# skip symlinks
if test ! -L $dylib ; then
@@ -360,7 +360,7 @@ if [ x$SAE != x ] ; then
echo "Creating SAE packaging directory"
rm -rf $SAE_PKG_DIR
mkdir $SAE_PKG_DIR
- mv Ardour2.app $SAE_PKG_DIR
+ mv Ardour2.app $SAE_PKG_DIR/Ardour2-SAE.app
cp HowToInstallArdourSAE.pdf "$SAE_PKG_DIR/How To Install Ardour SAE.pdf"
cp SAE-de-keypad.pdf "$SAE_PKG_DIR/Ardour SAE Shortcuts (keypad).pdf"
cp SAE-de-nokeypad.pdf "$SAE_PKG_DIR/Ardour SAE Shortcuts.pdf"