diff --git a/libs/ardour/ardour/vst3_plugin.h b/libs/ardour/ardour/vst3_plugin.h index 999ded3874..eac754fbfb 100644 --- a/libs/ardour/ardour/vst3_plugin.h +++ b/libs/ardour/ardour/vst3_plugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Robin Gareus + * Copyright (C) 2019-2023 Robin Gareus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ #include #include +#include #include "pbd/search_path.h" #include "pbd/signals.h" @@ -145,11 +146,13 @@ public: /* API for Ardour -- Parameters */ bool try_set_parameter_by_id (Vst::ParamID id, float value); - void set_parameter (uint32_t p, float value, int32 sample_off); + void set_parameter (uint32_t p, float value, int32 sample_off, bool to_list = true); float get_parameter (uint32_t p) const; std::string format_parameter (uint32_t p) const; Vst::ParamID index_to_id (uint32_t) const; + Glib::Threads::Mutex& process_lock () { return _process_lock; } + enum ParameterChange { BeginGesture, EndGesture, ValueChange, @@ -239,6 +242,7 @@ private: IPtr _processor; Vst::ProcessContext _context; + Glib::Threads::Mutex _process_lock; /* Parameters */ Vst3ParameterChanges _input_param_changes; @@ -398,6 +402,15 @@ private: std::vector _connected_inputs; std::vector _connected_outputs; + + struct PV { + PV () : port (0), val (0) {} + PV (uint32_t p, float v) : port (p), val (v) {} + uint32_t port; + float val; + }; + + PBD::RingBufferNPT _parameter_queue; }; /* ****************************************************************************/ diff --git a/libs/ardour/vst3_plugin.cc b/libs/ardour/vst3_plugin.cc index 37fe11f1ba..bb5b719c82 100644 --- a/libs/ardour/vst3_plugin.cc +++ b/libs/ardour/vst3_plugin.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 Robin Gareus + * Copyright (C) 2019-2023 Robin Gareus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -56,12 +56,14 @@ using namespace Presonus; VST3Plugin::VST3Plugin (AudioEngine& engine, Session& session, VST3PI* plug) : Plugin (engine, session) , _plug (plug) + , _parameter_queue (128 + plug->parameter_count ()) { init (); } VST3Plugin::VST3Plugin (const VST3Plugin& other) : Plugin (other) + , _parameter_queue (128 + other.parameter_count ()) { boost::shared_ptr nfo = boost::dynamic_pointer_cast (other.get_info ()); _plug = new VST3PI (nfo->m, nfo->unique_id); @@ -121,6 +123,7 @@ VST3Plugin::parameter_change_handler (VST3PI::ParameterChange t, uint32_t param, Plugin::EndTouch (param); break; case VST3PI::ValueChange: + _parameter_queue.write_one (PV (param, value)); /* emit ParameterChangedExternally, mark preset dirty */ Plugin::parameter_changed_externally (param, value); break; @@ -163,7 +166,13 @@ VST3Plugin::default_value (uint32_t port) void VST3Plugin::set_parameter (uint32_t port, float val, sampleoffset_t when) { - _plug->set_parameter (port, val, when); + if (AudioEngine::instance()->in_process_thread()) { + _plug->set_parameter (port, val, when); + } else { + assert (when == 0); + _plug->set_parameter (port, val, when, false); + _parameter_queue.write_one (PV (port, val)); + } Plugin::set_parameter (port, val, when); } @@ -622,6 +631,11 @@ VST3Plugin::connect_and_run (BufferSet& bufs, ChanMapping const& in_map, ChanMapping const& out_map, pframes_t n_samples, samplecnt_t offset) { + Glib::Threads::Mutex::Lock tm (_plug->process_lock (), Glib::Threads::TRY_LOCK); + if (!tm.locked ()) { + return 0; + } + DEBUG_TRACE (DEBUG::VST3Process, string_compose ("%1 run %2 offset %3\n", name (), n_samples, offset)); Plugin::connect_and_run (bufs, start, end, speed, in_map, out_map, n_samples, offset); @@ -717,6 +731,12 @@ VST3Plugin::connect_and_run (BufferSet& bufs, _connected_outputs[i] = valid; } + /* apply parameter changes */ + PV pv; + while (_parameter_queue.read (&pv, 1)) { + _plug->set_parameter (pv.port, pv.val, 0); + } + in_index = 0; for (int32_t i = 0; i < (int32_t)_plug->n_midi_inputs (); ++i) { bool valid = false; @@ -775,6 +795,8 @@ VST3Plugin::load_preset (PresetRecord r) return false; } + Glib::Threads::Mutex::Lock lx (_plug->process_lock ()); + if (tmp[0] == "VST3-P") { int program = PBD::atoi (tmp[2]); assert (!r.user); @@ -798,6 +820,8 @@ VST3Plugin::load_preset (PresetRecord r) } } + lx.release (); + if (ok) { Plugin::load_preset (r); } @@ -1385,6 +1409,7 @@ VST3PI::restartComponent (int32 flags) DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::restartComponent %1%2\n", std::hex, flags)); if (flags & Vst::kReloadComponent) { + Glib::Threads::Mutex::Lock pl (_process_lock); /* according to the spec, "The host has to unload completely * the plug-in (controller/processor) and reload it." * @@ -1397,9 +1422,11 @@ VST3PI::restartComponent (int32 flags) activate (); } if (flags & Vst::kParamValuesChanged) { + Glib::Threads::Mutex::Lock pl (_process_lock); update_shadow_data (); } if (flags & Vst::kLatencyChanged) { + Glib::Threads::Mutex::Lock pl (_process_lock); /* need to re-activate the plugin as per spec */ deactivate (); activate (); @@ -1442,7 +1469,7 @@ VST3PI::performEdit (Vst::ParamID id, Vst::ParamValue v) float value = v; _shadow_data[idx->second] = value; _update_ctrl[idx->second] = true; - set_parameter_internal (id, value, 0, true); + /* set_parameter_internal() is called via OnParameterChange */ value = _controller->normalizedParamToPlain (id, value); OnParameterChange (ValueChange, idx->second, v); /* EMIT SIGNAL */ } @@ -1770,14 +1797,16 @@ VST3PI::try_set_parameter_by_id (Vst::ParamID id, float value) if (idx == _ctrl_id_index.end ()) { return false; } - set_parameter (idx->second, value, 0); + set_parameter (idx->second, value, 0, true /* OK, called from set_state */); return true; } void -VST3PI::set_parameter (uint32_t p, float value, int32 sample_off) +VST3PI::set_parameter (uint32_t p, float value, int32 sample_off, bool to_list) { - set_parameter_internal (index_to_id (p), value, sample_off, false); + if (to_list) { + set_parameter_internal (index_to_id (p), value, sample_off, false); + } _shadow_data[p] = value; _update_ctrl[p] = true; } @@ -1810,6 +1839,7 @@ VST3PI::set_program (int pgm, int32 sample_off) #endif DEBUG_TRACE (DEBUG::VST3Config, string_compose ("VST3PI::set_program pgm: %1 val: %2 (norm: %3)\n", pgm, value, _controller->plainParamToNormalized (id, pgm))); + /* must not be called concurrently with processing */ int32 index; _input_param_changes.addParameterData (id, index)->addPoint (sample_off, value, index); _controller->setParamNormalized (id, value); @@ -1885,6 +1915,7 @@ VST3PI::update_contoller_param () void VST3PI::set_parameter_by_id (Vst::ParamID id, float value, int32 sample_off) { + /* called in rt-thread from evoral_to_vst3 */ set_parameter_internal (id, value, sample_off, true); std::map::const_iterator idx = _ctrl_id_index.find (id); if (idx != _ctrl_id_index.end ()) { @@ -1900,6 +1931,7 @@ VST3PI::set_parameter_internal (Vst::ParamID id, float& value, int32 sample_off, if (!normalized) { value = _controller->plainParamToNormalized (id, value); } + /* must not be called concurrently with processing */ _input_param_changes.addParameterData (id, index)->addPoint (sample_off, value, index); }