1
0
Fork 0

Don't require Sync for Plugin

In hindsight there's really no reason to, because process() requires
exclusive access and the other functions won't be called during
processing.
This commit is contained in:
Robbert van der Helm 2022-10-20 16:12:46 +02:00
parent 8765717793
commit f9bdaffc62
6 changed files with 29 additions and 28 deletions

View file

@ -8,6 +8,7 @@ code then it will not be listed here.
## [2022-10-20] ## [2022-10-20]
- Similar to the below change, `Plugin` also no longer requires `Sync`.
- `Editor` and the editor handle returned by `Editor::spawn` now only require - `Editor` and the editor handle returned by `Editor::spawn` now only require
`Send` and no longer need `Sync`. This is not a breaking change, but it might `Send` and no longer need `Sync`. This is not a breaking change, but it might
be worth being aware of. be worth being aware of.

View file

@ -24,7 +24,7 @@ use crate::wrapper::state::PluginState;
/// supported /// supported
/// - Audio thread thread pools (with host integration in CLAP) /// - Audio thread thread pools (with host integration in CLAP)
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait Plugin: Default + Send + Sync + 'static { pub trait Plugin: Default + Send + 'static {
const NAME: &'static str; const NAME: &'static str;
const VENDOR: &'static str; const VENDOR: &'static str;
const URL: &'static str; const URL: &'static str;

View file

@ -59,7 +59,7 @@ use clap_sys::stream::{clap_istream, clap_ostream};
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use crossbeam::channel::{self, SendTimeoutError}; use crossbeam::channel::{self, SendTimeoutError};
use crossbeam::queue::ArrayQueue; use crossbeam::queue::ArrayQueue;
use parking_lot::{Mutex, RwLock}; use parking_lot::Mutex;
use raw_window_handle::RawWindowHandle; use raw_window_handle::RawWindowHandle;
use std::any::Any; use std::any::Any;
use std::cmp; use std::cmp;
@ -104,7 +104,7 @@ pub struct Wrapper<P: ClapPlugin> {
this: AtomicRefCell<Weak<Self>>, this: AtomicRefCell<Weak<Self>>,
/// The wrapped plugin instance. /// The wrapped plugin instance.
plugin: RwLock<P>, plugin: Mutex<P>,
/// The plugin's parameters. These are fetched once during initialization. That way the /// The plugin's parameters. These are fetched once during initialization. That way the
/// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with /// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with
/// the `Params` object without having to acquire a lock on `plugin`. /// the `Params` object without having to acquire a lock on `plugin`.
@ -541,7 +541,7 @@ impl<P: ClapPlugin> Wrapper<P> {
this: AtomicRefCell::new(Weak::new()), this: AtomicRefCell::new(Weak::new()),
plugin: RwLock::new(plugin), plugin: Mutex::new(plugin),
params, params,
editor, editor,
editor_handle: Mutex::new(None), editor_handle: Mutex::new(None),
@ -1607,7 +1607,7 @@ impl<P: ClapPlugin> Wrapper<P> {
self.notify_param_values_changed(); self.notify_param_values_changed();
let bus_config = self.current_bus_config.load(); let bus_config = self.current_bus_config.load();
if let Some(buffer_config) = self.current_buffer_config.load() { if let Some(buffer_config) = self.current_buffer_config.load() {
let mut plugin = self.plugin.write(); let mut plugin = self.plugin.lock();
plugin.initialize(&bus_config, &buffer_config, &mut self.make_init_context()); plugin.initialize(&bus_config, &buffer_config, &mut self.make_init_context());
process_wrapper(|| plugin.reset()); process_wrapper(|| plugin.reset());
} }
@ -1705,7 +1705,7 @@ impl<P: ClapPlugin> Wrapper<P> {
param.update_smoother(buffer_config.sample_rate, true); param.update_smoother(buffer_config.sample_rate, true);
} }
let mut plugin = wrapper.plugin.write(); let mut plugin = wrapper.plugin.lock();
if plugin.initialize( if plugin.initialize(
&bus_config, &bus_config,
&buffer_config, &buffer_config,
@ -1779,7 +1779,7 @@ impl<P: ClapPlugin> Wrapper<P> {
check_null_ptr!((), plugin); check_null_ptr!((), plugin);
let wrapper = &*(plugin as *const Self); let wrapper = &*(plugin as *const Self);
wrapper.plugin.write().deactivate(); wrapper.plugin.lock().deactivate();
} }
unsafe extern "C" fn start_processing(plugin: *const clap_plugin) -> bool { unsafe extern "C" fn start_processing(plugin: *const clap_plugin) -> bool {
@ -1794,7 +1794,7 @@ impl<P: ClapPlugin> Wrapper<P> {
// To be consistent with the VST3 wrapper, we'll also reset the buffers here in addition to // To be consistent with the VST3 wrapper, we'll also reset the buffers here in addition to
// the dedicated `reset()` function. // the dedicated `reset()` function.
process_wrapper(|| wrapper.plugin.write().reset()); process_wrapper(|| wrapper.plugin.lock().reset());
true true
} }
@ -1810,7 +1810,7 @@ impl<P: ClapPlugin> Wrapper<P> {
check_null_ptr!((), plugin); check_null_ptr!((), plugin);
let wrapper = &*(plugin as *const Self); let wrapper = &*(plugin as *const Self);
process_wrapper(|| wrapper.plugin.write().reset()); process_wrapper(|| wrapper.plugin.lock().reset());
} }
unsafe extern "C" fn process( unsafe extern "C" fn process(
@ -2189,7 +2189,7 @@ impl<P: ClapPlugin> Wrapper<P> {
} }
let result = if buffer_is_valid { let result = if buffer_is_valid {
let mut plugin = wrapper.plugin.write(); let mut plugin = wrapper.plugin.lock();
// SAFETY: Shortening these borrows is safe as even if the plugin overwrites the // SAFETY: Shortening these borrows is safe as even if the plugin overwrites the
// slices (which it cannot do without using unsafe code), then they // slices (which it cannot do without using unsafe code), then they
// would still be reset on the next iteration // would still be reset on the next iteration
@ -2251,7 +2251,7 @@ impl<P: ClapPlugin> Wrapper<P> {
let bus_config = wrapper.current_bus_config.load(); let bus_config = wrapper.current_bus_config.load();
let buffer_config = wrapper.current_buffer_config.load().unwrap(); let buffer_config = wrapper.current_buffer_config.load().unwrap();
let mut plugin = wrapper.plugin.write(); let mut plugin = wrapper.plugin.lock();
// FIXME: This is obviously not realtime-safe, but loading presets without doing // FIXME: This is obviously not realtime-safe, but loading presets without doing
// this could lead to inconsistencies. It's the plugin's responsibility to // this could lead to inconsistencies. It's the plugin's responsibility to
// not perform any realtime-unsafe work when the initialize function is // not perform any realtime-unsafe work when the initialize function is
@ -3154,7 +3154,7 @@ impl<P: ClapPlugin> Wrapper<P> {
let bus_config = wrapper.current_bus_config.load(); let bus_config = wrapper.current_bus_config.load();
if let Some(buffer_config) = wrapper.current_buffer_config.load() { if let Some(buffer_config) = wrapper.current_buffer_config.load() {
let mut plugin = wrapper.plugin.write(); let mut plugin = wrapper.plugin.lock();
plugin.initialize( plugin.initialize(
&bus_config, &bus_config,
&buffer_config, &buffer_config,

View file

@ -2,7 +2,7 @@ use atomic_refcell::AtomicRefCell;
use baseview::{EventStatus, Window, WindowHandler, WindowOpenOptions}; use baseview::{EventStatus, Window, WindowHandler, WindowOpenOptions};
use crossbeam::channel; use crossbeam::channel;
use crossbeam::queue::ArrayQueue; use crossbeam::queue::ArrayQueue;
use parking_lot::{Mutex, RwLock}; use parking_lot::Mutex;
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use std::any::Any; use std::any::Any;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -33,7 +33,7 @@ pub struct Wrapper<P: Plugin, B: Backend> {
backend: AtomicRefCell<B>, backend: AtomicRefCell<B>,
/// The wrapped plugin instance. /// The wrapped plugin instance.
plugin: RwLock<P>, plugin: Mutex<P>,
/// The plugin's parameters. These are fetched once during initialization. That way the /// The plugin's parameters. These are fetched once during initialization. That way the
/// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with /// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with
/// the `Params` object without having to acquire a lock on `plugin`. /// the `Params` object without having to acquire a lock on `plugin`.
@ -176,7 +176,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
let wrapper = Arc::new(Wrapper { let wrapper = Arc::new(Wrapper {
backend: AtomicRefCell::new(backend), backend: AtomicRefCell::new(backend),
plugin: RwLock::new(plugin), plugin: Mutex::new(plugin),
params, params,
known_parameters: param_map.iter().map(|(_, ptr, _)| *ptr).collect(), known_parameters: param_map.iter().map(|(_, ptr, _)| *ptr).collect(),
param_map: param_map param_map: param_map
@ -209,7 +209,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
// Right now the IO configuration is fixed in the standalone target, so if the plugin cannot // Right now the IO configuration is fixed in the standalone target, so if the plugin cannot
// work with this then we cannot initialize the plugin at all. // work with this then we cannot initialize the plugin at all.
{ {
let mut plugin = wrapper.plugin.write(); let mut plugin = wrapper.plugin.lock();
if !plugin.accepts_bus_config(&wrapper.bus_config) { if !plugin.accepts_bus_config(&wrapper.bus_config) {
return Err(WrapperError::IncompatibleConfig { return Err(WrapperError::IncompatibleConfig {
input_channels: wrapper.bus_config.num_input_channels, input_channels: wrapper.bus_config.num_input_channels,
@ -309,7 +309,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
// Some plugins may use this to clean up resources. Should not be needed for the standalone // Some plugins may use this to clean up resources. Should not be needed for the standalone
// application, but it seems like a good idea to stay consistent. // application, but it seems like a good idea to stay consistent.
self.plugin.write().deactivate(); self.plugin.lock().deactivate();
Ok(()) Ok(())
} }
@ -386,7 +386,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
} }
let sample_rate = self.buffer_config.sample_rate; let sample_rate = self.buffer_config.sample_rate;
let mut plugin = self.plugin.write(); let mut plugin = self.plugin.lock();
if let ProcessStatus::Error(err) = plugin.process( if let ProcessStatus::Error(err) = plugin.process(
buffer, buffer,
// TODO: Provide extra inputs and outputs in the JACk backend // TODO: Provide extra inputs and outputs in the JACk backend

View file

@ -29,7 +29,7 @@ use crate::wrapper::util::{hash_param_id, process_wrapper};
/// its own struct. /// its own struct.
pub(crate) struct WrapperInner<P: Vst3Plugin> { pub(crate) struct WrapperInner<P: Vst3Plugin> {
/// The wrapped plugin instance. /// The wrapped plugin instance.
pub plugin: RwLock<P>, pub plugin: Mutex<P>,
/// The plugin's parameters. These are fetched once during initialization. That way the /// The plugin's parameters. These are fetched once during initialization. That way the
/// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with /// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with
/// the `Params` object without having to acquire a lock on `plugin`. /// the `Params` object without having to acquire a lock on `plugin`.
@ -272,7 +272,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
.collect(); .collect();
let wrapper = Self { let wrapper = Self {
plugin: RwLock::new(plugin), plugin: Mutex::new(plugin),
params, params,
editor, editor,
@ -477,7 +477,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
self.notify_param_values_changed(); self.notify_param_values_changed();
let bus_config = self.current_bus_config.load(); let bus_config = self.current_bus_config.load();
if let Some(buffer_config) = self.current_buffer_config.load() { if let Some(buffer_config) = self.current_buffer_config.load() {
let mut plugin = self.plugin.write(); let mut plugin = self.plugin.lock();
plugin.initialize(&bus_config, &buffer_config, &mut self.make_init_context()); plugin.initialize(&bus_config, &buffer_config, &mut self.make_init_context());
process_wrapper(|| plugin.reset()); process_wrapper(|| plugin.reset());
} }

View file

@ -389,7 +389,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
} }
let bus_config = self.inner.current_bus_config.load(); let bus_config = self.inner.current_bus_config.load();
let mut plugin = self.inner.plugin.write(); let mut plugin = self.inner.plugin.lock();
if plugin.initialize( if plugin.initialize(
&bus_config, &bus_config,
&buffer_config, &buffer_config,
@ -461,7 +461,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
} }
(true, None) => kResultFalse, (true, None) => kResultFalse,
(false, _) => { (false, _) => {
self.inner.plugin.write().deactivate(); self.inner.plugin.lock().deactivate();
kResultOk kResultOk
} }
@ -519,7 +519,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
let bus_config = self.inner.current_bus_config.load(); let bus_config = self.inner.current_bus_config.load();
if let Some(buffer_config) = self.inner.current_buffer_config.load() { if let Some(buffer_config) = self.inner.current_buffer_config.load() {
let mut plugin = self.inner.plugin.write(); let mut plugin = self.inner.plugin.lock();
plugin.initialize( plugin.initialize(
&bus_config, &bus_config,
&buffer_config, &buffer_config,
@ -864,7 +864,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
if self if self
.inner .inner
.plugin .plugin
.read() .lock()
.accepts_bus_config(&proposed_config) .accepts_bus_config(&proposed_config)
{ {
self.inner.current_bus_config.store(proposed_config); self.inner.current_bus_config.store(proposed_config);
@ -1001,7 +1001,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
// This function is also used to reset buffers on the plugin, so we should do the same // This function is also used to reset buffers on the plugin, so we should do the same
// thing. We don't call `reset()` in `setup_processing()` for that same reason. // thing. We don't call `reset()` in `setup_processing()` for that same reason.
if state { if state {
process_wrapper(|| self.inner.plugin.write().reset()); process_wrapper(|| self.inner.plugin.lock().reset());
} }
// We don't have any special handling for suspending and resuming plugins, yet // We don't have any special handling for suspending and resuming plugins, yet
@ -1530,7 +1530,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
} }
let result = if buffer_is_valid { let result = if buffer_is_valid {
let mut plugin = self.inner.plugin.write(); let mut plugin = self.inner.plugin.lock();
// SAFETY: Shortening these borrows is safe as even if the plugin overwrites the // SAFETY: Shortening these borrows is safe as even if the plugin overwrites the
// slices (which it cannot do without using unsafe code), then they // slices (which it cannot do without using unsafe code), then they
// would still be reset on the next iteration // would still be reset on the next iteration
@ -1784,7 +1784,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
let bus_config = self.inner.current_bus_config.load(); let bus_config = self.inner.current_bus_config.load();
let buffer_config = self.inner.current_buffer_config.load().unwrap(); let buffer_config = self.inner.current_buffer_config.load().unwrap();
let mut plugin = self.inner.plugin.write(); let mut plugin = self.inner.plugin.lock();
// FIXME: This is obviously not realtime-safe, but loading presets without doing // FIXME: This is obviously not realtime-safe, but loading presets without doing
// this could lead to inconsistencies. It's the plugin's responsibility to // this could lead to inconsistencies. It's the plugin's responsibility to
// not perform any realtime-unsafe work when the initialize function is // not perform any realtime-unsafe work when the initialize function is