Move VST3 GuiContext to a separate object
This commit is contained in:
parent
5766f037b2
commit
f91958e971
|
@ -1,12 +1,22 @@
|
||||||
use parking_lot::RwLockWriteGuard;
|
use parking_lot::RwLockWriteGuard;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use vst3_sys::vst::IComponentHandler;
|
||||||
|
|
||||||
use super::inner::{Task, WrapperInner};
|
use super::inner::{Task, WrapperInner};
|
||||||
use crate::context::ProcessContext;
|
use crate::context::{GuiContext, ProcessContext};
|
||||||
use crate::event_loop::EventLoop;
|
use crate::event_loop::EventLoop;
|
||||||
|
use crate::param::internals::ParamPtr;
|
||||||
use crate::plugin::{NoteEvent, Vst3Plugin};
|
use crate::plugin::{NoteEvent, Vst3Plugin};
|
||||||
|
|
||||||
|
/// A [GuiContext] implementation for the wrapper. This is passed to the plugin in
|
||||||
|
/// [crate::Editor::spawn()] so it can interact with the rest of the plugin and with the host for
|
||||||
|
/// things like setting parameters.
|
||||||
|
pub(crate) struct WrapperGuiContext<P: Vst3Plugin> {
|
||||||
|
pub(super) inner: Arc<WrapperInner<P>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A [ProcessContext] implementation for the wrapper. This is a separate object so it can hold on
|
/// A [ProcessContext] implementation for the wrapper. This is a separate object so it can hold on
|
||||||
/// to lock guards for event queues. Otherwise reading these events would require constant
|
/// to lock guards for event queues. Otherwise reading these events would require constant
|
||||||
/// unnecessary atomic operations to lock the uncontested RwLocks.
|
/// unnecessary atomic operations to lock the uncontested RwLocks.
|
||||||
|
@ -15,6 +25,71 @@ pub(crate) struct WrapperProcessContext<'a, P: Vst3Plugin> {
|
||||||
pub(super) input_events_guard: RwLockWriteGuard<'a, VecDeque<NoteEvent>>,
|
pub(super) input_events_guard: RwLockWriteGuard<'a, VecDeque<NoteEvent>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P: Vst3Plugin> GuiContext for WrapperGuiContext<P> {
|
||||||
|
// All of these functions are supposed to be called from the main thread, so we'll put some
|
||||||
|
// trust in the caller and assume that this is indeed the case
|
||||||
|
unsafe fn raw_begin_set_parameter(&self, param: ParamPtr) {
|
||||||
|
match &*self.inner.component_handler.read() {
|
||||||
|
Some(handler) => match self.inner.param_ptr_to_hash.get(¶m) {
|
||||||
|
Some(hash) => {
|
||||||
|
handler.begin_edit(*hash);
|
||||||
|
}
|
||||||
|
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
|
||||||
|
},
|
||||||
|
None => nih_debug_assert_failure!("Component handler not yet set"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn raw_set_parameter_normalized(&self, param: ParamPtr, normalized: f32) {
|
||||||
|
match &*self.inner.component_handler.read() {
|
||||||
|
Some(handler) => match self.inner.param_ptr_to_hash.get(¶m) {
|
||||||
|
Some(hash) => {
|
||||||
|
// Only update the parameters manually if the host is not processing audio. If
|
||||||
|
// the plugin is currently processing audio, the host will pass this change back
|
||||||
|
// to the plugin in the audio callback. This also prevents the values from
|
||||||
|
// changing in the middle of the process callback, which would be unsound.
|
||||||
|
if !self.inner.is_processing.load(Ordering::SeqCst) {
|
||||||
|
self.inner.set_normalized_value_by_hash(
|
||||||
|
*hash,
|
||||||
|
normalized,
|
||||||
|
self.inner
|
||||||
|
.current_buffer_config
|
||||||
|
.load()
|
||||||
|
.map(|c| c.sample_rate),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.perform_edit(*hash, normalized as f64);
|
||||||
|
}
|
||||||
|
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
|
||||||
|
},
|
||||||
|
None => nih_debug_assert_failure!("Component handler not yet set"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn raw_end_set_parameter(&self, param: ParamPtr) {
|
||||||
|
match &*self.inner.component_handler.read() {
|
||||||
|
Some(handler) => match self.inner.param_ptr_to_hash.get(¶m) {
|
||||||
|
Some(hash) => {
|
||||||
|
handler.end_edit(*hash);
|
||||||
|
}
|
||||||
|
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
|
||||||
|
},
|
||||||
|
None => nih_debug_assert_failure!("Component handler not yet set"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn raw_default_normalized_param_value(&self, param: ParamPtr) -> f32 {
|
||||||
|
match self.inner.param_ptr_to_hash.get(¶m) {
|
||||||
|
Some(hash) => self.inner.param_defaults_normalized[hash],
|
||||||
|
None => {
|
||||||
|
nih_debug_assert_failure!("Unknown parameter: {:?}", param);
|
||||||
|
0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
fn set_latency_samples(&self, samples: u32) {
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
// Only trigger a restart if it's actually needed
|
// Only trigger a restart if it's actually needed
|
||||||
|
|
|
@ -7,11 +7,10 @@ use std::sync::Arc;
|
||||||
use vst3_sys::base::{kInvalidArgument, kResultOk, tresult};
|
use vst3_sys::base::{kInvalidArgument, kResultOk, tresult};
|
||||||
use vst3_sys::vst::IComponentHandler;
|
use vst3_sys::vst::IComponentHandler;
|
||||||
|
|
||||||
use super::context::WrapperProcessContext;
|
use super::context::{WrapperGuiContext, WrapperProcessContext};
|
||||||
use super::util::{ObjectPtr, VstPtr, BYPASS_PARAM_HASH, BYPASS_PARAM_ID};
|
use super::util::{ObjectPtr, VstPtr, BYPASS_PARAM_HASH, BYPASS_PARAM_ID};
|
||||||
use super::view::WrapperView;
|
use super::view::WrapperView;
|
||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
use crate::context::GuiContext;
|
|
||||||
use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
|
use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
|
||||||
use crate::param::internals::ParamPtr;
|
use crate::param::internals::ParamPtr;
|
||||||
use crate::plugin::{BufferConfig, BusConfig, Editor, NoteEvent, ProcessStatus, Vst3Plugin};
|
use crate::plugin::{BufferConfig, BusConfig, Editor, NoteEvent, ProcessStatus, Vst3Plugin};
|
||||||
|
@ -188,6 +187,10 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
wrapper
|
wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_gui_context(self: Arc<Self>) -> Arc<WrapperGuiContext<P>> {
|
||||||
|
Arc::new(WrapperGuiContext { inner: self })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_process_context(&self) -> WrapperProcessContext<'_, P> {
|
pub fn make_process_context(&self) -> WrapperProcessContext<'_, P> {
|
||||||
WrapperProcessContext {
|
WrapperProcessContext {
|
||||||
inner: self,
|
inner: self,
|
||||||
|
@ -225,72 +228,6 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't use a nice standalone context object for this as we don't have any control over the
|
|
||||||
// lifetime since we currently try to stay GUI framework agnostic. Because of that, the only
|
|
||||||
// alternative is to pass an `Arc<Self as GuiContext>` to the plugin and hope it doesn't do anything
|
|
||||||
// weird with it.
|
|
||||||
impl<P: Vst3Plugin> GuiContext for WrapperInner<P> {
|
|
||||||
// All of these functions are supposed to be called from the main thread, so we'll put some
|
|
||||||
// trust in the caller and assume that this is indeed the case
|
|
||||||
unsafe fn raw_begin_set_parameter(&self, param: ParamPtr) {
|
|
||||||
match &*self.component_handler.read() {
|
|
||||||
Some(handler) => match self.param_ptr_to_hash.get(¶m) {
|
|
||||||
Some(hash) => {
|
|
||||||
handler.begin_edit(*hash);
|
|
||||||
}
|
|
||||||
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
|
|
||||||
},
|
|
||||||
None => nih_debug_assert_failure!("Component handler not yet set"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn raw_set_parameter_normalized(&self, param: ParamPtr, normalized: f32) {
|
|
||||||
match &*self.component_handler.read() {
|
|
||||||
Some(handler) => match self.param_ptr_to_hash.get(¶m) {
|
|
||||||
Some(hash) => {
|
|
||||||
// Only update the parameters manually if the host is not processing audio. If
|
|
||||||
// the plugin is currently processing audio, the host will pass this change back
|
|
||||||
// to the plugin in the audio callback. This also prevents the values from
|
|
||||||
// changing in the middle of the process callback, which would be unsound.
|
|
||||||
if !self.is_processing.load(Ordering::SeqCst) {
|
|
||||||
self.set_normalized_value_by_hash(
|
|
||||||
*hash,
|
|
||||||
normalized,
|
|
||||||
self.current_buffer_config.load().map(|c| c.sample_rate),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.perform_edit(*hash, normalized as f64);
|
|
||||||
}
|
|
||||||
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
|
|
||||||
},
|
|
||||||
None => nih_debug_assert_failure!("Component handler not yet set"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn raw_end_set_parameter(&self, param: ParamPtr) {
|
|
||||||
match &*self.component_handler.read() {
|
|
||||||
Some(handler) => match self.param_ptr_to_hash.get(¶m) {
|
|
||||||
Some(hash) => {
|
|
||||||
handler.end_edit(*hash);
|
|
||||||
}
|
|
||||||
None => nih_debug_assert_failure!("Unknown parameter: {:?}", param),
|
|
||||||
},
|
|
||||||
None => nih_debug_assert_failure!("Component handler not yet set"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn raw_default_normalized_param_value(&self, param: ParamPtr) -> f32 {
|
|
||||||
match self.param_ptr_to_hash.get(¶m) {
|
|
||||||
Some(hash) => self.param_defaults_normalized[hash],
|
|
||||||
None => {
|
|
||||||
nih_debug_assert_failure!("Unknown parameter: {:?}", param);
|
|
||||||
0.5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Vst3Plugin> MainThreadExecutor<Task> for WrapperInner<P> {
|
impl<P: Vst3Plugin> MainThreadExecutor<Task> for WrapperInner<P> {
|
||||||
unsafe fn execute(&self, task: Task) {
|
unsafe fn execute(&self, task: Task) {
|
||||||
// This function is always called from the main thread
|
// This function is always called from the main thread
|
||||||
|
|
|
@ -113,10 +113,10 @@ impl<P: Vst3Plugin> IPlugView for WrapperView<P> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
*editor_handle = Some(
|
*editor_handle = Some(self.editor.spawn(
|
||||||
self.editor
|
ParentWindowHandle { handle },
|
||||||
.spawn(ParentWindowHandle { handle }, self.inner.clone()),
|
self.inner.clone().make_gui_context(),
|
||||||
);
|
));
|
||||||
*self.inner.plug_view.write() = Some(ObjectPtr::from(self));
|
*self.inner.plug_view.write() = Some(ObjectPtr::from(self));
|
||||||
|
|
||||||
kResultOk
|
kResultOk
|
||||||
|
|
Loading…
Reference in a new issue