#include <cstdlib>
#include <cstring>
-#include <glib/gstdio.h>
+#include <pbd/gstdio_compat.h>
#include <glib/gprintf.h>
#include <glibmm.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"
+#include "lv2/lv2plug.in/ns/extensions/units/units.h"
#include "lv2/lv2plug.in/ns/ext/patch/patch.h"
#ifdef HAVE_LV2_1_2_0
#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
#include <suil/suil.h>
#endif
-// Compatibility for lv2-1.0.0
+// Compatibility for old LV2
#ifndef LV2_ATOM_CONTENTS_CONST
#define LV2_ATOM_CONTENTS_CONST(type, atom) \
((const void*)((const uint8_t*)(atom) + sizeof(type)))
#ifndef LV2_ATOM_BODY_CONST
#define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(LV2_Atom, atom)
#endif
+#ifndef LV2_PATCH__property
+#define LV2_PATCH__property LV2_PATCH_PREFIX "property"
+#endif
+#ifndef LV2_PATCH__value
+#define LV2_PATCH__value LV2_PATCH_PREFIX "value"
+#endif
+#ifndef LV2_PATCH__writable
+#define LV2_PATCH__writable LV2_PATCH_PREFIX "writable"
+#endif
/** The number of MIDI buffers that will fit in a UI/worker comm buffer.
This needs to be roughly the number of cycles the UI will get around to
using namespace ARDOUR;
using namespace PBD;
-URIMap LV2Plugin::_uri_map;
-
-LV2Plugin::URIDs LV2Plugin::urids = {
- _uri_map.uri_to_id(LV2_ATOM__Chunk),
- _uri_map.uri_to_id(LV2_ATOM__Path),
- _uri_map.uri_to_id(LV2_ATOM__Sequence),
- _uri_map.uri_to_id(LV2_ATOM__eventTransfer),
- _uri_map.uri_to_id(LV2_ATOM__URID),
- _uri_map.uri_to_id(LV2_ATOM__Blank),
- _uri_map.uri_to_id(LV2_ATOM__Object),
- _uri_map.uri_to_id(LV2_LOG__Error),
- _uri_map.uri_to_id(LV2_LOG__Note),
- _uri_map.uri_to_id(LV2_LOG__Warning),
- _uri_map.uri_to_id(LV2_MIDI__MidiEvent),
- _uri_map.uri_to_id(LV2_TIME__Position),
- _uri_map.uri_to_id(LV2_TIME__bar),
- _uri_map.uri_to_id(LV2_TIME__barBeat),
- _uri_map.uri_to_id(LV2_TIME__beatUnit),
- _uri_map.uri_to_id(LV2_TIME__beatsPerBar),
- _uri_map.uri_to_id(LV2_TIME__beatsPerMinute),
- _uri_map.uri_to_id(LV2_TIME__frame),
- _uri_map.uri_to_id(LV2_TIME__speed),
- _uri_map.uri_to_id(LV2_PATCH__Get),
- _uri_map.uri_to_id(LV2_PATCH__Set),
- _uri_map.uri_to_id(LV2_PATCH__property),
- _uri_map.uri_to_id(LV2_PATCH__value)
-};
-
class LV2World : boost::noncopyable {
public:
LV2World ();
LilvNode* lv2_freewheeling;
LilvNode* lv2_inPlaceBroken;
LilvNode* lv2_integer;
+ LilvNode* lv2_default;
+ LilvNode* lv2_minimum;
+ LilvNode* lv2_maximum;
LilvNode* lv2_reportsLatency;
LilvNode* lv2_sampleRate;
LilvNode* lv2_toggled;
LilvNode* ui_GtkUI;
LilvNode* ui_external;
LilvNode* ui_externalkx;
+ LilvNode* units_hz;
+ LilvNode* units_db;
LilvNode* units_unit;
+ LilvNode* units_render;
LilvNode* units_midiNote;
LilvNode* patch_writable;
LilvNode* patch_Message;
+#ifdef HAVE_LV2_1_2_0
+ LilvNode* bufz_powerOf2BlockLength;
+ LilvNode* bufz_fixedBlockLength;
+ LilvNode* bufz_nominalBlockLength;
+#endif
private:
bool _bundle_checked;
{
char* str = NULL;
const int ret = g_vasprintf(&str, fmt, args);
- if (type == LV2Plugin::urids.log_Error) {
+ if (type == URIMap::instance().urids.log_Error) {
error << str << endmsg;
- } else if (type == LV2Plugin::urids.log_Warning) {
+ } else if (type == URIMap::instance().urids.log_Warning) {
warning << str << endmsg;
- } else if (type == LV2Plugin::urids.log_Note) {
+ } else if (type == URIMap::instance().urids.log_Note) {
info << str << endmsg;
}
// TODO: Toggleable log:Trace message support
struct LV2Plugin::Impl {
Impl() : plugin(0), ui(0), ui_type(0), name(0), author(0), instance(0)
, work_iface(0)
+ , opts_iface(0)
, state(0)
+ , block_length(0)
+ , options(0)
{}
/** Find the LV2 input port with the given designation.
*/
const LilvPort* designated_input (const char* uri, void** bufptrs[], void** bufptr);
- const LilvPlugin* plugin;
- const LilvUI* ui;
- const LilvNode* ui_type;
- LilvNode* name;
- LilvNode* author;
- LilvInstance* instance;
- const LV2_Worker_Interface* work_iface;
- LilvState* state;
- LV2_Atom_Forge forge;
- LV2_Atom_Forge ui_forge;
+ const LilvPlugin* plugin;
+ const LilvUI* ui;
+ const LilvNode* ui_type;
+ LilvNode* name;
+ LilvNode* author;
+ LilvInstance* instance;
+ const LV2_Worker_Interface* work_iface;
+ const LV2_Options_Interface* opts_iface;
+ LilvState* state;
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge ui_forge;
+ int32_t block_length;
+ LV2_Options_Option* options;
};
LV2Plugin::LV2Plugin (AudioEngine& engine,
, _insert_id("0")
, _patch_port_in_index((uint32_t)-1)
, _patch_port_out_index((uint32_t)-1)
+ , _uri_map(URIMap::instance())
{
init(c_plugin, rate);
}
, _insert_id(other._insert_id)
, _patch_port_in_index((uint32_t)-1)
, _patch_port_out_index((uint32_t)-1)
+ , _uri_map(URIMap::instance())
{
init(other._impl->plugin, other._sample_rate);
_latency_control_port = 0;
_next_cycle_start = std::numeric_limits<framepos_t>::max();
_next_cycle_speed = 1.0;
- _block_length = _engine.samples_per_cycle();
_seq_size = _engine.raw_buffer_size(DataType::MIDI);
_state_version = 0;
_was_activated = false;
_has_state_interface = false;
+ _impl->block_length = _session.get_block_size();
_instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access";
_data_access_feature.URI = "http://lv2plug.in/ns/ext/data-access";
#ifdef HAVE_LV2_1_2_0
LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int);
+ static const int32_t _min_block_length = 1; // may happen during split-cycles
+ static const int32_t _max_block_length = 8192; // max possible (with all engines and during export)
+ /* Consider updating max-block-size whenever the buffersize changes.
+ * It requires re-instantiating the plugin (which is a non-realtime operation),
+ * so it should be done lightly and only for plugins that require it.
+ *
+ * given that the block-size can change at any time (split-cycles) ardour currently
+ * does not support plugins that require bufz_fixedBlockLength.
+ */
LV2_Options_Option options[] = {
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__minBlockLength),
- sizeof(int32_t), atom_Int, &_block_length },
+ sizeof(int32_t), atom_Int, &_min_block_length },
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__maxBlockLength),
- sizeof(int32_t), atom_Int, &_block_length },
+ sizeof(int32_t), atom_Int, &_max_block_length },
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__sequenceSize),
sizeof(int32_t), atom_Int, &_seq_size },
+ { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"),
+ sizeof(int32_t), atom_Int, &_impl->block_length },
{ LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL }
};
+ _impl->options = (LV2_Options_Option*) malloc (sizeof (options));
+ memcpy ((void*) _impl->options, (void*) options, sizeof (options));
+
_options_feature.URI = LV2_OPTIONS__options;
- _options_feature.data = options;
+ _options_feature.data = _impl->options;
_features[n_features++] = &_options_feature;
#endif
}
lilv_node_free(worker_iface_uri);
+
+ LilvNode* options_iface_uri = lilv_new_uri(_world.world, LV2_OPTIONS__interface);
+ if (lilv_plugin_has_extension_data(plugin, options_iface_uri)) {
+ _impl->opts_iface = (const LV2_Options_Interface*)extension_data(
+ LV2_OPTIONS__interface);
+ }
+ lilv_node_free(options_iface_uri);
+
if (lilv_plugin_has_feature(plugin, _world.lv2_inPlaceBroken)) {
error << string_compose(
- _("LV2: \"%1\" cannot be used, since it cannot do inplace processing"),
+ _("LV2: \"%1\" cannot be used, since it cannot do inplace processing."),
lilv_node_as_string(_impl->name)) << endmsg;
lilv_node_free(_impl->name);
lilv_node_free(_impl->author);
throw failed_constructor();
}
+#ifdef HAVE_LV2_1_2_0
+ LilvNodes *required_features = lilv_plugin_get_required_features (plugin);
+ if (lilv_nodes_contains (required_features, _world.bufz_powerOf2BlockLength) ||
+ lilv_nodes_contains (required_features, _world.bufz_fixedBlockLength)
+ ) {
+ error << string_compose(
+ _("LV2: \"%1\" buffer-size requirements cannot be satisfied."),
+ lilv_node_as_string(_impl->name)) << endmsg;
+ lilv_node_free(_impl->name);
+ lilv_node_free(_impl->author);
+ lilv_nodes_free(required_features);
+ throw failed_constructor();
+ }
+ lilv_nodes_free(required_features);
+#endif
+
#ifdef HAVE_LILV_0_16_0
// Load default state
LilvState* state = lilv_state_new_from_world(
}
}
+ load_supported_properties(_property_descriptors);
allocate_atom_event_buffers();
latency_compute_run();
}
+int
+LV2Plugin::set_block_size (pframes_t nframes)
+{
+#ifdef HAVE_LV2_1_2_0
+ if (_impl->opts_iface) {
+ LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int);
+ _impl->block_length = nframes;
+ LV2_Options_Option block_size_option = {
+ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id ("http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"),
+ sizeof(int32_t), atom_Int, (void*)&_impl->block_length
+ };
+ _impl->opts_iface->set (_impl->instance->lv2_handle, &block_size_option);
+ }
+#endif
+ return 0;
+}
+
LV2Plugin::~LV2Plugin ()
{
DEBUG_TRACE(DEBUG::LV2, string_compose("%1 destroy\n", name()));
lilv_instance_free(_impl->instance);
lilv_node_free(_impl->name);
lilv_node_free(_impl->author);
+ free(_impl->options);
free(_features);
free(_make_path_feature.data);
delete [] _control_data;
delete [] _shadow_data;
+ delete [] _defaults;
delete [] _ev_buffers;
}
XMLNode* child;
char buf[16];
- LocaleGuard lg(X_("POSIX"));
+ LocaleGuard lg(X_("C"));
for (uint32_t i = 0; i < parameter_count(); ++i) {
if (parameter_is_input(i) && parameter_is_control(i)) {
}
}
-static inline const LilvNode*
+// TODO: Once we can rely on lilv 0.16.0, lilv_world_get can replace this
+static LilvNode*
get_value(LilvWorld* world, const LilvNode* subject, const LilvNode* predicate)
{
LilvNodes* vs = lilv_world_find_nodes(world, subject, predicate, NULL);
- return vs ? lilv_nodes_get_first(vs) : NULL;
+ if (vs) {
+ LilvNode* node = lilv_node_duplicate(lilv_nodes_get_first(vs));
+ lilv_nodes_free(vs);
+ return node;
+ }
+ return NULL;
}
void
LILV_FOREACH(nodes, i, presets) {
const LilvNode* preset = lilv_nodes_get(presets, i);
lilv_world_load_resource(_world.world, preset);
- const LilvNode* name = get_value(_world.world, preset, rdfs_label);
+ LilvNode* name = get_value(_world.world, preset, rdfs_label);
if (name) {
_presets.insert(std::make_pair(lilv_node_as_string(preset),
Plugin::PresetRecord(
lilv_node_as_string(preset),
lilv_node_as_string(name))));
+ lilv_node_free(name);
} else {
warning << string_compose(
_("Plugin \"%1\" preset \"%2\" is missing a label\n"),
uint32_t type)
{
LV2Plugin* self = (LV2Plugin*)user_data;
- if (type != 0 && type != self->_uri_map.uri_to_id(LV2_ATOM__Float)) {
+ if (type != 0 && type != URIMap::instance().urids.atom_Float) {
return; // TODO: Support non-float ports
}
std::string
LV2Plugin::do_save_preset(string name)
{
+ LilvNode* plug_name = lilv_plugin_get_name(_impl->plugin);
+ 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";
const string bundle = Glib::build_filename(
Glib::get_home_dir(),
- Glib::build_filename(".lv2", base_name + ".lv2"));
+ Glib::build_filename(".lv2", prefix + "_" + base_name + ".lv2"));
+
+#ifdef HAVE_LILV_0_21_3
+ /* delete reference to old preset (if any) */
+ const PresetRecord* r = preset_by_label(name);
+ if (r) {
+ LilvNode* pset = lilv_new_uri (_world.world, r->uri.c_str());
+ if (pset) {
+ lilv_world_unload_resource (_world.world, pset);
+ lilv_node_free(pset);
+ }
+ }
+#endif
LilvState* state = lilv_state_new_from_instance(
_impl->plugin,
std::string uri = Glib::filename_to_uri(Glib::build_filename(bundle, file_name));
LilvNode *node_bundle = lilv_new_uri(_world.world, Glib::filename_to_uri(Glib::build_filename(bundle, "/")).c_str());
LilvNode *node_preset = lilv_new_uri(_world.world, uri.c_str());
-#ifdef HAVE_LILV_0_19_2
+#ifdef HAVE_LILV_0_21_3
lilv_world_unload_resource(_world.world, node_preset);
lilv_world_unload_bundle(_world.world, node_bundle);
#endif
lilv_world_load_resource(_world.world, node_preset);
lilv_node_free(node_bundle);
lilv_node_free(node_preset);
+ lilv_node_free(plug_name);
return uri;
}
void
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"
- )
- );
- ::g_unlink(preset_file.c_str());
+#ifdef HAVE_LILV_0_21_3
+ /* Look up preset record by label (FIXME: ick, label as ID) */
+ const PresetRecord* r = preset_by_label(name);
+ if (!r) {
+ return;
+ }
+
+ /* Load a LilvState for the preset. */
+ LilvWorld* world = _world.world;
+ LilvNode* pset = lilv_new_uri(world, r->uri.c_str());
+ LilvState* state = lilv_state_new_from_world(world, _uri_map.urid_map(), pset);
+ if (!state) {
+ lilv_node_free(pset);
+ return;
+ }
+
+ /* Unload preset from world. */
+ lilv_world_unload_resource(world, pset);
+
+ /* Delete it from the file system. This will remove the preset file and the entry
+ from the manifest. If this results in an empty manifest (i.e. the
+ preset is the only thing in the bundle), then the bundle is removed. */
+ lilv_state_delete(world, state);
+
+ lilv_state_free(state);
+ lilv_node_free(pset);
+#endif
+ /* Without lilv_state_delete(), we could delete the preset file, but this
+ would leave a broken bundle/manifest around, so the preset would still
+ be visible, but broken. Naively deleting a bundle is too dangerous, so
+ we simply do not support preset deletion with older Lilv */
}
bool
forge_variant(LV2_Atom_Forge* forge, const Variant& value)
{
switch (value.type()) {
+ case Variant::NOTHING:
+ break;
+ case Variant::BEATS:
+ // No atom type for this, just forge a double
+ lv2_atom_forge_double(forge, value.get_beats().to_double());
+ break;
case Variant::BOOL:
lv2_atom_forge_bool(forge, value.get_bool());
break;
if (_patch_port_in_index == (uint32_t)-1) {
error << "LV2: set_property called with unset patch_port_in_index" << endmsg;
return;
+ } else if (value.type() == Variant::NOTHING) {
+ error << "LV2: set_property called with void value" << endmsg;
+ return;
}
// Set up forge to write to temporary buffer on the stack
// Serialize patch:Set message to set property
#ifdef HAVE_LV2_1_10_0
- lv2_atom_forge_object(forge, &frame, 1, LV2Plugin::urids.patch_Set);
- lv2_atom_forge_key(forge, LV2Plugin::urids.patch_property);
+ lv2_atom_forge_object(forge, &frame, 1, _uri_map.urids.patch_Set);
+ lv2_atom_forge_key(forge, _uri_map.urids.patch_property);
lv2_atom_forge_urid(forge, key);
- lv2_atom_forge_key(forge, LV2Plugin::urids.patch_value);
+ lv2_atom_forge_key(forge, _uri_map.urids.patch_value);
#else
- lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.patch_Set);
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.patch_property, 0);
+ lv2_atom_forge_blank(forge, &frame, 1, _uri_map.urids.patch_Set);
+ lv2_atom_forge_property_head(forge, _uri_map.urids.patch_property, 0);
lv2_atom_forge_urid(forge, key);
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.patch_value, 0);
+ lv2_atom_forge_property_head(forge, _uri_map.urids.patch_value, 0);
#endif
forge_variant(forge, value);
// Write message to UI=>Plugin ring
const LV2_Atom* const atom = (const LV2_Atom*)buf;
write_from_ui(_patch_port_in_index,
- LV2Plugin::urids.atom_eventTransfer,
+ _uri_map.urids.atom_eventTransfer,
lv2_atom_total_size(atom),
(const uint8_t*)atom);
}
+const ParameterDescriptor&
+LV2Plugin::get_property_descriptor(uint32_t id) const
+{
+ PropertyDescriptors::const_iterator p = _property_descriptors.find(id);
+ if (p != _property_descriptors.end()) {
+ return p->second;
+ }
+ return Plugin::get_property_descriptor(id);
+}
+
+static void
+load_parameter_descriptor_units(LilvWorld* lworld, ParameterDescriptor& desc, const LilvNodes* units)
+{
+ if (lilv_nodes_contains(units, _world.units_midiNote)) {
+ desc.unit = ParameterDescriptor::MIDI_NOTE;
+ } else if (lilv_nodes_contains(units, _world.units_db)) {
+ desc.unit = ParameterDescriptor::DB;
+ } else if (lilv_nodes_contains(units, _world.units_hz)) {
+ desc.unit = ParameterDescriptor::HZ;
+ }
+ if (lilv_nodes_size(units) > 0) {
+ const LilvNode* unit = lilv_nodes_get_first(units);
+ LilvNode* render = get_value(lworld, unit, _world.units_render);
+ if (render) {
+ desc.print_fmt = lilv_node_as_string(render);
+ lilv_node_free(render);
+ }
+ }
+}
+
+static void
+load_parameter_descriptor(LV2World& world,
+ ParameterDescriptor& desc,
+ Variant::Type datatype,
+ const LilvNode* subject)
+{
+ LilvWorld* lworld = _world.world;
+ LilvNode* label = get_value(lworld, subject, _world.rdfs_label);
+ LilvNode* def = get_value(lworld, subject, _world.lv2_default);
+ LilvNode* minimum = get_value(lworld, subject, _world.lv2_minimum);
+ LilvNode* maximum = get_value(lworld, subject, _world.lv2_maximum);
+ LilvNodes* units = lilv_world_find_nodes(lworld, subject, _world.units_unit, NULL);
+ if (label) {
+ desc.label = lilv_node_as_string(label);
+ }
+ if (def && lilv_node_is_float(def)) {
+ desc.normal = lilv_node_as_float(def);
+ }
+ if (minimum && lilv_node_is_float(minimum)) {
+ desc.lower = lilv_node_as_float(minimum);
+ }
+ if (maximum && lilv_node_is_float(maximum)) {
+ desc.upper = lilv_node_as_float(maximum);
+ }
+ load_parameter_descriptor_units(lworld, desc, units);
+ desc.datatype = datatype;
+ desc.toggled |= datatype == Variant::BOOL;
+ desc.integer_step |= datatype == Variant::INT || datatype == Variant::LONG;
+ desc.update_steps();
+
+ lilv_nodes_free(units);
+ lilv_node_free(label);
+ lilv_node_free(def);
+ lilv_node_free(minimum);
+ lilv_node_free(maximum);
+}
+
void
-LV2Plugin::get_supported_properties(std::vector<ParameterDescriptor>& descs)
+LV2Plugin::load_supported_properties(PropertyDescriptors& descs)
{
LilvWorld* lworld = _world.world;
const LilvNode* subject = lilv_plugin_get_uri(_impl->plugin);
LILV_FOREACH(nodes, p, properties) {
// Get label and range
const LilvNode* prop = lilv_nodes_get(properties, p);
- LilvNode* label = lilv_world_get(lworld, prop, _world.rdfs_label, NULL);
- LilvNode* range = lilv_world_get(lworld, prop, _world.rdfs_range, NULL);
+ LilvNode* range = get_value(lworld, prop, _world.rdfs_range);
+ if (!range) {
+ warning << string_compose(_("LV2: property <%1> has no range datatype, ignoring"),
+ lilv_node_as_uri(prop)) << endmsg;
+ continue;
+ }
// Convert range to variant type (TODO: support for multiple range types)
Variant::Type datatype;
if (!uri_to_variant_type(lilv_node_as_uri(range), datatype)) {
- error << string_compose(_("LV2: unknown variant datatype \"%1\""),
- lilv_node_as_uri(range));
+ error << string_compose(_("LV2: property <%1> has unsupported datatype <%1>"),
+ lilv_node_as_uri(prop), lilv_node_as_uri(range)) << endmsg;
continue;
}
// Add description to result
ParameterDescriptor desc;
- desc.key = _uri_map.uri_to_id(lilv_node_as_uri(prop));
- desc.label = lilv_node_as_string(label);
- desc.datatype = datatype;
- desc.toggled = datatype == Variant::BOOL;
- desc.integer_step = datatype == Variant::INT || datatype == Variant::LONG;
- descs.push_back(desc);
-
- lilv_node_free(label);
+ desc.key = _uri_map.uri_to_id(lilv_node_as_uri(prop));
+ desc.datatype = datatype;
+ load_parameter_descriptor(_world, desc, datatype, prop);
+ descs.insert(std::make_pair(desc.key, desc));
+
lilv_node_free(range);
}
lilv_nodes_free(properties);
// Serialize patch:Get message with no subject (implicitly plugin instance)
#ifdef HAVE_LV2_1_10_0
- lv2_atom_forge_object(forge, &frame, 1, LV2Plugin::urids.patch_Get);
+ lv2_atom_forge_object(forge, &frame, 1, _uri_map.urids.patch_Get);
#else
- lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.patch_Get);
+ lv2_atom_forge_blank(forge, &frame, 1, _uri_map.urids.patch_Get);
#endif
// Write message to UI=>Plugin ring
const LV2_Atom* const atom = (const LV2_Atom*)buf;
write_from_ui(_patch_port_in_index,
- LV2Plugin::urids.atom_eventTransfer,
+ _uri_map.urids.atom_eventTransfer,
lv2_atom_total_size(atom),
(const uint8_t*)atom);
}
}
void
-LV2Plugin::set_insert_info(const PluginInsert* insert)
+LV2Plugin::set_insert_id(PBD::ID id)
{
- _insert_id = insert->id();
+ _insert_id = id;
}
int
const char* sym;
const char* value;
uint32_t port_id;
- LocaleGuard lg(X_("POSIX"));
+ LocaleGuard lg(X_("C"));
if (node.name() != state_node_name()) {
error << _("Bad node sent to LV2Plugin::set_state") << endmsg;
LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) const
{
const LilvPort* port = lilv_plugin_get_port_by_index(_impl->plugin, which);
+ if (!port) {
+ error << string_compose("LV2: get descriptor of non-existent port %1", which)
+ << endmsg;
+ return 1;
+ }
LilvNodes* portunits;
LilvNode *def, *min, *max;
lilv_port_get_range(_impl->plugin, port, &def, &min, &max);
portunits = lilv_port_get_value(_impl->plugin, port, _world.units_unit);
+ // TODO: Once we can rely on lilv 0.18.0 being present,
+ // load_parameter_descriptor() can be used for ports as well
desc.integer_step = lilv_port_has_property(_impl->plugin, port, _world.lv2_integer);
desc.toggled = lilv_port_has_property(_impl->plugin, port, _world.lv2_toggled);
desc.logarithmic = lilv_port_has_property(_impl->plugin, port, _world.ext_logarithmic);
desc.sr_dependent = lilv_port_has_property(_impl->plugin, port, _world.lv2_sampleRate);
desc.label = lilv_node_as_string(lilv_port_get_name(_impl->plugin, port));
+ desc.normal = def ? lilv_node_as_float(def) : 0.0f;
desc.lower = min ? lilv_node_as_float(min) : 0.0f;
desc.upper = max ? lilv_node_as_float(max) : 1.0f;
- desc.midinote = lilv_nodes_contains(portunits, _world.units_midiNote);
+ load_parameter_descriptor_units(_world.world, desc, portunits);
if (desc.sr_dependent) {
desc.lower *= _session.frame_rate ();
desc.min_unbound = false; // TODO: LV2 extension required
desc.max_unbound = false; // TODO: LV2 extension required
- if (desc.integer_step) {
- desc.step = 1.0;
- desc.smallstep = 0.1;
- desc.largestep = 10.0;
- } else {
- const float delta = desc.upper - desc.lower;
- desc.step = delta / 1000.0f;
- desc.smallstep = delta / 10000.0f;
- desc.largestep = delta / 10.0f;
- }
-
desc.enumeration = lilv_port_has_property(_impl->plugin, port, _world.lv2_enumeration);
desc.scale_points = get_scale_points(which);
+ desc.update_steps();
+
lilv_node_free(def);
lilv_node_free(min);
lilv_node_free(max);
}
}
+ for (PropertyDescriptors::const_iterator p = _property_descriptors.begin();
+ p != _property_descriptors.end();
+ ++p) {
+ ret.insert(ret.end(), Evoral::Parameter(PluginPropertyAutomation, 0, p->first));
+ }
return ret;
}
_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(minimumSize, LV2_EVBUF_ATOM,
- LV2Plugin::urids.atom_Chunk, LV2Plugin::urids.atom_Sequence);
+ _uri_map.urids.atom_Chunk, _uri_map.urids.atom_Sequence);
}
_atom_ev_buffers[total_atom_buffers] = 0;
return;
framepos_t position,
framecnt_t offset)
{
+ const URIMap::URIDs& urids = URIMap::instance().urids;
+
uint8_t pos_buf[256];
lv2_atom_forge_set_buffer(forge, pos_buf, sizeof(pos_buf));
LV2_Atom_Forge_Frame frame;
#ifdef HAVE_LV2_1_10_0
- lv2_atom_forge_object(forge, &frame, 1, LV2Plugin::urids.time_Position);
- lv2_atom_forge_key(forge, LV2Plugin::urids.time_frame);
+ lv2_atom_forge_object(forge, &frame, 1, urids.time_Position);
+ lv2_atom_forge_key(forge, urids.time_frame);
lv2_atom_forge_long(forge, position);
- lv2_atom_forge_key(forge, LV2Plugin::urids.time_speed);
+ lv2_atom_forge_key(forge, urids.time_speed);
lv2_atom_forge_float(forge, speed);
- lv2_atom_forge_key(forge, LV2Plugin::urids.time_barBeat);
+ lv2_atom_forge_key(forge, urids.time_barBeat);
lv2_atom_forge_float(forge, bbt.beats - 1 +
(bbt.ticks / Timecode::BBT_Time::ticks_per_beat));
- lv2_atom_forge_key(forge, LV2Plugin::urids.time_bar);
+ lv2_atom_forge_key(forge, urids.time_bar);
lv2_atom_forge_long(forge, bbt.bars - 1);
- lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatUnit);
+ lv2_atom_forge_key(forge, urids.time_beatUnit);
lv2_atom_forge_int(forge, t.meter().note_divisor());
- lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatsPerBar);
+ lv2_atom_forge_key(forge, urids.time_beatsPerBar);
lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
- lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatsPerMinute);
+ lv2_atom_forge_key(forge, urids.time_beatsPerMinute);
lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
#else
- lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.time_Position);
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_frame, 0);
+ lv2_atom_forge_blank(forge, &frame, 1, urids.time_Position);
+ lv2_atom_forge_property_head(forge, urids.time_frame, 0);
lv2_atom_forge_long(forge, position);
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_speed, 0);
+ lv2_atom_forge_property_head(forge, urids.time_speed, 0);
lv2_atom_forge_float(forge, speed);
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_barBeat, 0);
+ lv2_atom_forge_property_head(forge, urids.time_barBeat, 0);
lv2_atom_forge_float(forge, bbt.beats - 1 +
(bbt.ticks / Timecode::BBT_Time::ticks_per_beat));
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_bar, 0);
+ lv2_atom_forge_property_head(forge, urids.time_bar, 0);
lv2_atom_forge_long(forge, bbt.bars - 1);
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatUnit, 0);
+ lv2_atom_forge_property_head(forge, urids.time_beatUnit, 0);
lv2_atom_forge_int(forge, t.meter().note_divisor());
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatsPerBar, 0);
+ lv2_atom_forge_property_head(forge, urids.time_beatsPerBar, 0);
lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
- lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatsPerMinute, 0);
+ lv2_atom_forge_property_head(forge, urids.time_beatsPerMinute, 0);
lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
#endif
TempoMetric tmetric = tmap.metric_at(_session.transport_frame(), &metric_i);
if (_freewheel_control_port) {
- *_freewheel_control_port = _session.engine().freewheeling();
+ *_freewheel_control_port = _session.engine().freewheeling() ? 1.f : 0.f;
}
if (_bpm_control_port) {
: m;
// Now merge MIDI and any transport events into the buffer
- const uint32_t type = LV2Plugin::urids.midi_MidiEvent;
+ const uint32_t type = _uri_map.urids.midi_MidiEvent;
const framepos_t tend = _session.transport_frame() + nframes;
++metric_i;
while (m != m_end || (metric_i != tmap.metrics_end() &&
error << "Error reading from UI=>Plugin RingBuffer" << endmsg;
break;
}
- if (msg.protocol == urids.atom_eventTransfer) {
+ if (msg.protocol == URIMap::instance().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[0];
- if (!lv2_evbuf_write(&i, nframes, 0, atom->type, atom->size,
+ if (!lv2_evbuf_write(&i, nframes - 1, 0, atom->type, atom->size,
(const uint8_t*)(atom + 1))) {
error << "Failed to write data to LV2 event buffer\n";
}
// Intercept patch change messages to emit PropertyChanged signal
if ((flags & PORT_PATCHMSG)) {
LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom));
- if (atom->type == LV2Plugin::urids.atom_Blank ||
- atom->type == LV2Plugin::urids.atom_Object) {
+ if (atom->type == _uri_map.urids.atom_Blank ||
+ atom->type == _uri_map.urids.atom_Object) {
LV2_Atom_Object* obj = (LV2_Atom_Object*)atom;
- if (obj->body.otype == LV2Plugin::urids.patch_Set) {
+ if (obj->body.otype == _uri_map.urids.patch_Set) {
const LV2_Atom* property = NULL;
const LV2_Atom* value = NULL;
lv2_atom_object_get(obj,
- LV2Plugin::urids.patch_property, &property,
- LV2Plugin::urids.patch_value, &value,
+ _uri_map.urids.patch_property, &property,
+ _uri_map.urids.patch_value, &value,
0);
if (!property || !value ||
- property->type != LV2Plugin::urids.atom_URID ||
- value->type != LV2Plugin::urids.atom_Path) {
+ property->type != _uri_map.urids.atom_URID ||
+ value->type != _uri_map.urids.atom_Path) {
std::cerr << "warning: patch:Set for unknown property" << std::endl;
continue;
}
const char* path = (const char*)LV2_ATOM_BODY_CONST(value);
// Emit PropertyChanged signal for UI
+ // TODO: This should emit the control's Changed signal
PropertyChanged(prop_id, Variant(Variant::PATH, path));
}
}
}
if (!_to_ui) continue;
- write_to_ui(port_index, urids.atom_eventTransfer,
+ write_to_ui(port_index, URIMap::instance().urids.atom_eventTransfer,
size + sizeof(LV2_Atom),
data - sizeof(LV2_Atom));
}
}
}
-boost::shared_ptr<Plugin::ScalePoints>
+boost::shared_ptr<ScalePoints>
LV2Plugin::get_scale_points(uint32_t port_index) const
{
const LilvPort* port = lilv_plugin_get_port_by_index(_impl->plugin, port_index);
LilvScalePoints* points = lilv_port_get_scale_points(_impl->plugin, port);
- boost::shared_ptr<Plugin::ScalePoints> ret;
+ boost::shared_ptr<ScalePoints> ret;
if (!points) {
return ret;
}
- ret = boost::shared_ptr<Plugin::ScalePoints>(new ScalePoints());
+ ret = boost::shared_ptr<ScalePoints>(new ScalePoints());
LILV_FOREACH(scale_points, i, points) {
const LilvScalePoint* p = lilv_scale_points_get(points, i);
: 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);
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_default = lilv_new_uri(world, LV2_CORE__default);
+ lv2_minimum = lilv_new_uri(world, LV2_CORE__minimum);
+ lv2_maximum = lilv_new_uri(world, LV2_CORE__maximum);
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);
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");
- units_unit = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/units#unit");
- units_midiNote = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/units#midiNote");
+ units_unit = lilv_new_uri(world, LV2_UNITS__unit);
+ units_render = lilv_new_uri(world, LV2_UNITS__render);
+ units_hz = lilv_new_uri(world, LV2_UNITS__hz);
+ units_midiNote = lilv_new_uri(world, LV2_UNITS__midiNote);
+ units_db = lilv_new_uri(world, LV2_UNITS__db);
patch_writable = lilv_new_uri(world, LV2_PATCH__writable);
patch_Message = lilv_new_uri(world, LV2_PATCH__Message);
+#ifdef HAVE_LV2_1_2_0
+ bufz_powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);
+ bufz_fixedBlockLength = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength);
+ bufz_nominalBlockLength = lilv_new_uri(world, "http://lv2plug.in/ns/ext/buf-size#nominalBlockLength");
+#endif
+
}
LV2World::~LV2World()
{
+#ifdef HAVE_LV2_1_2_0
+ lilv_node_free(bufz_nominalBlockLength);
+ lilv_node_free(bufz_fixedBlockLength);
+ lilv_node_free(bufz_powerOf2BlockLength);
+#endif
lilv_node_free(patch_Message);
lilv_node_free(patch_writable);
+ lilv_node_free(units_hz);
lilv_node_free(units_midiNote);
+ lilv_node_free(units_db);
lilv_node_free(units_unit);
+ lilv_node_free(units_render);
lilv_node_free(ui_externalkx);
lilv_node_free(ui_external);
lilv_node_free(ui_GtkUI);
lilv_node_free(node);
}
+ lilv_world_load_all(world);
_bundle_checked = true;
}
}
PluginInfoList* plugs = new PluginInfoList;
const LilvPlugins* plugins = lilv_world_get_all_plugins(world.world);
- if (!Config->get_show_plugin_scan_window()) {
- info << "LV2: Discovering " << lilv_plugins_size(plugins) << " plugins" << endmsg;
- }
-
LILV_FOREACH(plugins, i, plugins) {
const LilvPlugin* p = lilv_plugins_get(plugins, i);
const LilvNode* pun = lilv_plugin_get_uri(p);
continue;
}
+ if (lilv_plugin_has_feature(p, world.lv2_inPlaceBroken)) {
+ warning << string_compose(
+ _("Ignoring LV2 plugin \"%1\" since it cannot do inplace processing."),
+ lilv_node_as_string(name)) << endmsg;
+ lilv_node_free(name);
+ continue;
+ }
+
+#ifdef HAVE_LV2_1_2_0
+ LilvNodes *required_features = lilv_plugin_get_required_features (p);
+ if (lilv_nodes_contains (required_features, world.bufz_powerOf2BlockLength) ||
+ lilv_nodes_contains (required_features, world.bufz_fixedBlockLength)
+ ) {
+ warning << string_compose(
+ _("Ignoring LV2 plugin \"%1\" because its buffer-size requirements cannot be satisfied."),
+ lilv_node_as_string(name)) << endmsg;
+ lilv_nodes_free(required_features);
+ lilv_node_free(name);
+ continue;
+ }
+ lilv_nodes_free(required_features);
+#endif
+
info->type = LV2;
info->name = string(lilv_node_as_string(name));