#include "pbd/compose.h"
#include "pbd/error.h"
#include "pbd/locale_guard.h"
+#include "pbd/pthread_utils.h"
#include "pbd/replace_all.h"
#include "pbd/xml++.h"
#include "pbd/windows_special_dirs.h"
#endif
+#ifdef WAF_BUILD
#include "libardour-config.h"
+#endif
#include "ardour/audio_buffer.h"
#include "ardour/audioengine.h"
+#include "ardour/directory_names.h"
#include "ardour/debug.h"
#include "ardour/lv2_plugin.h"
#include "ardour/midi_patch_manager.h"
, _no_sample_accurate_ctrl (false)
{
init(c_plugin, rate);
+ latency_compute_run();
}
LV2Plugin::LV2Plugin (const LV2Plugin& other)
{
init(other._impl->plugin, other._sample_rate);
+ XMLNode root (other.state_node_name ());
+ other.add_state (&root);
+ set_state (root, Stateful::loading_state_version);
+
for (uint32_t i = 0; i < parameter_count(); ++i) {
_control_data[i] = other._shadow_data[i];
_shadow_data[i] = other._shadow_data[i];
}
+
+ latency_compute_run();
}
void
_was_activated = false;
_has_state_interface = false;
_can_write_automation = false;
+#ifdef LV2_EXTENDED
_inline_display_in_gui = false;
+#endif
_max_latency = 0;
_current_latency = 0;
_impl->block_length = _session.get_block_size();
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)
+ static const int32_t rt_policy = PBD_SCHED_FIFO;
+ static const int32_t rt_priority = pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority () - 2);
/* 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.
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, _uri_map.uri_to_id("http://ardour.org/lv2/threads/#schedPolicy"),
+ sizeof(int32_t), atom_Int, &rt_policy },
+ { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://ardour.org/lv2/threads/#schedPriority"),
+ sizeof(int32_t), atom_Int, &rt_priority },
{ LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL }
};
lilv_nodes_free(optional_features);
#endif
-#ifdef HAVE_LILV_0_16_0
// Load default state
if (_worker) {
/* immediately schedule any work,
lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL);
}
lilv_state_free(state);
-#endif
_sample_rate = rate;
if (params[i]) {
*params[i] = (void*)&_shadow_data[i];
}
+ } else {
+ _shadow_data[i] = 0;
}
} else {
_defaults[i] = 0.0f;
load_supported_properties(_property_descriptors);
allocate_atom_event_buffers();
- latency_compute_run();
}
int
unsigned int saved_state = _state_version;;
g_mkdir_with_parents(new_dir.c_str(), 0744);
+ std::string xternal_dir = _session.externals_dir ();
+
+ if (!_plugin_state_dir.empty()) {
+ xternal_dir = Glib::build_filename (_plugin_state_dir, externals_dir_name);
+ g_mkdir_with_parents(xternal_dir.c_str(), 0744);
+ }
+
LilvState* state = lilv_state_new_from_instance(
_impl->plugin,
_impl->instance,
_uri_map.urid_map(),
scratch_dir().c_str(),
file_dir().c_str(),
- _session.externals_dir().c_str(),
+ xternal_dir.c_str(),
new_dir.c_str(),
NULL,
const_cast<LV2Plugin*>(this),
} else {
// template save (dedicated state-dir)
lilv_state_free(state);
+ g_rmdir (xternal_dir.c_str()); // try remove unused dir
--_state_version;
}
} else {
}
}
-// 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);
- if (vs) {
- LilvNode* node = lilv_node_duplicate(lilv_nodes_get_first(vs));
- lilv_nodes_free(vs);
- return node;
- }
- return NULL;
+ return lilv_world_get(world, subject, predicate, NULL);
}
void
LV2Plugin::find_presets()
{
+ /* see also LV2PluginInfo::get_presets */
LilvNode* lv2_appliesTo = lilv_new_uri(_world.world, LV2_CORE__appliesTo);
LilvNode* pset_Preset = lilv_new_uri(_world.world, LV2_PRESETS__Preset);
LilvNode* rdfs_label = lilv_new_uri(_world.world, LILV_NS_RDFS "label");
+ LilvNode* rdfs_comment = lilv_new_uri(_world.world, LILV_NS_RDFS "comment");
LilvNodes* presets = lilv_plugin_get_related(_impl->plugin, pset_Preset);
LILV_FOREACH(nodes, i, presets) {
const LilvNode* preset = lilv_nodes_get(presets, i);
lilv_world_load_resource(_world.world, preset);
LilvNode* name = get_value(_world.world, preset, rdfs_label);
- bool userpreset = true; // TODO
+ LilvNode* comment = get_value(_world.world, preset, rdfs_comment);
+ /* TODO properly identify user vs factory presets.
+ * here's an indirect condition: only factory presets can have comments
+ */
+ bool userpreset = comment ? false : true;
if (name) {
_presets.insert(std::make_pair(lilv_node_as_string(preset),
Plugin::PresetRecord(
lilv_node_as_string(preset),
lilv_node_as_string(name),
- userpreset)));
+ userpreset,
+ comment ? lilv_node_as_string (comment) : ""
+ )));
lilv_node_free(name);
} else {
warning << string_compose(
lilv_node_as_string(lilv_plugin_get_uri(_impl->plugin)),
lilv_node_as_string(preset)) << endmsg;
}
+ if (comment) {
+ lilv_node_free(comment);
+ }
}
lilv_nodes_free(presets);
+ lilv_node_free(rdfs_comment);
lilv_node_free(rdfs_label);
lilv_node_free(pset_Preset);
lilv_node_free(lv2_appliesTo);
Glib::build_filename(".lv2", prefix + "_" + base_name + ".lv2"));
#endif
-#ifdef HAVE_LILV_0_21_3
/* delete reference to old preset (if any) */
const PresetRecord* r = preset_by_label(name);
if (r) {
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_21_3
lilv_world_unload_resource(_world.world, node_preset);
lilv_world_unload_bundle(_world.world, node_bundle);
-#endif
lilv_world_load_bundle(_world.world, node_bundle);
lilv_world_load_resource(_world.world, node_preset);
lilv_node_free(node_bundle);
void
LV2Plugin::do_remove_preset(string name)
{
-#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) {
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
lilv_nodes_free(properties);
}
+Variant
+LV2Plugin::get_property_value (uint32_t prop_id) const
+{
+ std::map<uint32_t, Variant>::const_iterator it;
+ if ((it = _property_values.find (prop_id)) == _property_values.end()) {
+ return Variant();
+ }
+ return it->second;
+}
+
void
LV2Plugin::announce_property_values()
{
set_state_dir ("");
}
- latency_compute_run();
+ /* Do not call latency_compute_run() concurrently with connect_and_run().
+ * So far this can only guarnteed when the session is loading,
+ * and the plugin has not been added to the processor chain.
+ *
+ * Ideally this would clso be called when copying a plugin from another track,
+ * but NOT when copying the state from a plugin to another (active) plugin
+ * instance.
+ */
+ if (_session.loading ()) {
+ latency_compute_run();
+ }
#endif
return Plugin::set_state(node, version);
{
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)) {
+ const LilvPort* port = lilv_plugin_get_port_by_index(_impl->plugin, which.id());
+
+ if (lilv_port_has_property(_impl->plugin, port, _world.ext_notOnGUI)) {
+ return X_("hidden");
+ }
+
+ const LilvPort* fwport = lilv_plugin_get_port_by_designation(_impl->plugin, _world.lv2_InputPort, _world.lv2_freewheeling);
+ if (fwport && fwport == port) {
return X_("hidden");
}
- if (lilv_port_has_property(_impl->plugin,
- lilv_plugin_get_port_by_index(_impl->plugin, which.id()), _world.lv2_freewheeling)) {
+ if (lilv_port_has_property(_impl->plugin, port, _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)) {
+ if (lilv_port_has_property(_impl->plugin, port, _world.lv2_reportsLatency)) {
return X_("latency");
}
}
samplecnt_t
-LV2Plugin::signal_latency() const
+LV2Plugin::plugin_latency() const
{
if (_latency_control_port) {
return (samplecnt_t)floor(*_latency_control_port);
LV2_Atom_Forge_Frame sample;
#ifdef HAVE_LV2_1_10_0
lv2_atom_forge_object(forge, &sample, 0, urids.time_Position);
- lv2_atom_forge_key(forge, urids.time_sample);
+ lv2_atom_forge_key(forge, urids.time_frame);
lv2_atom_forge_long(forge, position);
lv2_atom_forge_key(forge, urids.time_speed);
lv2_atom_forge_float(forge, speed);
lv2_atom_forge_float(forge, bpm);
#else
lv2_atom_forge_blank(forge, &sample, 1, urids.time_Position);
- lv2_atom_forge_property_head(forge, urids.time_sample, 0);
+ lv2_atom_forge_property_head(forge, urids.time_frame, 0);
lv2_atom_forge_long(forge, position);
lv2_atom_forge_property_head(forge, urids.time_speed, 0);
lv2_atom_forge_float(forge, speed);
int
LV2Plugin::connect_and_run(BufferSet& bufs,
samplepos_t start, samplepos_t end, double speed,
- ChanMapping in_map, ChanMapping out_map,
+ ChanMapping const& in_map, ChanMapping const& out_map,
pframes_t nframes, samplecnt_t offset)
{
DEBUG_TRACE(DEBUG::LV2, string_compose("%1 run %2 offset %3\n", name(), nframes, offset));
// Emit PropertyChanged signal for UI
// TODO: This should emit the control's Changed signal
PropertyChanged(prop_id, Variant(Variant::PATH, path));
+ _property_values[prop_id] = Variant(Variant::PATH, path);
} else {
std::cerr << "warning: patch:Set for unknown property" << std::endl;
}
return UINT32_MAX;
}
-void
-LV2Plugin::print_parameter(uint32_t param, char* buf, uint32_t len) const
-{
- if (buf && len) {
- if (param < parameter_count()) {
- snprintf(buf, len, "%.3f", get_parameter(param));
- } else {
- strcat(buf, "0");
- }
- }
-}
-
boost::shared_ptr<ScalePoints>
LV2Plugin::get_scale_points(uint32_t port_index) const
{
LilvNode* lv2_appliesTo = lilv_new_uri(_world.world, LV2_CORE__appliesTo);
LilvNode* pset_Preset = lilv_new_uri(_world.world, LV2_PRESETS__Preset);
LilvNode* rdfs_label = lilv_new_uri(_world.world, LILV_NS_RDFS "label");
+ LilvNode* rdfs_comment = lilv_new_uri(_world.world, LILV_NS_RDFS "comment");
LilvNodes* presets = lilv_plugin_get_related(lp, pset_Preset);
LILV_FOREACH(nodes, i, presets) {
const LilvNode* preset = lilv_nodes_get(presets, i);
lilv_world_load_resource(_world.world, preset);
LilvNode* name = get_value(_world.world, preset, rdfs_label);
- bool userpreset = true; // TODO
+ LilvNode* comment = get_value(_world.world, preset, rdfs_comment);
+ /* TODO properly identify user vs factory presets.
+ * here's an indirect condition: only factory presets can have comments
+ */
+ bool userpreset = comment ? false : true;
if (name) {
- p.push_back (Plugin::PresetRecord (lilv_node_as_string(preset), lilv_node_as_string(name), userpreset));
+ p.push_back (Plugin::PresetRecord (lilv_node_as_string(preset), lilv_node_as_string(name), userpreset, comment ? lilv_node_as_string (comment) : ""));
lilv_node_free(name);
}
+ if (comment) {
+ lilv_node_free(comment);
+ }
}
lilv_nodes_free(presets);
+ lilv_node_free(rdfs_comment);
lilv_node_free(rdfs_label);
lilv_node_free(pset_Preset);
lilv_node_free(lv2_appliesTo);
return p;
}
-bool
-LV2PluginInfo::in_category (const std::string &c) const
-{
- // TODO use untranslated lilv_plugin_get_class()
- // match gtk2_ardour/plugin_selector.cc
- return category == c;
-}
-
-bool
-LV2PluginInfo::is_instrument () const
-{
- if (category == "Instrument") {
- return true;
- }
-#if 1
- /* until we make sure that category remains untranslated in the lv2.ttl spec
- * and until most instruments also classify themselves as such, there's a 2nd check:
- */
- if (n_inputs.n_midi() > 0 && n_inputs.n_audio() == 0 && n_outputs.n_audio() > 0) {
- return true;
- }
-#endif
- return false;
-}
-
PluginInfoList*
LV2PluginInfo::discover()
{