remove unnecessary and unused changes from grygorii
[ardour.git] / libs / ardour / audio_unit.cc
index 1a24977399e504d8c4a1ca8522b0c7f723d49f7f..4b9a81eb9f3aa2869bb93e488708bb047c8de639 100644 (file)
@@ -19,6 +19,7 @@
 */
 
 #include <sstream>
+#include <fstream>
 #include <errno.h>
 #include <string.h>
 #include <math.h>
@@ -28,7 +29,7 @@
 #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/threads.h>
@@ -71,6 +72,76 @@ 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;
+FILE * AUPluginInfo::_crashlog_fd = NULL;
+
+
+static void au_blacklist (std::string id)
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
+       FILE * blacklist_fd = NULL;
+       if (! (blacklist_fd = fopen(fn.c_str(), "a"))) {
+               PBD::error << "Cannot append to AU blacklist for '"<< id <<"'\n";
+               return;
+       }
+       assert(id.find("\n") == string::npos);
+       fprintf(blacklist_fd, "%s\n", id.c_str());
+       ::fclose(blacklist_fd);
+}
+
+static void au_unblacklist (std::string id)
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
+       if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
+               PBD::warning << "Expected Blacklist file does not exist.\n";
+               return;
+       }
+
+       std::string bl;
+       std::ifstream ifs(fn.c_str());
+       bl.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+       ::g_unlink(fn.c_str());
+
+       assert(id.find("\n") == string::npos);
+
+       id += "\n"; // add separator
+       const size_t rpl = bl.find(id);
+       if (rpl != string::npos) {
+               bl.replace(rpl, id.size(), "");
+       }
+       if (bl.empty()) {
+               return;
+       }
+
+       FILE * blacklist_fd = NULL;
+       if (! (blacklist_fd = fopen(fn.c_str(), "w"))) {
+               PBD::error << "Cannot open AU blacklist.\n";
+               return;
+       }
+       fprintf(blacklist_fd, "%s", bl.c_str());
+       ::fclose(blacklist_fd);
+}
+
+static bool is_blacklisted (std::string id)
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_blacklist.txt");
+       if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
+               return false;
+       }
+       std::string bl;
+       std::ifstream ifs(fn.c_str());
+       bl.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+
+       assert(id.find("\n") == string::npos);
+
+       id += "\n"; // add separator
+       const size_t rpl = bl.find(id);
+       if (rpl != string::npos) {
+               return true;
+       }
+       return false;
+}
+
+
 
 static OSStatus
 _render_callback(void *userData,
@@ -358,6 +429,7 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAC
                p += preset_search_path;
                preset_search_path = p;
                preset_search_path_initialized = true;
+               DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Preset Path: %1\n", preset_search_path));
        }
 
        init ();
@@ -432,6 +504,7 @@ AUPlugin::discover_factory_presets ()
 
                string name = CFStringRefToStdString (preset->presetName);
                factory_preset_map[name] = preset->presetNumber;
+               DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Factory Preset: %1 > %2\n", name, preset->presetNumber));
        }
 
        CFRelease (presets);
