summaryrefslogtreecommitdiffstats
path: root/src/server/LV2Block.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/LV2Block.cpp')
-rw-r--r--src/server/LV2Block.cpp742
1 files changed, 742 insertions, 0 deletions
diff --git a/src/server/LV2Block.cpp b/src/server/LV2Block.cpp
new file mode 100644
index 00000000..f4792f39
--- /dev/null
+++ b/src/server/LV2Block.cpp
@@ -0,0 +1,742 @@
+/*
+ This file is part of Ingen.
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
+
+ Ingen is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+
+#include "raul/Maid.hpp"
+#include "raul/Array.hpp"
+
+#include "ingen/FilePath.hpp"
+#include "ingen/Log.hpp"
+#include "ingen/URI.hpp"
+#include "ingen/URIMap.hpp"
+#include "ingen/URIs.hpp"
+#include "ingen/World.hpp"
+
+#include "Buffer.hpp"
+#include "Engine.hpp"
+#include "GraphImpl.hpp"
+#include "InputPort.hpp"
+#include "LV2Block.hpp"
+#include "LV2Plugin.hpp"
+#include "OutputPort.hpp"
+#include "PortImpl.hpp"
+#include "RunContext.hpp"
+#include "Worker.hpp"
+
+namespace Ingen {
+namespace Server {
+
+/** Partially construct a LV2Block.
+ *
+ * Object is not usable until instantiate() is called with success.
+ * (It _will_ crash!)
+ */
+LV2Block::LV2Block(LV2Plugin* plugin,
+ const Raul::Symbol& symbol,
+ bool polyphonic,
+ GraphImpl* parent,
+ SampleRate srate)
+ : BlockImpl(plugin, symbol, polyphonic, parent, srate)
+ , _lv2_plugin(plugin)
+ , _worker_iface(nullptr)
+{
+ assert(_lv2_plugin);
+}
+
+LV2Block::~LV2Block()
+{
+ // Explicitly drop instances first to prevent reference cycles
+ drop_instances(_instances);
+ drop_instances(_prepared_instances);
+}
+
+SPtr<LV2Block::Instance>
+LV2Block::make_instance(URIs& uris,
+ SampleRate rate,
+ uint32_t voice,
+ bool preparing)
+{
+ const Engine& engine = parent_graph()->engine();
+ const LilvPlugin* lplug = _lv2_plugin->lilv_plugin();
+ LilvInstance* inst = lilv_plugin_instantiate(
+ lplug, rate, _features->array());
+
+ if (!inst) {
+ engine.log().error(fmt("Failed to instantiate <%1%>\n")
+ % _lv2_plugin->uri().c_str());
+ return SPtr<Instance>();
+ }
+
+ const LV2_Options_Interface* options_iface = nullptr;
+ if (lilv_plugin_has_extension_data(lplug, uris.opt_interface)) {
+ options_iface = (const LV2_Options_Interface*)
+ lilv_instance_get_extension_data(inst, LV2_OPTIONS__interface);
+ }
+
+ for (uint32_t p = 0; p < num_ports(); ++p) {
+ PortImpl* const port = _ports->at(p);
+ Buffer* const buffer = (preparing)
+ ? port->prepared_buffer(voice).get()
+ : port->buffer(voice).get();
+ if (port->is_morph() && port->is_a(PortType::CV)) {
+ if (options_iface) {
+ const LV2_URID port_type = uris.lv2_CVPort;
+ const LV2_Options_Option options[] = {
+ { LV2_OPTIONS_PORT, p, uris.morph_currentType,
+ sizeof(LV2_URID), uris.atom_URID, &port_type },
+ { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr }
+ };
+ options_iface->set(inst->lv2_handle, options);
+ }
+ }
+
+ if (buffer) {
+ if (port->is_a(PortType::CONTROL)) {
+ buffer->set_value(port->value());
+ } else if (port->is_a(PortType::CV)) {
+ buffer->set_block(port->value().get<float>(), 0, engine.block_length());
+ } else {
+ buffer->clear();
+ }
+ }
+ }
+
+ if (options_iface) {
+ for (uint32_t p = 0; p < num_ports(); ++p) {
+ PortImpl* const port = _ports->at(p);
+ if (port->is_auto_morph()) {
+ LV2_Options_Option options[] = {
+ { LV2_OPTIONS_PORT, p, uris.morph_currentType, 0, 0, nullptr },
+ { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr }
+ };
+
+ options_iface->get(inst->lv2_handle, options);
+ if (options[0].value) {
+ LV2_URID type = *(const LV2_URID*)options[0].value;
+ if (type == _uris.lv2_ControlPort) {
+ port->set_type(PortType::CONTROL, 0);
+ } else if (type == _uris.lv2_CVPort) {
+ port->set_type(PortType::CV, 0);
+ } else {
+ parent_graph()->engine().log().error(
+ fmt("%1% auto-morphed to unknown type %2%\n")
+ % port->path().c_str() % type);
+ return SPtr<Instance>();
+ }
+ } else {
+ parent_graph()->engine().log().error(
+ fmt("Failed to get auto-morphed type of %1%\n")
+ % port->path().c_str());
+ }
+ }
+ }
+ }
+
+ return std::make_shared<Instance>(inst);
+}
+
+bool
+LV2Block::prepare_poly(BufferFactory& bufs, uint32_t poly)
+{
+ if (!_polyphonic) {
+ poly = 1;
+ }
+
+ BlockImpl::prepare_poly(bufs, poly);
+
+ if (_polyphony == poly) {
+ return true;
+ }
+
+ const SampleRate rate = bufs.engine().sample_rate();
+ assert(!_prepared_instances);
+ _prepared_instances = bufs.maid().make_managed<Instances>(
+ poly, *_instances, SPtr<Instance>());
+ for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) {
+ SPtr<Instance> inst = make_instance(bufs.uris(), rate, i, true);
+ if (!inst) {
+ _prepared_instances.reset();
+ return false;
+ }
+
+ _prepared_instances->at(i) = inst;
+
+ if (_activated) {
+ lilv_instance_activate(inst->instance);
+ }
+ }
+
+ return true;
+}
+
+bool
+LV2Block::apply_poly(RunContext& context, uint32_t poly)
+{
+ if (!_polyphonic) {
+ poly = 1;
+ }
+
+ if (_prepared_instances) {
+ _instances = std::move(_prepared_instances);
+ }
+ assert(poly <= _instances->size());
+
+ return BlockImpl::apply_poly(context, poly);
+}
+
+/** Instantiate self from LV2 plugin descriptor.
+ *
+ * Implemented as a seperate function (rather than in the constructor) to
+ * allow graceful error-catching of broken plugins.
+ *
+ * Returns whether or not plugin was successfully instantiated. If return
+ * value is false, this object may not be used.
+ */
+bool
+LV2Block::instantiate(BufferFactory& bufs, const LilvState* state)
+{
+ const Ingen::URIs& uris = bufs.uris();
+ Ingen::World* world = bufs.engine().world();
+ const LilvPlugin* plug = _lv2_plugin->lilv_plugin();
+ Ingen::Forge& forge = bufs.forge();
+ const uint32_t num_ports = lilv_plugin_get_num_ports(plug);
+
+ LilvNode* lv2_connectionOptional = lilv_new_uri(
+ bufs.engine().world()->lilv_world(), LV2_CORE__connectionOptional);
+
+ _ports = bufs.maid().make_managed<BlockImpl::Ports>(num_ports, nullptr);
+
+ bool ret = true;
+
+ float* min_values = new float[num_ports];
+ float* max_values = new float[num_ports];
+ float* def_values = new float[num_ports];
+ lilv_plugin_get_port_ranges_float(plug, min_values, max_values, def_values);
+ uint32_t max_sequence_size = 0;
+
+ // Get all the necessary information about ports
+ for (uint32_t j = 0; j < num_ports; ++j) {
+ const LilvPort* id = lilv_plugin_get_port_by_index(plug, j);
+
+ /* LV2 port symbols are guaranteed to be unique, valid C identifiers,
+ and Lilv guarantees that lilv_port_get_symbol() is valid. */
+ const Raul::Symbol port_sym(
+ lilv_node_as_string(lilv_port_get_symbol(plug, id)));
+
+ // Get port type
+ Atom val;
+ PortType port_type = PortType::UNKNOWN;
+ LV2_URID buffer_type = 0;
+ bool is_morph = false;
+ bool is_auto_morph = false;
+ if (lilv_port_is_a(plug, id, uris.lv2_ControlPort)) {
+ if (lilv_port_is_a(plug, id, uris.morph_MorphPort)) {
+ is_morph = true;
+ LilvNodes* types = lilv_port_get_value(
+ plug, id, uris.morph_supportsType);
+ LILV_FOREACH(nodes, i, types) {
+ const LilvNode* type = lilv_nodes_get(types, i);
+ if (lilv_node_equals(type, uris.lv2_CVPort)) {
+ port_type = PortType::CV;
+ buffer_type = uris.atom_Sound;
+ }
+ }
+ lilv_nodes_free(types);
+ }
+ if (port_type == PortType::UNKNOWN) {
+ port_type = PortType::CONTROL;
+ buffer_type = uris.atom_Sequence;
+ val = forge.make(def_values[j]);
+ }
+ } else if (lilv_port_is_a(plug, id, uris.lv2_CVPort)) {
+ port_type = PortType::CV;
+ buffer_type = uris.atom_Sound;
+ } else if (lilv_port_is_a(plug, id, uris.lv2_AudioPort)) {
+ port_type = PortType::AUDIO;
+ buffer_type = uris.atom_Sound;
+ } else if (lilv_port_is_a(plug, id, uris.atom_AtomPort)) {
+ port_type = PortType::ATOM;
+ }
+
+ if (lilv_port_is_a(plug, id, uris.morph_AutoMorphPort)) {
+ is_auto_morph = true;
+ }
+
+ // Get buffer type if necessary (atom ports)
+ if (!buffer_type) {
+ LilvNodes* types = lilv_port_get_value(
+ plug, id, uris.atom_bufferType);
+ LILV_FOREACH(nodes, i, types) {
+ const LilvNode* type = lilv_nodes_get(types, i);
+ if (lilv_node_is_uri(type)) {
+ buffer_type = bufs.engine().world()->uri_map().map_uri(
+ lilv_node_as_uri(type));
+ }
+ }
+ lilv_nodes_free(types);
+ }
+
+ const bool optional = lilv_port_has_property(
+ plug, id, lv2_connectionOptional);
+
+ uint32_t port_buffer_size = bufs.default_size(buffer_type);
+ if (port_buffer_size == 0 && !optional) {
+ parent_graph()->engine().log().error(
+ fmt("<%1%> port `%2%' has unknown buffer type\n")
+ % _lv2_plugin->uri().c_str() % port_sym.c_str());
+ ret = false;
+ break;
+ }
+
+ if (port_type == PortType::ATOM) {
+ // Get default value, and its length
+ LilvNodes* defaults = lilv_port_get_value(plug, id, uris.lv2_default);
+ LILV_FOREACH(nodes, i, defaults) {
+ const LilvNode* d = lilv_nodes_get(defaults, i);
+ if (lilv_node_is_string(d)) {
+ const char* str_val = lilv_node_as_string(d);
+ const uint32_t str_val_len = strlen(str_val);
+ val = forge.alloc(str_val);
+ port_buffer_size = std::max(port_buffer_size, str_val_len);
+ } else if (lilv_node_is_uri(d)) {
+ const char* uri_val = lilv_node_as_uri(d);
+ val = forge.make_urid(
+ bufs.engine().world()->uri_map().map_uri(uri_val));
+ }
+ }
+ lilv_nodes_free(defaults);
+
+ if (!val.type() && buffer_type == _uris.atom_URID) {
+ val = forge.make_urid(0);
+ }
+
+ // Get minimum size, if set in data
+ LilvNodes* sizes = lilv_port_get_value(plug, id, uris.rsz_minimumSize);
+ LILV_FOREACH(nodes, i, sizes) {
+ const LilvNode* d = lilv_nodes_get(sizes, i);
+ if (lilv_node_is_int(d)) {
+ uint32_t size_val = lilv_node_as_int(d);
+ port_buffer_size = std::max(port_buffer_size, size_val);
+ }
+ }
+ lilv_nodes_free(sizes);
+ max_sequence_size = std::max(port_buffer_size, max_sequence_size);
+ bufs.set_seq_size(max_sequence_size);
+ }
+
+ enum { UNKNOWN, INPUT, OUTPUT } direction = UNKNOWN;
+ if (lilv_port_is_a(plug, id, uris.lv2_InputPort)) {
+ direction = INPUT;
+ } else if (lilv_port_is_a(plug, id, uris.lv2_OutputPort)) {
+ direction = OUTPUT;
+ }
+
+ if ((port_type == PortType::UNKNOWN && !optional) ||
+ direction == UNKNOWN) {
+ parent_graph()->engine().log().error(
+ fmt("<%1%> port `%2%' has unknown type or direction\n")
+ % _lv2_plugin->uri().c_str() % port_sym.c_str());
+ ret = false;
+ break;
+ }
+
+ if (!val.type() && (port_type != PortType::ATOM)) {
+ // Ensure numeric ports have a value, use 0 by default
+ val = forge.make(std::isnan(def_values[j]) ? 0.0f : def_values[j]);
+ }
+
+ PortImpl* port = (direction == INPUT)
+ ? static_cast<PortImpl*>(
+ new InputPort(bufs, this, port_sym, j, _polyphony,
+ port_type, buffer_type, val))
+ : static_cast<PortImpl*>(
+ new OutputPort(bufs, this, port_sym, j, _polyphony,
+ port_type, buffer_type, val));
+
+ port->set_morphable(is_morph, is_auto_morph);
+ if (direction == INPUT && (port_type == PortType::CONTROL
+ || port_type == PortType::CV)) {
+ port->set_value(val);
+ if (!std::isnan(min_values[j])) {
+ port->set_minimum(forge.make(min_values[j]));
+ }
+ if (!std::isnan(max_values[j])) {
+ port->set_maximum(forge.make(max_values[j]));
+ }
+ }
+
+ // Inherit certain properties from plugin port
+ const LilvNode* preds[] = { uris.lv2_designation,
+ uris.lv2_portProperty,
+ uris.atom_supports,
+ nullptr };
+ for (int p = 0; preds[p]; ++p) {
+ LilvNodes* values = lilv_port_get_value(plug, id, preds[p]);
+ LILV_FOREACH(nodes, v, values) {
+ const LilvNode* val = lilv_nodes_get(values, v);
+ if (lilv_node_is_uri(val)) {
+ port->add_property(URI(lilv_node_as_uri(preds[p])),
+ forge.make_urid(URI(lilv_node_as_uri(val))));
+ }
+ }
+ lilv_nodes_free(values);
+ }
+
+ port->cache_properties();
+
+ _ports->at(j) = port;
+ }
+
+ delete[] min_values;
+ delete[] max_values;
+ delete[] def_values;
+
+ lilv_node_free(lv2_connectionOptional);
+
+ if (!ret) {
+ _ports.reset();
+ return ret;
+ }
+
+ _features = world->lv2_features().lv2_features(world, this);
+
+ // Actually create plugin instances and port buffers.
+ const SampleRate rate = bufs.engine().sample_rate();
+ _instances = bufs.maid().make_managed<Instances>(
+ _polyphony, SPtr<Instance>());
+ for (uint32_t i = 0; i < _polyphony; ++i) {
+ _instances->at(i) = make_instance(bufs.uris(), rate, i, false);
+ if (!_instances->at(i)) {
+ return false;
+ }
+ }
+
+ // Load initial state if no state is explicitly given
+ LilvState* default_state = nullptr;
+ if (!state) {
+ state = default_state = load_preset(_lv2_plugin->uri());
+ }
+
+ // Apply state
+ if (state) {
+ apply_state(nullptr, state);
+ }
+
+ if (default_state) {
+ lilv_state_free(default_state);
+ }
+
+ // FIXME: Polyphony + worker?
+ if (lilv_plugin_has_feature(plug, uris.work_schedule)) {
+ _worker_iface = (const LV2_Worker_Interface*)
+ lilv_instance_get_extension_data(instance(0),
+ LV2_WORKER__interface);
+ }
+
+ return ret;
+}
+
+bool
+LV2Block::save_state(const FilePath& dir) const
+{
+ World* world = _lv2_plugin->world();
+ LilvWorld* lworld = world->lilv_world();
+
+ LilvState* state = lilv_state_new_from_instance(
+ _lv2_plugin->lilv_plugin(), const_cast<LV2Block*>(this)->instance(0),
+ &world->uri_map().urid_map_feature()->urid_map,
+ nullptr, dir.c_str(), dir.c_str(), dir.c_str(), nullptr, nullptr,
+ LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, nullptr);
+
+ if (!state) {
+ return false;
+ } else if (lilv_state_get_num_properties(state) == 0) {
+ lilv_state_free(state);
+ return false;
+ }
+
+ lilv_state_save(lworld,
+ &world->uri_map().urid_map_feature()->urid_map,
+ &world->uri_map().urid_unmap_feature()->urid_unmap,
+ state,
+ nullptr,
+ dir.c_str(),
+ "state.ttl");
+
+ lilv_state_free(state);
+
+ return true;
+}
+
+BlockImpl*
+LV2Block::duplicate(Engine& engine,
+ const Raul::Symbol& symbol,
+ GraphImpl* parent)
+{
+ const SampleRate rate = engine.sample_rate();
+
+ // Get current state
+ LilvState* state = lilv_state_new_from_instance(
+ _lv2_plugin->lilv_plugin(), instance(0),
+ &engine.world()->uri_map().urid_map_feature()->urid_map,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, LV2_STATE_IS_NATIVE, nullptr);
+
+ // Duplicate and instantiate block
+ LV2Block* dup = new LV2Block(_lv2_plugin, symbol, _polyphonic, parent, rate);
+ if (!dup->instantiate(*engine.buffer_factory(), state)) {
+ delete dup;
+ return nullptr;
+ }
+ dup->set_properties(properties());
+
+ // Set duplicate port values and properties to the same as ours
+ for (uint32_t p = 0; p < num_ports(); ++p) {
+ const Atom& val = port_impl(p)->value();
+ if (val.is_valid()) {
+ dup->port_impl(p)->set_value(val);
+ }
+ dup->port_impl(p)->set_properties(port_impl(p)->properties());
+ }
+
+ return dup;
+}
+
+void
+LV2Block::activate(BufferFactory& bufs)
+{
+ BlockImpl::activate(bufs);
+
+ for (uint32_t i = 0; i < _polyphony; ++i) {
+ lilv_instance_activate(instance(i));
+ }
+}
+
+void
+LV2Block::deactivate()
+{
+ BlockImpl::deactivate();
+
+ for (uint32_t i = 0; i < _polyphony; ++i) {
+ lilv_instance_deactivate(instance(i));
+ }
+}
+
+LV2_Worker_Status
+LV2Block::work_respond(LV2_Worker_Respond_Handle handle,
+ uint32_t size,
+ const void* data)
+{
+ LV2Block* block = (LV2Block*)handle;
+ LV2Block::Response* r = new LV2Block::Response(size, data);
+ block->_responses.push_back(*r);
+ return LV2_WORKER_SUCCESS;
+}
+
+LV2_Worker_Status
+LV2Block::work(uint32_t size, const void* data)
+{
+ if (_worker_iface) {
+ std::lock_guard<std::mutex> lock(_work_mutex);
+
+ LV2_Handle inst = lilv_instance_get_handle(instance(0));
+ LV2_Worker_Status st = _worker_iface->work(inst, work_respond, this, size, data);
+ if (st) {
+ parent_graph()->engine().log().error(
+ fmt("Error calling %1% work method\n") % _path);
+ }
+ return st;
+ }
+ return LV2_WORKER_ERR_UNKNOWN;
+}
+
+void
+LV2Block::run(RunContext& context)
+{
+ for (uint32_t i = 0; i < _polyphony; ++i) {
+ lilv_instance_run(instance(i), context.nframes());
+ }
+}
+
+void
+LV2Block::post_process(RunContext& context)
+{
+ /* Handle any worker responses. Note that this may write to output ports,
+ so must be done first to prevent clobbering worker responses and
+ monitored notification ports. */
+ if (_worker_iface) {
+ LV2_Handle inst = lilv_instance_get_handle(instance(0));
+ while (!_responses.empty()) {
+ Response& r = _responses.front();
+ _worker_iface->work_response(inst, r.size, r.data);
+ _responses.pop_front();
+ context.engine().maid()->dispose(&r);
+ }
+
+ if (_worker_iface->end_run) {
+ _worker_iface->end_run(inst);
+ }
+ }
+
+ /* Run cycle truly finished, finalise output ports. */
+ BlockImpl::post_process(context);
+}
+
+LilvState*
+LV2Block::load_preset(const URI& uri)
+{
+ World* world = _lv2_plugin->world();
+ LilvWorld* lworld = world->lilv_world();
+ LilvNode* preset = lilv_new_uri(lworld, uri.c_str());
+
+ // Load preset into world if necessary
+ lilv_world_load_resource(lworld, preset);
+
+ // Load preset from world
+ LV2_URID_Map* map = &world->uri_map().urid_map_feature()->urid_map;
+ LilvState* state = lilv_state_new_from_world(lworld, map, preset);
+
+ lilv_node_free(preset);
+ return state;
+}
+
+LilvState*
+LV2Block::load_state(World* world, const FilePath& path)
+{
+ LilvWorld* lworld = world->lilv_world();
+ const URI uri = URI(path);
+ LilvNode* subject = lilv_new_uri(lworld, uri.c_str());
+
+ LilvState* state = lilv_state_new_from_file(
+ lworld,
+ &world->uri_map().urid_map_feature()->urid_map,
+ subject,
+ path.c_str());
+
+ lilv_node_free(subject);
+ return state;
+}
+
+void
+LV2Block::apply_state(const UPtr<Worker>& worker, const LilvState* state)
+{
+ World* world = parent_graph()->engine().world();
+ SPtr<LV2_Feature> sched;
+ if (worker) {
+ sched = worker->schedule_feature()->feature(world, this);
+ }
+
+ const LV2_Feature* state_features[2] = { nullptr, nullptr };
+ if (sched) {
+ state_features[0] = sched.get();
+ }
+
+ for (uint32_t v = 0; v < _polyphony; ++v) {
+ lilv_state_restore(state, instance(v), nullptr, nullptr, 0, state_features);
+ }
+}
+
+static const void*
+get_port_value(const char* port_symbol,
+ void* user_data,
+ uint32_t* size,
+ uint32_t* type)
+{
+ LV2Block* const block = (LV2Block*)user_data;
+ PortImpl* const port = block->port_by_symbol(port_symbol);
+
+ if (port && port->is_input() && port->value().is_valid()) {
+ *size = port->value().size();
+ *type = port->value().type();
+ return port->value().get_body();
+ }
+
+ return nullptr;
+}
+
+boost::optional<Resource>
+LV2Block::save_preset(const URI& uri,
+ const Properties& props)
+{
+ World* world = parent_graph()->engine().world();
+ LilvWorld* lworld = _lv2_plugin->world()->lilv_world();
+ LV2_URID_Map* lmap = &world->uri_map().urid_map_feature()->urid_map;
+ LV2_URID_Unmap* lunmap = &world->uri_map().urid_unmap_feature()->urid_unmap;
+
+ const FilePath path = FilePath(uri.path());
+ const FilePath dirname = path.parent_path();
+ const FilePath basename = path.stem();
+
+ LilvState* state = lilv_state_new_from_instance(
+ _lv2_plugin->lilv_plugin(), instance(0), lmap,
+ nullptr, nullptr, nullptr, path.c_str(),
+ get_port_value, this, LV2_STATE_IS_NATIVE, nullptr);
+
+ if (state) {
+ const Properties::const_iterator l = props.find(_uris.rdfs_label);
+ if (l != props.end() && l->second.type() == _uris.atom_String) {
+ lilv_state_set_label(state, l->second.ptr<char>());
+ }
+
+ lilv_state_save(lworld, lmap, lunmap, state, nullptr,
+ dirname.c_str(), basename.c_str());
+
+ const URI uri(lilv_node_as_uri(lilv_state_get_uri(state)));
+ const std::string label(lilv_state_get_label(state)
+ ? lilv_state_get_label(state)
+ : basename);
+ lilv_state_free(state);
+
+ Resource preset(_uris, uri);
+ preset.set_property(_uris.rdf_type, _uris.pset_Preset);
+ preset.set_property(_uris.rdfs_label, world->forge().alloc(label));
+ preset.set_property(_uris.lv2_appliesTo,
+ world->forge().make_urid(_lv2_plugin->uri()));
+
+ const std::string bundle_uri = URI(dirname).string() + '/';
+ LilvNode* lbundle = lilv_new_uri(lworld, bundle_uri.c_str());
+ lilv_world_load_bundle(lworld, lbundle);
+ lilv_node_free(lbundle);
+
+ return preset;
+ }
+
+ return boost::optional<Resource>();
+}
+
+void
+LV2Block::set_port_buffer(uint32_t voice,
+ uint32_t port_num,
+ BufferRef buf,
+ SampleCount offset)
+{
+ BlockImpl::set_port_buffer(voice, port_num, buf, offset);
+ lilv_instance_connect_port(
+ instance(voice),
+ port_num,
+ buf ? buf->port_data(_ports->at(port_num)->type(), offset) : nullptr);
+}
+
+} // namespace Server
+} // namespace Ingen