From 07b8779b6928a0e1c9e50a06b4e7536ec408922c Mon Sep 17 00:00:00 2001 From: Micah Johnston Date: Wed, 25 Oct 2023 23:16:57 -0500 Subject: [PATCH] make ParamValues reusable between backends Extract the ParamValues type from the VST3 backend into a reusable type in the sync module. ParamValues is now effectively an MPSC channel for param changes. --- src/format/vst3/component.rs | 38 +++++++++++++++++++--------- src/format/vst3/mod.rs | 1 - src/format/vst3/params.rs | 47 ----------------------------------- src/sync/mod.rs | 1 + src/sync/params.rs | 48 ++++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 59 deletions(-) delete mode 100644 src/format/vst3/params.rs create mode 100644 src/sync/params.rs diff --git a/src/format/vst3/component.rs b/src/format/vst3/component.rs index 756542e..910f534 100644 --- a/src/format/vst3/component.rs +++ b/src/format/vst3/component.rs @@ -7,12 +7,12 @@ use std::sync::Arc; use vst3::{Class, ComRef, ComWrapper, Steinberg::Vst::*, Steinberg::*}; use super::buffers::ScratchBuffers; -use super::params::ParamValues; use super::util::{copy_wstring, utf16_from_ptr}; use super::view::View; use crate::bus::{BusDir, Format, Layout}; use crate::events::{Data, Event, Events}; use crate::param::{ParamInfo, Range}; +use crate::sync::params::ParamValues; use crate::util::slice_from_raw_parts_checked; use crate::{Config, Editor, Host, ParamId, Plugin, PluginInfo, Processor}; @@ -65,7 +65,8 @@ pub struct Component { output_bus_map: Vec, layout_set: HashSet, param_map: HashMap, - param_values: ParamValues, + plugin_params: ParamValues, + processor_params: ParamValues, main_thread_state: Arc>>, // When the audio processor is *not* active, references to ProcessState may only be formed from // the main thread. When the audio processor *is* active, references to ProcessState may only @@ -111,7 +112,8 @@ impl Component

{ output_bus_map, layout_set, param_map, - param_values: ParamValues::new(&info.params), + plugin_params: ParamValues::new(&info.params), + processor_params: ParamValues::new(&info.params), main_thread_state: Arc::new(UnsafeCell::new(MainThreadState { config: config.clone(), plugin: P::new(Host {}), @@ -126,6 +128,20 @@ impl Component

{ }), } } + + fn sync_plugin(&self, plugin: &mut P) { + for (index, value) in self.plugin_params.poll() { + let id = self.info.params[index].id; + plugin.set_param(id, value); + } + } + + fn sync_processor(&self, processor: &mut P::Processor) { + for (index, value) in self.processor_params.poll() { + let id = self.info.params[index].id; + processor.set_param(id, value); + } + } } impl Class for Component

{ @@ -258,7 +274,7 @@ impl IComponentTrait for Component

{ let main_thread_state = &mut *self.main_thread_state.get(); let process_state = &mut *self.process_state.get(); - self.param_values.sync_plugin(&self.info.params, &mut main_thread_state.plugin); + self.sync_plugin(&mut main_thread_state.plugin); if state == 0 { process_state.processor = None; @@ -295,12 +311,12 @@ impl IComponentTrait for Component

{ if let Some(state) = ComRef::from_raw(state) { let main_thread_state = &mut *self.main_thread_state.get(); - self.param_values.sync_plugin(&self.info.params, &mut main_thread_state.plugin); + self.sync_plugin(&mut main_thread_state.plugin); if let Ok(_) = main_thread_state.plugin.load(&mut StreamReader(state)) { for (index, param) in self.info.params.iter().enumerate() { let value = main_thread_state.plugin.get_param(param.id); - self.param_values.set_from_plugin(index, value); + self.processor_params.set(index, value); main_thread_state.editor_params[index] = value; } @@ -338,7 +354,7 @@ impl IComponentTrait for Component

{ if let Some(state) = ComRef::from_raw(state) { let main_thread_state = &mut *self.main_thread_state.get(); - self.param_values.sync_plugin(&self.info.params, &mut main_thread_state.plugin); + self.sync_plugin(&mut main_thread_state.plugin); if let Ok(_) = main_thread_state.plugin.save(&mut StreamWriter(state)) { return kResultOk; @@ -434,7 +450,7 @@ impl IAudioProcessorTrait for Component

{ unsafe fn getLatencySamples(&self) -> uint32 { let main_thread_state = &mut *self.main_thread_state.get(); - self.param_values.sync_plugin(&self.info.params, &mut main_thread_state.plugin); + self.sync_plugin(&mut main_thread_state.plugin); main_thread_state.plugin.latency(&main_thread_state.config) as uint32 } @@ -456,7 +472,7 @@ impl IAudioProcessorTrait for Component

{ }; if state == 0 { - self.param_values.sync_processor(&self.info.params, processor); + self.sync_processor(processor); processor.reset(); } @@ -470,7 +486,7 @@ impl IAudioProcessorTrait for Component

{ return kNotInitialized; }; - self.param_values.sync_processor(&self.info.params, processor); + self.sync_processor(processor); let data = &*data; @@ -516,7 +532,7 @@ impl IAudioProcessorTrait for Component

{ data: Data::ParamChange { id, value }, }); - self.param_values.set_from_processor(param_index, value); + self.plugin_params.set(param_index, value); } } } diff --git a/src/format/vst3/mod.rs b/src/format/vst3/mod.rs index 556aced..f6f5f50 100644 --- a/src/format/vst3/mod.rs +++ b/src/format/vst3/mod.rs @@ -7,7 +7,6 @@ use vst3::{ComWrapper, Steinberg::IPluginFactory}; mod buffers; mod component; mod factory; -mod params; mod util; mod view; diff --git a/src/format/vst3/params.rs b/src/format/vst3/params.rs deleted file mode 100644 index b3b9c02..0000000 --- a/src/format/vst3/params.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::sync::atomic::Ordering; - -use crate::sync::{bitset::AtomicBitset, float::AtomicF64}; -use crate::{ParamInfo, ParamValue}; -use crate::{Plugin, Processor}; - -pub struct ParamValues { - values: Vec, - plugin_dirty: AtomicBitset, - processor_dirty: AtomicBitset, -} - -impl ParamValues { - pub fn new(params: &[ParamInfo]) -> ParamValues { - ParamValues { - values: params.iter().map(|p| AtomicF64::new(p.default)).collect(), - plugin_dirty: AtomicBitset::with_len(params.len()), - processor_dirty: AtomicBitset::with_len(params.len()), - } - } - - pub fn set_from_processor(&self, index: usize, value: ParamValue) { - self.values[index].store(value, Ordering::Relaxed); - self.plugin_dirty.set(index, Ordering::Release); - } - - pub fn sync_processor(&self, params: &[ParamInfo], processor: &mut P) { - for index in self.processor_dirty.drain(Ordering::Acquire) { - let id = params[index].id; - let value = self.values[index].load(Ordering::Relaxed); - processor.set_param(id, value); - } - } - - pub fn set_from_plugin(&self, index: usize, value: ParamValue) { - self.values[index].store(value, Ordering::Relaxed); - self.processor_dirty.set(index, Ordering::Release); - } - - pub fn sync_plugin(&self, params: &[ParamInfo], plugin: &mut P) { - for index in self.plugin_dirty.drain(Ordering::Acquire) { - let id = params[index].id; - let value = self.values[index].load(Ordering::Relaxed); - plugin.set_param(id, value); - } - } -} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index bab95cd..92900d5 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -1,2 +1,3 @@ pub mod bitset; pub mod float; +pub mod params; diff --git a/src/sync/params.rs b/src/sync/params.rs new file mode 100644 index 0000000..eacaf13 --- /dev/null +++ b/src/sync/params.rs @@ -0,0 +1,48 @@ +use std::sync::atomic::Ordering; + +use super::bitset::{self, AtomicBitset}; +use super::float::AtomicF64; +use crate::{ParamInfo, ParamValue}; + +pub struct ParamValues { + values: Vec, + dirty: AtomicBitset, +} + +impl ParamValues { + pub fn new(params: &[ParamInfo]) -> ParamValues { + ParamValues { + values: params.iter().map(|p| AtomicF64::new(p.default)).collect(), + dirty: AtomicBitset::with_len(params.len()), + } + } + + pub fn set(&self, index: usize, value: ParamValue) { + self.values[index].store(value, Ordering::Relaxed); + self.dirty.set(index, Ordering::Release); + } + + pub fn poll(&self) -> ParamChanges { + ParamChanges { + values: &self.values, + iter: self.dirty.drain(Ordering::Acquire), + } + } +} + +pub struct ParamChanges<'a> { + values: &'a [AtomicF64], + iter: bitset::Drain<'a>, +} + +impl<'a> Iterator for ParamChanges<'a> { + type Item = (usize, ParamValue); + + fn next(&mut self) -> Option { + if let Some(index) = self.iter.next() { + Some((index, self.values[index].load(Ordering::Relaxed))) + } else { + None + } + } +}