diff options
-rw-r--r-- | SConstruct | 9 | ||||
-rw-r--r-- | gtk2_ardour/ardour_ui.cc | 42 | ||||
-rw-r--r-- | gtk2_ardour/ardour_ui.h | 2 | ||||
-rw-r--r-- | gtk2_ardour/au_pluginui.mm | 3 | ||||
-rw-r--r-- | gtk2_ardour/plugin_ui.cc | 31 | ||||
-rw-r--r-- | gtk2_ardour/plugin_ui.h | 3 | ||||
-rw-r--r-- | libs/ardour/SConscript | 3 | ||||
-rw-r--r-- | libs/ardour/ardour/audio_unit.h | 15 | ||||
-rw-r--r-- | libs/ardour/ardour/plugin.h | 3 | ||||
-rw-r--r-- | libs/ardour/audio_unit.cc | 613 | ||||
-rw-r--r-- | libs/ardour/plugin.cc | 2 | ||||
-rw-r--r-- | libs/pbd/pathscanner.cc | 86 | ||||
-rw-r--r-- | libs/pbd/pbd/pathscanner.h | 104 | ||||
-rwxr-xr-x | tools/osx_packaging/osx_build | 4 |
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 ®exp, 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 ®exp, 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 ®exp, - 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 ®exp, + bool match_fullpath = true, + bool return_fullpath = true, + long limit = -1, + bool recurse = false); - string *find_first (const string &dirpath, - const string ®exp, - 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 ®exp, + 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" |