Skip to content

Commit

Permalink
make ParamValues reusable between backends
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
micahrj committed Oct 26, 2023
1 parent c95a659 commit 07b8779
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 59 deletions.
38 changes: 27 additions & 11 deletions src/format/vst3/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -65,7 +65,8 @@ pub struct Component<P: Plugin> {
output_bus_map: Vec<usize>,
layout_set: HashSet<Layout>,
param_map: HashMap<ParamId, usize>,
param_values: ParamValues,
plugin_params: ParamValues,
processor_params: ParamValues,
main_thread_state: Arc<UnsafeCell<MainThreadState<P>>>,
// 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
Expand Down Expand Up @@ -111,7 +112,8 @@ impl<P: Plugin> Component<P> {
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 {}),
Expand All @@ -126,6 +128,20 @@ impl<P: Plugin> Component<P> {
}),
}
}

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<P: Plugin> Class for Component<P> {
Expand Down Expand Up @@ -258,7 +274,7 @@ impl<P: Plugin> IComponentTrait for Component<P> {
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;
Expand Down Expand Up @@ -295,12 +311,12 @@ impl<P: Plugin> IComponentTrait for Component<P> {
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;
}

Expand Down Expand Up @@ -338,7 +354,7 @@ impl<P: Plugin> IComponentTrait for Component<P> {
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;
Expand Down Expand Up @@ -434,7 +450,7 @@ impl<P: Plugin> IAudioProcessorTrait for Component<P> {
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
}

Expand All @@ -456,7 +472,7 @@ impl<P: Plugin> IAudioProcessorTrait for Component<P> {
};

if state == 0 {
self.param_values.sync_processor(&self.info.params, processor);
self.sync_processor(processor);
processor.reset();
}

Expand All @@ -470,7 +486,7 @@ impl<P: Plugin> IAudioProcessorTrait for Component<P> {
return kNotInitialized;
};

self.param_values.sync_processor(&self.info.params, processor);
self.sync_processor(processor);

let data = &*data;

Expand Down Expand Up @@ -516,7 +532,7 @@ impl<P: Plugin> IAudioProcessorTrait for Component<P> {
data: Data::ParamChange { id, value },
});

self.param_values.set_from_processor(param_index, value);
self.plugin_params.set(param_index, value);
}
}
}
Expand Down
1 change: 0 additions & 1 deletion src/format/vst3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use vst3::{ComWrapper, Steinberg::IPluginFactory};
mod buffers;
mod component;
mod factory;
mod params;
mod util;
mod view;

Expand Down
47 changes: 0 additions & 47 deletions src/format/vst3/params.rs

This file was deleted.

1 change: 1 addition & 0 deletions src/sync/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod bitset;
pub mod float;
pub mod params;
48 changes: 48 additions & 0 deletions src/sync/params.rs
Original file line number Diff line number Diff line change
@@ -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<AtomicF64>,
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<Self::Item> {
if let Some(index) = self.iter.next() {
Some((index, self.values[index].load(Ordering::Relaxed)))
} else {
None
}
}
}

0 comments on commit 07b8779

Please sign in to comment.