X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=e45270ebcb6326e6baabb87883c494b35b151893;hb=935fd3b32f600d6f20c134fa2ccae78aa4781de5;hp=c6c2a6b127ab26cfd826fcf47636d71c4c059b10;hpb=56c4eebfdd1033b9dd92ddcdf8698dda38545997;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index c6c2a6b127..e45270ebcb 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -52,7 +52,7 @@ #include "ardour/worker.h" #include "ardour/search_paths.h" -#include "i18n.h" +#include "pbd/i18n.h" #include #include @@ -196,15 +196,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. */ @@ -213,15 +207,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 @@ -258,8 +246,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; } @@ -327,6 +316,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) @@ -342,6 +332,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) @@ -476,19 +467,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); @@ -562,6 +558,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) { @@ -847,6 +850,7 @@ LV2Plugin::~LV2Plugin () delete _to_ui; delete _from_ui; delete _worker; + delete _state_worker; if (_atom_ev_buffers) { LV2_Evbuf** b = _atom_ev_buffers; @@ -1063,38 +1067,36 @@ LV2Plugin::get_layout (uint32_t which, UILayoutHint& h) const } h.knob = true; switch (which) { - case 0: h.x0 = 0; h.x1 = 1; h.y0 = 4; h.y1 = 5; break; // Shelf toggle L - case 1: h.x0 = 0; h.x1 = 1; h.y0 = 2; h.y1 = 3; break; // Frequency L - case 2: h.x0 = 0; h.x1 = 1; h.y0 = 0; h.y1 = 1; break; // Gain L - case 19: h.x0 = 0; h.x1 = 1; h.y0 = 5; h.y1 = 6; break; // enable L - - case 3: h.x0 = 1; h.x1 = 3; h.y0 = 2; h.y1 = 3; break; // Frequency 1 - case 4: h.x0 = 1; h.x1 = 3; h.y0 = 0; h.y1 = 1; break; // Gain 1 - case 5: h.x0 = 2; h.x1 = 4; h.y0 = 1; h.y1 = 2; break; // Bandwidth 1 - case 20: h.x0 = 1; h.x1 = 4; h.y0 = 5; h.y1 = 6; break; // enable 1 - - case 6: h.x0 = 4; h.x1 = 6; h.y0 = 2; h.y1 = 3; break; // Frequency 2 - case 7: h.x0 = 4; h.x1 = 6; h.y0 = 0; h.y1 = 1; break; // Gain 2 - case 8: h.x0 = 5; h.x1 = 7; h.y0 = 1; h.y1 = 2; break; // Bandwidth 2 - case 21: h.x0 = 4; h.x1 = 7; h.y0 = 5; h.y1 = 6; break; // enable 2 - - case 9: h.x0 = 7; h.x1 = 9; h.y0 = 2; h.y1 = 3; break; // Frequency 3 - case 10: h.x0 = 7; h.x1 = 9; h.y0 = 0; h.y1 = 1; break; // Gain 3 - case 11: h.x0 = 8; h.x1 = 10; h.y0 = 1; h.y1 = 2; break; // Bandwidth 3 - case 22: h.x0 = 7; h.x1 = 10; h.y0 = 5; h.y1 = 6; break; // enable 3 - - case 12: h.x0 = 10; h.x1 = 12; h.y0 = 2; h.y1 = 3; break; // Frequency 4 - case 13: h.x0 = 10; h.x1 = 12; h.y0 = 0; h.y1 = 1; break; // Gain 4 - case 14: h.x0 = 11; h.x1 = 13; h.y0 = 1; h.y1 = 2; break; // Bandwidth 4 - case 23: h.x0 = 10; h.x1 = 13; h.y0 = 5; h.y1 = 6; break; // enable 4 - - case 15: h.x0 = 13; h.x1 = 14; h.y0 = 4; h.y1 = 5; break; // Shelf toggle H - case 16: h.x0 = 13; h.x1 = 14; h.y0 = 2; h.y1 = 3; break; // Frequency H - case 17: h.x0 = 13; h.x1 = 14; h.y0 = 0; h.y1 = 1; break; // Gain H - case 24: h.x0 = 13; h.x1 = 14; h.y0 = 5; h.y1 = 6; break; // enable H - - - case 18: h.x0 = 14; h.x1 = 15; h.y0 = 4; h.y1 = 6; break; // Master Gain + 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; } @@ -1341,8 +1343,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); } @@ -1844,10 +1853,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 @@ -2273,6 +2283,7 @@ write_position(LV2_Atom_Forge* forge, const TempoMetric& t, Timecode::BBT_Time& bbt, double speed, + double bpm, framepos_t position, framecnt_t offset) { @@ -2297,7 +2308,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); @@ -2314,7 +2325,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); @@ -2343,7 +2354,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } 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 @@ -2413,14 +2424,20 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } if (valid && (flags & PORT_INPUT)) { - Timecode::BBT_Time bbt; if ((flags & PORT_POSITION)) { + 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) { - // Transport has changed, write position at cycle start - bbt = tmap.bbt_at_frame (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, speed, start, 0); + tmetric, bbt, speed, bpm, start, 0); } } @@ -2449,9 +2466,11 @@ 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, speed, + tmetric, bbt, speed, bpm, metric->frame(), metric->frame() - start); ++metric_i; @@ -2705,6 +2724,21 @@ LV2Plugin::connect_and_run(BufferSet& bufs, _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 (); _current_latency = new_latency; @@ -2803,7 +2837,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) { @@ -2812,10 +2846,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); } @@ -2861,7 +2909,7 @@ LV2Plugin::latency_compute_run() port_index++; } - run(bufsize); + run(bufsize, true); deactivate(); if (was_activated) { activate();