LV2/midnam tweaks - fix race conditions
[ardour.git] / libs / ardour / lv2_plugin.cc
index d1cf26f266d1067d493dc8e61adb542770f26bb8..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;
@@ -550,6 +591,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        _midname_interface = (const LV2_Midnam_Interface*)
                extension_data (LV2_MIDNAM__interface);
        if (_midname_interface) {
+               _midnam_dirty = true;
                read_midnam ();
        }
 #endif
@@ -593,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
 
@@ -890,6 +935,7 @@ LV2Plugin::~LV2Plugin ()
 #ifdef LV2_EXTENDED
        free(_impl->queue_draw);
        free(_impl->midnam);
+       free(_impl->bankpatch);
 #endif
 
        free(_features);
@@ -962,6 +1008,11 @@ LV2Plugin::has_inline_display () {
        return _display_interface ? true : false;
 }
 
+bool
+LV2Plugin::inline_display_in_gui () {
+       return _inline_display_in_gui;
+}
+
 Plugin::Display_Image_Surface*
 LV2Plugin::render_inline_display (uint32_t w, uint32_t h) {
        if (_display_interface) {
@@ -980,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);
@@ -998,6 +1049,10 @@ LV2Plugin::read_midnam () {
        }
 #endif
        _midname_interface->free (midnam);
+       if (rv) {
+               UpdatedMidnam ();
+               _midnam_dirty = false;
+       }
        return rv;
 }
 
@@ -1493,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) */
@@ -2811,7 +2885,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                                                AutomationCtrlPtr c = get_automation_control (p);
                                                                DEBUG_TRACE(DEBUG::LV2Automate, string_compose ("End Touch p: %1\n", p));
                                                                if (c) {
-                                                                       c->ac->stop_touch (true, std::max ((framepos_t)0, start - _current_latency));
+                                                                       c->ac->stop_touch (std::max ((framepos_t)0, start - _current_latency));
                                                                }
                                                        }
                                                }
@@ -3156,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);