#include <cstdlib>
#include <cstring>
-#include <glib/gstdio.h>
+#include "pbd/gstdio_compat.h"
#include <glib/gprintf.h>
#include <glibmm.h>
LilvNode* units_midiNote;
LilvNode* patch_writable;
LilvNode* patch_Message;
+ LilvNode* lv2_noSampleAccurateCtrl;
+#ifdef HAVE_LV2_1_2_0
+ LilvNode* bufz_powerOf2BlockLength;
+ LilvNode* bufz_fixedBlockLength;
+ LilvNode* bufz_nominalBlockLength;
+#endif
private:
bool _bundle_checked;
struct LV2Plugin::Impl {
Impl() : plugin(0), ui(0), ui_type(0), name(0), author(0), instance(0)
, work_iface(0)
+#ifdef HAVE_LV2_1_2_0
+ , opts_iface(0)
+#endif
, state(0)
+ , block_length(0)
+#ifdef HAVE_LV2_1_2_0
+ , options(0)
+#endif
{}
/** 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;
+#ifdef HAVE_LV2_1_2_0
+ const LV2_Options_Interface* opts_iface;
+#endif
+ LilvState* state;
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge ui_forge;
+ int32_t block_length;
+#ifdef HAVE_LV2_1_2_0
+ LV2_Options_Option* options;
+#endif
};
LV2Plugin::LV2Plugin (AudioEngine& engine,
, _patch_port_in_index((uint32_t)-1)
, _patch_port_out_index((uint32_t)-1)
, _uri_map(URIMap::instance())
+ , _no_sample_accurate_ctrl (false)
{
init(c_plugin, rate);
}
, _patch_port_in_index((uint32_t)-1)
, _patch_port_out_index((uint32_t)-1)
, _uri_map(URIMap::instance())
+ , _no_sample_accurate_ctrl (false)
{
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);
+
+#ifdef HAVE_LV2_1_2_0
+ 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);
+#endif
+
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);
+
+ LilvNodes* optional_features = lilv_plugin_get_optional_features (plugin);
+ if (lilv_nodes_contains (optional_features, _world.lv2_noSampleAccurateCtrl)) {
+ _no_sample_accurate_ctrl = true;
+ }
+ lilv_nodes_free(optional_features);
+#endif
+
#ifdef HAVE_LILV_0_16_0
// Load default state
LilvState* state = lilv_state_new_from_world(
if (state && _has_state_interface) {
lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL);
}
+ lilv_state_free(state);
#endif
_sample_rate = rate;
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;
+}
+
+bool
+LV2Plugin::requires_fixed_sized_buffers () const
+{
+ /* This controls if Ardour will split the plugin's run()
+ * on automation events in order to pass sample-accurate automation
+ * via standard control-ports.
+ *
+ * When returning true Ardour will *not* sub-divide the process-cycle.
+ * Automation events that happen between cycle-start and cycle-end will be
+ * ignored (ctrl values are interpolated to cycle-start).
+ * NB. Atom Sequences are still sample accurate.
+ *
+ * Note: This does not guarantee a fixed block-size.
+ * e.g The process cycle may be split when looping, also
+ * the period-size may change any time: see set_block_size()
+ */
+ return _no_sample_accurate_ctrl;
+}
+
LV2Plugin::~LV2Plugin ()
{
DEBUG_TRACE(DEBUG::LV2, string_compose("%1 destroy\n", name()));
cleanup();
lilv_instance_free(_impl->instance);
+ lilv_state_free(_impl->state);
lilv_node_free(_impl->name);
lilv_node_free(_impl->author);
+#ifdef HAVE_LV2_1_2_0
+ free(_impl->options);
+#endif
free(_features);
free(_make_path_feature.data);
delete [] _control_data;
delete [] _shadow_data;
+ delete [] _defaults;
delete [] _ev_buffers;
}
assert(_insert_id != PBD::ID("0"));
XMLNode* child;
- char buf[16];
+ char buf[32];
LocaleGuard lg(X_("C"));
for (uint32_t i = 0; i < parameter_count(); ++i) {
Glib::get_home_dir(),
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,
_impl->instance,
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. */
_world.world, _uri_map.urid_map(), NULL, state_file.c_str());
lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL);
+ lilv_state_free(_impl->state);
+ _impl->state = state;
}
latency_compute_run();
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) {
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));
}
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);
+ lv2_noSampleAccurateCtrl = lilv_new_uri(world, LV2_CORE_PREFIX "NoSampleAccurateCtrl"); // XXX
+#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(lv2_noSampleAccurateCtrl);
lilv_node_free(patch_Message);
lilv_node_free(patch_writable);
lilv_node_free(units_hz);
return PluginPtr();
}
+bool
+LV2PluginInfo::in_category (const std::string &c) const
+{
+ // TODO use untranslated lilv_plugin_get_class()
+ // match gtk2_ardour/plugin_selector.cc
+ if (category == c) {
+ return true;
+ }
+ return false;
+}
+
PluginInfoList*
LV2PluginInfo::discover()
{
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));