X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=2d19ea94581e18f6c1743a90cdb96203557700ac;hb=6946bdc0830c9f0971d2cd0d54b27e343c54d96a;hp=ce090a79a40adbaa0d59f426dd2b0375d07a160e;hpb=86607097d618f778ea984cba2a7dc9c403fe8966;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index ce090a79a4..2d19ea9458 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -36,6 +36,7 @@ #include "pbd/stl_delete.h" #include "pbd/compose.h" #include "pbd/error.h" +#include "pbd/replace_all.h" #include "pbd/xml++.h" #include "libardour-config.h" @@ -51,7 +52,7 @@ #include "ardour/worker.h" #include "ardour/search_paths.h" -#include "i18n.h" +#include "pbd/i18n.h" #include #include @@ -108,6 +109,8 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +bool LV2Plugin::force_state_save = false; + class LV2World : boost::noncopyable { public: LV2World (); @@ -129,6 +132,7 @@ public: LilvNode* ext_expensive; LilvNode* ext_causesArtifacts; LilvNode* ext_notAutomatic; + LilvNode* ext_rangeSteps; LilvNode* lv2_AudioPort; LilvNode* lv2_ControlPort; LilvNode* lv2_InputPort; @@ -164,6 +168,7 @@ public: LilvNode* bufz_powerOf2BlockLength; LilvNode* bufz_fixedBlockLength; LilvNode* bufz_nominalBlockLength; + LilvNode* bufz_coarseBlockLength; #endif #ifdef HAVE_LV2_1_10_0 @@ -194,15 +199,9 @@ work_schedule(LV2_Worker_Schedule_Handle handle, uint32_t size, const void* data) { - LV2Plugin* plugin = (LV2Plugin*)handle; - if (plugin->session().engine().freewheeling()) { - // Freewheeling, do the work immediately in this (audio) thread - return (LV2_Worker_Status)plugin->work(size, data); - } else { - // Enqueue message for the worker thread - return plugin->worker()->schedule(size, data) ? - LV2_WORKER_SUCCESS : LV2_WORKER_ERR_UNKNOWN; - } + return (((Worker*)handle)->schedule(size, data) + ? LV2_WORKER_SUCCESS + : LV2_WORKER_ERR_UNKNOWN); } /** Called by the plugin to respond to non-RT work. */ @@ -211,15 +210,9 @@ work_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void* data) { - LV2Plugin* plugin = (LV2Plugin*)handle; - if (plugin->session().engine().freewheeling()) { - // Freewheeling, respond immediately in this (audio) thread - return (LV2_Worker_Status)plugin->work_response(size, data); - } else { - // Enqueue response for the worker - return plugin->worker()->respond(size, data) ? - LV2_WORKER_SUCCESS : LV2_WORKER_ERR_UNKNOWN; - } + return (((Worker*)handle)->respond(size, data) + ? LV2_WORKER_SUCCESS + : LV2_WORKER_ERR_UNKNOWN); } #ifdef LV2_EXTENDED @@ -256,8 +249,9 @@ log_vprintf(LV2_Log_Handle /*handle*/, warning << str << endmsg; } else if (type == URIMap::instance().urids.log_Note) { info << str << endmsg; + } else if (type == URIMap::instance().urids.log_Trace) { + DEBUG_TRACE(DEBUG::LV2, str); } - // TODO: Toggleable log:Trace message support return ret; } @@ -325,6 +319,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine, , _impl(new Impl()) , _features(NULL) , _worker(NULL) + , _state_worker(NULL) , _insert_id("0") , _patch_port_in_index((uint32_t)-1) , _patch_port_out_index((uint32_t)-1) @@ -340,6 +335,7 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other) , _impl(new Impl()) , _features(NULL) , _worker(NULL) + , _state_worker(NULL) , _insert_id(other._insert_id) , _patch_port_in_index((uint32_t)-1) , _patch_port_out_index((uint32_t)-1) @@ -474,19 +470,24 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) log->vprintf = &log_vprintf; _log_feature.data = log; + const size_t ring_size = _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS; LilvNode* worker_schedule = lilv_new_uri(_world.world, LV2_WORKER__schedule); if (lilv_plugin_has_feature(plugin, worker_schedule)) { LV2_Worker_Schedule* schedule = (LV2_Worker_Schedule*)malloc( sizeof(LV2_Worker_Schedule)); - size_t buf_size = _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS; - _worker = new Worker(this, buf_size); - schedule->handle = this; + _worker = new Worker(this, ring_size); + schedule->handle = _worker; schedule->schedule_work = work_schedule; _work_schedule_feature.data = schedule; _features[n_features++] = &_work_schedule_feature; } lilv_node_free(worker_schedule); + if (_has_state_interface) { + // Create a non-threaded worker for use by state restore + _state_worker = new Worker(this, ring_size, false); + } + _impl->instance = lilv_plugin_instantiate(plugin, rate, _features); _impl->name = lilv_plugin_get_name(plugin); _impl->author = lilv_plugin_get_author_name(plugin); @@ -547,9 +548,15 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) lilv_nodes_free(required_features); #endif -#ifdef LV2_EXTENDED LilvNodes* optional_features = lilv_plugin_get_optional_features (plugin); +#ifdef HAVE_LV2_1_2_0 + if (lilv_nodes_contains (optional_features, _world.bufz_coarseBlockLength)) { + _no_sample_accurate_ctrl = true; + } +#endif +#ifdef LV2_EXTENDED if (lilv_nodes_contains (optional_features, _world.lv2_noSampleAccurateCtrl)) { + /* deprecated 2016-Sep-18 in favor of bufz_coarseBlockLength */ _no_sample_accurate_ctrl = true; } if (lilv_nodes_contains (optional_features, _world.auto_can_write_automatation)) { @@ -560,6 +567,13 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) #ifdef HAVE_LILV_0_16_0 // Load default state + if (_worker) { + /* immediately schedule any work, + * so that state restore later will not find a busy + * worker. latency_compute_run() flushes any replies + */ + _worker->set_synchronous(true); + } LilvState* state = lilv_state_new_from_world( _world.world, _uri_map.urid_map(), lilv_plugin_get_uri(_impl->plugin)); if (state && _has_state_interface) { @@ -845,6 +859,7 @@ LV2Plugin::~LV2Plugin () delete _to_ui; delete _from_ui; delete _worker; + delete _state_worker; if (_atom_ev_buffers) { LV2_Evbuf** b = _atom_ev_buffers; @@ -1052,6 +1067,51 @@ LV2Plugin::get_parameter_docs(uint32_t which) const return ""; } +bool +LV2Plugin::get_layout (uint32_t which, UILayoutHint& h) const +{ + /// TODO lookup port-properties + if (unique_id () != "urn:ardour:a-eq") { + return false; + } + h.knob = true; + switch (which) { + case 0: h.x0 = 0; h.x1 = 1; h.y0 = 2; h.y1 = 3; break; // Frequency L + case 1: h.x0 = 0; h.x1 = 1; h.y0 = 0; h.y1 = 1; break; // Gain L + case 17: h.x0 = 0; h.x1 = 1; h.y0 = 5; h.y1 = 6; break; // enable L + + case 2: h.x0 = 1; h.x1 = 3; h.y0 = 2; h.y1 = 3; break; // Frequency 1 + case 3: h.x0 = 1; h.x1 = 3; h.y0 = 0; h.y1 = 1; break; // Gain 1 + case 4: h.x0 = 2; h.x1 = 4; h.y0 = 1; h.y1 = 2; break; // Bandwidth 1 + case 18: h.x0 = 1; h.x1 = 4; h.y0 = 5; h.y1 = 6; break; // enable 1 + + case 5: h.x0 = 4; h.x1 = 6; h.y0 = 2; h.y1 = 3; break; // Frequency 2 + case 6: h.x0 = 4; h.x1 = 6; h.y0 = 0; h.y1 = 1; break; // Gain 2 + case 7: h.x0 = 5; h.x1 = 7; h.y0 = 1; h.y1 = 2; break; // Bandwidth 2 + case 19: h.x0 = 4; h.x1 = 7; h.y0 = 5; h.y1 = 6; break; // enable 2 + + case 8: h.x0 = 7; h.x1 = 9; h.y0 = 2; h.y1 = 3; break; // Frequency 3 + case 9: h.x0 = 7; h.x1 = 9; h.y0 = 0; h.y1 = 1; break; // Gain 3 + case 10: h.x0 = 8; h.x1 = 10; h.y0 = 1; h.y1 = 2; break; // Bandwidth 3 + case 20: h.x0 = 7; h.x1 = 10; h.y0 = 5; h.y1 = 6; break; // enable 3 + + case 11: h.x0 = 10; h.x1 = 12; h.y0 = 2; h.y1 = 3; break; // Frequency 4 + case 12: h.x0 = 10; h.x1 = 12; h.y0 = 0; h.y1 = 1; break; // Gain 4 + case 13: h.x0 = 11; h.x1 = 13; h.y0 = 1; h.y1 = 2; break; // Bandwidth 4 + case 21: h.x0 = 10; h.x1 = 13; h.y0 = 5; h.y1 = 6; break; // enable 4 + + case 14: h.x0 = 13; h.x1 = 14; h.y0 = 2; h.y1 = 3; break; // Frequency H + case 15: h.x0 = 13; h.x1 = 14; h.y0 = 0; h.y1 = 1; break; // Gain H + case 22: h.x0 = 13; h.x1 = 14; h.y0 = 5; h.y1 = 6; break; // enable H + + case 16: h.x0 = 14; h.x1 = 15; h.y0 = 1; h.y1 = 3; break; // Master Gain + case 23: h.x0 = 14; h.x1 = 15; h.y0 = 5; h.y1 = 6; break; // Master Enable + default: + return false; + } + return true; +} + uint32_t LV2Plugin::nth_parameter(uint32_t n, bool& ok) const { @@ -1190,7 +1250,7 @@ LV2Plugin::add_state(XMLNode* root) const 0, NULL); - if (!_plugin_state_dir.empty() + if (!_plugin_state_dir.empty() || force_state_save || !_impl->state || !lilv_state_equals(state, _impl->state)) { lilv_state_save(_world.world, @@ -1201,13 +1261,19 @@ LV2Plugin::add_state(XMLNode* root) const new_dir.c_str(), "state.ttl"); - if (_plugin_state_dir.empty()) { + if (force_state_save) { + // archive or save-as + lilv_state_free(state); + --_state_version; + } + else 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); + --_state_version; } } else { // State is identical, decrement version and nuke directory @@ -1292,8 +1358,15 @@ LV2Plugin::load_preset(PresetRecord r) LilvNode* pset = lilv_new_uri(world, r.uri.c_str()); LilvState* state = lilv_state_new_from_world(world, _uri_map.urid_map(), pset); + const LV2_Feature* state_features[2] = { NULL, NULL }; + LV2_Worker_Schedule schedule = { _state_worker, work_schedule }; + const LV2_Feature state_sched_feature = { LV2_WORKER__schedule, &schedule }; + if (_state_worker) { + state_features[0] = &state_sched_feature; + } + if (state) { - lilv_state_restore(state, _impl->instance, set_port_value, this, 0, NULL); + lilv_state_restore(state, _impl->instance, set_port_value, this, 0, state_features); lilv_state_free(state); Plugin::load_preset(r); } @@ -1644,6 +1717,7 @@ load_parameter_descriptor_units(LilvWorld* lworld, ParameterDescriptor& desc, co LilvNode* render = get_value(lworld, unit, _world.units_render); if (render) { desc.print_fmt = lilv_node_as_string(render); + replace_all (desc.print_fmt, "%f", "%.2f"); lilv_node_free(render); } } @@ -1794,10 +1868,11 @@ LV2Plugin::emit_to_ui(void* controller, UIMessageSink sink) } int -LV2Plugin::work(uint32_t size, const void* data) +LV2Plugin::work(Worker& worker, uint32_t size, const void* data) { + Glib::Threads::Mutex::Lock lm(_work_mutex); return _impl->work_iface->work( - _impl->instance->lv2_handle, work_respond, this, size, data); + _impl->instance->lv2_handle, work_respond, &worker, size, data); } int @@ -1932,6 +2007,8 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c lilv_port_get_range(_impl->plugin, port, &def, &min, &max); portunits = lilv_port_get_value(_impl->plugin, port, _world.units_unit); + LilvNode* steps = lilv_port_get(_impl->plugin, port, _world.ext_rangeSteps); + // TODO: Once we can rely on lilv 0.18.0 being present, // load_parameter_descriptor() can be used for ports as well desc.integer_step = lilv_port_has_property(_impl->plugin, port, _world.lv2_integer); @@ -1957,9 +2034,34 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c desc.update_steps(); + if (steps) { + //override auto-calculated steps in update_steps() + float s = lilv_node_as_float (steps); + const float delta = desc.upper - desc.lower; + + desc.step = desc.smallstep = (delta / s); + desc.largestep = std::min ((delta / 5.0f), 10.f * desc.smallstep); + + if (desc.logarithmic) { + // TODO marry AutomationControl::internal_to_interface () with + // http://lv2plug.in/ns/ext/port-props/#rangeSteps + desc.smallstep = desc.smallstep / logf(s); + desc.step = desc.step / logf(s); + desc.largestep = desc.largestep / logf(s); + } else if (desc.integer_step) { + desc.smallstep = 1.0; + desc.step = std::max(1.f, rintf (desc.step)); + desc.largestep = std::max(1.f, rintf (desc.largestep)); + } + DEBUG_TRACE(DEBUG::LV2, string_compose("parameter %1 small: %2, step: %3 largestep: %4\n", + which, desc.smallstep, desc.step, desc.largestep)); + } + + lilv_node_free(def); lilv_node_free(min); lilv_node_free(max); + lilv_node_free(steps); lilv_nodes_free(portunits); return 0; @@ -2196,6 +2298,7 @@ write_position(LV2_Atom_Forge* forge, const TempoMetric& t, Timecode::BBT_Time& bbt, double speed, + double bpm, framepos_t position, framecnt_t offset) { @@ -2220,7 +2323,7 @@ write_position(LV2_Atom_Forge* forge, lv2_atom_forge_key(forge, urids.time_beatsPerBar); lv2_atom_forge_float(forge, t.meter().divisions_per_bar()); lv2_atom_forge_key(forge, urids.time_beatsPerMinute); - lv2_atom_forge_float(forge, t.tempo().beats_per_minute()); + lv2_atom_forge_float(forge, bpm); #else lv2_atom_forge_blank(forge, &frame, 1, urids.time_Position); lv2_atom_forge_property_head(forge, urids.time_frame, 0); @@ -2237,7 +2340,7 @@ write_position(LV2_Atom_Forge* forge, lv2_atom_forge_property_head(forge, urids.time_beatsPerBar, 0); lv2_atom_forge_float(forge, t.meter().divisions_per_bar()); lv2_atom_forge_property_head(forge, urids.time_beatsPerMinute, 0); - lv2_atom_forge_float(forge, t.tempo().beats_per_minute()); + lv2_atom_forge_float(forge, bpm); #endif LV2_Evbuf_Iterator end = lv2_evbuf_end(buf); @@ -2248,28 +2351,29 @@ write_position(LV2_Atom_Forge* forge, int LV2Plugin::connect_and_run(BufferSet& bufs, - ChanMapping in_map, ChanMapping out_map, - pframes_t nframes, framecnt_t offset) + framepos_t start, framepos_t end, double speed, + ChanMapping in_map, ChanMapping out_map, + pframes_t nframes, framecnt_t offset) { DEBUG_TRACE(DEBUG::LV2, string_compose("%1 run %2 offset %3\n", name(), nframes, offset)); - Plugin::connect_and_run(bufs, in_map, out_map, nframes, offset); + Plugin::connect_and_run(bufs, start, end, speed, in_map, out_map, nframes, offset); cycles_t then = get_cycles(); TempoMap& tmap = _session.tempo_map(); Metrics::const_iterator metric_i = tmap.metrics_end(); - TempoMetric tmetric = tmap.metric_at(_session.transport_frame(), &metric_i); + TempoMetric tmetric = tmap.metric_at(start, &metric_i); if (_freewheel_control_port) { *_freewheel_control_port = _session.engine().freewheeling() ? 1.f : 0.f; } if (_bpm_control_port) { - *_bpm_control_port = tmetric.tempo().beats_per_minute(); + *_bpm_control_port = tmap.tempo_at_frame (start).beats_per_minute(); } #ifdef LV2_EXTENDED - if (_can_write_automation && _session.transport_frame() != _next_cycle_start) { + if (_can_write_automation && start != _next_cycle_start) { // add guard-points after locating for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) { i->second->guard = true; @@ -2335,15 +2439,20 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } if (valid && (flags & PORT_INPUT)) { - Timecode::BBT_Time bbt; if ((flags & PORT_POSITION)) { - if (_session.transport_frame() != _next_cycle_start || - _session.transport_speed() != _next_cycle_speed) { - // Transport has changed, write position at cycle start - bbt = tmap.bbt_at_frame (_session.transport_frame()); + Timecode::BBT_Time bbt (tmap.bbt_at_frame (start)); + double bpm = tmap.tempo_at_frame (start).beats_per_minute(); + double beatpos = (bbt.bars - 1) * tmetric.meter().divisions_per_bar() + + (bbt.beats - 1) + + (bbt.ticks / Timecode::BBT_Time::ticks_per_beat); + beatpos *= tmetric.meter().note_divisor() / 4.0; + if (start != _next_cycle_start || + speed != _next_cycle_speed || + rint (1000 * beatpos) != rint(1000 * _next_cycle_beat) || + bpm != _current_bpm) { + // Transport or Tempo has changed, write position at cycle start write_position(&_impl->forge, _ev_buffers[port_index], - tmetric, bbt, _session.transport_speed(), - _session.transport_frame(), 0); + tmetric, bbt, speed, bpm, start, 0); } } @@ -2357,7 +2466,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, // Now merge MIDI and any transport events into the buffer const uint32_t type = _uri_map.urids.midi_MidiEvent; - const framepos_t tend = _session.transport_frame() + nframes; + const framepos_t tend = end; ++metric_i; while (m != m_end || (metric_i != tmap.metrics_end() && (*metric_i)->frame() < tend)) { @@ -2372,11 +2481,13 @@ LV2Plugin::connect_and_run(BufferSet& bufs, ++m; } else { tmetric.set_metric(metric); + Timecode::BBT_Time bbt; bbt = tmap.bbt_at_pulse (metric->pulse()); + double bpm = tmap.tempo_at_frame (start/*XXX*/).beats_per_minute(); write_position(&_impl->forge, _ev_buffers[port_index], - tmetric, bbt, _session.transport_speed(), + tmetric, bbt, speed, bpm, metric->frame(), - metric->frame() - _session.transport_frame()); + metric->frame() - start); ++metric_i; } } @@ -2497,8 +2608,8 @@ LV2Plugin::connect_and_run(BufferSet& bufs, if (c && (c->ac->automation_state() == Touch || c->ac->automation_state() == Write) ) { - framepos_t when = std::max ((framepos_t) 0, _session.transport_frame() + frames - _current_latency); - assert (_session.transport_frame() + frames - _current_latency >= 0); + framepos_t when = std::max ((framepos_t) 0, start + frames - _current_latency); + assert (start + frames - _current_latency >= 0); if (c->guard) { c->guard = false; c->ac->list()->add (when, v, true, true); @@ -2560,7 +2671,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, AutomationCtrlPtr c = get_automation_control (p); DEBUG_TRACE(DEBUG::LV2Automate, string_compose ("Start Touch p: %1\n", p)); if (c) { - c->ac->start_touch (std::max ((framepos_t)0, _session.transport_frame() - _current_latency)); + c->ac->start_touch (std::max ((framepos_t)0, start - _current_latency)); c->guard = true; } } @@ -2575,7 +2686,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, AutomationCtrlPtr c = get_automation_control (p); DEBUG_TRACE(DEBUG::LV2Automate, string_compose ("End Touch p: %1\n", p)); if (c) { - c->ac->stop_touch (true, std::max ((framepos_t)0, _session.transport_frame() - _current_latency)); + c->ac->stop_touch (true, std::max ((framepos_t)0, start - _current_latency)); } } } @@ -2625,14 +2736,26 @@ LV2Plugin::connect_and_run(BufferSet& bufs, set_cycles((uint32_t)(now - then)); // Update expected transport information for next cycle so we can detect changes - _next_cycle_speed = _session.transport_speed(); - _next_cycle_start = _session.transport_frame() + (nframes * _next_cycle_speed); + _next_cycle_speed = speed; + _next_cycle_start = end; + + { + /* keep track of lv2:timePosition like plugins can do. + * Note: for no-midi plugins, we only ever send information at cycle-start, + * so it needs to be realative to that. + */ + TempoMetric t = tmap.metric_at(start); + _current_bpm = tmap.tempo_at_frame (start).beats_per_minute(); + Timecode::BBT_Time bbt (tmap.bbt_at_frame (start)); + double beatpos = (bbt.bars - 1) * t.meter().divisions_per_bar() + + (bbt.beats - 1) + + (bbt.ticks / Timecode::BBT_Time::ticks_per_beat); + beatpos *= tmetric.meter().note_divisor() / 4.0; + _next_cycle_beat = beatpos + nframes * speed * _current_bpm / (60.f * _session.frame_rate()); + } if (_latency_control_port) { framecnt_t new_latency = signal_latency (); - if (_current_latency != new_latency) { - LatencyChanged (_current_latency, new_latency); /* EMIT SIGNAL */ - } _current_latency = new_latency; } return 0; @@ -2673,6 +2796,30 @@ LV2Plugin::parameter_is_input(uint32_t param) const return _port_flags[param] & PORT_INPUT; } +uint32_t +LV2Plugin::designated_bypass_port () +{ + const LilvPort* port = NULL; + LilvNode* designation = lilv_new_uri (_world.world, LV2_CORE_PREFIX "enabled"); + port = lilv_plugin_get_port_by_designation ( + _impl->plugin, _world.lv2_InputPort, designation); + lilv_node_free(designation); + if (port) { + return lilv_port_get_index (_impl->plugin, port); + } +#ifdef LV2_EXTENDED + /* deprecated on 2016-Sep-18 in favor of lv2:enabled */ + designation = lilv_new_uri (_world.world, LV2_PROCESSING_URI__enable); + port = lilv_plugin_get_port_by_designation ( + _impl->plugin, _world.lv2_InputPort, designation); + lilv_node_free(designation); + if (port) { + return lilv_port_get_index (_impl->plugin, port); + } +#endif + return UINT32_MAX; +} + void LV2Plugin::print_parameter(uint32_t param, char* buf, uint32_t len) const { @@ -2713,7 +2860,7 @@ LV2Plugin::get_scale_points(uint32_t port_index) const } void -LV2Plugin::run(pframes_t nframes) +LV2Plugin::run(pframes_t nframes, bool sync_work) { uint32_t const N = parameter_count(); for (uint32_t i = 0; i < N; ++i) { @@ -2722,10 +2869,24 @@ LV2Plugin::run(pframes_t nframes) } } + if (_worker) { + // Execute work synchronously if we're freewheeling (export) + _worker->set_synchronous(sync_work || session().engine().freewheeling()); + } + + // Run the plugin for this cycle lilv_instance_run(_impl->instance, nframes); - if (_impl->work_iface) { + // Emit any queued worker responses (calls a plugin callback) + if (_state_worker) { + _state_worker->emit_responses(); + } + if (_worker) { _worker->emit_responses(); + } + + // Notify the plugin that a work run cycle is complete + if (_impl->work_iface) { if (_impl->work_iface->end_run) { _impl->work_iface->end_run(_impl->instance->lv2_handle); } @@ -2771,7 +2932,7 @@ LV2Plugin::latency_compute_run() port_index++; } - run(bufsize); + run(bufsize, true); deactivate(); if (was_activated) { activate(); @@ -2817,6 +2978,7 @@ LV2World::LV2World() ext_expensive = lilv_new_uri(world, LV2_PORT_PROPS__expensive); ext_causesArtifacts= lilv_new_uri(world, LV2_PORT_PROPS__causesArtifacts); ext_notAutomatic = lilv_new_uri(world, LV2_PORT_PROPS__notAutomatic); + ext_rangeSteps = lilv_new_uri(world, LV2_PORT_PROPS__rangeSteps); lv2_AudioPort = lilv_new_uri(world, LILV_URI_AUDIO_PORT); lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT); lv2_InputPort = lilv_new_uri(world, LILV_URI_INPUT_PORT); @@ -2849,7 +3011,7 @@ LV2World::LV2World() patch_writable = lilv_new_uri(world, LV2_PATCH__writable); patch_Message = lilv_new_uri(world, LV2_PATCH__Message); #ifdef LV2_EXTENDED - lv2_noSampleAccurateCtrl = lilv_new_uri(world, "http://ardour.org/lv2/ext#noSampleAccurateControls"); + lv2_noSampleAccurateCtrl = lilv_new_uri(world, "http://ardour.org/lv2/ext#noSampleAccurateControls"); // deprecated 2016-09-18 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); @@ -2859,6 +3021,7 @@ LV2World::LV2World() 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"); + bufz_coarseBlockLength = lilv_new_uri(world, "http://lv2plug.in/ns/ext/buf-size#coarseBlockLength"); #endif } @@ -2869,6 +3032,7 @@ LV2World::~LV2World() return; } #ifdef HAVE_LV2_1_2_0 + lilv_node_free(bufz_coarseBlockLength); lilv_node_free(bufz_nominalBlockLength); lilv_node_free(bufz_fixedBlockLength); lilv_node_free(bufz_powerOf2BlockLength); @@ -2908,6 +3072,7 @@ LV2World::~LV2World() lilv_node_free(lv2_InputPort); lilv_node_free(lv2_ControlPort); lilv_node_free(lv2_AudioPort); + lilv_node_free(ext_rangeSteps); lilv_node_free(ext_notAutomatic); lilv_node_free(ext_causesArtifacts); lilv_node_free(ext_expensive);