refactor MIDISceneChange color property addition by moving it into SceneChange to...
[ardour.git] / libs / ardour / audio_unit.cc
index 9151e76bec5f93eb7c8adca011f6296651ddacfb..add2407f841cfc87f9602aed7f4911c934fc3e64 100644 (file)
 #include "pbd/xml++.h"
 #include "pbd/convert.h"
 #include "pbd/whitespace.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
 #include "pbd/locale_guard.h"
 
-#include <glibmm/thread.h>
+#include <glibmm/threads.h>
 #include <glibmm/fileutils.h>
 #include <glibmm/miscutils.h>
+#include <glib/gstdio.h>
 
 #include "ardour/ardour.h"
 #include "ardour/audioengine.h"
@@ -43,6 +44,7 @@
 #include "ardour/filesystem_paths.h"
 #include "ardour/io.h"
 #include "ardour/audio_unit.h"
+#include "ardour/route.h"
 #include "ardour/session.h"
 #include "ardour/tempo.h"
 #include "ardour/utils.h"
@@ -64,13 +66,6 @@ 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";
@@ -783,6 +778,10 @@ AUPlugin::set_parameter (uint32_t which, float val)
                return;
        }
 
+       if (get_parameter(which) == val) {
+               return;
+       }
+
        const AUParameterDescriptor& d (descriptors[which]);
        DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("set parameter %1 in scope %2 element %3 to %4\n", d.id, d.scope, d.element, val));
        unit->SetParameter (d.id, d.scope, d.element, val);
@@ -809,7 +808,7 @@ AUPlugin::get_parameter (uint32_t which) const
        float val = 0.0;
        if (which < descriptors.size()) {
                const AUParameterDescriptor& d (descriptors[which]);
-               DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("get value of parameter %1 in scope %2 element %3\n", d.id, d.scope, d.element));
+               // DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("get value of parameter %1 in scope %2 element %3\n", d.id, d.scope, d.element));
                unit->GetParameter(d.id, d.scope, d.element, val);
        }
        return val;
@@ -1000,7 +999,7 @@ AUPlugin::output_streams() const
 }
 
 bool
-AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out) const
+AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out)
 {
        // Note: We never attempt to multiply-instantiate plugins to meet io configurations.
 
@@ -1079,8 +1078,21 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out) con
                                audio_out = 2;
                                found = true;
                        } else if (possible_out < -2) {
-                               /* explicitly variable number of outputs, pick maximum */
-                               audio_out = -possible_out;
+                               /* explicitly variable number of outputs. 
+
+                                   Since Ardour can handle any configuration,
+                                   we have to somehow pick a number. 
+
+                                   We'll use the number of inputs
+                                   to the master bus, or 2 if there
+                                   is no master bus.
+                                */
+                                boost::shared_ptr<Route> master = _session.master_out();
+                                if (master) {
+                                        audio_out = master->input()->n_ports().n_audio();
+                                } else {
+                                        audio_out = 2;
+                                }
                                found = true;
                        } else {
                                /* exact number of outputs */
@@ -1143,6 +1155,7 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out) con
                                /* request is too large */
                        }
 
