View Issue Details

IDProjectCategoryView StatusLast Update
0004698ardourbugspublic2015-09-18 15:29
Reportercolinf Assigned Topaul  
PrioritynormalSeverityminorReproducibilityhave not tried
Status closedResolutionfixed 
Target Version3.0 
Summary0004698: Adding LV2 plugin presets doesn't work.
DescriptionAlthough clicking the 'Add' button in the top of LV2 plugin windows pops up the dialog to name a new preset, actually adding the preset appears to do nothing: the name doesn't appear in the drop-down preset list.
Additional InformationI've tried with various LV2 plugins from Calf, Invada and drobilla's MDala, and this doesn't work for any I've tried, whether they have their own GUIs or use ardour's 'generic' plugin UI.

I have pretty recent drobilla and lv2 svn, so maybe there's something not quite right there, but I thought I should report it here anyway in case anyone else sees the same thing.
TagsNo tags attached.

Activities

colinf

2012-02-14 15:28

updater   ~0012797

As far as I can see, this is just not implemented yet, so I suppose it's really a missing feature rather than a bug.

2012-05-22 11:22

 

lv2-save-preset-experimental.patch (4,069 bytes)   
Index: libs/ardour/lv2_plugin.cc
===================================================================
--- libs/ardour/lv2_plugin.cc	(revision 12363)
+++ libs/ardour/lv2_plugin.cc	(working copy)
@@ -841,7 +841,6 @@
 bool
 LV2Plugin::load_preset(PresetRecord r)
 {
-	Plugin::load_preset(r);
 
 	std::map<std::string,uint32_t>::iterator it;
 
@@ -868,19 +867,99 @@
 	lilv_node_free(lv2_symbol);
 	lilv_node_free(lv2_port);
 
+	Plugin::load_preset(r);
+
 	return true;
 }
 
+const void*
+ARDOUR::lv2plugin_get_port_value(const char* port_symbol,
+                                 void*       user_data,
+                                 uint32_t*   size,
+                                 uint32_t*   type)
+{
+	cerr << "get_port_value(" << port_symbol << ", ...) ... ";
+	LV2Plugin *plugin = (LV2Plugin *) user_data;
+
+	uint32_t index = plugin->port_index(port_symbol);
+	if (index != (uint32_t) -1) {
+		if (plugin->parameter_is_input(index) && plugin->parameter_is_control(index)) {
+			float *value;
+			*size = sizeof(float);
+			*type = plugin->_uri_map.uri_to_id(
+				NULL, LV2_ATOM__Float);
+			value = &plugin->_shadow_data[index];
+			cerr << "index="<< index << ",*size=" << *size << ",*type=" << *type << ",*value=" << *value << endl;
+
+			return value;
+		}
+		cerr << "port is not input control port! ";
+	}
+
+	cerr << "returning NULL!" << endl;
+	*size = *type = 0;
+	return NULL;
+}
+
+
 std::string
-LV2Plugin::do_save_preset(string /*name*/)
+LV2Plugin::do_save_preset(string name)
 {
-	return "";
+	cerr << "LV2Plugin::do_save_preset(" << name << ")" << endl;
+
+	string pset_uri = uri();
+	pset_uri += "#";
+	pset_uri += name;
+
+	string save_dir = Glib::build_filename(
+		Glib::get_home_dir(), 
+		Glib::build_filename(".lv2", "presets") 
+	);
+
+	LilvState* state = lilv_state_new_from_instance(
+		_impl->plugin,
+		_impl->instance,
+		_uri_map.urid_map(),
+		scratch_dir().c_str(),			// file_dir
+		NULL, 					// copy_dir
+		NULL, 					// link_dir
+		save_dir.c_str(),			// save_dir
+		lv2plugin_get_port_value,		// get_value
+		(void*) this,				// user_data
+		LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE,	// flags
+		_features				// features
+	);
+
+	lilv_state_set_label(state, name.c_str());
+	lilv_state_save(
+		_world.world,		// world
+		_uri_map.urid_map(),	// map
+		_uri_map.urid_unmap(),	// unmap
+		state,			// state
+		pset_uri.c_str(),	// uri
+		save_dir.c_str(),	// dir
+		(name + ".ttl").c_str()	// filename
+
+	);
+
+	lilv_state_free(state);
+	return pset_uri;
 }
 
 void
