summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-02-25 08:43:23 +0000
committerDavid Robillard <d@drobilla.net>2012-02-25 08:43:23 +0000
commit4d7810dee89b36107b61f4124fd5ce3908abd705 (patch)
treef53d514f8df2578601c16b0810b1dcde3214d1d6
parent1cf6e280b877127e76733589f990c0b438054e36 (diff)
Full round-trip message communication between LV2 UIs and plugins.
Still a little bit rough around the edges, but it works. This can be tested with the eg-sampler plugin from LV2 svn (whose UI can load different samples). git-svn-id: svn://localhost/ardour2/branches/3.0@11519 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/lv2_plugin_ui.cc49
-rw-r--r--gtk2_ardour/lv2_plugin_ui.h19
-rw-r--r--libs/ardour/ardour/lv2_plugin.h40
-rw-r--r--libs/ardour/lv2_plugin.cc186
4 files changed, 252 insertions, 42 deletions
diff --git a/gtk2_ardour/lv2_plugin_ui.cc b/gtk2_ardour/lv2_plugin_ui.cc
index 751985871d..70fbf448c6 100644
--- a/gtk2_ardour/lv2_plugin_ui.cc
+++ b/gtk2_ardour/lv2_plugin_ui.cc
@@ -40,23 +40,45 @@ using namespace PBD;
static SuilHost* ui_host = NULL;
void
-LV2PluginUI::lv2_ui_write(void* controller,
- uint32_t port_index,
- uint32_t /*buffer_size*/,
- uint32_t /*format*/,
- const void* buffer)
+LV2PluginUI::write_from_ui(void* controller,
+ uint32_t port_index,
+ uint32_t buffer_size,
+ uint32_t format,
+ const void* buffer)
{
LV2PluginUI* me = (LV2PluginUI*)controller;
+ if (format == 0) {
+ if (port_index >= me->_controllables.size()) {
+ return;
+ }
- if (port_index >= me->_controllables.size()) {
- return;
+ boost::shared_ptr<AutomationControl> ac = me->_controllables[port_index];
+ if (ac) {
+ ac->set_value(*(float*)buffer);
+ }
+ } else if (format == me->_lv2->atom_eventTransfer()) {
+ me->_lv2->write_from_ui(port_index, format, buffer_size, (uint8_t*)buffer);
}
+}
- boost::shared_ptr<AutomationControl> ac = me->_controllables[port_index];
+void
+LV2PluginUI::write_to_ui(void* controller,
+ uint32_t port_index,
+ uint32_t buffer_size,
+ uint32_t format,
+ const void* buffer)
+{
+ LV2PluginUI* me = (LV2PluginUI*)controller;
+ fprintf(stderr, "MESSAGE FROM PLUGIN %u BYTES\n", buffer_size);
+ suil_instance_port_event((SuilInstance*)me->_inst,
+ port_index, buffer_size, format, buffer);
+}
- if (ac) {
- ac->set_value(*(float*)buffer);
- }
+bool
+LV2PluginUI::update_timeout()
+{
+ _lv2->emit_to_ui(this, &LV2PluginUI::write_to_ui);
+ return true;
}
void
@@ -173,7 +195,7 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
}
if (!ui_host) {
- ui_host = suil_host_new(LV2PluginUI::lv2_ui_write, NULL, NULL, NULL);
+ ui_host = suil_host_new(LV2PluginUI::write_from_ui, NULL, NULL, NULL);
}
const char* container_type = (is_external_ui)
? NS_UI "external"
@@ -245,6 +267,9 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
}
}
}
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(*this, &LV2PluginUI::update_timeout), 500);
}
void
diff --git a/gtk2_ardour/lv2_plugin_ui.h b/gtk2_ardour/lv2_plugin_ui.h
index b9d644081c..8500312856 100644
--- a/gtk2_ardour/lv2_plugin_ui.h
+++ b/gtk2_ardour/lv2_plugin_ui.h
@@ -79,12 +79,19 @@ class LV2PluginUI : public PlugUIBase, public Gtk::VBox
void* _inst;
- static void lv2_ui_write(
- void* controller,
- uint32_t port_index,
- uint32_t buffer_size,
- uint32_t format,
- const void* buffer);
+ static void write_from_ui(void* controller,
+ uint32_t port_index,
+ uint32_t buffer_size,
+ uint32_t format,
+ const void* buffer);
+
+ static void write_to_ui(void* controller,
+ uint32_t port_index,
+ uint32_t buffer_size,
+ uint32_t format,
+ const void* buffer);
+
+ bool update_timeout();
void lv2ui_instantiate(const std::string& title);
void lv2ui_free();
diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h
index 77ad8ead24..65b63269e1 100644
--- a/libs/ardour/ardour/lv2_plugin.h
+++ b/libs/ardour/ardour/lv2_plugin.h
@@ -26,6 +26,7 @@
#include "ardour/plugin.h"
#include "ardour/uri_map.h"
+#include "pbd/ringbuffer.h"
namespace ARDOUR {
@@ -113,6 +114,20 @@ class LV2Plugin : public ARDOUR::Plugin
bool has_editor () const;
+ uint32_t atom_eventTransfer() const;
+
+ void write_from_ui(uint32_t index, uint32_t protocol, uint32_t size, uint8_t* body);
+
+ typedef void UIMessageSink(void* controller,
+ uint32_t index,
+ uint32_t size,
+ uint32_t format,
+ const void* buffer);
+
+ void emit_to_ui(void* controller, UIMessageSink sink);
+
+ static URIMap _uri_map;
+
private:
struct Impl;
Impl* _impl;
@@ -122,6 +137,7 @@ class LV2Plugin : public ARDOUR::Plugin
float* _control_data;
float* _shadow_data;
float* _defaults;
+ LV2_Evbuf** _ev_buffers;
float* _latency_control_port;
PBD::ID _insert_id;
@@ -139,6 +155,28 @@ class LV2Plugin : public ARDOUR::Plugin
std::vector<PortFlags> _port_flags;
std::map<std::string,uint32_t> _port_indices;
+ /// Message send to/from UI via ports
+ struct UIMessage {
+ uint32_t index;
+ uint32_t protocol;
+ uint32_t size;
+ };
+
+ void write_to_ui(uint32_t index,
+ uint32_t protocol,
+ uint32_t size,
+ uint8_t* body);
+
+ void write_to(RingBuffer<uint8_t>* dest,
+ uint32_t index,
+ uint32_t protocol,
+ uint32_t size,
+ uint8_t* body);
+
+ // Created on demand so the space is only consumed if necessary
+ RingBuffer<uint8_t>* _to_ui;
+ RingBuffer<uint8_t>* _from_ui;
+
typedef struct {
const void* (*extension_data) (const char* uri);
} LV2_DataAccess;
@@ -153,10 +191,10 @@ class LV2Plugin : public ARDOUR::Plugin
bool _was_activated;
bool _has_state_interface;
- static URIMap _uri_map;
static uint32_t _midi_event_type_ev;
static uint32_t _midi_event_type;
static uint32_t _sequence_type;
+ static uint32_t _event_transfer_type;
static uint32_t _state_path_type;
const std::string plugin_dir () const;
diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc
index 3ff3d06337..2907c2f904 100644
--- a/libs/ardour/lv2_plugin.cc
+++ b/libs/ardour/lv2_plugin.cc
@@ -76,6 +76,8 @@ uint32_t LV2Plugin::_midi_event_type = _uri_map.uri_to_id(
"http://lv2plug.in/ns/ext/midi#MidiEvent");
uint32_t LV2Plugin::_sequence_type = _uri_map.uri_to_id(
NULL, LV2_ATOM__Sequence);
+uint32_t LV2Plugin::_event_transfer_type = _uri_map.uri_to_id(
+ NULL, LV2_ATOM__eventTransfer);
uint32_t LV2Plugin::_state_path_type = _uri_map.uri_to_id(
NULL, LV2_STATE_PATH_URI);
@@ -89,6 +91,7 @@ public:
LilvNode* atom_MessagePort;
LilvNode* atom_Sequence;
LilvNode* atom_bufferType;
+ LilvNode* atom_eventTransfer;
LilvNode* ev_EventPort;
LilvNode* ext_logarithmic;
LilvNode* lv2_AudioPort;
@@ -157,8 +160,11 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate)
_impl->plugin = (LilvPlugin*)c_plugin;
_impl->ui = NULL;
_impl->ui_type = NULL;
+ _to_ui = NULL;
+ _from_ui = NULL;
_control_data = 0;
_shadow_data = 0;
+ _ev_buffers = 0;
_latency_control_port = 0;
_state_version = 0;
_was_activated = false;
@@ -265,6 +271,8 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate)
_control_data = new float[num_ports];
_shadow_data = new float[num_ports];
_defaults = new float[num_ports];
+ _ev_buffers = new LV2_Evbuf*[num_ports];
+ memset(_ev_buffers, 0, sizeof(LV2_Evbuf*) * num_ports);
for (uint32_t i = 0; i < num_ports; ++i) {
const LilvPort* port = lilv_plugin_get_port_by_index(plugin, i);
@@ -354,8 +362,12 @@ LV2Plugin::~LV2Plugin ()
lilv_node_free(_impl->name);
lilv_node_free(_impl->author);
+ delete _to_ui;
+ delete _from_ui;
+
delete [] _control_data;
delete [] _shadow_data;
+ delete [] _ev_buffers;
}
bool
@@ -751,6 +763,85 @@ LV2Plugin::has_editor() const
return _impl->ui != NULL;
}
+uint32_t
+LV2Plugin::atom_eventTransfer() const
+{
+ return _event_transfer_type;
+}
+
+void
+LV2Plugin::write_to(RingBuffer<uint8_t>* dest,
+ uint32_t index,
+ uint32_t protocol,
+ uint32_t size,
+ uint8_t* body)
+{
+ const uint32_t buf_size = sizeof(UIMessage) + size;
+ uint8_t buf[buf_size];
+
+ UIMessage* msg = (UIMessage*)buf;
+ msg->index = index;
+ msg->protocol = protocol;
+ msg->size = size;
+ memcpy(msg + 1, body, size);
+
+ if (dest->write(buf, buf_size) != buf_size) {
+ error << "Error writing to UI=>Plugin RingBuffer" << endmsg;
+ }
+}
+
+void
+LV2Plugin::write_from_ui(uint32_t index,
+ uint32_t protocol,
+ uint32_t size,
+ uint8_t* body)
+{
+ if (!_from_ui) {
+ _from_ui = new RingBuffer<uint8_t>(4096);
+ }
+
+ write_to(_from_ui, index, protocol, size, body);
+}
+
+void
+LV2Plugin::write_to_ui(uint32_t index,
+ uint32_t protocol,
+ uint32_t size,
+ uint8_t* body)
+{
+ if (!_to_ui) {
+ _to_ui = new RingBuffer<uint8_t>(4096);
+ }
+
+ write_to(_to_ui, index, protocol, size, body);
+}
+
+void
+LV2Plugin::emit_to_ui(void* controller, UIMessageSink sink)
+{
+ if (!_to_ui) {
+ return;
+ }
+
+ uint32_t read_space = _to_ui->read_space();
+ while (read_space > sizeof(UIMessage)) {
+ UIMessage msg;
+ if (_to_ui->read((uint8_t*)&msg, sizeof(msg)) != sizeof(msg)) {
+ error << "Error reading from Plugin=>UI RingBuffer" << endmsg;
+ break;
+ }
+ uint8_t body[msg.size];
+ if (_to_ui->read(body, msg.size) != msg.size) {
+ error << "Error reading from Plugin=>UI RingBuffer" << endmsg;
+ break;
+ }
+
+ sink(controller, msg.index, msg.size, msg.protocol, body);
+
+ read_space -= sizeof(msg) + msg.size;
+ }
+}
+
void
LV2Plugin::set_insert_info(const PluginInsert* insert)
{
@@ -994,14 +1085,16 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
const uint32_t atom_type = (flags & PORT_MESSAGE) ? _sequence_type : 0;
if (flags & PORT_INPUT) {
index = in_map.get(DataType::MIDI, midi_in_index++, &valid);
- buf = (valid && bufs.count().n_midi() > index)
- ? lv2_evbuf_get_buffer(bufs.get_lv2_midi(true, index, atom_type))
- : lv2_evbuf_get_buffer(silent_bufs.get_lv2_midi(true, 0, atom_type));
+ _ev_buffers[port_index] = (valid && bufs.count().n_midi() > index)
+ ? bufs.get_lv2_midi(true, index, atom_type)
+ : silent_bufs.get_lv2_midi(true, 0, atom_type);
+ buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
} else {
index = out_map.get(DataType::MIDI, midi_out_index++, &valid);
- buf = (valid && bufs.count().n_midi() > index)
- ? lv2_evbuf_get_buffer(bufs.get_lv2_midi(false, index, atom_type))
- : lv2_evbuf_get_buffer(scratch_bufs.get_lv2_midi(false, 0, atom_type));
+ _ev_buffers[port_index] = (valid && bufs.count().n_midi() > index)
+ ? bufs.get_lv2_midi(false, index, atom_type)
+ : scratch_bufs.get_lv2_midi(false, 0, atom_type);
+ buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
}
} else {
continue; // Control port, leave buffer alone
@@ -1009,17 +1102,62 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
lilv_instance_connect_port(_impl->instance, port_index, buf);
}
+ // Read messages from UI and push into appropriate buffers
+ if (_from_ui) {
+ uint32_t read_space = _from_ui->read_space();
+ while (read_space > sizeof(UIMessage)) {
+ UIMessage msg;
+ if (_from_ui->read((uint8_t*)&msg, sizeof(msg)) != sizeof(msg)) {
+ error << "Error reading from UI=>Plugin RingBuffer" << endmsg;
+ break;
+ }
+ uint8_t body[msg.size];
+ if (_from_ui->read(body, msg.size) != msg.size) {
+ error << "Error reading from UI=>Plugin RingBuffer" << endmsg;
+ break;
+ }
+ if (msg.protocol == _event_transfer_type) {
+ LV2_Evbuf* buf = _ev_buffers[msg.index];
+ LV2_Evbuf_Iterator i = lv2_evbuf_end(buf);
+ const LV2_Atom* const atom = (const LV2_Atom*)body;
+ lv2_evbuf_write(&i, nframes, 0, atom->type, atom->size,
+ (const uint8_t*)LV2_ATOM_BODY(atom));
+ } else {
+ error << "Received unknown message type from UI" << endmsg;
+ }
+ read_space -= sizeof(UIMessage) + msg.size;
+ }
+ }
+
run(nframes);
midi_out_index = 0;
for (uint32_t port_index = 0; port_index < num_ports; ++port_index) {
- if (parameter_is_event(port_index) && parameter_is_output(port_index)) {
+ PortFlags flags = _port_flags[port_index];
+
+ // Flush MIDI (write back to Ardour MIDI buffers)
+ if ((flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_MESSAGE))) {
const uint32_t buf_index = out_map.get(
DataType::MIDI, midi_out_index++, &valid);
if (valid) {
bufs.flush_lv2_midi(true, buf_index);
}
}
+
+ // Write messages to UI
+ if ((flags & PORT_OUTPUT) && (flags & PORT_MESSAGE)) {
+ LV2_Evbuf* buf = _ev_buffers[port_index];
+ for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(buf);
+ lv2_evbuf_is_valid(i);
+ i = lv2_evbuf_next(i)) {
+ uint32_t frames, subframes, type, size;
+ uint8_t* data;
+ lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
+ write_to_ui(port_index, _event_transfer_type,
+ size + sizeof(LV2_Atom),
+ data - sizeof(LV2_Atom));
+ }
+ }
}
cycles_t now = get_cycles();
@@ -1160,22 +1298,23 @@ LV2World::LV2World()
: world(lilv_world_new())
{
lilv_world_load_all(world);
- atom_MessagePort = lilv_new_uri(world, LV2_ATOM__MessagePort);
- atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence);
- atom_bufferType = lilv_new_uri(world, LV2_ATOM__bufferType);
- ev_EventPort = lilv_new_uri(world, LILV_URI_EVENT_PORT);
- ext_logarithmic = lilv_new_uri(world, "http://lv2plug.in/ns/dev/extportinfo#logarithmic");
- lv2_AudioPort = lilv_new_uri(world, LILV_URI_AUDIO_PORT);
- lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
- lv2_InputPort = lilv_new_uri(world, LILV_URI_INPUT_PORT);
- lv2_OutputPort = lilv_new_uri(world, LILV_URI_OUTPUT_PORT);
- lv2_inPlaceBroken = lilv_new_uri(world, LILV_NS_LV2 "inPlaceBroken");
- lv2_integer = lilv_new_uri(world, LILV_NS_LV2 "integer");
- lv2_sampleRate = lilv_new_uri(world, LILV_NS_LV2 "sampleRate");
- lv2_toggled = lilv_new_uri(world, LILV_NS_LV2 "toggled");
- midi_MidiEvent = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
- ui_GtkUI = lilv_new_uri(world, NS_UI "GtkUI");
- ui_external = lilv_new_uri(world, NS_UI "external");
+ atom_MessagePort = lilv_new_uri(world, LV2_ATOM__MessagePort);
+ atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence);
+ atom_bufferType = lilv_new_uri(world, LV2_ATOM__bufferType);
+ atom_eventTransfer = lilv_new_uri(world, LV2_ATOM__eventTransfer);
+ ev_EventPort = lilv_new_uri(world, LILV_URI_EVENT_PORT);
+ ext_logarithmic = lilv_new_uri(world, "http://lv2plug.in/ns/dev/extportinfo#logarithmic");
+ lv2_AudioPort = lilv_new_uri(world, LILV_URI_AUDIO_PORT);
+ lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
+ lv2_InputPort = lilv_new_uri(world, LILV_URI_INPUT_PORT);
+ lv2_OutputPort = lilv_new_uri(world, LILV_URI_OUTPUT_PORT);
+ lv2_inPlaceBroken = lilv_new_uri(world, LILV_NS_LV2 "inPlaceBroken");
+ lv2_integer = lilv_new_uri(world, LILV_NS_LV2 "integer");
+ lv2_sampleRate = lilv_new_uri(world, LILV_NS_LV2 "sampleRate");
+ lv2_toggled = lilv_new_uri(world, LILV_NS_LV2 "toggled");
+ midi_MidiEvent = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
+ ui_GtkUI = lilv_new_uri(world, NS_UI "GtkUI");
+ ui_external = lilv_new_uri(world, NS_UI "external");
}
LV2World::~LV2World()
@@ -1193,6 +1332,7 @@ LV2World::~LV2World()
lilv_node_free(lv2_AudioPort);
lilv_node_free(ext_logarithmic);
lilv_node_free(ev_EventPort);
+ lilv_node_free(atom_eventTransfer);
lilv_node_free(atom_bufferType);
lilv_node_free(atom_Sequence);
lilv_node_free(atom_MessagePort);