summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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"