-LV2Plugin::do_remove_preset(string /*name*/)
-{}
+LV2Plugin::do_remove_preset(string name)
+{
+	string preset_file = Glib::build_filename(
+		Glib::get_home_dir(), 
+		Glib::build_filename(
+			Glib::build_filename(".lv2", "presets"),
+			name + ".ttl"
+		)
+	);
+	unlink(preset_file.c_str());
 
+}
+
 bool
 LV2Plugin::has_editor() const
 {
Index: libs/ardour/ardour/lv2_plugin.h
===================================================================
--- libs/ardour/ardour/lv2_plugin.h	(revision 12363)
+++ libs/ardour/ardour/lv2_plugin.h	(working copy)
@@ -24,6 +24,8 @@
 #include <string>
 #include <vector>
 
+#include <lilv/lilv.h>
+
 #include "ardour/plugin.h"
 #include "ardour/uri_map.h"
 #include "ardour/worker.h"
@@ -31,6 +33,12 @@
 
 namespace ARDOUR {
 
+const void* lv2plugin_get_port_value(const char* port_symbol,
+                                     void*       user_data,
+                                     uint32_t*   size,
+                                     uint32_t*   type);
+
+
 class AudioEngine;
 class Session;
 
@@ -162,6 +170,11 @@
 	float*        _freewheel_control_port;  ///< Special input set by ardour
 	float*        _latency_control_port;  ///< Special output set by ardour
 	PBD::ID       _insert_id;
+	
+	friend const void* lv2plugin_get_port_value(const char* port_symbol,
+                                                    void*       user_data,
+	                                            uint32_t*   size,
+	                                            uint32_t*   type);
 
 	typedef enum {
 		PORT_INPUT   = 1,

colinf

2012-05-22 11:44

updater   ~0013274

In the hope of learning a bit more about LV2, I've had a go at implementing this. I've managed to make it seem to more-or-less work, though sadly my understanding of how LV2 works hasn't really improved much in the process: it was mostly trial-and-error even getting to this point.

Anyway, work-in-progress patch attached: drobilla will almost certainly be able to spot nineteen things wrong with it straight away, and there are also a few oddities that seem also to happen when saving & loading LADSPA plugin presets.

So it's not ready to be applied, but here for safekeeping, in case my computer gets caught in any undesirable weather events or the like.

2012-05-22 17:14

 

plugin-presets-allow-clear.patch (1,685 bytes)   
Index: gtk2_ardour/plugin_ui.cc
===================================================================
--- gtk2_ardour/plugin_ui.cc	(revision 12381)
+++ gtk2_ardour/plugin_ui.cc	(working copy)
@@ -584,6 +584,9 @@
 			warning << string_compose(_("Plugin preset %1 not found"),
 						  _preset_combo.get_active_text()) << endmsg;
 		}
+	} else {
+		// blank selected = no preset
+		plugin->clear_preset();
 	}
 }
 
@@ -736,6 +739,8 @@
 		preset_labels.push_back (i->label);
 	}
 
+	preset_labels.push_back("");
+
 	set_popdown_strings (_preset_combo, preset_labels);
 
 	--_no_load_preset;
