VST3: synchronize parameter-changes
IParameterChanges (_input_param_changes) queue should not be modified while the plugin processes. Doing so can lead to invalid iterators. Also activate/deactivate and state restore must not happen concurrently with processing.
This commit is contained in:
parent
a0452eeb57
commit
b27467157b
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2019-2020 Robin Gareus <robin@gareus.org>
|
* Copyright (C) 2019-2023 Robin Gareus <robin@gareus.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -24,6 +24,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
#include <glibmm/threads.h>
|
||||||
|
|
||||||
#include "pbd/search_path.h"
|
#include "pbd/search_path.h"
|
||||||
#include "pbd/signals.h"
|
#include "pbd/signals.h"
|
||||||
@ -145,11 +146,13 @@ public:
|
|||||||
|
|
||||||
/* API for Ardour -- Parameters */
|
/* API for Ardour -- Parameters */
|
||||||
bool try_set_parameter_by_id (Vst::ParamID id, float value);
|
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;
|
float get_parameter (uint32_t p) const;
|
||||||
std::string format_parameter (uint32_t p) const;
|
std::string format_parameter (uint32_t p) const;
|
||||||
Vst::ParamID index_to_id (uint32_t) const;
|
Vst::ParamID index_to_id (uint32_t) const;
|
||||||
|
|
||||||
|
Glib::Threads::Mutex& process_lock () { return _process_lock; }
|
||||||
|
|
||||||
enum ParameterChange { BeginGesture,
|
enum ParameterChange { BeginGesture,
|
||||||
EndGesture,
|
EndGesture,
|
||||||
ValueChange,
|
ValueChange,
|
||||||
@ -239,6 +242,7 @@ private:
|
|||||||
|
|
||||||
IPtr<Vst::IAudioProcessor> _processor;
|
IPtr<Vst::IAudioProcessor> _processor;
|
||||||
Vst::ProcessContext _context;
|
Vst::ProcessContext _context;
|
||||||
|
Glib::Threads::Mutex _process_lock;
|
||||||
|
|
||||||
/* Parameters */
|
/* Parameters */
|
||||||
Vst3ParameterChanges _input_param_changes;
|
Vst3ParameterChanges _input_param_changes;
|
||||||
@ -398,6 +402,15 @@ private:
|
|||||||
|
|
||||||
std::vector<bool> _connected_inputs;
|
std::vector<bool> _connected_inputs;
|
||||||
std::vector<bool> _connected_outputs;
|
std::vector<bool> _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<PV> _parameter_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ****************************************************************************/
|
/* ****************************************************************************/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2019-2020 Robin Gareus <robin@gareus.org>
|
* Copyright (C) 2019-2023 Robin Gareus <robin@gareus.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* 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)
|
VST3Plugin::VST3Plugin (AudioEngine& engine, Session& session, VST3PI* plug)
|
||||||
: Plugin (engine, session)
|
: Plugin (engine, session)
|
||||||
, _plug (plug)
|
, _plug (plug)
|
||||||
|
, _parameter_queue (128 + plug->parameter_count ())
|
||||||
{
|
{
|
||||||
init ();
|
init ();
|
||||||
}
|
}
|
||||||
|
|
||||||
VST3Plugin::VST3Plugin (const VST3Plugin& other)
|
VST3Plugin::VST3Plugin (const VST3Plugin& other)
|
||||||
: Plugin (other)
|
: Plugin (other)
|
||||||
|
, _parameter_queue (128 + other.parameter_count ())
|
||||||
{
|
{
|
||||||
boost::shared_ptr<VST3PluginInfo> nfo = boost::dynamic_pointer_cast<VST3PluginInfo> (other.get_info ());
|
boost::shared_ptr<VST3PluginInfo> nfo = boost::dynamic_pointer_cast<VST3PluginInfo> (other.get_info ());
|
||||||
_plug = new VST3PI (nfo->m, nfo->unique_id);
|
_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);
|
Plugin::EndTouch (param);
|
||||||
break;
|
break;
|
||||||
case VST3PI::ValueChange:
|
case VST3PI::ValueChange:
|
||||||
|
_parameter_queue.write_one (PV (param, value));
|
||||||
/* emit ParameterChangedExternally, mark preset dirty */
|
/* emit ParameterChangedExternally, mark preset dirty */
|
||||||
Plugin::parameter_changed_externally (param, value);
|
Plugin::parameter_changed_externally (param, value);
|
||||||
break;
|
break;
|
||||||
@ -163,7 +166,13 @@ VST3Plugin::default_value (uint32_t port)
|
|||||||
void
|
void
|
||||||
VST3Plugin::set_parameter (uint32_t port, float val, sampleoffset_t when)
|
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);
|
Plugin::set_parameter (port, val, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,6 +631,11 @@ VST3Plugin::connect_and_run (BufferSet& bufs,
|
|||||||
ChanMapping const& in_map, ChanMapping const& out_map,
|
ChanMapping const& in_map, ChanMapping const& out_map,
|
||||||
pframes_t n_samples, samplecnt_t offset)
|
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));
|
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);
|
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;
|
_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;
|
in_index = 0;
|
||||||
for (int32_t i = 0; i < (int32_t)_plug->n_midi_inputs (); ++i) {
|
for (int32_t i = 0; i < (int32_t)_plug->n_midi_inputs (); ++i) {
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
@ -775,6 +795,8 @@ VST3Plugin::load_preset (PresetRecord r)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Glib::Threads::Mutex::Lock lx (_plug->process_lock ());
|
||||||
|
|
||||||
if (tmp[0] == "VST3-P") {
|
if (tmp[0] == "VST3-P") {
|
||||||
int program = PBD::atoi (tmp[2]);
|
int program = PBD::atoi (tmp[2]);
|
||||||
assert (!r.user);
|
assert (!r.user);
|
||||||
@ -798,6 +820,8 @@ VST3Plugin::load_preset (PresetRecord r)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lx.release ();
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
Plugin::load_preset (r);
|
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));
|
DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::restartComponent %1%2\n", std::hex, flags));
|
||||||
|
|
||||||
if (flags & Vst::kReloadComponent) {
|
if (flags & Vst::kReloadComponent) {
|
||||||
|
Glib::Threads::Mutex::Lock pl (_process_lock);
|
||||||
/* according to the spec, "The host has to unload completely
|
/* according to the spec, "The host has to unload completely
|
||||||
* the plug-in (controller/processor) and reload it."
|
* the plug-in (controller/processor) and reload it."
|
||||||
*
|
*
|
||||||
@ -1397,9 +1422,11 @@ VST3PI::restartComponent (int32 flags)
|
|||||||
activate ();
|
activate ();
|
||||||
}
|
}
|
||||||
if (flags & Vst::kParamValuesChanged) {
|
if (flags & Vst::kParamValuesChanged) {
|
||||||
|
Glib::Threads::Mutex::Lock pl (_process_lock);
|
||||||
update_shadow_data ();
|
update_shadow_data ();
|
||||||
}
|
}
|
||||||
if (flags & Vst::kLatencyChanged) {
|
if (flags & Vst::kLatencyChanged) {
|
||||||
|
Glib::Threads::Mutex::Lock pl (_process_lock);
|
||||||
/* need to re-activate the plugin as per spec */
|
/* need to re-activate the plugin as per spec */
|
||||||
deactivate ();
|
deactivate ();
|
||||||
activate ();
|
activate ();
|
||||||
@ -1442,7 +1469,7 @@ VST3PI::performEdit (Vst::ParamID id, Vst::ParamValue v)
|
|||||||
float value = v;
|
float value = v;
|
||||||
_shadow_data[idx->second] = value;
|
_shadow_data[idx->second] = value;
|
||||||
_update_ctrl[idx->second] = true;
|
_update_ctrl[idx->second] = true;
|
||||||
set_parameter_internal (id, value, 0, true);
|
/* set_parameter_internal() is called via OnParameterChange */
|
||||||
value = _controller->normalizedParamToPlain (id, value);
|
value = _controller->normalizedParamToPlain (id, value);
|
||||||
OnParameterChange (ValueChange, idx->second, v); /* EMIT SIGNAL */
|
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 ()) {
|
if (idx == _ctrl_id_index.end ()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
set_parameter (idx->second, value, 0);
|
set_parameter (idx->second, value, 0, true /* OK, called from set_state */);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
_shadow_data[p] = value;
|
||||||
_update_ctrl[p] = true;
|
_update_ctrl[p] = true;
|
||||||
}
|
}
|
||||||
@ -1810,6 +1839,7 @@ VST3PI::set_program (int pgm, int32 sample_off)
|
|||||||
#endif
|
#endif
|
||||||
DEBUG_TRACE (DEBUG::VST3Config, string_compose ("VST3PI::set_program pgm: %1 val: %2 (norm: %3)\n", pgm, value, _controller->plainParamToNormalized (id, pgm)));
|
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;
|
int32 index;
|
||||||
_input_param_changes.addParameterData (id, index)->addPoint (sample_off, value, index);
|
_input_param_changes.addParameterData (id, index)->addPoint (sample_off, value, index);
|
||||||
_controller->setParamNormalized (id, value);
|
_controller->setParamNormalized (id, value);
|
||||||
@ -1885,6 +1915,7 @@ VST3PI::update_contoller_param ()
|
|||||||
void
|
void
|
||||||
VST3PI::set_parameter_by_id (Vst::ParamID id, float value, int32 sample_off)
|
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);
|
set_parameter_internal (id, value, sample_off, true);
|
||||||
std::map<Vst::ParamID, uint32_t>::const_iterator idx = _ctrl_id_index.find (id);
|
std::map<Vst::ParamID, uint32_t>::const_iterator idx = _ctrl_id_index.find (id);
|
||||||
if (idx != _ctrl_id_index.end ()) {
|
if (idx != _ctrl_id_index.end ()) {
|
||||||
@ -1900,6 +1931,7 @@ VST3PI::set_parameter_internal (Vst::ParamID id, float& value, int32 sample_off,
|
|||||||
if (!normalized) {
|
if (!normalized) {
|
||||||
value = _controller->plainParamToNormalized (id, value);
|
value = _controller->plainParamToNormalized (id, value);
|
||||||
}
|
}
|
||||||
|
/* must not be called concurrently with processing */
|
||||||
_input_param_changes.addParameterData (id, index)->addPoint (sample_off, value, index);
|
_input_param_changes.addParameterData (id, index)->addPoint (sample_off, value, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user