+
                        if (possible_out == -1) {
                                /* any output configuration possible, provide stereo out */
                                audio_out = 2;
@@ -1154,8 +1167,21 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out) con
                                audio_out = 2;
                                found = true;
                        } else if (possible_out < -2) {
-                               /* explicitly variable number of outputs, pick maximum */
-                               audio_out = -possible_out;
+                               /* explicitly variable number of outputs. 
+
+                                   Since Ardour can handle any configuration,
+                                   we have to somehow pick a number. 
+
+                                   We'll use the number of inputs
+                                   to the master bus, or 2 if there
+                                   is no master bus.
+                                */
+                                boost::shared_ptr<Route> master = _session.master_out();
+                                if (master) {
+                                        audio_out = master->input()->n_ports().n_audio();
+                                } else {
+                                        audio_out = 2;
+                                }
                                found = true;
                        } else {
                                /* exact number of outputs */
@@ -1267,8 +1293,8 @@ AUPlugin::render_callback(AudioUnitRenderActionFlags*,
 {
        /* not much to do with audio - the data is already in the buffers given to us in connect_and_run() */
 
-       DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1: render callback, frames %2 bufs %3\n",
-                                                       name(), inNumberFrames, ioData->mNumberBuffers));
+       // DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1: render callback, frames %2 bufs %3\n",
+        // name(), inNumberFrames, ioData->mNumberBuffers));
 
        if (input_maxbuf == 0) {
                error << _("AUPlugin: render callback called illegally!") << endmsg;
@@ -1329,8 +1355,10 @@ AUPlugin::connect_and_run (BufferSet& bufs, ChanMapping in_map, ChanMapping out_
                buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample);
                /* setting this to 0 indicates to the AU that it can provide buffers here
                   if necessary. if it can process in-place, it will use the buffers provided
-                  as input by ::render_callback() above. no documentation on what setting it
-                  to a non-null value means.
+                  as input by ::render_callback() above. 
+                   
+                   a non-null values tells the plugin to render into the buffer pointed
+                   at by the value.
                */
                buffers->mBuffers[i].mData = 0;
        }
@@ -1365,8 +1393,8 @@ AUPlugin::connect_and_run (BufferSet& bufs, ChanMapping in_map, ChanMapping out_
        ts.mSampleTime = frames_processed;
        ts.mFlags = kAudioTimeStampSampleTimeValid;
 
-       // DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 render flags=%2 time=%3 nframes=%4 buffers=%5\n",
-       // name(), flags, frames_processed, nframes, buffers->mNumberBuffers));
+       DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 render flags=%2 time=%3 nframes=%4 buffers=%5\n",
+                                                        name(), flags, frames_processed, nframes, buffers->mNumberBuffers));
 
        if ((err = unit->Render (&flags, &ts, 0, nframes, buffers)) == noErr) {
 
@@ -1645,8 +1673,6 @@ void
 AUPlugin::add_state (XMLNode* root) const
 {
        LocaleGuard lg (X_("POSIX"));
-
-#ifdef AU_STATE_SUPPORT
        CFDataRef xmlData;
        CFPropertyListRef propertyList;
 
@@ -1678,20 +1704,11 @@ AUPlugin::add_state (XMLNode* root) const
 
        CFRelease (xmlData);
        CFRelease (propertyList);
-#else
-       if (!seen_get_state_message) {
-               info << string_compose (_("Saving AudioUnit settings is not supported in this build of %1. Consider paying for a newer version"),
-                                       PROGRAM_NAME)
-                    << endmsg;
-               seen_get_state_message = true;
-       }
-#endif
 }
 
 int
 AUPlugin::set_state(const XMLNode& node, int version)
 {
-#ifdef AU_STATE_SUPPORT
        int ret = -1;
        CFPropertyListRef propertyList;
        LocaleGuard lg (X_("POSIX"));
@@ -1701,6 +1718,7 @@ AUPlugin::set_state(const XMLNode& node, int version)
                return -1;
        }
 
+#ifndef NO_PLUGIN_STATE
        if (node.children().empty()) {
                return -1;
        }
@@ -1736,17 +1754,10 @@ AUPlugin::set_state(const XMLNode& node, int version)
                }
                CFRelease (propertyList);
        }
+#endif
 
        Plugin::set_state (node, version);
        return ret;
-#else
-       if (!seen_set_state_message) {
-               info << string_compose (_("Restoring AudioUnit settings is not supported in this build of %1. Consider paying for a newer version"),
-                                       PROGRAM_NAME)
-                    << endmsg;
-       }
-       return Plugin::set_state (node, version);
-#endif
 }
 
 bool
