From b2e6bd5515f5737c5c48b8293e4be0e788acf387 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 27 May 2022 01:17:15 +0200 Subject: [PATCH] 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. --- BREAKING_CHANGES.md | 8 ++++++ plugins/crisp/src/lib.rs | 2 +- plugins/diopser/src/lib.rs | 2 +- plugins/examples/gain_gui_egui/src/lib.rs | 2 +- plugins/examples/gain_gui_iced/src/lib.rs | 2 +- plugins/examples/gain_gui_vizia/src/lib.rs | 2 +- plugins/examples/sine/src/lib.rs | 2 +- plugins/examples/stft/src/lib.rs | 2 +- plugins/loudness_war_winner/src/lib.rs | 2 +- plugins/puberty_simulator/src/lib.rs | 2 +- plugins/safety_limiter/src/lib.rs | 2 +- src/context.rs | 21 ++++++++++++-- src/plugin.rs | 4 +-- src/prelude.rs | 2 +- src/util/stft.rs | 3 +- src/wrapper/clap/context.rs | 32 ++++++++++++++-------- src/wrapper/clap/wrapper.rs | 30 +++++++++++++------- src/wrapper/standalone/context.rs | 20 +++++++++++++- src/wrapper/standalone/wrapper.rs | 8 ++++-- src/wrapper/vst3/context.rs | 30 +++++++++++++------- src/wrapper/vst3/inner.rs | 22 +++++++++++---- src/wrapper/vst3/wrapper.rs | 12 ++------ 22 files changed, 144 insertions(+), 68 deletions(-) diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 8d8bb2dc..b84891d1 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -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 diff --git a/plugins/crisp/src/lib.rs b/plugins/crisp/src/lib.rs index d771df60..ed7d3532 100644 --- a/plugins/crisp/src/lib.rs +++ b/plugins/crisp/src/lib.rs @@ -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); diff --git a/plugins/diopser/src/lib.rs b/plugins/diopser/src/lib.rs index 733c1e54..54378a60 100644 --- a/plugins/diopser/src/lib.rs +++ b/plugins/diopser/src/lib.rs @@ -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; diff --git a/plugins/examples/gain_gui_egui/src/lib.rs b/plugins/examples/gain_gui_egui/src/lib.rs index 938bf58c..e2fe4fed 100644 --- a/plugins/examples/gain_gui_egui/src/lib.rs +++ b/plugins/examples/gain_gui_egui/src/lib.rs @@ -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); diff --git a/plugins/examples/gain_gui_iced/src/lib.rs b/plugins/examples/gain_gui_iced/src/lib.rs index 668f0e45..1c9d63eb 100644 --- a/plugins/examples/gain_gui_iced/src/lib.rs +++ b/plugins/examples/gain_gui_iced/src/lib.rs @@ -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); diff --git a/plugins/examples/gain_gui_vizia/src/lib.rs b/plugins/examples/gain_gui_vizia/src/lib.rs index c61bf8af..3b9a1621 100644 --- a/plugins/examples/gain_gui_vizia/src/lib.rs +++ b/plugins/examples/gain_gui_vizia/src/lib.rs @@ -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); diff --git a/plugins/examples/sine/src/lib.rs b/plugins/examples/sine/src/lib.rs index 44a57555..90154512 100644 --- a/plugins/examples/sine/src/lib.rs +++ b/plugins/examples/sine/src/lib.rs @@ -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; diff --git a/plugins/examples/stft/src/lib.rs b/plugins/examples/stft/src/lib.rs index 6c6185d7..7ee83996 100644 --- a/plugins/examples/stft/src/lib.rs +++ b/plugins/examples/stft/src/lib.rs @@ -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 diff --git a/plugins/loudness_war_winner/src/lib.rs b/plugins/loudness_war_winner/src/lib.rs index 779c4db0..3c26ff98 100644 --- a/plugins/loudness_war_winner/src/lib.rs +++ b/plugins/loudness_war_winner/src/lib.rs @@ -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; diff --git a/plugins/puberty_simulator/src/lib.rs b/plugins/puberty_simulator/src/lib.rs index 574198c0..238b97df 100644 --- a/plugins/puberty_simulator/src/lib.rs +++ b/plugins/puberty_simulator/src/lib.rs @@ -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 diff --git a/plugins/safety_limiter/src/lib.rs b/plugins/safety_limiter/src/lib.rs index fc4d9d4c..ad57d309 100644 --- a/plugins/safety_limiter/src/lib.rs +++ b/plugins/safety_limiter/src/lib.rs @@ -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 = diff --git a/src/context.rs b/src/context.rs index 51cbfad2..4b6c37d9 100644 --- a/src/context.rs +++ b/src/context.rs @@ -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 diff --git a/src/plugin.rs b/src/plugin.rs index bf8d4ccf..05342391 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -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 } diff --git a/src/prelude.rs b/src/prelude.rs index d7bb777e..a0393d89 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -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}; diff --git a/src/util/stft.rs b/src/util/stft.rs index 21b97464..3f3244f4 100644 --- a/src/util/stft.rs +++ b/src/util/stft.rs @@ -253,8 +253,7 @@ impl StftHelper { /// 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 diff --git a/src/wrapper/clap/context.rs b/src/wrapper/clap/context.rs index 3947f70a..2ab6de41 100644 --- a/src/wrapper/clap/context.rs +++ b/src/wrapper/clap/context.rs @@ -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 { pub(super) wrapper: Arc>, } +/// 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

, +} + /// 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 GuiContext for WrapperGuiContext

