Create a separate InitContext
Only a couple of these functions would be needed during initialization. In the next couple commits ProcessContext will get a way to access auxiliary IO, so this really had to be separated.
This commit is contained in:
parent
c555aff768
commit
b2e6bd5515
|
@ -6,6 +6,14 @@ new and what's changed, this document lists all breaking changes in reverse
|
||||||
chronological order. If a new feature did not require any changes to existing
|
chronological order. If a new feature did not require any changes to existing
|
||||||
code then it will not be listed here.
|
code then it will not be listed here.
|
||||||
|
|
||||||
|
## [2022-05-2y]
|
||||||
|
|
||||||
|
- The `Plugin::initialize()` method now takes a `&mut impl InitContext` instead
|
||||||
|
of a `&mut impl ProcessContext`. This is to avoid soundness issues when
|
||||||
|
`ProcessContext` lets you access sidechain buffer and auxiliary outputs in the
|
||||||
|
future and because most of the methods on `ProcessContext` would not be
|
||||||
|
applicable to initialization.
|
||||||
|
|
||||||
## [2022-05-22]
|
## [2022-05-22]
|
||||||
|
|
||||||
- Previously calling `param.non_automatable()` when constructing a parameter
|
- Previously calling `param.non_automatable()` when constructing a parameter
|
||||||
|
|
|
@ -315,7 +315,7 @@ impl Plugin for Crisp {
|
||||||
&mut self,
|
&mut self,
|
||||||
bus_config: &BusConfig,
|
bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
nih_debug_assert_eq!(bus_config.num_input_channels, NUM_CHANNELS);
|
nih_debug_assert_eq!(bus_config.num_input_channels, NUM_CHANNELS);
|
||||||
nih_debug_assert_eq!(bus_config.num_output_channels, NUM_CHANNELS);
|
nih_debug_assert_eq!(bus_config.num_output_channels, NUM_CHANNELS);
|
||||||
|
|
|
@ -253,7 +253,7 @@ impl Plugin for Diopser {
|
||||||
&mut self,
|
&mut self,
|
||||||
_bus_config: &BusConfig,
|
_bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.sample_rate = buffer_config.sample_rate;
|
self.sample_rate = buffer_config.sample_rate;
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ impl Plugin for Gain {
|
||||||
&mut self,
|
&mut self,
|
||||||
_bus_config: &BusConfig,
|
_bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// TODO: How do you tie this exponential decay to an actual time span?
|
// TODO: How do you tie this exponential decay to an actual time span?
|
||||||
self.peak_meter_decay_weight = 0.9992f32.powf(44_100.0 / buffer_config.sample_rate);
|
self.peak_meter_decay_weight = 0.9992f32.powf(44_100.0 / buffer_config.sample_rate);
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl Plugin for Gain {
|
||||||
&mut self,
|
&mut self,
|
||||||
_bus_config: &BusConfig,
|
_bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// TODO: How do you tie this exponential decay to an actual time span?
|
// TODO: How do you tie this exponential decay to an actual time span?
|
||||||
self.peak_meter_decay_weight = 0.9992f32.powf(44_100.0 / buffer_config.sample_rate);
|
self.peak_meter_decay_weight = 0.9992f32.powf(44_100.0 / buffer_config.sample_rate);
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl Plugin for Gain {
|
||||||
&mut self,
|
&mut self,
|
||||||
_bus_config: &BusConfig,
|
_bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// TODO: How do you tie this exponential decay to an actual time span?
|
// TODO: How do you tie this exponential decay to an actual time span?
|
||||||
self.peak_meter_decay_weight = 0.9992f32.powf(44_100.0 / buffer_config.sample_rate);
|
self.peak_meter_decay_weight = 0.9992f32.powf(44_100.0 / buffer_config.sample_rate);
|
||||||
|
|
|
@ -124,7 +124,7 @@ impl Plugin for Sine {
|
||||||
&mut self,
|
&mut self,
|
||||||
_bus_config: &BusConfig,
|
_bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.sample_rate = buffer_config.sample_rate;
|
self.sample_rate = buffer_config.sample_rate;
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ impl Plugin for Stft {
|
||||||
&mut self,
|
&mut self,
|
||||||
_bus_config: &BusConfig,
|
_bus_config: &BusConfig,
|
||||||
_buffer_config: &BufferConfig,
|
_buffer_config: &BufferConfig,
|
||||||
context: &mut impl ProcessContext,
|
context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// The plugin's latency consists of the block size from the overlap-add procedure and half
|
// The plugin's latency consists of the block size from the overlap-add procedure and half
|
||||||
// of the filter kernel's size (since we're using a linear phase/symmetrical convolution
|
// of the filter kernel's size (since we're using a linear phase/symmetrical convolution
|
||||||
|
|
|
@ -135,7 +135,7 @@ impl Plugin for LoudnessWarWinner {
|
||||||
&mut self,
|
&mut self,
|
||||||
bus_config: &BusConfig,
|
bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.sample_rate = buffer_config.sample_rate;
|
self.sample_rate = buffer_config.sample_rate;
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ impl Plugin for PubertySimulator {
|
||||||
&mut self,
|
&mut self,
|
||||||
_bus_config: &BusConfig,
|
_bus_config: &BusConfig,
|
||||||
_buffer_config: &BufferConfig,
|
_buffer_config: &BufferConfig,
|
||||||
context: &mut impl ProcessContext,
|
context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Planning with RustFFT is very fast, but it will still allocate we we'll plan all of the
|
// Planning with RustFFT is very fast, but it will still allocate we we'll plan all of the
|
||||||
// FFTs we might need in advance
|
// FFTs we might need in advance
|
||||||
|
|
|
@ -170,7 +170,7 @@ impl Plugin for SafetyLimiter {
|
||||||
&mut self,
|
&mut self,
|
||||||
_bus_config: &BusConfig,
|
_bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.buffer_config = *buffer_config;
|
self.buffer_config = *buffer_config;
|
||||||
self.morse_fadeout_samples_start =
|
self.morse_fadeout_samples_start =
|
||||||
|
|
|
@ -7,10 +7,25 @@ use crate::param::internals::ParamPtr;
|
||||||
use crate::param::Param;
|
use crate::param::Param;
|
||||||
use crate::wrapper::state::PluginState;
|
use crate::wrapper::state::PluginState;
|
||||||
|
|
||||||
// TODO: ProcessContext for parameter automation and sending events
|
/// Callbacks the plugin can make while it is being initialized. This is passed to the plugin during
|
||||||
|
/// [`Plugin::initialize()`][crate::plugin::Plugin::initialize()].
|
||||||
|
//
|
||||||
|
// # Safety
|
||||||
|
//
|
||||||
|
// The implementing wrapper needs to be able to handle concurrent requests, and it should perform
|
||||||
|
// the actual callback within [MainThreadQueue::do_maybe_async].
|
||||||
|
pub trait InitContext {
|
||||||
|
/// Get the current plugin API.
|
||||||
|
fn plugin_api(&self) -> PluginApi;
|
||||||
|
|
||||||
/// General callbacks the plugin can make during its lifetime. This is passed to the plugin during
|
/// Update the current latency of the plugin. If the plugin is currently processing audio, then
|
||||||
/// [`Plugin::initialize()`][crate::plugin::Plugin::initialize()] and as part of
|
/// this may cause audio playback to be restarted.
|
||||||
|
fn set_latency_samples(&self, samples: u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contains both context data and callbacks the plugin can use during processing. Most notably this
|
||||||
|
/// is how a plugin sends and receives note events, gets transport information, and accesses
|
||||||
|
/// sidechain inputs and auxiliary outputs. This is passed to the plugin during as part of
|
||||||
/// [`Plugin::process()`][crate::plugin::Plugin::process()].
|
/// [`Plugin::process()`][crate::plugin::Plugin::process()].
|
||||||
//
|
//
|
||||||
// # Safety
|
// # Safety
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::any::Any;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
use crate::context::{GuiContext, ProcessContext};
|
use crate::context::{GuiContext, InitContext, ProcessContext};
|
||||||
use crate::midi::MidiConfig;
|
use crate::midi::MidiConfig;
|
||||||
use crate::param::internals::Params;
|
use crate::param::internals::Params;
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ pub trait Plugin: Default + Send + Sync + 'static {
|
||||||
&mut self,
|
&mut self,
|
||||||
bus_config: &BusConfig,
|
bus_config: &BusConfig,
|
||||||
buffer_config: &BufferConfig,
|
buffer_config: &BufferConfig,
|
||||||
context: &mut impl ProcessContext,
|
context: &mut impl InitContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub use crate::formatters;
|
||||||
pub use crate::util;
|
pub use crate::util;
|
||||||
|
|
||||||
pub use crate::buffer::Buffer;
|
pub use crate::buffer::Buffer;
|
||||||
pub use crate::context::{GuiContext, ParamSetter, PluginApi, ProcessContext};
|
pub use crate::context::{GuiContext, InitContext, ParamSetter, PluginApi, ProcessContext};
|
||||||
// This also includes the derive macro
|
// This also includes the derive macro
|
||||||
pub use crate::midi::{control_change, MidiConfig, NoteEvent};
|
pub use crate::midi::{control_change, MidiConfig, NoteEvent};
|
||||||
pub use crate::param::enums::{Enum, EnumParam};
|
pub use crate::param::enums::{Enum, EnumParam};
|
||||||
|
|
|
@ -253,8 +253,7 @@ impl<const NUM_SIDECHAIN_INPUTS: usize> StftHelper<NUM_SIDECHAIN_INPUTS> {
|
||||||
/// Process the audio in `main_buffer` in small overlapping blocks, adding up the results for
|
/// Process the audio in `main_buffer` in small overlapping blocks, adding up the results for
|
||||||
/// the main buffer so they can eventually be written back to the host one block later. This
|
/// the main buffer so they can eventually be written back to the host one block later. This
|
||||||
/// means that this function will introduce one block of latency. This can be compensated by
|
/// means that this function will introduce one block of latency. This can be compensated by
|
||||||
/// calling
|
/// calling [`InitContext::set_latency()`][`crate::prelude::InitContext::set_latency_samples()`]
|
||||||
/// [`ProcessContext::set_latency()`][`crate::prelude::ProcessContext::set_latency_samples()`]
|
|
||||||
/// in your plugin's initialization function.
|
/// in your plugin's initialization function.
|
||||||
///
|
///
|
||||||
/// If a padding value was specified in [`new()`][Self::new()], then the yielded blocks will
|
/// If a padding value was specified in [`new()`][Self::new()], then the yielded blocks will
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use atomic_refcell::AtomicRefMut;
|
use atomic_refcell::AtomicRefMut;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::wrapper::{OutputParamEvent, Task, Wrapper};
|
use super::wrapper::{OutputParamEvent, Wrapper};
|
||||||
use crate::context::{GuiContext, PluginApi, ProcessContext, Transport};
|
use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport};
|
||||||
use crate::event_loop::EventLoop;
|
|
||||||
use crate::midi::NoteEvent;
|
use crate::midi::NoteEvent;
|
||||||
use crate::param::internals::ParamPtr;
|
use crate::param::internals::ParamPtr;
|
||||||
use crate::plugin::ClapPlugin;
|
use crate::plugin::ClapPlugin;
|
||||||
|
@ -17,6 +15,13 @@ pub(crate) struct WrapperGuiContext<P: ClapPlugin> {
|
||||||
pub(super) wrapper: Arc<Wrapper<P>>,
|
pub(super) wrapper: Arc<Wrapper<P>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`InitContext`] 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
|
||||||
|
/// unnecessary atomic operations to lock the uncontested RwLocks.
|
||||||
|
pub(crate) struct WrapperInitContext<'a, P: ClapPlugin> {
|
||||||
|
pub(super) wrapper: &'a Wrapper<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.
|
||||||
|
@ -95,6 +100,16 @@ impl<P: ClapPlugin> GuiContext for WrapperGuiContext<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P: ClapPlugin> InitContext for WrapperInitContext<'_, P> {
|
||||||
|
fn plugin_api(&self) -> PluginApi {
|
||||||
|
PluginApi::Clap
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
|
self.wrapper.set_latency_samples(samples)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
|
impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
fn plugin_api(&self) -> PluginApi {
|
fn plugin_api(&self) -> PluginApi {
|
||||||
PluginApi::Clap
|
PluginApi::Clap
|
||||||
|
@ -113,13 +128,6 @@ impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_latency_samples(&self, samples: u32) {
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
// Only make a callback if it's actually needed
|
self.wrapper.set_latency_samples(samples)
|
||||||
// XXX: For CLAP we could move this handling to the Plugin struct, but it may be worthwhile
|
|
||||||
// to keep doing it this way to stay consistent with VST3.
|
|
||||||
let old_latency = self.wrapper.current_latency.swap(samples, Ordering::SeqCst);
|
|
||||||
if old_latency != samples {
|
|
||||||
let task_posted = self.wrapper.do_maybe_async(Task::LatencyChanged);
|
|
||||||
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ use std::sync::{Arc, Weak};
|
||||||
use std::thread::{self, ThreadId};
|
use std::thread::{self, ThreadId};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::context::{WrapperGuiContext, WrapperProcessContext};
|
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
|
||||||
use super::descriptor::PluginDescriptor;
|
use super::descriptor::PluginDescriptor;
|
||||||
use super::util::ClapPtr;
|
use super::util::ClapPtr;
|
||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
|
@ -615,6 +615,10 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
Arc::new(WrapperGuiContext { wrapper: self })
|
Arc::new(WrapperGuiContext { wrapper: self })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_init_context(&self) -> WrapperInitContext<'_, P> {
|
||||||
|
WrapperInitContext { wrapper: self }
|
||||||
|
}
|
||||||
|
|
||||||
fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P> {
|
fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P> {
|
||||||
WrapperProcessContext {
|
WrapperProcessContext {
|
||||||
wrapper: self,
|
wrapper: self,
|
||||||
|
@ -1484,11 +1488,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
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.write();
|
||||||
plugin.initialize(
|
plugin.initialize(&bus_config, &buffer_config, &mut self.make_init_context());
|
||||||
&bus_config,
|
|
||||||
&buffer_config,
|
|
||||||
&mut self.make_process_context(Transport::new(buffer_config.sample_rate)),
|
|
||||||
);
|
|
||||||
process_wrapper(|| plugin.reset());
|
process_wrapper(|| plugin.reset());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1501,6 +1501,17 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_latency_samples(&self, samples: u32) {
|
||||||
|
// Only make a callback if it's actually needed
|
||||||
|
// XXX: For CLAP we could move this handling to the Plugin struct, but it may be worthwhile
|
||||||
|
// to keep doing it this way to stay consistent with VST3.
|
||||||
|
let old_latency = self.current_latency.swap(samples, Ordering::SeqCst);
|
||||||
|
if old_latency != samples {
|
||||||
|
let task_posted = self.do_maybe_async(Task::LatencyChanged);
|
||||||
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
|
unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
|
||||||
check_null_ptr!(false, plugin);
|
check_null_ptr!(false, plugin);
|
||||||
let wrapper = &*(plugin as *const Self);
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
@ -1550,7 +1561,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
if plugin.initialize(
|
if plugin.initialize(
|
||||||
&bus_config,
|
&bus_config,
|
||||||
&buffer_config,
|
&buffer_config,
|
||||||
&mut wrapper.make_process_context(Transport::new(buffer_config.sample_rate)),
|
&mut wrapper.make_init_context(),
|
||||||
) {
|
) {
|
||||||
// NOTE: `Plugin::reset()` is called in `clap_plugin::start_processing()` instead of in
|
// NOTE: `Plugin::reset()` is called in `clap_plugin::start_processing()` instead of in
|
||||||
// this function
|
// this function
|
||||||
|
@ -1927,8 +1938,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
plugin.initialize(
|
plugin.initialize(
|
||||||
&bus_config,
|
&bus_config,
|
||||||
&buffer_config,
|
&buffer_config,
|
||||||
&mut wrapper
|
&mut wrapper.make_init_context(),
|
||||||
.make_process_context(Transport::new(buffer_config.sample_rate)),
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
plugin.reset();
|
plugin.reset();
|
||||||
|
@ -2810,7 +2820,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
plugin.initialize(
|
plugin.initialize(
|
||||||
&bus_config,
|
&bus_config,
|
||||||
&buffer_config,
|
&buffer_config,
|
||||||
&mut wrapper.make_process_context(Transport::new(buffer_config.sample_rate)),
|
&mut wrapper.make_init_context(),
|
||||||
);
|
);
|
||||||
// TODO: This also goes for the VST3 version, but should we call reset here? Won't the
|
// TODO: This also goes for the VST3 version, but should we call reset here? Won't the
|
||||||
// host always restart playback? Check this with a couple of hosts and remove the
|
// host always restart playback? Check this with a couple of hosts and remove the
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use super::backend::Backend;
|
use super::backend::Backend;
|
||||||
use super::wrapper::{GuiTask, Wrapper};
|
use super::wrapper::{GuiTask, Wrapper};
|
||||||
use crate::context::{GuiContext, PluginApi, ProcessContext, Transport};
|
use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport};
|
||||||
use crate::midi::NoteEvent;
|
use crate::midi::NoteEvent;
|
||||||
use crate::param::internals::ParamPtr;
|
use crate::param::internals::ParamPtr;
|
||||||
use crate::plugin::Plugin;
|
use crate::plugin::Plugin;
|
||||||
|
@ -19,6 +19,14 @@ pub(crate) struct WrapperGuiContext<P: Plugin, B: Backend> {
|
||||||
pub(super) gui_task_sender: channel::Sender<GuiTask>,
|
pub(super) gui_task_sender: channel::Sender<GuiTask>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`InitContext`] implementation for the standalone wrapper. This is a separate object so it
|
||||||
|
/// can hold on to lock guards for event queues. Otherwise reading these events would require
|
||||||
|
/// constant unnecessary atomic operations to lock the uncontested RwLocks.
|
||||||
|
pub(crate) struct WrapperInitContext<'a, P: Plugin, B: Backend> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) wrapper: &'a Wrapper<P, B>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A [`ProcessContext`] implementation for the standalone wrapper. This is a separate object so it
|
/// A [`ProcessContext`] implementation for the standalone wrapper. This is a separate object so it
|
||||||
/// can hold on to lock guards for event queues. Otherwise reading these events would require
|
/// can hold on to lock guards for event queues. Otherwise reading these events would require
|
||||||
/// constant unnecessary atomic operations to lock the uncontested RwLocks.
|
/// constant unnecessary atomic operations to lock the uncontested RwLocks.
|
||||||
|
@ -72,6 +80,16 @@ impl<P: Plugin, B: Backend> GuiContext for WrapperGuiContext<P, B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P: Plugin, B: Backend> InitContext for WrapperInitContext<'_, P, B> {
|
||||||
|
fn plugin_api(&self) -> PluginApi {
|
||||||
|
PluginApi::Standalone
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_latency_samples(&self, _samples: u32) {
|
||||||
|
nih_debug_assert_failure!("TODO: WrapperInitContext::set_latency_samples()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<P: Plugin, B: Backend> ProcessContext for WrapperProcessContext<'_, P, B> {
|
impl<P: Plugin, B: Backend> ProcessContext for WrapperProcessContext<'_, P, B> {
|
||||||
fn plugin_api(&self) -> PluginApi {
|
fn plugin_api(&self) -> PluginApi {
|
||||||
PluginApi::Standalone
|
PluginApi::Standalone
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use super::backend::Backend;
|
use super::backend::Backend;
|
||||||
use super::context::{WrapperGuiContext, WrapperProcessContext};
|
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
|
||||||
use crate::context::Transport;
|
use crate::context::Transport;
|
||||||
use crate::param::internals::{ParamPtr, Params};
|
use crate::param::internals::{ParamPtr, Params};
|
||||||
use crate::param::ParamFlags;
|
use crate::param::ParamFlags;
|
||||||
|
@ -229,7 +229,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
if !plugin.initialize(
|
if !plugin.initialize(
|
||||||
&wrapper.bus_config,
|
&wrapper.bus_config,
|
||||||
&wrapper.buffer_config,
|
&wrapper.buffer_config,
|
||||||
&mut wrapper.make_process_context(Transport::new(wrapper.config.sample_rate)),
|
&mut wrapper.make_init_context(),
|
||||||
) {
|
) {
|
||||||
return Err(WrapperError::InitializationFailed);
|
return Err(WrapperError::InitializationFailed);
|
||||||
}
|
}
|
||||||
|
@ -494,6 +494,10 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_init_context(&self) -> WrapperInitContext<'_, P, B> {
|
||||||
|
WrapperInitContext { wrapper: self }
|
||||||
|
}
|
||||||
|
|
||||||
fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P, B> {
|
fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P, B> {
|
||||||
WrapperProcessContext {
|
WrapperProcessContext {
|
||||||
wrapper: self,
|
wrapper: self,
|
||||||
|
|
|
@ -2,10 +2,10 @@ use atomic_refcell::AtomicRefMut;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vst3_sys::vst::{IComponentHandler, RestartFlags};
|
use vst3_sys::vst::IComponentHandler;
|
||||||
|
|
||||||
use super::inner::{Task, WrapperInner};
|
use super::inner::{Task, WrapperInner};
|
||||||
use crate::context::{GuiContext, PluginApi, ProcessContext, Transport};
|
use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport};
|
||||||
use crate::midi::NoteEvent;
|
use crate::midi::NoteEvent;
|
||||||
use crate::param::internals::ParamPtr;
|
use crate::param::internals::ParamPtr;
|
||||||
use crate::plugin::Vst3Plugin;
|
use crate::plugin::Vst3Plugin;
|
||||||
|
@ -18,6 +18,13 @@ pub(crate) struct WrapperGuiContext<P: Vst3Plugin> {
|
||||||
pub(super) inner: Arc<WrapperInner<P>>,
|
pub(super) inner: Arc<WrapperInner<P>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`InitContext`] 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 unnecessary
|
||||||
|
/// atomic operations to lock the uncontested locks.
|
||||||
|
pub(crate) struct WrapperInitContext<'a, P: Vst3Plugin> {
|
||||||
|
pub(super) inner: &'a 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 locks.
|
/// unnecessary atomic operations to lock the uncontested locks.
|
||||||
|
@ -108,6 +115,16 @@ impl<P: Vst3Plugin> GuiContext for WrapperGuiContext<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P: Vst3Plugin> InitContext for WrapperInitContext<'_, P> {
|
||||||
|
fn plugin_api(&self) -> PluginApi {
|
||||||
|
PluginApi::Vst3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
|
self.inner.set_latency_samples(samples)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
fn plugin_api(&self) -> PluginApi {
|
fn plugin_api(&self) -> PluginApi {
|
||||||
PluginApi::Vst3
|
PluginApi::Vst3
|
||||||
|
@ -126,13 +143,6 @@ 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
|
self.inner.set_latency_samples(samples)
|
||||||
let old_latency = self.inner.current_latency.swap(samples, Ordering::SeqCst);
|
|
||||||
if old_latency != samples {
|
|
||||||
let task_posted = self
|
|
||||||
.inner
|
|
||||||
.do_maybe_async(Task::TriggerRestart(RestartFlags::kLatencyChanged as i32));
|
|
||||||
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::time::Duration;
|
||||||
use vst3_sys::base::{kInvalidArgument, kResultOk, tresult};
|
use vst3_sys::base::{kInvalidArgument, kResultOk, tresult};
|
||||||
use vst3_sys::vst::{IComponentHandler, RestartFlags};
|
use vst3_sys::vst::{IComponentHandler, RestartFlags};
|
||||||
|
|
||||||
use super::context::{WrapperGuiContext, WrapperProcessContext};
|
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
|
||||||
use super::note_expressions::NoteExpressionController;
|
use super::note_expressions::NoteExpressionController;
|
||||||
use super::param_units::ParamUnits;
|
use super::param_units::ParamUnits;
|
||||||
use super::util::{ObjectPtr, VstPtr, VST3_MIDI_PARAMS_END, VST3_MIDI_PARAMS_START};
|
use super::util::{ObjectPtr, VstPtr, VST3_MIDI_PARAMS_END, VST3_MIDI_PARAMS_START};
|
||||||
|
@ -313,6 +313,10 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
Arc::new(WrapperGuiContext { inner: self })
|
Arc::new(WrapperGuiContext { inner: self })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_init_context(&self) -> WrapperInitContext<'_, P> {
|
||||||
|
WrapperInitContext { inner: self }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P> {
|
pub fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P> {
|
||||||
WrapperProcessContext {
|
WrapperProcessContext {
|
||||||
inner: self,
|
inner: self,
|
||||||
|
@ -450,11 +454,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
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.write();
|
||||||
plugin.initialize(
|
plugin.initialize(&bus_config, &buffer_config, &mut self.make_init_context());
|
||||||
&bus_config,
|
|
||||||
&buffer_config,
|
|
||||||
&mut self.make_process_context(Transport::new(buffer_config.sample_rate)),
|
|
||||||
);
|
|
||||||
process_wrapper(|| plugin.reset());
|
process_wrapper(|| plugin.reset());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,6 +468,16 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
);
|
);
|
||||||
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_latency_samples(&self, samples: u32) {
|
||||||
|
// Only trigger a restart if it's actually needed
|
||||||
|
let old_latency = self.current_latency.swap(samples, Ordering::SeqCst);
|
||||||
|
if old_latency != samples {
|
||||||
|
let task_posted =
|
||||||
|
self.do_maybe_async(Task::TriggerRestart(RestartFlags::kLatencyChanged as i32));
|
||||||
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Vst3Plugin> MainThreadExecutor<Task> for WrapperInner<P> {
|
impl<P: Vst3Plugin> MainThreadExecutor<Task> for WrapperInner<P> {
|
||||||
|
|
|
@ -370,9 +370,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
||||||
if plugin.initialize(
|
if plugin.initialize(
|
||||||
&bus_config,
|
&bus_config,
|
||||||
&buffer_config,
|
&buffer_config,
|
||||||
&mut self
|
&mut self.inner.make_init_context(),
|
||||||
.inner
|
|
||||||
.make_process_context(Transport::new(buffer_config.sample_rate)),
|
|
||||||
) {
|
) {
|
||||||
// NOTE: We don't call `Plugin::reset()` here. The call is done in `set_process()`
|
// NOTE: We don't call `Plugin::reset()` here. The call is done in `set_process()`
|
||||||
// instead. Otherwise we would call the function twice, and `set_process()` needs
|
// instead. Otherwise we would call the function twice, and `set_process()` needs
|
||||||
|
@ -459,9 +457,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
||||||
plugin.initialize(
|
plugin.initialize(
|
||||||
&bus_config,
|
&bus_config,
|
||||||
&buffer_config,
|
&buffer_config,
|
||||||
&mut self
|
&mut self.inner.make_init_context(),
|
||||||
.inner
|
|
||||||
.make_process_context(Transport::new(buffer_config.sample_rate)),
|
|
||||||
);
|
);
|
||||||
// TODO: This also goes for the CLAP version, but should we call reset here? Won't the
|
// TODO: This also goes for the CLAP version, but should we call reset here? Won't the
|
||||||
// host always restart playback? Check this with a couple of hosts and remove the
|
// host always restart playback? Check this with a couple of hosts and remove the
|
||||||
|
@ -1531,9 +1527,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
||||||
plugin.initialize(
|
plugin.initialize(
|
||||||
&bus_config,
|
&bus_config,
|
||||||
&buffer_config,
|
&buffer_config,
|
||||||
&mut self
|
&mut self.inner.make_init_context(),
|
||||||
.inner
|
|
||||||
.make_process_context(Transport::new(buffer_config.sample_rate)),
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
plugin.reset();
|
plugin.reset();
|
||||||
|
|
Loading…
Reference in a new issue