@@ -441,6 +514,7 @@ void
 AUPlugin::init ()
 {
        OSErr err;
+       CFStringRef itemName;
 
        /* these keep track of *configured* channel set up,
           not potential set ups.
@@ -448,6 +522,20 @@ AUPlugin::init ()
 
        input_channels = -1;
        output_channels = -1;
+       {
+               CAComponentDescription temp;
+               GetComponentInfo (comp.get()->Comp(), &temp, NULL, NULL, NULL);
+               CFStringRef compTypeString = UTCreateStringForOSType(temp.componentType);
+               CFStringRef compSubTypeString = UTCreateStringForOSType(temp.componentSubType);
+               CFStringRef compManufacturerString = UTCreateStringForOSType(temp.componentManufacturer);
+               itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
+                               compTypeString, compManufacturerString, compSubTypeString);
+               if (compTypeString != NULL) CFRelease(compTypeString);
+               if (compSubTypeString != NULL) CFRelease(compSubTypeString);
+               if (compManufacturerString != NULL) CFRelease(compManufacturerString);
+       }
+
+       au_blacklist(CFStringRefToStdString(itemName));
 
        try {
                DEBUG_TRACE (DEBUG::AudioUnits, "opening AudioUnit\n");
@@ -513,6 +601,9 @@ AUPlugin::init ()
        discover_factory_presets ();
 
        // Plugin::setup_controls ();
+
+       au_unblacklist(CFStringRefToStdString(itemName));
+       if (itemName != NULL) CFRelease(itemName);
 }
 
 void
@@ -530,7 +621,7 @@ AUPlugin::discover_parameters ()
 
        for (uint32_t i = 0; i < sizeof (scopes) / sizeof (scopes[0]); ++i) {
 
-               AUParamInfo param_info (unit->AU(), false, false, scopes[i]);
+               AUParamInfo param_info (unit->AU(), false, /* include read only */ true, scopes[i]);
 
                for (uint32_t i = 0; i < param_info.NumParams(); ++i) {
 
@@ -604,24 +695,33 @@ AUPlugin::discover_parameters ()
 
                        d.lower = info.minValue;
                        d.upper = info.maxValue;
-                       d.default_value = info.defaultValue;
+                       d.normal = info.defaultValue;
 
                        d.integer_step = (info.unit == kAudioUnitParameterUnit_Indexed);
                        d.toggled = (info.unit == kAudioUnitParameterUnit_Boolean) ||
                                (d.integer_step && ((d.upper - d.lower) == 1.0));
                        d.sr_dependent = (info.unit == kAudioUnitParameterUnit_SampleFrames);
-                       d.automatable = !d.toggled &&
+                       d.automatable = /* !d.toggled && -- ardour can automate toggles, can AU ? */
                                !(info.flags & kAudioUnitParameterFlag_NonRealTime) &&
                                (info.flags & kAudioUnitParameterFlag_IsWritable);
 
                        d.logarithmic = (info.flags & kAudioUnitParameterFlag_DisplayLogarithmic);
-                       d.unit = info.unit;
+                       d.au_unit = info.unit;
+                       switch (info.unit) {
+                       case kAudioUnitParameterUnit_Decibels:
+                               d.unit = ParameterDescriptor::DB;
+                               break;
+                       case kAudioUnitParameterUnit_MIDINoteNumber:
+                               d.unit = ParameterDescriptor::MIDI_NOTE;
+                               break;
+                       case kAudioUnitParameterUnit_Hertz:
+                               d.unit = ParameterDescriptor::HZ;
+                               break;
+                       }
 
-                       d.step = 1.0;
-                       d.smallstep = 0.1;
-                       d.largestep = 10.0;
                        d.min_unbound = 0; // lower is bound
                        d.max_unbound = 0; // upper is bound
+                       d.update_steps();
 
                        descriptors.push_back (d);
 
@@ -759,7 +859,7 @@ float
 AUPlugin::default_value (uint32_t port)
 {
        if (port < descriptors.size()) {
-               return descriptors[port].default_value;
+               return descriptors[port].normal;
        }
 
        return 0;
@@ -966,11 +1066,11 @@ AUPlugin::input_streams() const
 {
        ChanCount c;
 
-       c.set (DataType::AUDIO, 1);
-       c.set (DataType::MIDI, 0);
 
        if (input_channels < 0) {
-               warning << string_compose (_("AUPlugin: %1 input_streams() called without any format set!"), name()) << endmsg;
+               // force PluginIoReConfigure -- see also commit msg e38eb06
+               c.set (DataType::AUDIO, 0);
+               c.set (DataType::MIDI, 0);
        } else {
                c.set (DataType::AUDIO, input_channels);
                c.set (DataType::MIDI, _has_midi_input ? 1 : 0);
@@ -985,11 +1085,10 @@ AUPlugin::output_streams() const
 {
        ChanCount c;
 
-       c.set (DataType::AUDIO, 1);
-       c.set (DataType::MIDI, 0);
-
        if (output_channels < 0) {
-               warning << string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg;
+               // force PluginIoReConfigure - see also commit msg e38eb06
+               c.set (DataType::AUDIO, 0);
+               c.set (DataType::MIDI, 0);
        } else {
                c.set (DataType::AUDIO, output_channels);
                c.set (DataType::MIDI, _has_midi_output ? 1 : 0);
@@ -1026,7 +1125,11 @@ AUPlugin::can_support_io_configuration (const ChanCount& in, ChanCount& out)
        //configurations in most cases. so first lets see
        //if there's a configuration that keeps out==in
 
-       audio_out = audio_in;
+       if (in.n_midi() > 0 && audio_in == 0) {
+               audio_out = 2; // prefer stereo version if available.
+       } else {
+               audio_out = audio_in;
+       }
 
        for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
 
@@ -1652,27 +1755,42 @@ AUPlugin::parameter_is_audio (uint32_t) const
 }
 
 bool
-AUPlugin::parameter_is_control (uint32_t) const
+AUPlugin::parameter_is_control (uint32_t param) const
 {
-       return true;
+       assert(param < descriptors.size());
+       if (descriptors[param].automatable) {
+               /* corrently ardour expects all controls to be automatable
+                * IOW ardour GUI elements mandate an Evoral::Parameter
+                * for all input+control ports.
+                */
+               return true;
+       }
+       return false;
 }
 
 bool
-AUPlugin::parameter_is_input (uint32_t) const
+AUPlugin::parameter_is_input (uint32_t param) const
 {
-       return false;
+       /* AU params that are both readable and writeable,
+        * are listed in kAudioUnitScope_Global
+        */
+       return (descriptors[param].scope == kAudioUnitScope_Input || descriptors[param].scope == kAudioUnitScope_Global);
 }
 
 bool
-AUPlugin::parameter_is_output (uint32_t) const
+AUPlugin::parameter_is_output (uint32_t param) const
 {
-       return false;
+       assert(param < descriptors.size());
+       // TODO check if ardour properly handles ports
+       // that report is_input + is_output == true
+       // -> add || descriptors[param].scope == kAudioUnitScope_Global
+       return (descriptors[param].scope == kAudioUnitScope_Output);
 }
 
 void
 AUPlugin::add_state (XMLNode* root) const
 {
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
        CFDataRef xmlData;
        CFPropertyListRef propertyList;
 
@@ -1711,7 +1829,7 @@ AUPlugin::set_state(const XMLNode& node, int version)
 {
        int ret = -1;
        CFPropertyListRef propertyList;
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg (X_("C"));
 
        if (node.name() != state_node_name()) {
                error << _("Bad node sent to AUPlugin::set_state") << endmsg;
@@ -1868,6 +1986,10 @@ AUPlugin::do_save_preset (string preset_name)
 
        CFRelease(propertyList);
 
+       user_preset_map[preset_name] = user_preset_path;;
+
+       DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Saving Preset to %1\n", user_preset_path));
+
        return string ("file:///") + user_preset_path;
 }
 
@@ -2049,20 +2171,20 @@ AUPlugin::current_preset() const
 void
 AUPlugin::find_presets ()
 {
-       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()) {
+               DEBUG_TRACE (DEBUG::AudioUnits, "AU No Preset Files found for given plugin.\n");
                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 */
@@ -2077,17 +2199,18 @@ AUPlugin::find_presets ()
 
                if (check_and_get_preset_name (get_comp()->Comp(), path, preset_name)) {
                        user_preset_map[preset_name] = path;
+                       DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Preset File: %1 > %2\n", preset_name, path));
+               } else {
+                       DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU INVALID Preset: %1 > %2\n", 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) {
                _presets.insert (make_pair (i->second, Plugin::PresetRecord (i->second, i->first)));
+               DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Adding User Preset: %1 > %2\n", i->first, i->second));
        }
 
        /* add factory presets */
@@ -2096,6 +2219,7 @@ AUPlugin::find_presets ()
                /* XXX: dubious */
                string const uri = string_compose ("%1", _presets.size ());
                _presets.insert (make_pair (uri, Plugin::PresetRecord (uri, i->first, i->second)));
+               DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Adding Factory Preset: %1 > %2\n", i->first, i->second));
        }
 }
 
@@ -2160,6 +2284,8 @@ AUPluginInfo::discover ()
        if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS)) {
                ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)"));
        }
+       // create crash log file
+       au_start_crashlog ();
 
        PluginInfoList* plugs = new PluginInfoList;
 
@@ -2168,6 +2294,9 @@ AUPluginInfo::discover ()
        discover_generators (*plugs);
        discover_instruments (*plugs);
 
+       // all fine if we get here
+       au_remove_crashlog ();
+
        DEBUG_TRACE (DEBUG::PluginManager, string_compose ("AU: discovered %1 plugins\n", plugs->size()));
 
        return plugs;
@@ -2225,16 +2354,90 @@ AUPluginInfo::discover_instruments (PluginInfoList& plugs)
        discover_by_description (plugs, desc);
 }
 
+
+bool
+AUPluginInfo::au_get_crashlog (std::string &msg)
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
+       if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
+               return false;
+       }
+       std::ifstream ifs(fn.c_str());
+       msg.assign ((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+       au_remove_crashlog ();
+       return true;
+}
+
+void
+AUPluginInfo::au_start_crashlog ()
+{
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
+       assert(!_crashlog_fd);
+       DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("Creating AU Log: %1\n", fn));
+       if (!(_crashlog_fd = fopen(fn.c_str(), "w"))) {
+               PBD::error << "Cannot create AU error-log" << fn << "\n";
+               cerr << "Cannot create AU error-log" << fn << "\n";
+       }
+}
+
+void
+AUPluginInfo::au_remove_crashlog ()
+{
+       if (_crashlog_fd) {
+               ::fclose(_crashlog_fd);
+               _crashlog_fd = NULL;
+       }
+       string fn = Glib::build_filename (ARDOUR::user_cache_directory(), "au_crashlog.txt");
+       ::g_unlink(fn.c_str());
+       DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("Remove AU Log: %1\n", fn));
+}
+
+
+void
+AUPluginInfo::au_crashlog (std::string msg)
+{
+       if (!_crashlog_fd) {
+               fprintf(stderr, "AU: %s\n", msg.c_str());
+       } else {
+               fprintf(_crashlog_fd, "AU: %s\n", msg.c_str());
+               ::fflush(_crashlog_fd);
+       }
+}
+
 void
 AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescription& desc)
 {
        Component comp = 0;
+       au_crashlog(string_compose("Start AU discovery for Type: %1", (int)desc.componentType));
 
        comp = FindNextComponent (NULL, &desc);
 
        while (comp != NULL) {
                CAComponentDescription temp;
                GetComponentInfo (comp, &temp, NULL, NULL, NULL);
+               CFStringRef itemName = NULL;
+
+               {
+                       if (itemName != NULL) CFRelease(itemName);
+                       CFStringRef compTypeString = UTCreateStringForOSType(temp.componentType);
+                       CFStringRef compSubTypeString = UTCreateStringForOSType(temp.componentSubType);
+                       CFStringRef compManufacturerString = UTCreateStringForOSType(temp.componentManufacturer);
+                       itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
+                                       compTypeString, compManufacturerString, compSubTypeString);
+                       au_crashlog(string_compose("Scanning ID: %1", CFStringRefToStdString(itemName)));
+                       if (compTypeString != NULL)
+                               CFRelease(compTypeString);
+                       if (compSubTypeString != NULL)
+                               CFRelease(compSubTypeString);
+                       if (compManufacturerString != NULL)
+                               CFRelease(compManufacturerString);
+               }
+
+               if (is_blacklisted(CFStringRefToStdString(itemName))) {
+                       info << string_compose (_("Skipped blacklisted AU plugin %1 "), CFStringRefToStdString(itemName)) << endmsg;
+                       comp = FindNextComponent (comp, &desc);
+                       continue;
+               }
 
                AUPluginInfoPtr info (new AUPluginInfo
                                      (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription(temp))));
