2022-03-04 07:18:57 +11:00
|
|
|
use atomic_refcell::AtomicRefMut;
|
2022-02-07 04:51:46 +11:00
|
|
|
use std::collections::VecDeque;
|
|
|
|
use std::sync::atomic::Ordering;
|
2022-03-04 02:33:41 +11:00
|
|
|
use std::sync::Arc;
|
|
|
|
use vst3_sys::vst::IComponentHandler;
|
2022-02-07 04:51:46 +11:00
|
|
|
|
|
|
|
use super::inner::{Task, WrapperInner};
|
2022-03-05 01:05:00 +11:00
|
|
|
use crate::context::{GuiContext, ProcessContext, Transport};
|
2022-02-28 22:55:59 +11:00
|
|
|
use crate::event_loop::EventLoop;
|
2022-03-04 02:33:41 +11:00
|
|
|
use crate::param::internals::ParamPtr;
|
2022-03-01 05:21:14 +11:00
|
|
|
use crate::plugin::{NoteEvent, Vst3Plugin};
|
2022-02-07 04:51:46 +11:00
|
|
|
|
2022-03-04 09:05:01 +11:00
|
|
|
/// A [`GuiContext`] implementation for the wrapper. This is passed to the plugin in
|
2022-03-04 09:30:29 +11:00
|
|
|
/// [`Editor::spawn()`][crate::prelude::Editor::spawn()] so it can interact with the rest of the plugin and
|
2022-03-04 09:05:01 +11:00
|
|
|
/// with the host for things like setting parameters.
|
2022-03-04 02:33:41 +11:00
|
|
|
pub(crate) struct WrapperGuiContext<P: Vst3Plugin> {
|
|
|
|
pub(super) inner: Arc<WrapperInner<P>>,
|
|
|
|
}
|
|
|
|
|
2022-03-04 09:05:01 +11:00
|
|
|
/// A [`ProcessContext`] implementation for the wrapper. This is a separate object so it can hold on
|
2022-02-07 04:51:46 +11:00
|
|
|
/// to lock guards for event queues. Otherwise reading these events would require constant
|
2022-03-04 07:18:57 +11:00
|
|
|
/// unnecessary atomic operations to lock the uncontested locks.
|
2022-03-01 05:21:14 +11:00
|
|
|
pub(crate) struct WrapperProcessContext<'a, P: Vst3Plugin> {
|
2022-03-01 05:45:41 +11:00
|
|
|
pub(super) inner: &'a WrapperInner<P>,
|
2022-03-04 07:18:57 +11:00
|
|
|
pub(super) input_events_guard: AtomicRefMut<'a, VecDeque<NoteEvent>>,
|
2022-03-05 01:05:00 +11:00
|
|
|
pub(super) transport: Transport,
|
2022-02-07 04:51:46 +11:00
|
|
|
}
|
|
|
|
|
2022-03-04 02:33:41 +11:00
|
|
|
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) {
|
2022-03-04 07:18:57 +11:00
|
|
|
match &*self.inner.component_handler.borrow() {
|
2022-03-04 02:33:41 +11:00
|
|
|
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) {
|
2022-03-04 07:18:57 +11:00
|
|
|
match &*self.inner.component_handler.borrow() {
|
2022-03-04 02:33:41 +11:00
|
|
|
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),
|
|
|
|
);
|
2022-03-14 04:13:11 +11:00
|
|
|
self.inner.notify_param_values_changed();
|
2022-03-04 02:33:41 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2022-03-04 07:18:57 +11:00
|
|
|
match &*self.inner.component_handler.borrow() {
|
2022-03-04 02:33:41 +11:00
|
|
|
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"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-01 05:21:14 +11:00
|
|
|
impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
2022-03-05 01:05:00 +11:00
|
|
|
fn transport(&self) -> &Transport {
|
|
|
|
&self.transport
|
|
|
|
}
|
|
|
|
|
2022-03-04 23:22:58 +11:00
|
|
|
fn next_midi_event(&mut self) -> Option<NoteEvent> {
|
|
|
|
self.input_events_guard.pop_front()
|
|
|
|
}
|
|
|
|
|
2022-02-07 04:51:46 +11:00
|
|
|
fn set_latency_samples(&self, samples: u32) {
|
|
|
|
// Only trigger a restart if it's actually needed
|
|
|
|
let old_latency = self.inner.current_latency.swap(samples, Ordering::SeqCst);
|
|
|
|
if old_latency != samples {
|
2022-03-04 07:18:57 +11:00
|
|
|
let task_posted = unsafe { self.inner.event_loop.borrow().assume_init_ref() }
|
2022-02-07 04:51:46 +11:00
|
|
|
.do_maybe_async(Task::TriggerRestart(
|
|
|
|
vst3_sys::vst::RestartFlags::kLatencyChanged as i32,
|
|
|
|
));
|
|
|
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|