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
|
||||
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]
|
||||
|
||||
- Previously calling `param.non_automatable()` when constructing a parameter
|
||||
|
|
|
@ -315,7 +315,7 @@ impl Plugin for Crisp {
|
|||
&mut self,
|
||||
bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl ProcessContext,
|
||||
_context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
nih_debug_assert_eq!(bus_config.num_input_channels, NUM_CHANNELS);
|
||||
nih_debug_assert_eq!(bus_config.num_output_channels, NUM_CHANNELS);
|
||||
|
|
|
@ -253,7 +253,7 @@ impl Plugin for Diopser {
|
|||
&mut self,
|
||||
_bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl ProcessContext,
|
||||
_context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
self.sample_rate = buffer_config.sample_rate;
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ impl Plugin for Gain {
|
|||
&mut self,
|
||||
_bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl ProcessContext,
|
||||
_context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
// 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);
|
||||
|
|
|
@ -90,7 +90,7 @@ impl Plugin for Gain {
|
|||
&mut self,
|
||||
_bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl ProcessContext,
|
||||
_context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
// 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);
|
||||
|
|
|
@ -90,7 +90,7 @@ impl Plugin for Gain {
|
|||
&mut self,
|
||||
_bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl ProcessContext,
|
||||
_context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
// 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);
|
||||
|
|
|
@ -124,7 +124,7 @@ impl Plugin for Sine {
|
|||
&mut self,
|
||||
_bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl ProcessContext,
|
||||
_context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
self.sample_rate = buffer_config.sample_rate;
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ impl Plugin for Stft {
|
|||
&mut self,
|
||||
_bus_config: &BusConfig,
|
||||
_buffer_config: &BufferConfig,
|
||||
context: &mut impl ProcessContext,
|
||||
context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
// 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
|
||||
|
|
|
@ -135,7 +135,7 @@ impl Plugin for LoudnessWarWinner {
|
|||
&mut self,
|
||||
bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl ProcessContext,
|
||||
_context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
self.sample_rate = buffer_config.sample_rate;
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ impl Plugin for PubertySimulator {
|
|||
&mut self,
|
||||
_bus_config: &BusConfig,
|
||||
_buffer_config: &BufferConfig,
|
||||
context: &mut impl ProcessContext,
|
||||
context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
// Planning with RustFFT is very fast, but it will still allocate we we'll plan all of the
|
||||
// FFTs we might need in advance
|
||||
|
|
|
@ -170,7 +170,7 @@ impl Plugin for SafetyLimiter {
|
|||
&mut self,
|
||||
_bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
_context: &mut impl ProcessContext,
|
||||
_context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
self.buffer_config = *buffer_config;
|
||||
self.morse_fadeout_samples_start =
|
||||
|
|
|
@ -7,10 +7,25 @@ use crate::param::internals::ParamPtr;
|
|||
use crate::param::Param;
|
||||
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
|
||||
/// [`Plugin::initialize()`][crate::plugin::Plugin::initialize()] and as part of
|
||||
/// Update the current latency of the plugin. If the plugin is currently processing audio, then
|
||||
/// 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()].
|
||||
//
|
||||
// # Safety
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::any::Any;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::buffer::Buffer;
|
||||
use crate::context::{GuiContext, ProcessContext};
|
||||
use crate::context::{GuiContext, InitContext, ProcessContext};
|
||||
use crate::midi::MidiConfig;
|
||||
use crate::param::internals::Params;
|
||||
|
||||
|
@ -111,7 +111,7 @@ pub trait Plugin: Default + Send + Sync + 'static {
|
|||
&mut self,
|
||||
bus_config: &BusConfig,
|
||||
buffer_config: &BufferConfig,
|
||||
context: &mut impl ProcessContext,
|
||||
context: &mut impl InitContext,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ pub use crate::formatters;
|
|||
pub use crate::util;
|
||||
|
||||
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
|
||||
pub use crate::midi::{control_change, MidiConfig, NoteEvent};
|
||||
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
|
||||
/// 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
|
||||
/// calling
|
||||
/// [`ProcessContext::set_latency()`][`crate::prelude::ProcessContext::set_latency_samples()`]
|
||||
/// calling [`InitContext::set_latency()`][`crate::prelude::InitContext::set_latency_samples()`]
|
||||
/// in your plugin's initialization function.
|
||||
///
|
||||
/// If a padding value was specified in [`new()`][Self::new()], then the yielded blocks will
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use atomic_refcell::AtomicRefMut;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::wrapper::{OutputParamEvent, Task, Wrapper};
|
||||
use crate::context::{GuiContext, PluginApi, ProcessContext, Transport};
|
||||
use crate::event_loop::EventLoop;
|
||||
use super::wrapper::{OutputParamEvent, Wrapper};
|
||||
use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport};
|
||||
use crate::midi::NoteEvent;
|
||||
use crate::param::internals::ParamPtr;
|
||||
use crate::plugin::ClapPlugin;
|
||||
|
@ -17,6 +15,13 @@ pub(crate) struct WrapperGuiContext<P: ClapPlugin> {
|
|||
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
|
||||
/// to lock guards for event queues. Otherwise reading these events would require constant
|
||||
/// 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> {
|
||||
fn plugin_api(&self) -> PluginApi {
|
||||
PluginApi::Clap
|
||||
|
@ -113,13 +128,6 @@ impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
|
|||
}
|
||||
|
||||
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.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...");
|
||||
}
|
||||
self.wrapper.set_latency_samples(samples)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ use std::sync::{Arc, Weak};
|
|||
use std::thread::{self, ThreadId};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::context::{WrapperGuiContext, WrapperProcessContext};
|
||||
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
|
||||
use super::descriptor::PluginDescriptor;
|
||||
use super::util::ClapPtr;
|
||||
use crate::buffer::Buffer;
|
||||
|
@ -615,6 +615,10 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
Arc::new(WrapperGuiContext { wrapper: self })
|
||||
}
|
||||
|
||||
fn make_init_context(&self) -> WrapperInitContext<'_, P> {
|
||||
WrapperInitContext { wrapper: self }
|
||||
}
|
||||
|
||||
fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P> {
|
||||
WrapperProcessContext {
|
||||
wrapper: self,
|
||||
|
@ -1484,11 +1488,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
let bus_config = self.current_bus_config.load();
|
||||
if let Some(buffer_config) = self.current_buffer_config.load() {
|
||||
let mut plugin = self.plugin.write();
|
||||
plugin.initialize(
|
||||
&bus_config,
|
||||
&buffer_config,
|
||||
&mut self.make_process_context(Transport::new(buffer_config.sample_rate)),
|
||||
);
|
||||
plugin.initialize(&bus_config, &buffer_config, &mut self.make_init_context());
|
||||
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...");
|
||||
}
|
||||
|
||||
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 {
|
||||
check_null_ptr!(false, plugin);
|
||||
let wrapper = &*(plugin as *const Self);
|
||||
|
@ -1550,7 +1561,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
if plugin.initialize(
|
||||
&bus_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
|
||||
// this function
|
||||
|
@ -1927,8 +1938,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
plugin.initialize(
|
||||
&bus_config,
|
||||
&buffer_config,
|
||||
&mut wrapper
|
||||
.make_process_context(Transport::new(buffer_config.sample_rate)),
|
||||
&mut wrapper.make_init_context(),
|
||||
)
|
||||
});
|
||||
plugin.reset();
|
||||
|
@ -2810,7 +2820,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
plugin.initialize(
|
||||
&bus_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
|
||||
// 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::wrapper::{GuiTask, Wrapper};
|
||||
use crate::context::{GuiContext, PluginApi, ProcessContext, Transport};
|
||||
use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport};
|
||||
use crate::midi::NoteEvent;
|
||||
use crate::param::internals::ParamPtr;
|
||||
use crate::plugin::Plugin;
|
||||
|
@ -19,6 +19,14 @@ pub(crate) struct WrapperGuiContext<P: Plugin, B: Backend> {
|
|||
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
|
||||
/// can hold on to lock guards for event queues. Otherwise reading these events would require
|
||||
/// 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> {
|
||||
fn plugin_api(&self) -> PluginApi {
|
||||
PluginApi::Standalone
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::sync::Arc;
|
|||
use std::thread;
|
||||
|
||||
use super::backend::Backend;
|
||||
use super::context::{WrapperGuiContext, WrapperProcessContext};
|
||||
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
|
||||
use crate::context::Transport;
|
||||
use crate::param::internals::{ParamPtr, Params};
|
||||
use crate::param::ParamFlags;
|
||||
|
@ -229,7 +229,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
|||
if !plugin.initialize(
|
||||
&wrapper.bus_config,
|
||||
&wrapper.buffer_config,
|
||||
&mut wrapper.make_process_context(Transport::new(wrapper.config.sample_rate)),
|
||||
&mut wrapper.make_init_context(),
|
||||
) {
|
||||
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> {
|
||||
WrapperProcessContext {
|
||||
wrapper: self,
|
||||
|
|
|
@ -2,10 +2,10 @@ use atomic_refcell::AtomicRefMut;
|
|||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use vst3_sys::vst::{IComponentHandler, RestartFlags};
|
||||
use vst3_sys::vst::IComponentHandler;
|
||||
|
||||
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::param::internals::ParamPtr;
|
||||
use crate::plugin::Vst3Plugin;
|
||||
|
@ -18,6 +18,13 @@ pub(crate) struct WrapperGuiContext<P: Vst3Plugin> {
|
|||
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
|
||||
/// to lock guards for event queues. Otherwise reading these events would require constant
|
||||
/// 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> {
|
||||
fn plugin_api(&self) -> PluginApi {
|
||||
PluginApi::Vst3
|
||||
|
@ -126,13 +143,6 @@ impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
|||
}
|
||||
|
||||
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 {
|
||||
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...");
|
||||
}
|
||||
self.inner.set_latency_samples(samples)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::time::Duration;
|
|||
use vst3_sys::base::{kInvalidArgument, kResultOk, tresult};
|
||||
use vst3_sys::vst::{IComponentHandler, RestartFlags};
|
||||
|
||||
use super::context::{WrapperGuiContext, WrapperProcessContext};
|
||||
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
|
||||
use super::note_expressions::NoteExpressionController;
|
||||
use super::param_units::ParamUnits;
|
||||
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 })
|
||||
}
|
||||
|
||||
pub fn make_init_context(&self) -> WrapperInitContext<'_, P> {
|
||||
WrapperInitContext { inner: self }
|
||||
}
|
||||
|
||||
pub fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P> {
|
||||
WrapperProcessContext {
|
||||
inner: self,
|
||||
|
@ -450,11 +454,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
|||
let bus_config = self.current_bus_config.load();
|
||||
if let Some(buffer_config) = self.current_buffer_config.load() {
|
||||
let mut plugin = self.plugin.write();
|
||||
plugin.initialize(
|
||||
&bus_config,
|
||||
&buffer_config,
|
||||
&mut self.make_process_context(Transport::new(buffer_config.sample_rate)),
|
||||
);
|
||||
plugin.initialize(&bus_config, &buffer_config, &mut self.make_init_context());
|
||||
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...");
|
||||
}
|
||||
|
||||
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> {
|
||||
|
|
|
@ -370,9 +370,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
|||
if plugin.initialize(
|
||||
&bus_config,
|
||||
&buffer_config,
|
||||
&mut self
|
||||
.inner
|
||||
.make_process_context(Transport::new(buffer_config.sample_rate)),
|
||||
&mut self.inner.make_init_context(),
|
||||
) {
|
||||
// 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
|
||||
|
@ -459,9 +457,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
|||
plugin.initialize(
|
||||
&bus_config,
|
||||
&buffer_config,
|
||||
&mut self
|
||||
.inner
|
||||
.make_process_context(Transport::new(buffer_config.sample_rate)),
|
||||
&mut self.inner.make_init_context(),
|
||||
);
|
||||
// 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
|
||||
|
@ -1531,9 +1527,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
|||
plugin.initialize(
|
||||
&bus_config,
|
||||
&buffer_config,
|
||||
&mut self
|
||||
.inner
|
||||
.make_process_context(Transport::new(buffer_config.sample_rate)),
|
||||
&mut self.inner.make_init_context(),
|
||||
)
|
||||
});
|
||||
plugin.reset();
|
||||
|
|
Loading…
Reference in a new issue