{ } } +impl 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 ProcessContext for WrapperProcessContext<'_, P> { fn plugin_api(&self) -> PluginApi { PluginApi::Clap @@ -113,13 +128,6 @@ impl 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) } } diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index dd8bd008..74cb9324 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -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 Wrapper

{ 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 Wrapper

{ 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 Wrapper

{ 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 Wrapper

{ 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 Wrapper

{ 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 Wrapper

{ 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 diff --git a/src/wrapper/standalone/context.rs b/src/wrapper/standalone/context.rs index 46fc0032..8e12d4bc 100644 --- a/src/wrapper/standalone/context.rs +++ b/src/wrapper/standalone/context.rs @@ -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 { pub(super) gui_task_sender: channel::Sender, } +/// 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, +} + /// 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 GuiContext for WrapperGuiContext { } } +impl 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 ProcessContext for WrapperProcessContext<'_, P, B> { fn plugin_api(&self) -> PluginApi { PluginApi::Standalone diff --git a/src/wrapper/standalone/wrapper.rs b/src/wrapper/standalone/wrapper.rs index 94b58aaf..a35e9f98 100644 --- a/src/wrapper/standalone/wrapper.rs +++ b/src/wrapper/standalone/wrapper.rs @@ -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 Wrapper { 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 Wrapper { }) } + fn make_init_context(&self) -> WrapperInitContext<'_, P, B> { + WrapperInitContext { wrapper: self } + } + fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P, B> { WrapperProcessContext { wrapper: self, diff --git a/src/wrapper/vst3/context.rs b/src/wrapper/vst3/context.rs index 36a05f30..885e8c41 100644 --- a/src/wrapper/vst3/context.rs +++ b/src/wrapper/vst3/context.rs @@ -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 { pub(super) inner: Arc>, } +/// 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

, +} + /// 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 GuiContext for WrapperGuiContext

{ } } +impl 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 ProcessContext for WrapperProcessContext<'_, P> { fn plugin_api(&self) -> PluginApi { PluginApi::Vst3 @@ -126,13 +143,6 @@ impl 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) } } diff --git a/src/wrapper/vst3/inner.rs b/src/wrapper/vst3/inner.rs index d44d3f27..f18584d7 100644 --- a/src/wrapper/vst3/inner.rs +++ b/src/wrapper/vst3/inner.rs @@ -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 WrapperInner

{ 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 WrapperInner

{ 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 WrapperInner

{ ); 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 MainThreadExecutor for WrapperInner

{ diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index 71b7dded..927b9b87 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -370,9 +370,7 @@ impl IComponent for Wrapper

{ 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 IComponent for Wrapper

{ 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 IAudioProcessor for Wrapper

{ 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();