LV2/midnam tweaks - fix race conditions
[ardour.git] / libs / ardour / lv2_plugin.cc
index 2b6e8021725b6369135da373e468aa5d4261970d..b51db6548526efd1184ae173a602342d731c6c92 100644 (file)
 #include "pbd/replace_all.h"
 #include "pbd/xml++.h"
 
+#ifdef PLATFORM_WINDOWS
+#include <shlobj.h> // CSIDL_*
+#include "pbd/windows_special_dirs.h"
+#endif
+
 #include "libardour-config.h"
 
 #include "ardour/audio_buffer.h"
@@ -190,6 +195,7 @@ public:
        LilvNode* auto_automation_control; // atom:supports
        LilvNode* auto_automation_controlled; // lv2:portProperty
        LilvNode* auto_automation_controller; // lv2:portProperty
+       LilvNode* inline_display_in_gui; // lv2:optionalFeature
 #endif
 
 private:
@@ -224,19 +230,36 @@ work_respond(LV2_Worker_Respond_Handle handle,
 
 #ifdef LV2_EXTENDED
 /* inline display extension */
-static void
-queue_draw (LV2_Inline_Display_Handle handle)
+void
+LV2Plugin::queue_draw (LV2_Inline_Display_Handle handle)
 {
        LV2Plugin* plugin = (LV2Plugin*)handle;
        plugin->QueueDraw(); /* EMIT SIGNAL */
 }
 
-static void
-midnam_update (LV2_Midnam_Handle handle)
+void
+LV2Plugin::midnam_update (LV2_Midnam_Handle handle)
 {
        LV2Plugin* plugin = (LV2Plugin*)handle;
+       plugin->_midnam_dirty = true;
        plugin->UpdateMidnam (); /* EMIT SIGNAL */
 }
+
+void
+LV2Plugin::bankpatch_notify (LV2_BankPatch_Handle handle, uint8_t chn, uint32_t bank, uint8_t pgm)
+{
+       LV2Plugin* plugin = (LV2Plugin*)handle;
+       if (chn > 15) {
+               return;
+       }
+       plugin->seen_bankpatch = true;
+       if (pgm > 127 || bank > 16383) {
+               plugin->_bankpatch[chn] = UINT32_MAX;
+       } else {
+               plugin->_bankpatch[chn] = (bank << 7) | pgm;
+       }
+       plugin->BankPatchChange (chn); /* EMIT SIGNAL */
+}
 #endif
 
 /* log extension */
@@ -323,6 +346,7 @@ struct LV2Plugin::Impl {
 #ifdef LV2_EXTENDED
        LV2_Inline_Display*          queue_draw;
        LV2_Midnam*                  midnam;
+       LV2_BankPatch*               bankpatch;
 #endif
 };
 
@@ -390,6 +414,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        _was_activated          = false;
        _has_state_interface    = false;
        _can_write_automation   = false;
+       _inline_display_in_gui  = false;
        _max_latency            = 0;
        _current_latency        = 0;
        _impl->block_length     = _session.get_block_size();
@@ -415,7 +440,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        lilv_node_free(state_uri);
        lilv_node_free(state_iface_uri);
 
-       _features    = (LV2_Feature**)calloc(13, sizeof(LV2_Feature*));
+       _features    = (LV2_Feature**)calloc(14, sizeof(LV2_Feature*));
        _features[0] = &_instance_access_feature;
        _features[1] = &_data_access_feature;
        _features[2] = &_make_path_feature;
@@ -450,6 +475,15 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        _midnam_feature.URI  = LV2_MIDNAM__update;
        _midnam_feature.data = _impl->midnam;
        _features[n_features++]  = &_midnam_feature;
+
+       _impl->bankpatch = (LV2_BankPatch*)
+               malloc (sizeof(LV2_BankPatch));
+       _impl->bankpatch->handle = this;
+       _impl->bankpatch->notify = bankpatch_notify;
+
+       _bankpatch_feature.URI  = LV2_BANKPATCH__notify;
+       _bankpatch_feature.data = _impl->bankpatch;
+       _features[n_features++]  = &_bankpatch_feature;
 #endif
 
 #ifdef HAVE_LV2_1_2_0
@@ -483,6 +517,13 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        _features[n_features++] = &_options_feature;
 #endif
 
+#ifdef LV2_EXTENDED
+       seen_bankpatch = false;
+       for (uint32_t chn = 0; chn < 16; ++chn) {
+               _bankpatch[chn] = UINT32_MAX;
+       }
+#endif
+
        LV2_State_Make_Path* make_path = (LV2_State_Make_Path*)malloc(
                sizeof(LV2_State_Make_Path));
        make_path->handle = this;
@@ -547,11 +588,10 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        _display_interface = (const LV2_Inline_Display_Interface*)
                extension_data (LV2_INLINEDISPLAY__interface);
 
-       _show_display_in_generic_gui = (bool) extension_data (LV2_INLINEDISPLAY__in_gui);
-
        _midname_interface = (const LV2_Midnam_Interface*)
                extension_data (LV2_MIDNAM__interface);
        if (_midname_interface) {
+               _midnam_dirty = true;
                read_midnam ();
        }
 #endif
@@ -595,6 +635,9 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        if (lilv_nodes_contains (optional_features, _world.auto_can_write_automatation)) {
                _can_write_automation = true;
        }
+       if (lilv_nodes_contains (optional_features, _world.inline_display_in_gui)) {
+               _inline_display_in_gui = true;
+       }
        lilv_nodes_free(optional_features);
 #endif
 
@@ -892,6 +935,7 @@ LV2Plugin::~LV2Plugin ()
 #ifdef LV2_EXTENDED
        free(_impl->queue_draw);
        free(_impl->midnam);
+       free(_impl->bankpatch);
 #endif
 
        free(_features);
@@ -966,7 +1010,7 @@ LV2Plugin::has_inline_display () {
 
 bool
 LV2Plugin::inline_display_in_gui () {
-       return _show_display_in_generic_gui;
+       return _inline_display_in_gui;
 }
 
 Plugin::Display_Image_Surface*
@@ -987,7 +1031,7 @@ LV2Plugin::has_midnam () {
 bool
 LV2Plugin::read_midnam () {
        bool rv = false;
-       if (!_midname_interface) {
+       if (!_midname_interface || !_midnam_dirty) {
                return rv;
        }
        char* midnam = _midname_interface->midnam ((void*)_impl->instance->lv2_handle);
@@ -1005,6 +1049,10 @@ LV2Plugin::read_midnam () {
        }
 #endif
        _midname_interface->free (midnam);
+       if (rv) {
+               UpdatedMidnam ();
+               _midnam_dirty = false;
+       }
        return rv;
 }
 
@@ -1500,9 +1548,28 @@ LV2Plugin::do_save_preset(string name)
        const string prefix    = legalize_for_uri(lilv_node_as_string(plug_name));
        const string base_name = legalize_for_uri(name);
        const string file_name = base_name + ".ttl";
+#ifdef PLATFORM_WINDOWS
+       /* http://lv2plug.in/pages/filesystem-hierarchy-standard.html */
+       std::string appdata = PBD::get_win_special_folder_path (CSIDL_APPDATA);
+       if (appdata.empty ()) {
+               // TODO consider a fallback location
+               return "";
+       }
+       const string bundle = Glib::build_filename (
+                       appdata, "LV2", prefix + "_" + base_name + ".lv2");
+#else
+       /* while macOS/OSX user-specific path is
+        *
+        *   $HOME/Library/Audio/Plug-Ins/LV2/
+        *
+        * liblilv's LV2 search path on all unices does include ~/.lv2/
+        * Ardour has been saving lv2 presets to ~/.lv2 for along time,
+        * so just keep them there.
+        */
        const string bundle    = Glib::build_filename(
                Glib::get_home_dir(),
                Glib::build_filename(".lv2", prefix + "_" + base_name + ".lv2"));
+#endif
 
 #ifdef HAVE_LILV_0_21_3
        /* delete reference to old preset (if any) */
@@ -3163,6 +3230,7 @@ LV2World::LV2World()
        auto_automation_control     = lilv_new_uri(world, LV2_AUTOMATE_URI__control);
        auto_automation_controlled  = lilv_new_uri(world, LV2_AUTOMATE_URI__controlled);
        auto_automation_controller  = lilv_new_uri(world, LV2_AUTOMATE_URI__controller);
+       inline_display_in_gui       = lilv_new_uri(world, LV2_INLINEDISPLAY__in_gui);
 #endif
 #ifdef HAVE_LV2_1_2_0
        bufz_powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);