#include <cstdlib>
#include <cstring>
+#include <glib/gstdio.h>
#include <giomm/file.h>
#include <glib/gprintf.h>
#include <glibmm.h>
#include <boost/utility.hpp>
+#include "pbd/pathscanner.h"
#include "pbd/compose.h"
#include "pbd/error.h"
#include "pbd/xml++.h"
#include "ardour/types.h"
#include "ardour/utils.h"
#include "ardour/worker.h"
+#include "ardour/lv2_bundled_search_path.h"
#include "i18n.h"
#include <locale.h>
#include "lv2/lv2plug.in/ns/ext/state/state.h"
#include "lv2/lv2plug.in/ns/ext/time/time.h"
#include "lv2/lv2plug.in/ns/ext/worker/worker.h"
+#include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h"
#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
#ifdef HAVE_NEW_LV2
#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
LV2World ();
~LV2World ();
+ void load_bundled_plugins();
+
LilvWorld* world;
LilvNode* atom_AtomPort;
LilvNode* atom_supports;
LilvNode* ev_EventPort;
LilvNode* ext_logarithmic;
+ LilvNode* ext_notOnGUI;
LilvNode* lv2_AudioPort;
LilvNode* lv2_ControlPort;
LilvNode* lv2_InputPort;
LilvNode* lv2_OutputPort;
LilvNode* lv2_enumeration;
+ LilvNode* lv2_freewheeling;
LilvNode* lv2_inPlaceBroken;
LilvNode* lv2_integer;
+ LilvNode* lv2_reportsLatency;
LilvNode* lv2_sampleRate;
LilvNode* lv2_toggled;
LilvNode* midi_MidiEvent;
LilvNode* rdfs_comment;
+ LilvNode* rsz_minimumSize;
LilvNode* time_Position;
LilvNode* ui_GtkUI;
LilvNode* ui_external;
+ LilvNode* ui_externalkx;
+
+private:
+ bool _bundle_checked;
};
static LV2World _world;
_latency_control_port = 0;
_next_cycle_start = std::numeric_limits<framepos_t>::max();
_next_cycle_speed = 1.0;
- _block_length = _engine.frames_per_cycle();
+ _block_length = _engine.samples_per_cycle();
_seq_size = _engine.raw_buffer_size(DataType::MIDI);
_state_version = 0;
_was_activated = false;
for (uint32_t i = 0; i < num_ports; ++i) {
const LilvPort* port = lilv_plugin_get_port_by_index(_impl->plugin, i);
PortFlags flags = 0;
+ size_t minimumSize = 0;
if (lilv_port_is_a(_impl->plugin, port, _world.lv2_OutputPort)) {
flags |= PORT_OUTPUT;
flags |= PORT_POSITION;
}
}
+ LilvNodes* min_size_v = lilv_port_get_value(_impl->plugin, port, _world.rsz_minimumSize);
+ LilvNode* min_size = min_size_v ? lilv_nodes_get_first(min_size_v) : NULL;
+ if (min_size && lilv_node_is_int(min_size)) {
+ minimumSize = lilv_node_as_int(min_size);
+ }
+ lilv_nodes_free(min_size_v);
lilv_nodes_free(buffer_types);
lilv_nodes_free(atom_supports);
} else {
}
_port_flags.push_back(flags);
+ _port_minimumSize.push_back(minimumSize);
}
_control_data = new float[num_ports];
if (!_impl->ui) {
LILV_FOREACH(uis, i, uis) {
const LilvUI* ui = lilv_uis_get(uis, i);
- if (lilv_ui_is_a(ui, _world.ui_external)) {
+ if (lilv_ui_is_a(ui, _world.ui_externalkx)) {
_impl->ui = ui;
_impl->ui_type = _world.ui_external;
break;
}
+ if (lilv_ui_is_a(ui, _world.ui_external)) {
+ _impl->ui = ui;
+ _impl->ui_type = _world.ui_external;
+ }
}
}
}
if (!_impl->ui) {
return false;
}
- return lilv_ui_is_a(_impl->ui, _world.ui_external);
+ return lilv_ui_is_a(_impl->ui, _world.ui_external) || lilv_ui_is_a(_impl->ui, _world.ui_externalkx);
+}
+
+bool
+LV2Plugin::is_external_kx() const
+{
+ if (!_impl->ui) {
+ return false;
+ }
+ return lilv_ui_is_a(_impl->ui, _world.ui_externalkx);
}
bool
lilv_node_as_string(name))));
} else {
warning << string_compose(
- _("Plugin \"%1\% preset \"%2%\" is missing a label\n"),
+ _("Plugin \"%1\" preset \"%2\" is missing a label\n"),
lilv_node_as_string(lilv_plugin_get_uri(_impl->plugin)),
lilv_node_as_string(preset)) << endmsg;
}
name + ".ttl"
)
);
- unlink(preset_file.c_str());
+ ::g_unlink(preset_file.c_str());
}
bool
uint32_t size,
const uint8_t* body)
{
- const uint32_t buf_size = sizeof(UIMessage) + size;
- uint8_t buf[buf_size];
+ const uint32_t buf_size = sizeof(UIMessage) + size;
+ vector<uint8_t> buf(buf_size);
- UIMessage* msg = (UIMessage*)buf;
+ UIMessage* msg = (UIMessage*)&buf[0];
msg->index = index;
msg->protocol = protocol;
msg->size = size;
memcpy(msg + 1, body, size);
- return (dest->write(buf, buf_size) == buf_size);
+ return (dest->write(&buf[0], buf_size) == buf_size);
}
bool
const uint8_t* body)
{
if (!_from_ui) {
- _from_ui = new RingBuffer<uint8_t>(
- _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS);
+ size_t rbs = _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS;
+ /* buffer data communication from plugin UI to plugin instance.
+ * this buffer needs to potentially hold
+ * (port's minimumSize) * (audio-periods) / (UI-periods)
+ * bytes.
+ *
+ * e.g 48kSPS / 128fpp -> audio-periods = 375 Hz
+ * ui-periods = 25 Hz (SuperRapidScreenUpdate)
+ * default minimumSize = 32K (see LV2Plugin::allocate_atom_event_buffers()
+ * -> 15 * 32K
+ * it is safe to overflow (but the plugin state may be inconsistent).
+ */
+ rbs = max((size_t) 32768 * 6, rbs);
+ _from_ui = new RingBuffer<uint8_t>(rbs);
}
if (!write_to(_from_ui, index, protocol, size, body)) {
LV2Plugin::enable_ui_emmission()
{
if (!_to_ui) {
- _to_ui = new RingBuffer<uint8_t>(
- _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS);
+ /* see note in LV2Plugin::write_from_ui() */
+ size_t rbs = _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS;
+ rbs = max((size_t) 32768 * 8, rbs);
+ _to_ui = new RingBuffer<uint8_t>(rbs);
}
}
error << "Error reading from Plugin=>UI RingBuffer" << endmsg;
break;
}
- uint8_t body[msg.size];
- if (_to_ui->read(body, msg.size) != msg.size) {
+ vector<uint8_t> body(msg.size);
+ if (_to_ui->read(&body[0], msg.size) != msg.size) {
error << "Error reading from Plugin=>UI RingBuffer" << endmsg;
break;
}
- sink(controller, msg.index, msg.size, msg.protocol, body);
+ sink(controller, msg.index, msg.size, msg.protocol, &body[0]);
read_space -= sizeof(msg) + msg.size;
}
return -1;
}
+#ifndef NO_PLUGIN_STATE
+
if (version < 3000) {
nodes = node.children("port");
} else {
}
latency_compute_run();
+#endif
return Plugin::set_state(node, version);
}
LV2Plugin::describe_parameter(Evoral::Parameter which)
{
if (( which.type() == PluginAutomation) && ( which.id() < parameter_count()) ) {
+
+ if (lilv_port_has_property(_impl->plugin,
+ lilv_plugin_get_port_by_index(_impl->plugin, which.id()), _world.ext_notOnGUI)) {
+ return X_("hidden");
+ }
+
+ if (lilv_port_has_property(_impl->plugin,
+ lilv_plugin_get_port_by_index(_impl->plugin, which.id()), _world.lv2_freewheeling)) {
+ return X_("hidden");
+ }
+
+ if (lilv_port_has_property(_impl->plugin,
+ lilv_plugin_get_port_by_index(_impl->plugin, which.id()), _world.lv2_reportsLatency)) {
+ return X_("latency");
+ }
+
LilvNode* name = lilv_port_get_name(_impl->plugin,
lilv_plugin_get_port_by_index(_impl->plugin, which.id()));
string ret(lilv_node_as_string(name));
*/
int count_atom_out = 0;
int count_atom_in = 0;
+ int minimumSize = 32768; // TODO use a per-port minimum-size
for (uint32_t i = 0; i < lilv_plugin_get_num_ports(p); ++i) {
const LilvPort* port = lilv_plugin_get_port_by_index(p, i);
if (lilv_port_is_a(p, port, _world.atom_AtomPort)) {
if (lilv_port_is_a(p, port, _world.lv2_OutputPort)) {
count_atom_out++;
}
+ LilvNodes* min_size_v = lilv_port_get_value(_impl->plugin, port, _world.rsz_minimumSize);
+ LilvNode* min_size = min_size_v ? lilv_nodes_get_first(min_size_v) : NULL;
+ if (min_size && lilv_node_is_int(min_size)) {
+ minimumSize = std::max(minimumSize, lilv_node_as_int(min_size));
+ }
+ lilv_nodes_free(min_size_v);
}
lilv_nodes_free(buffer_types);
lilv_nodes_free(atom_supports);
DEBUG_TRACE(DEBUG::LV2, string_compose("allocate %1 atom_ev_buffers\n", total_atom_buffers));
_atom_ev_buffers = (LV2_Evbuf**) malloc((total_atom_buffers + 1) * sizeof(LV2_Evbuf*));
for (int i = 0; i < total_atom_buffers; ++i ) {
- _atom_ev_buffers[i] = lv2_evbuf_new(32768, LV2_EVBUF_ATOM,
+ _atom_ev_buffers[i] = lv2_evbuf_new(minimumSize, LV2_EVBUF_ATOM,
LV2Plugin::urids.atom_Chunk, LV2Plugin::urids.atom_Sequence);
}
_atom_ev_buffers[total_atom_buffers] = 0;
index = out_map.get(DataType::MIDI, midi_out_index++, &valid);
}
if (valid && bufs.count().n_midi() > index) {
+ /* Note, ensure_lv2_bufsize() is not RT safe!
+ * However free()/alloc() is only called if a
+ * plugin requires a rsz:minimumSize buffersize
+ * and the existing buffer if smaller.
+ */
+ bufs.ensure_lv2_bufsize((flags & PORT_INPUT), index, _port_minimumSize[port_index]);
_ev_buffers[port_index] = bufs.get_lv2_midi(
(flags & PORT_INPUT), index, (flags & PORT_EVENT));
}
: m;
// Now merge MIDI and any transport events into the buffer
- LV2_Evbuf_Iterator i = lv2_evbuf_end(_ev_buffers[port_index]);
const uint32_t type = LV2Plugin::urids.midi_MidiEvent;
const framepos_t tend = _session.transport_frame() + nframes;
++metric_i;
}
} else if (!valid) {
// Nothing we understand or care about, connect to scratch
- _ev_buffers[port_index] = silent_bufs.get_lv2_midi(
+ _ev_buffers[port_index] = scratch_bufs.get_lv2_midi(
(flags & PORT_INPUT), 0, (flags & PORT_EVENT));
}
buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
error << "Error reading from UI=>Plugin RingBuffer" << endmsg;
break;
}
- uint8_t body[msg.size];
- if (_from_ui->read(body, msg.size) != msg.size) {
+ vector<uint8_t> body(msg.size);
+ if (_from_ui->read(&body[0], msg.size) != msg.size) {
error << "Error reading from UI=>Plugin RingBuffer" << endmsg;
break;
}
if (msg.protocol == urids.atom_eventTransfer) {
LV2_Evbuf* buf = _ev_buffers[msg.index];
LV2_Evbuf_Iterator i = lv2_evbuf_end(buf);
- const LV2_Atom* const atom = (const LV2_Atom*)body;
+ const LV2_Atom* const atom = (const LV2_Atom*)&body[0];
if (!lv2_evbuf_write(&i, nframes, 0, atom->type, atom->size,
(const uint8_t*)(atom + 1))) {
error << "Failed to write data to LV2 event buffer\n";
PortFlags flags = _port_flags[port_index];
bool valid = false;
- // Flush MIDI (write back to Ardour MIDI buffers)
- if ((flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
+ /* TODO ask drobilla about comment
+ * "Make Ardour event buffers generic so plugins can communicate"
+ * in libs/ardour/buffer_set.cc:310
+ *
+ * ideally the user could choose which of the following two modes
+ * to use (e.g. instrument/effect chains MIDI OUT vs MIDI TRHU).
+ *
+ * This implementation follows the discussion on IRC Mar 16 2013 16:47 UTC
+ * 16:51 < drobilla> rgareus: [..] i.e always replace with MIDI output [of LV2 plugin] if it's there
+ * 16:52 < drobilla> rgareus: That would probably be good enough [..] to make users not complain
+ * for quite a while at least ;)
+ */
+ // copy output of LV2 plugin's MIDI port to Ardour MIDI buffers -- MIDI OUT
+ if ((flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE|PORT_MIDI))) {
+ const uint32_t buf_index = out_map.get(
+ DataType::MIDI, midi_out_index++, &valid);
+ if (valid) {
+ bufs.forward_lv2_midi(_ev_buffers[port_index], buf_index);
+ }
+ }
+ // Flush MIDI (write back to Ardour MIDI buffers) -- MIDI THRU
+ else if ((flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
const uint32_t buf_index = out_map.get(
DataType::MIDI, midi_out_index++, &valid);
if (valid) {
return port;
}
+static bool lv2_filter (const string& str, void* /*arg*/)
+{
+ /* Not a dotfile, has a prefix before a period, suffix is "lv2" */
+
+ return str[0] != '.' && (str.length() > 3 && str.find (".lv2") == (str.length() - 4));
+}
+
+
LV2World::LV2World()
: world(lilv_world_new())
+ , _bundle_checked(false)
{
lilv_world_load_all(world);
+
atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort);
atom_Chunk = lilv_new_uri(world, LV2_ATOM__Chunk);
atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence);
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, LV2_PORT_PROPS__logarithmic);
+ ext_notOnGUI = lilv_new_uri(world, LV2_PORT_PROPS__notOnGUI);
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, LV2_CORE__inPlaceBroken);
lv2_integer = lilv_new_uri(world, LV2_CORE__integer);
+ lv2_reportsLatency = lilv_new_uri(world, LV2_CORE__reportsLatency);
lv2_sampleRate = lilv_new_uri(world, LV2_CORE__sampleRate);
lv2_toggled = lilv_new_uri(world, LV2_CORE__toggled);
lv2_enumeration = lilv_new_uri(world, LV2_CORE__enumeration);
+ lv2_freewheeling = lilv_new_uri(world, LV2_CORE__freeWheeling);
midi_MidiEvent = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment");
+ rsz_minimumSize = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize);
time_Position = lilv_new_uri(world, LV2_TIME__Position);
ui_GtkUI = lilv_new_uri(world, LV2_UI__GtkUI);
ui_external = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/ui#external");
+ ui_externalkx = lilv_new_uri(world, "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget");
}
LV2World::~LV2World()
{
+ lilv_node_free(ui_externalkx);
lilv_node_free(ui_external);
lilv_node_free(ui_GtkUI);
+ lilv_node_free(time_Position);
+ lilv_node_free(rsz_minimumSize);
+ lilv_node_free(rdfs_comment);
lilv_node_free(midi_MidiEvent);
+ lilv_node_free(lv2_enumeration);
+ lilv_node_free(lv2_freewheeling);
lilv_node_free(lv2_toggled);
lilv_node_free(lv2_sampleRate);
+ lilv_node_free(lv2_reportsLatency);
lilv_node_free(lv2_integer);
lilv_node_free(lv2_inPlaceBroken);
lilv_node_free(lv2_OutputPort);
lilv_node_free(lv2_InputPort);
lilv_node_free(lv2_ControlPort);
lilv_node_free(lv2_AudioPort);
+ lilv_node_free(ext_notOnGUI);
lilv_node_free(ext_logarithmic);
lilv_node_free(ev_EventPort);
+ lilv_node_free(atom_supports);
lilv_node_free(atom_eventTransfer);
lilv_node_free(atom_bufferType);
lilv_node_free(atom_Sequence);
lilv_node_free(atom_AtomPort);
}
+void
+LV2World::load_bundled_plugins()
+{
+ if (!_bundle_checked) {
+ cout << "Scanning folders for bundled LV2s: " << ARDOUR::lv2_bundled_search_path().to_string() << endl;
+ PathScanner scanner;
+ vector<string *> *plugin_objects = scanner (ARDOUR::lv2_bundled_search_path().to_string(), lv2_filter, 0, true, true);
+ if (plugin_objects) {
+ for ( vector<string *>::iterator x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
+#ifdef PLATFORM_WINDOWS
+ string uri = "file:///" + **x + "/";
+#else
+ string uri = "file://" + **x + "/";
+#endif
+ LilvNode *node = lilv_new_uri(world, uri.c_str());
+ lilv_world_load_bundle(world, node);
+ lilv_node_free(node);
+ }
+ }
+ delete (plugin_objects);
+
+ _bundle_checked = true;
+ }
+}
+
LV2PluginInfo::LV2PluginInfo (const void* c_plugin)
: _c_plugin(c_plugin)
{
PluginInfoList*
LV2PluginInfo::discover()
{
+ _world.load_bundled_plugins();
+
PluginInfoList* plugs = new PluginInfoList;
const LilvPlugins* plugins = lilv_world_get_all_plugins(_world.world);