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
+#ifdef HAVE_LV2_1_10_0
+ LilvNode* atom_int;
+ LilvNode* atom_float;
+ LilvNode* atom_object; // new in 1.8
+ LilvNode* atom_vector;
+#endif
+#ifdef LV2_EXTENDED
+ LilvNode* lv2_noSampleAccurateCtrl;
+ LilvNode* auto_can_write_automatation; // lv2:optionalFeature
+ LilvNode* auto_automation_control; // atom:supports
+ LilvNode* auto_automation_controlled; // lv2:portProperty
+#endif
+
private:
bool _bundle_checked;
};
_state_version = 0;
_was_activated = false;
_has_state_interface = false;
+ _can_write_automation = false;
_impl->block_length = _session.get_block_size();
_instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access";
throw failed_constructor();
}
lilv_nodes_free(required_features);
+#endif
+#ifdef LV2_EXTENDED
LilvNodes* optional_features = lilv_plugin_get_optional_features (plugin);
if (lilv_nodes_contains (optional_features, _world.lv2_noSampleAccurateCtrl)) {
_no_sample_accurate_ctrl = true;
}
+ if (lilv_nodes_contains (optional_features, _world.auto_can_write_automatation)) {
+ _can_write_automation = true;
+ }
lilv_nodes_free(optional_features);
#endif
if (lilv_nodes_contains(atom_supports, _world.time_Position)) {
flags |= PORT_POSITION;
}
+#ifdef LV2_EXTENDED
+ if (lilv_nodes_contains(atom_supports, _world.auto_automation_control)) {
+ flags |= PORT_AUTOCTRL;
+ }
+#endif
if (lilv_nodes_contains(atom_supports, _world.patch_Message)) {
flags |= PORT_PATCHMSG;
if (flags & PORT_INPUT) {
throw failed_constructor();
}
+#ifdef LV2_EXTENDED
+ if (lilv_port_has_property(_impl->plugin, port, _world.auto_automation_controlled)) {
+ if ((flags & PORT_INPUT) && (flags & PORT_CONTROL)) {
+ flags |= PORT_CTRLED;
+ }
+ }
+#endif
+
_port_flags.push_back(flags);
_port_minimumSize.push_back(minimumSize);
}
* e.g The process cycle may be split when looping, also
* the period-size may change any time: see set_block_size()
*/
+ if (get_info()->n_inputs.n_midi() > 0) {
+ /* we don't yet implement midi buffer offsets (for split cycles).
+ * Also connect_and_run() also uses _session.transport_frame() directly
+ * (for BBT) which is not offset for plugin cycle split.
+ */
+ return true;
+ }
return _no_sample_accurate_ctrl;
}
const std::string
LV2Plugin::plugin_dir() const
{
- return Glib::build_filename(_session.plugins_dir(), _insert_id.to_s());
+ if (!_plugin_state_dir.empty ()){
+ return Glib::build_filename(_plugin_state_dir, _insert_id.to_s());
+ } else {
+ return Glib::build_filename(_session.plugins_dir(), _insert_id.to_s());
+ }
}
/** Directory for files created by the plugin (except during save). */
}
}
+ if (!_plugin_state_dir.empty()) {
+ root->add_property("template-dir", _plugin_state_dir);
+ }
+
if (_has_state_interface) {
// Provisionally increment state version and create directory
const std::string new_dir = state_dir(++_state_version);
0,
NULL);
- if (!_impl->state || !lilv_state_equals(state, _impl->state)) {
+ if (!_plugin_state_dir.empty()
+ || !_impl->state
+ || !lilv_state_equals(state, _impl->state)) {
lilv_state_save(_world.world,
_uri_map.urid_map(),
_uri_map.urid_unmap(),
new_dir.c_str(),
"state.ttl");
- lilv_state_free(_impl->state);
- _impl->state = state;
+ if (_plugin_state_dir.empty()) {
+ // normal session save
+ lilv_state_free(_impl->state);
+ _impl->state = state;
+ } else {
+ // template save (dedicated state-dir)
+ lilv_state_free(state);
+ }
} else {
// State is identical, decrement version and nuke directory
lilv_state_free(state);
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
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_as_string(name),
+ userpreset)));
lilv_node_free(name);
} else {
warning << string_compose(
}
}
+void
+LV2Plugin::set_state_dir (const std::string& d)
+{
+ _plugin_state_dir = d;
+}
+
int
LV2Plugin::set_state(const XMLNode& node, int version)
{
set_parameter(port_id, atof(value));
}
+ if ((prop = node.property("template-dir")) != 0) {
+ set_state_dir (prop->value ());
+ }
+
_state_version = 0;
if ((prop = node.property("state-dir")) != 0) {
if (sscanf(prop->value().c_str(), "state%u", &_state_version) != 1) {
prop->value()) << endmsg;
}
- // TODO: special case track-templates
- // (state must be saved with the template)
std::string state_file = Glib::build_filename(
plugin_dir(),
Glib::build_filename(prop->value(), "state.ttl"));
_impl->state = state;
}
+ if (!_plugin_state_dir.empty ()) {
+ // force save with session, next time (increment counter)
+ lilv_state_free (_impl->state);
+ _impl->state = NULL;
+ set_state_dir ("");
+ }
+
latency_compute_run();
#endif
return ret;
}
+void
+LV2Plugin::set_automation_control (uint32_t i, boost::shared_ptr<AutomationControl> c)
+{
+ if ((_port_flags[i] & PORT_CTRLED)) {
+ _ctrl_map [i] = AutomationCtrlPtr (new AutomationCtrl(c));
+ }
+}
+
+LV2Plugin::AutomationCtrlPtr
+LV2Plugin::get_automation_control (uint32_t i)
+{
+ if (_ctrl_map.find (i) == _ctrl_map.end()) {
+ return AutomationCtrlPtr ();
+ }
+ return _ctrl_map[i];
+}
+
void
LV2Plugin::activate()
{
{
DEBUG_TRACE(DEBUG::LV2, string_compose("%1 cleanup\n", name()));
- activate();
deactivate();
lilv_instance_free(_impl->instance);
_impl->instance = NULL;
*_bpm_control_port = tmetric.tempo().beats_per_minute();
}
+#ifdef LV2_EXTENDED
+ if (_can_write_automation && _session.transport_frame() != _next_cycle_start) {
+ // add guard-points after locating
+ for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) {
+ i->second->guard = true;
+ }
+ }
+#endif
+
ChanCount bufs_count;
bufs_count.set(DataType::AUDIO, 1);
bufs_count.set(DataType::MIDI, 1);
? *metric_i : NULL;
if (m != m_end && (!metric || metric->frame() > (*m).time())) {
const Evoral::MIDIEvent<framepos_t> ev(*m, false);
- LV2_Evbuf_Iterator eend = lv2_evbuf_end(_ev_buffers[port_index]);
- lv2_evbuf_write(&eend, ev.time(), 0, type, ev.size(), ev.buffer());
+ if (ev.time() < nframes) {
+ LV2_Evbuf_Iterator eend = lv2_evbuf_end(_ev_buffers[port_index]);
+ lv2_evbuf_write(&eend, ev.time(), 0, type, ev.size(), ev.buffer());
+ }
++m;
} else {
tmetric.set_metric(metric);
}
}
-
// Write messages to UI
- if ((_to_ui || _patch_port_out_index != (uint32_t)-1) &&
+ if ((_to_ui || _can_write_automation || _patch_port_out_index != (uint32_t)-1) &&
(flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
LV2_Evbuf* buf = _ev_buffers[port_index];
for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(buf);
uint8_t* data;
lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
+#ifdef LV2_EXTENDED
+ // Intercept Automation Write Events
+ if ((flags & PORT_AUTOCTRL)) {
+ LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom));
+ 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 == _uri_map.urids.auto_event) {
+ // only if transport_rolling ??
+ const LV2_Atom* parameter = NULL;
+ const LV2_Atom* value = NULL;
+ lv2_atom_object_get(obj,
+ _uri_map.urids.auto_parameter, ¶meter,
+ _uri_map.urids.auto_value, &value,
+ 0);
+ if (parameter && value) {
+ const uint32_t p = ((const LV2_Atom_Int*)parameter)->body;
+ const float v = ((const LV2_Atom_Float*)value)->body;
+ // -> add automation event..
+ AutomationCtrlPtr c = get_automation_control (p);
+ if (c && c->ac->automation_state() == Touch) {
+ if (c->guard) {
+ c->guard = false;
+ c->ac->list()->add (_session.transport_frame() + frames, v, true, true);
+ } else {
+ c->ac->set_double (v, _session.transport_frame() + frames, true);
+ }
+ }
+ }
+ }
+ else if (obj->body.otype == _uri_map.urids.auto_setup) {
+ // TODO optional arguments, for now we assume the plugin
+ // writes automation for its own inputs
+ // -> put them in "touch" mode (preferably "exclusive plugin touch(TM)"
+ for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) {
+ i->second->ac->set_automation_state (Touch);
+ }
+ }
+ else if (obj->body.otype == _uri_map.urids.auto_finalize) {
+ // set [touched] parameters to "play" ??
+ }
+ else if (obj->body.otype == _uri_map.urids.auto_start) {
+ const LV2_Atom* parameter = NULL;
+ lv2_atom_object_get(obj,
+ _uri_map.urids.auto_parameter, ¶meter,
+ 0);
+ if (parameter) {
+ const uint32_t p = ((const LV2_Atom_Int*)parameter)->body;
+ AutomationCtrlPtr c = get_automation_control (p);
+ if (c) {
+ c->ac->start_touch (_session.transport_frame());
+ c->guard = true;
+ }
+ }
+ }
+ else if (obj->body.otype == _uri_map.urids.auto_end) {
+ const LV2_Atom* parameter = NULL;
+ lv2_atom_object_get(obj,
+ _uri_map.urids.auto_parameter, ¶meter,
+ 0);
+ if (parameter) {
+ const uint32_t p = ((const LV2_Atom_Int*)parameter)->body;
+ AutomationCtrlPtr c = get_automation_control (p);
+ if (c) {
+ c->ac->stop_touch (true, _session.transport_frame());
+ }
+ }
+ }
+ }
+ }
+#endif
+
// Intercept patch change messages to emit PropertyChanged signal
if ((flags & PORT_PATCHMSG)) {
LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom));
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 "noSampleAccurateControls");
+#ifdef LV2_EXTENDED
+ lv2_noSampleAccurateCtrl = lilv_new_uri(world, LV2_CORE_PREFIX "noSampleAccurateControls");
+ auto_can_write_automatation = lilv_new_uri(world, LV2_AUTOMATE_URI__can_write);
+ auto_automation_control = lilv_new_uri(world, LV2_AUTOMATE_URI__control);
+ auto_automation_controlled = lilv_new_uri(world, LV2_AUTOMATE_URI__controlled);
+#endif
#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);
LV2World::~LV2World()
{
+ if (!world) {
+ return;
+ }
#ifdef HAVE_LV2_1_2_0
lilv_node_free(bufz_nominalBlockLength);
lilv_node_free(bufz_fixedBlockLength);
lilv_node_free(bufz_powerOf2BlockLength);
#endif
+#ifdef LV2_EXTENDED
lilv_node_free(lv2_noSampleAccurateCtrl);
+ lilv_node_free(auto_can_write_automatation);
+ lilv_node_free(auto_automation_control);
+ lilv_node_free(auto_automation_controlled);
+#endif
lilv_node_free(patch_Message);
lilv_node_free(patch_writable);
lilv_node_free(units_hz);
lilv_node_free(atom_Chunk);
lilv_node_free(atom_AtomPort);
lilv_world_free(world);
+ world = NULL;
}
void
return PluginPtr();
}
+std::vector<Plugin::PresetRecord>
+LV2PluginInfo::get_presets (bool /*user_only*/) const
+{
+ std::vector<Plugin::PresetRecord> p;
+#ifndef NO_PLUGIN_STATE
+ const LilvPlugin* lp = NULL;
+ try {
+ PluginPtr plugin;
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(_world.world);
+ LilvNode* uri = lilv_new_uri(_world.world, _plugin_uri);
+ if (!uri) { throw failed_constructor(); }
+ lp = lilv_plugins_get_by_uri(plugins, uri);
+ if (!lp) { throw failed_constructor(); }
+ lilv_node_free(uri);
+ } catch (failed_constructor& err) {
+ return p;
+ }
+ assert (lp);
+ // see LV2Plugin::find_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");
+
+ 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
+ if (name) {
+ p.push_back (Plugin::PresetRecord (lilv_node_as_string(preset), lilv_node_as_string(name), userpreset));
+ lilv_node_free(name);
+ }
+ }
+ lilv_nodes_free(presets);
+ lilv_node_free(rdfs_label);
+ lilv_node_free(pset_Preset);
+ lilv_node_free(lv2_appliesTo);
+#endif
+ return p;
+}
+
bool
LV2PluginInfo::in_category (const std::string &c) const
{