@@ -2252,6 +2455,7 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip
                case kAudioUnitType_Panner:
                case kAudioUnitType_OfflineEffect:
                case kAudioUnitType_FormatConverter:
+                       comp = FindNextComponent (comp, &desc);
                        continue;
 
                case kAudioUnitType_Output:
@@ -2277,7 +2481,10 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip
                        break;
                }
 
+               au_blacklist(CFStringRefToStdString(itemName));
                AUPluginInfo::get_names (temp, info->name, info->creator);
+               ARDOUR::PluginScanMessage(_("AU"), info->name, false);
+               au_crashlog(string_compose("Plugin: %1", info->name));
 
                info->type = ARDOUR::AudioUnit;
                info->unique_id = stringify_descriptor (*info->descriptor);
@@ -2330,8 +2537,12 @@ AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescrip
                        error << string_compose (_("Cannot get I/O configuration info for AU %1"), info->name) << endmsg;
                }
 
+               au_unblacklist(CFStringRefToStdString(itemName));
+               au_crashlog("Success.");
                comp = FindNextComponent (comp, &desc);
+               if (itemName != NULL) CFRelease(itemName); itemName = NULL;
        }
+       au_crashlog(string_compose("End AU discovery for Type: %1", (int)desc.componentType));
 }
 
 bool