+LV2Plugin::set_property(uint32_t key, const Variant& value)
+{
+ 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
+ LV2_Atom_Forge* forge = &_impl->ui_forge;
+ LV2_Atom_Forge_Frame frame;
+ uint8_t buf[PATH_MAX]; // Ought to be enough for anyone...
+
+ lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
+
+ // Serialize patch:Set message to set property
+#ifdef HAVE_LV2_1_10_0
+ 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, _uri_map.urids.patch_value);
+#else
+ 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, _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,
+ _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::load_supported_properties(PropertyDescriptors& descs)
+{
+ LilvWorld* lworld = _world.world;
+ const LilvNode* subject = lilv_plugin_get_uri(_impl->plugin);
+ LilvNodes* properties = lilv_world_find_nodes(
+ lworld, subject, _world.patch_writable, NULL);
+ LILV_FOREACH(nodes, p, properties) {
+ // Get label and range
+ const LilvNode* prop = lilv_nodes_get(properties, p);
+ 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: 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.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);
+}
+
+void
+LV2Plugin::announce_property_values()
+{
+ if (_patch_port_in_index == (uint32_t)-1) {
+ return;
+ }
+
+ // Set up forge to write to temporary buffer on the stack
+ LV2_Atom_Forge* forge = &_impl->ui_forge;
+ LV2_Atom_Forge_Frame frame;
+ uint8_t buf[PATH_MAX]; // Ought to be enough for anyone...
+
+ lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
+
+ // Serialize patch:Get message with no subject (implicitly plugin instance)
+#ifdef HAVE_LV2_1_10_0
+ lv2_atom_forge_object(forge, &frame, 1, _uri_map.urids.patch_Get);
+#else
+ 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,
+ _uri_map.urids.atom_eventTransfer,
+ lv2_atom_total_size(atom),
+ (const uint8_t*)atom);
+}
+
+void
+LV2Plugin::enable_ui_emission()