From b2394958e74e8e86ea95211bb84149c5c68a22e4 Mon Sep 17 00:00:00 2001 From: Heewon Oh Date: Fri, 25 Sep 2026 17:16:14 +0900 Subject: [PATCH] feat(context): expose the host's mid-session render mode (AUD-1692) Add ProcessContext::host_render_mode(), defaulting to None. The CLAP wrapper returns the mode last set through clap_plugin_render::set(). nih-plug already stores that value, but only hands it to the plugin via BufferConfig on the next initialize(). CLAP hosts - and clap-wrapper's AAX wrapper when Pro Tools enters an offline bounce - may switch the render mode without re-activating, so a plugin whose real-time path polls a worker thread without blocking had no way to know it should block for the length of a faster-than-real-time bounce. The default implementation keeps every other wrapper and every existing ProcessContext implementor source-compatible. --- src/context/process.rs | 18 +++++++++++++++++- src/wrapper/clap/context.rs | 6 +++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/context/process.rs b/src/context/process.rs index 1922486ea..ca22dd775 100644 --- a/src/context/process.rs +++ b/src/context/process.rs @@ -1,7 +1,7 @@ //! A context passed during the process function. use super::PluginApi; -use crate::prelude::{Plugin, PluginNoteEvent}; +use crate::prelude::{Plugin, PluginNoteEvent, ProcessMode}; /// 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 @@ -92,6 +92,22 @@ pub trait ProcessContext { /// monophonic modulation when dropping the capacity down to 1. fn set_current_voice_capacity(&self, capacity: u32); + /// The render mode the host has requested **after** [`Plugin::initialize()`], for plugin APIs + /// that let a host switch it mid-session. `None` means the API has no such mechanism, in which + /// case [`BufferConfig::process_mode`][crate::prelude::BufferConfig::process_mode] from the + /// last `initialize()` call is still authoritative. + /// + /// CLAP hosts may call `clap_plugin_render::set()` at any time on the main thread, for + /// instance to render an offline bounce faster than real time without re-activating the + /// plugin. That change only reaches `BufferConfig` on the next `initialize()`, so a plugin + /// whose real-time path cannot keep up with faster-than-real-time processing (e.g. one that + /// hands audio to a worker thread and polls for results without blocking) can read this to + /// switch to a blocking strategy for the duration of the bounce. The value is a lock-free + /// atomic load and is safe to call from the audio thread. + fn host_render_mode(&self) -> Option { + None + } + // TODO: Add this, this works similar to [GuiContext::set_parameter] but it adds the parameter // change to a queue (or directly to the VST3 plugin's parameter output queues) instead of // using main thread host automation (and all the locks involved there). diff --git a/src/wrapper/clap/context.rs b/src/wrapper/clap/context.rs index cf3668f2a..933511c1f 100644 --- a/src/wrapper/clap/context.rs +++ b/src/wrapper/clap/context.rs @@ -12,7 +12,7 @@ use super::wrapper::{OutputParamEvent, Task, Wrapper}; use crate::event_loop::EventLoop; use crate::prelude::{ ClapPlugin, GuiContext, InitContext, ParamPtr, PluginApi, PluginNoteEvent, ProcessContext, - RemoteControlsContext, RemoteControlsPage, RemoteControlsSection, Transport, + ProcessMode, RemoteControlsContext, RemoteControlsPage, RemoteControlsSection, Transport, }; use crate::wrapper::util::strlcpy; @@ -127,6 +127,10 @@ impl ProcessContext

for WrapperProcessContext<'_, P> { fn set_current_voice_capacity(&self, capacity: u32) { self.wrapper.set_current_voice_capacity(capacity) } + + fn host_render_mode(&self) -> Option { + Some(self.wrapper.current_process_mode.load()) + } } impl GuiContext for WrapperGuiContext

{