@@ -759,6 +764,7 @@
 void
 PlugUIBase::update_preset_modified ()
 {
+
 	if (plugin->last_preset().uri.empty()) {
 		_preset_modified.set_text ("");
 		return;
Index: libs/ardour/ardour/plugin.h
===================================================================
--- libs/ardour/ardour/plugin.h	(revision 12381)
+++ libs/ardour/ardour/plugin.h	(working copy)
@@ -182,6 +182,7 @@
 	void remove_preset (std::string);
 
 	virtual bool load_preset (PresetRecord);
+	void clear_preset ();
 
 	const PresetRecord * preset_by_label (const std::string &);
 	const PresetRecord * preset_by_uri (const std::string &);
Index: libs/ardour/plugin.cc
===================================================================
--- libs/ardour/plugin.cc	(revision 12381)
+++ libs/ardour/plugin.cc	(working copy)
@@ -321,6 +321,16 @@
 	return true;
 }
 
+void
+Plugin::clear_preset ()
+{
+	_last_preset.uri = "";
+	_last_preset.label = "";
+	_parameter_changed_since_last_preset = false;
+
+	PresetLoaded (); /* EMIT SIGNAL */
+}
+
 /** @param val `plugin' value */
 void
 Plugin::set_parameter (uint32_t which, float val)

2012-05-22 17:14

 

lv2-save-preset.patch (4,571 bytes)   
Index: gtk2_ardour/lv2_plugin_ui.cc
===================================================================
--- gtk2_ardour/lv2_plugin_ui.cc	(revision 12381)
+++ gtk2_ardour/lv2_plugin_ui.cc	(working copy)
@@ -268,6 +268,7 @@
 			_ardour_buttons_box->pack_end (save_button, false, false);
 			_ardour_buttons_box->pack_end (add_button, false, false);
 			_ardour_buttons_box->pack_end (_preset_combo, false, false);
+			_ardour_buttons_box->pack_end (_preset_modified, false, false);
 			_ardour_buttons_box->show_all();
 			pack_start(*_ardour_buttons_box, false, false);
 
Index: libs/ardour/ardour/lv2_plugin.h
===================================================================
--- libs/ardour/ardour/lv2_plugin.h	(revision 12381)
+++ libs/ardour/ardour/lv2_plugin.h	(working copy)
@@ -31,6 +31,13 @@
 
 namespace ARDOUR {
 
+// a callback function for lilv_state_new_from_instance(). friend of LV2Plugin
+// so we can pass an LV2Plugin* in user_data and access its private members.
+const void* lv2plugin_get_port_value(const char* port_symbol,
+                                     void*       user_data,
+                                     uint32_t*   size,
+                                     uint32_t*   type);
+
 class AudioEngine;
 class Session;
 
@@ -163,6 +170,11 @@
 	float*        _latency_control_port;  ///< Special output set by ardour
 	PBD::ID       _insert_id;
 
+	friend const void* lv2plugin_get_port_value(const char* port_symbol,
+	                                            void*       user_data,
+	                                            uint32_t*   size,
+	                                            uint32_t*   type);
+
 	typedef enum {
 		PORT_INPUT   = 1,
 		PORT_OUTPUT  = 1 << 1,
Index: libs/ardour/lv2_plugin.cc
===================================================================
--- libs/ardour/lv2_plugin.cc	(revision 12381)
+++ libs/ardour/lv2_plugin.cc	(working copy)
@@ -841,7 +841,6 @@
 bool
 LV2Plugin::load_preset(PresetRecord r)
 {
-	Plugin::load_preset(r);
 
 	std::map<std::string,uint32_t>::iterator it;
 
@@ -868,19 +867,99 @@
 	lilv_node_free(lv2_symbol);
 	lilv_node_free(lv2_port);
 
+	Plugin::load_preset(r);
+
 	return true;
 }
 
+const void*
+ARDOUR::lv2plugin_get_port_value(const char* port_symbol,
+                                 void*       user_data,
+                                 uint32_t*   size,
+                                 uint32_t*   type)
+{
+	cerr << "get_port_value(" << port_symbol << ", ...) ... ";
+	LV2Plugin *plugin = (LV2Plugin *) user_data;
+
+	uint32_t index = plugin->port_index(port_symbol);
+	if (index != (uint32_t) -1) {
+		if (plugin->parameter_is_input(index) && plugin->parameter_is_control(index)) {
+			float *value;
+			*size = sizeof(float);
+			*type = plugin->_uri_map.uri_to_id(
+				NULL, LV2_ATOM__Float);
+			value = &plugin->_shadow_data[index];
+			cerr << "index="<< index << ",*size=" << *size << ",*type=" << *type << ",*value=" << *value << endl;
+
+			return value;
+		}
+		cerr << "port is not input control port! ";
+	}
+
+	cerr << "returning NULL!" << endl;
+	*size = *type = 0;
+	return NULL;
+}
+
+
 std::string
-LV2Plugin::do_save_preset(string /*name*/)
+LV2Plugin::do_save_preset(string name)
 {
-	return "";
+	cerr << "LV2Plugin::do_save_preset(" << name << ")" << endl;
+
+	string pset_uri = uri();
+	pset_uri += "#";
+	pset_uri += name;
+
+	string save_dir = Glib::build_filename(
+		Glib::get_home_dir(), 
+		Glib::build_filename(".lv2", "presets") 
+	);
+
+	LilvState* state = lilv_state_new_from_instance(
+		_impl->plugin,
+		_impl->instance,
+		_uri_map.urid_map(),
+		scratch_dir().c_str(),			// file_dir
+		NULL, 					// copy_dir
+		NULL, 					// link_dir
+		save_dir.c_str(),			// save_dir
+		lv2plugin_get_port_value,		// get_value
+		(void*) this,				// user_data
+		LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE,	// flags
+		_features				// features
+	);
+
+	lilv_state_set_label(state, name.c_str());
+	lilv_state_save(
+		_world.world,		// world
+		_uri_map.urid_map(),	// map
+		_uri_map.urid_unmap(),	// unmap
+		state,			// state
+		pset_uri.c_str(),	// uri
+		save_dir.c_str(),	// dir
+		(name + ".ttl").c_str()	// filename
+
+	);
+
+	lilv_state_free(state);
+	return pset_uri;
 }
 
 void
-LV2Plugin::do_remove_preset(string /*name*/)
-{}
+LV2Plugin::do_remove_preset(string name)
+{
+	string preset_file = Glib::build_filename(
+		Glib::get_home_dir(), 
+		Glib::build_filename(
+			Glib::build_filename(".lv2", "presets"),
+			name + ".ttl"
+		)
+	);
+	unlink(preset_file.c_str());
 
+}
+
 bool
 LV2Plugin::has_editor() const
 {

lv2-save-preset.patch (4,571 bytes)   

colinf

2012-05-22 17:22

updater   ~0013275

A small update, and another patch.

The update, 'lv2-save-preset.patch', is as before, but enables the display of '*' in the top pane of LV2 plugin windows when the settings have changed from the current preset.

The other patch, 'plugin-presets-allow-clear.patch', adds an 'empty' preset to the preset drop-down list, so that plugins can be set back to the 'no preset' state rather than being stuck in the state of 'preset loaded and modified'. This should apply to all plugin types, though I've only tried it on LV2 and LADSPA.

2012-05-28 12:13

 

lv2-save-preset-r12462.patch (4,573 bytes)   
Index: libs/ardour/lv2_plugin.cc
===================================================================
--- libs/ardour/lv2_plugin.cc	(revision 12462)
+++ libs/ardour/lv2_plugin.cc	(working copy)
@@ -835,7 +835,6 @@
 bool
 LV2Plugin::load_preset(PresetRecord r)
 {
-	Plugin::load_preset(r);
 
 	std::map<std::string,uint32_t>::iterator it;
 
@@ -862,19 +861,98 @@
 	lilv_node_free(lv2_symbol);
 	lilv_node_free(lv2_port);
 
+	Plugin::load_preset(r);
+
 	return true;
 }
 
+const void*
+ARDOUR::lv2plugin_get_port_value(const char* port_symbol,
+                                 void*       user_data,
+                                 uint32_t*   size,
+                                 uint32_t*   type)
+{
+	// cerr << "get_port_value(" << port_symbol << ", ...) ... ";
+	LV2Plugin *plugin = (LV2Plugin *) user_data;
+
+	uint32_t index = plugin->port_index(port_symbol);
+	if (index != (uint32_t) -1) {
+		if (plugin->parameter_is_input(index) && plugin->parameter_is_control(index)) {
+			float *value;
+			*size = sizeof(float);
+			*type = plugin->_uri_map.uri_to_id(LV2_ATOM__Float);
+			value = &plugin->_shadow_data[index];
+			// cerr << "index="<< index << ",*size=" << *size << ",*type=" << *type << ",*value=" << *value << endl;
+
+			return value;
+		}
+		// cerr << "port is not input control port! ";
+	}
+
+	// cerr << "returning NULL!" << endl;
+	*size = *type = 0;
+	return NULL;
+}
+
+
 std::string
-LV2Plugin::do_save_preset(string /*name*/)
+LV2Plugin::do_save_preset(string name)
 {
-	return "";
+	// cerr << "LV2Plugin::do_save_preset(" << name << ")" << endl;
+
+	string pset_uri = uri();
+	pset_uri += "#";
+	pset_uri += name;
+
+	string save_dir = Glib::build_filename(
+		Glib::get_home_dir(), 
+		Glib::build_filename(".lv2", "presets") 
+	);
+
+	LilvState* state = lilv_state_new_from_instance(
+		_impl->plugin,
+		_impl->instance,
+		_uri_map.urid_map(),
+		scratch_dir().c_str(),			// file_dir
+		NULL, 					// copy_dir
+		NULL, 					// link_dir
+		save_dir.c_str(),			// save_dir
+		lv2plugin_get_port_value,		// get_value
+		(void*) this,				// user_data
+		LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE,	// flags
+		_features				// features
+	);
+
+	lilv_state_set_label(state, name.c_str());
+	lilv_state_save(
+		_world.world,		// world
+		_uri_map.urid_map(),	// map
+		_uri_map.urid_unmap(),	// unmap
+		state,			// state
+		pset_uri.c_str(),	// uri
+		save_dir.c_str(),	// dir
+		(name + ".ttl").c_str()	// filename
+
+	);
+
+	lilv_state_free(state);
+	return pset_uri;
 }
 
 void
-LV2Plugin::do_remove_preset(string /*name*/)
-{}
+LV2Plugin::do_remove_preset(string name)
+{
+	string preset_file = Glib::build_filename(
+		Glib::get_home_dir(), 
+		Glib::build_filename(
+			Glib::build_filename(".lv2", "presets"),
+			name + ".ttl"
+		)
+	);
+	unlink(preset_file.c_str());
 
+}
+
 bool
 LV2Plugin::has_editor() const
 {
Index: libs/ardour/ardour/lv2_plugin.h
===================================================================
--- libs/ardour/ardour/lv2_plugin.h	(revision 12462)
+++ libs/ardour/ardour/lv2_plugin.h	(working copy)
@@ -31,6 +31,13 @@
 
 namespace ARDOUR {
 
+// a callback function for lilv_state_new_from_instance(). friend of LV2Plugin
+// so we can pass an LV2Plugin* in user_data and access its private members.
+const void* lv2plugin_get_port_value(const char* port_symbol,
+                                     void*       user_data,
+                                     uint32_t*   size,
+                                     uint32_t*   type);
+
 class AudioEngine;
 class Session;
 
@@ -159,6 +166,11 @@
 	float*        _latency_control_port;  ///< Special output set by ardour
 	PBD::ID       _insert_id;
 
+	friend const void* lv2plugin_get_port_value(const char* port_symbol,
+	                                            void*       user_data,
+	                                            uint32_t*   size,
+	                                            uint32_t*   type);
+
 	typedef enum {
 		PORT_INPUT   = 1,
 		PORT_OUTPUT  = 1 << 1,
Index: gtk2_ardour/lv2_plugin_ui.cc
===================================================================
--- gtk2_ardour/lv2_plugin_ui.cc	(revision 12462)
+++ gtk2_ardour/lv2_plugin_ui.cc	(working copy)
@@ -268,6 +268,7 @@
 			_ardour_buttons_box->pack_end (save_button, false, false);
 			_ardour_buttons_box->pack_end (add_button, false, false);
 			_ardour_buttons_box->pack_end (_preset_combo, false, false);
+			_ardour_buttons_box->pack_end (_preset_modified, false, false);
 			_ardour_buttons_box->show_all();
 			pack_start(*_ardour_buttons_box, false, false);
 
lv2-save-preset-r12462.patch (4,573 bytes)   

colinf

2012-05-28 12:14

updater   ~0013314

Last edited: 2012-05-28 12:14

Updated patch to fix compilation after changes in r12462, in case anyone's interested.

paul

2012-06-19 16:02

administrator   ~0013609

committed at or before 12787. very nice work, thanks as usual.

tweed

2013-02-25 17:33

reporter   ~0014660

stupid question (?) is this now iplemented? 14088 doesn't save lv2 presets.
do i need to manually apply this patch? if so where? how?
thansk.

paul

2013-02-25 17:51

administrator   ~0014661

when a bug report has a note saying "committed a rev NNNN" it means that the patch or proposed change/fix was added to the source code repository already. it means that if you build Ardour directly from the repository, you will get the fix, and if not, it will show up in the next release.

in your case, you appear to be reporting that you believe that the fix does not work. correct?

colinf

2013-02-25 18:02

updater   ~0014662

The patch was committed last June, so saving LV2 presets should just work.

There is a small issue still, which is that if you save a preset, it sometimes won't show up in the list of presets until you quit and restart Ardour 3. I should probably make a separate bug report about that.

tweed

2013-02-25 20:12

reporter   ~0014663

@paul yes:
built 14088 with ./waf configure --lv2 --lxvst, ./waf, ./waf install (root)
open ardour, new session, add tracks, open calf comp (also tried invada comp),
then:
change parameters, add preset, change params again. add new preset, go back to 1st preset and there appears to be no change. close/reopen plugin -same.
@colinf: close/reopen A3 - presets gone.
also tried: change plugin params, add preset, save preset - same.

tweed

2013-02-26 20:27

reporter   ~0014664

14104: saved lv2 presets are there after close/reopen A3. awesome.

paul

2013-02-26 21:08

administrator   ~0014665

nothing was done to change this.

tweed

2013-02-26 22:21

reporter   ~0014666

thanks. have to figure out what I did different.

colinf

2015-09-18 15:29

updater   ~0017318

Closing old issues reported by me: these have long since been fixed.

Issue History

Date Modified Username Field Change
2012-02-08 18:14 colinf New Issue
2012-02-09 20:19 cth103 cost => 0.00
2012-02-09 20:19 cth103 Target Version => 3.0-beta3
2012-02-14 15:28 colinf Note Added: 0012797
2012-02-14 17:20 paul Target Version 3.0-beta3 => 3.0 beta4
2012-05-22 11:22 colinf File Added: lv2-save-preset-experimental.patch
2012-05-22 11:44 colinf Note Added: 0013274
2012-05-22 17:14 colinf File Added: plugin-presets-allow-clear.patch
2012-05-22 17:14 colinf File Added: lv2-save-preset.patch
2012-05-22 17:22 colinf Note Added: 0013275
2012-05-23 15:07 cth103 Target Version 3.0 beta4 => 3.0
2012-05-28 12:13 colinf File Added: lv2-save-preset-r12462.patch
2012-05-28 12:14 colinf Note Added: 0013314
2012-05-28 12:14 colinf Note Edited: 0013314
2012-06-19 16:02 paul Note Added: 0013609
2012-06-19 16:02 paul Status new => resolved
2012-06-19 16:02 paul Resolution open => fixed
2012-06-19 16:02 paul Assigned To => paul
2013-02-25 17:33 tweed Note Added: 0014660
2013-02-25 17:51 paul Note Added: 0014661
2013-02-25 18:02 colinf Note Added: 0014662
2013-02-25 20:12 tweed Note Added: 0014663
2013-02-26 20:27 tweed Note Added: 0014664
2013-02-26 21:08 paul Note Added: 0014665
2013-02-26 22:21 tweed Note Added: 0014666
2015-09-18 15:29 colinf Note Added: 0017318
2015-09-18 15:29 colinf Status resolved => closed