@@ -1754,7 +1765,6 @@ AUPlugin::load_preset (PresetRecord r)
 {
        Plugin::load_preset (r);
 
-#ifdef AU_STATE_SUPPORT
        bool ret = false;
        CFPropertyListRef propertyList;
        Glib::ustring path;
@@ -1802,15 +1812,6 @@ AUPlugin::load_preset (PresetRecord r)
        }
 
        return ret;
-#else
-       if (!seen_loading_message) {
-               info << string_compose (_("Loading AudioUnit presets is not supported in this build of %1. Consider paying for a newer version"),
-                                       PROGRAM_NAME)
-                    << endmsg;
-               seen_loading_message = true;
-       }
-       return true;
-#endif
 }
 
 void
@@ -1821,11 +1822,9 @@ AUPlugin::do_remove_preset (std::string)
 string
 AUPlugin::do_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();
@@ -1844,12 +1843,12 @@ AUPlugin::do_save_preset (string preset_name)
 
        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;
+               return string();
        }
 
        DEBUG_TRACE (DEBUG::AudioUnits, "get current preset\n");
        if (unit->GetAUPreset (propertyList) != noErr) {
-               return false;
+               return string();
        }
 
        // add the actual preset name */
@@ -1864,21 +1863,12 @@ AUPlugin::do_save_preset (string 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;
+               return string();
        }
 
        CFRelease(propertyList);
 
        return string ("file:///") + user_preset_path;
-#else
-       if (!seen_saving_message) {
-               info << string_compose (_("Saving AudioUnit presets is not supported in this build of %1. Consider paying for a newer version"),
-                                       PROGRAM_NAME)
-                    << endmsg;
-               seen_saving_message = true;
-       }
-       return string();
-#endif
 }
 
 //-----------------------------------------------------------------------------
@@ -2045,7 +2035,6 @@ AUPlugin::current_preset() const
 {
        string preset_name;
 
-#ifdef AU_STATE_SUPPORT
        CFPropertyListRef propertyList;
 
        DEBUG_TRACE (DEBUG::AudioUnits, "get current preset for current_preset()\n");
@@ -2053,28 +2042,26 @@ AUPlugin::current_preset() const
                preset_name = get_preset_name_in_plist (propertyList);
                CFRelease(propertyList);
        }
-#endif
+
        return preset_name;
 }
 
 void
 AUPlugin::find_presets ()
 {
-#ifdef AU_STATE_SUPPORT
-       vector<string*>* preset_files;
-       PathScanner scanner;
+       vector<string> preset_files;
 
        user_preset_map.clear ();
 
-       preset_files = scanner (preset_search_path, au_preset_filter, this, true, true, -1, true);
+       find_files_matching_filter (preset_files, preset_search_path, au_preset_filter, this, true, true, true);
 
-       if (!preset_files) {
+       if (preset_files.empty()) {
                return;
        }
 
-       for (vector<string*>::iterator x = preset_files->begin(); x != preset_files->end(); ++x) {
+       for (vector<string>::iterator x = preset_files.begin(); x != preset_files.end(); ++x) {
 
-               string path = *(*x);
+               string path = *x;
                string preset_name;
 
                /* make an initial guess at the preset name using the path */
@@ -2091,11 +2078,8 @@ AUPlugin::find_presets ()
                        user_preset_map[preset_name] = path;
                }
 
-               delete *x;
        }
 
-       delete preset_files;
-
        /* now fill the vector<string> with the names we have */
 
        for (UserPresetMap::iterator i = user_preset_map.begin(); i != user_preset_map.end(); ++i) {
@@ -2107,10 +2091,8 @@ AUPlugin::find_presets ()
        for (FactoryPresetMap::iterator i = factory_preset_map.begin(); i != factory_preset_map.end(); ++i) {
                /* XXX: dubious */
                string const uri = string_compose ("%1", _presets.size ());
-               _presets.insert (make_pair (uri, Plugin::PresetRecord (uri, i->first)));
+               _presets.insert (make_pair (uri, Plugin::PresetRecord (uri, i->first, i->second)));
        }
-
-#endif
 }
 
 bool
@@ -2163,7 +2145,7 @@ AUPluginInfo::load (Session& session)
 Glib::ustring
 AUPluginInfo::au_cache_path ()
 {
-       return Glib::build_filename (ARDOUR::user_config_directory().to_string(), "au_cache");
+       return Glib::build_filename (ARDOUR::user_config_directory(), "au_cache");
 }
 
 PluginInfoList*
@@ -2292,6 +2274,7 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip
                }
 
                AUPluginInfo::get_names (temp, info->name, info->creator);
+               ARDOUR::PluginScanMessage(_("AU"), info->name, false);
 
                info->type = ARDOUR::AudioUnit;
                info->unique_id = stringify_descriptor (*info->descriptor);
@@ -2468,7 +2451,7 @@ AUPluginInfo::save_cached_info ()
 
        if (!tree.write (path)) {
                error << string_compose (_("could not save AU cache to %1"), path) << endmsg;
-               unlink (path.c_str());
+               g_unlink (path.c_str());
        }
 }
 
@@ -2723,6 +2706,26 @@ AUPlugin::listen_to_parameter (uint32_t param_id)
                return -1;
        } 
 
+       event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
+       event.mArgument.mParameter.mAudioUnit = unit->AU();
+       event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
+       event.mArgument.mParameter.mScope = descriptors[param_id].scope;
+       event.mArgument.mParameter.mElement = descriptors[param_id].element;
+
+       if (AUEventListenerAddEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
+               return -1;
+       } 
+
+       event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
+       event.mArgument.mParameter.mAudioUnit = unit->AU();
+       event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
+       event.mArgument.mParameter.mScope = descriptors[param_id].scope;
+       event.mArgument.mParameter.mElement = descriptors[param_id].element;
+
+       if (AUEventListenerAddEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
+               return -1;
+       } 
+
        return 0;
 }
 
@@ -2745,6 +2748,26 @@ AUPlugin::end_listen_to_parameter (uint32_t param_id)
                return -1;
        } 
 
+       event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
+       event.mArgument.mParameter.mAudioUnit = unit->AU();
+       event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
+       event.mArgument.mParameter.mScope = descriptors[param_id].scope;
+       event.mArgument.mParameter.mElement = descriptors[param_id].element;
+
+       if (AUEventListenerRemoveEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
+               return -1;
+       } 
+
+       event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
+       event.mArgument.mParameter.mAudioUnit = unit->AU();
+       event.mArgument.mParameter.mParameterID = descriptors[param_id].id;
+       event.mArgument.mParameter.mScope = descriptors[param_id].scope;
+       event.mArgument.mParameter.mElement = descriptors[param_id].element;
+
+       if (AUEventListenerRemoveEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
+               return -1;
+       } 
+
        return 0;
 }
 
@@ -2757,9 +2780,23 @@ AUPlugin::_parameter_change_listener (void* arg, void* src, const AudioUnitEvent
 void
 AUPlugin::parameter_change_listener (void* /*arg*/, void* /*src*/, const AudioUnitEvent* event, UInt64 /*host_time*/, Float32 new_value)
 {
-       ParameterMap::iterator i = parameter_map.find (event->mArgument.mParameter.mParameterID);
-
-       if (i != parameter_map.end()) {
-               ParameterChanged (i->second, new_value);
-       }
+        ParameterMap::iterator i;
+
+        if ((i = parameter_map.find (event->mArgument.mParameter.mParameterID)) == parameter_map.end()) {
+                return;
+        }
+        
+        switch (event->mEventType) {
+        case kAudioUnitEvent_BeginParameterChangeGesture:
+                StartTouch (i->second);
+                break;
+        case kAudioUnitEvent_EndParameterChangeGesture:
+                EndTouch (i->second);
+                break;
+        case kAudioUnitEvent_ParameterValueChange:
+                ParameterChanged (i->second, new_value);
+                break;
+        default:
+                break;
+        }
 }