1
0
Fork 0

Add a way to fetch the current processing mode

This commit is contained in:
Robbert van der Helm 2022-05-22 00:58:53 +02:00
parent 9601c9c0c1
commit 665108721a
8 changed files with 93 additions and 7 deletions

View file

@ -24,6 +24,9 @@ pub trait ProcessContext {
/// Get information about the current transport position and status.
fn transport(&self) -> &Transport;
/// The current processing mode. The host will reinitialize the plugin any time this changes.
fn process_mode(&self) -> ProcessMode;
/// Returns the next note event, if there is one. Use [`NoteEvent::timing()`] to get the event's
/// timing within the buffer. Only available when
/// [`Plugin::MIDI_INPUT`][crate::prelude::Plugin::MIDI_INPUT] is set.
@ -204,6 +207,22 @@ pub enum PluginApi {
Vst3,
}
/// The plugin's current processing mode. Can be queried through [`ProcessContext::process_mode()`].
/// The host will reinitialize the plugin whenever this changes.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcessMode {
/// The plugin is processing audio in real time at a fixed rate.
Realtime,
/// The plugin is processing audio at a real time-like pace, but at irregular intervals. The
/// host may do this to process audio ahead of time to loosen realtime constraints and to reduce
/// the chance of xruns happening. This is only used by VST3.
Buffered,
/// The plugin is rendering audio offline, potentially faster than realtime ('freewheeling').
/// The host will continuously call the process function back to back until all audio has been
/// processed.
Offline,
}
impl Display for PluginApi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {

View file

@ -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, ParamSetter, PluginApi, ProcessContext, ProcessMode};
// This also includes the derive macro
pub use crate::midi::{control_change, MidiConfig, NoteEvent};
pub use crate::param::enums::{Enum, EnumParam};

View file

@ -4,7 +4,7 @@ use std::sync::atomic::Ordering;
use std::sync::Arc;
use super::wrapper::{OutputParamEvent, Task, Wrapper};
use crate::context::{GuiContext, PluginApi, ProcessContext, Transport};
use crate::context::{GuiContext, PluginApi, ProcessContext, ProcessMode, Transport};
use crate::event_loop::EventLoop;
use crate::midi::NoteEvent;
use crate::param::internals::ParamPtr;
@ -104,6 +104,10 @@ impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
&self.transport
}
fn process_mode(&self) -> ProcessMode {
self.wrapper.current_process_mode.load()
}
fn next_event(&mut self) -> Option<NoteEvent> {
self.input_events_guard.pop_front()
}

View file

@ -41,6 +41,9 @@ use clap_sys::ext::params::{
CLAP_PARAM_IS_MODULATABLE, CLAP_PARAM_IS_READONLY, CLAP_PARAM_IS_STEPPED,
CLAP_PARAM_RESCAN_VALUES,
};
use clap_sys::ext::render::{
clap_plugin_render, clap_plugin_render_mode, CLAP_RENDER_OFFLINE, CLAP_RENDER_REALTIME,
};
use clap_sys::ext::state::{clap_plugin_state, CLAP_EXT_STATE};
use clap_sys::ext::tail::{clap_plugin_tail, CLAP_EXT_TAIL};
use clap_sys::ext::thread_check::{clap_host_thread_check, CLAP_EXT_THREAD_CHECK};
@ -75,7 +78,7 @@ use super::context::{WrapperGuiContext, WrapperProcessContext};
use super::descriptor::PluginDescriptor;
use super::util::ClapPtr;
use crate::buffer::Buffer;
use crate::context::Transport;
use crate::context::{ProcessMode, Transport};
use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
use crate::midi::{MidiConfig, NoteEvent};
use crate::param::internals::{ParamPtr, Params};
@ -125,6 +128,8 @@ pub struct Wrapper<P: ClapPlugin> {
/// The current buffer configuration, containing the sample rate and the maximum block size.
/// Will be set in `clap_plugin::activate()`.
current_buffer_config: AtomicCell<Option<BufferConfig>>,
/// The current audio processing mode. Set through the render extension. Defaults to realtime.
pub current_process_mode: AtomicCell<ProcessMode>,
/// The incoming events for the plugin, if `P::MIDI_INPUT` is set to `MidiConfig::Basic` or
/// higher.
///
@ -217,6 +222,8 @@ pub struct Wrapper<P: ClapPlugin> {
host_thread_check: AtomicRefCell<Option<ClapPtr<clap_host_thread_check>>>,
clap_plugin_render: clap_plugin_render,
clap_plugin_state: clap_plugin_state,
clap_plugin_tail: clap_plugin_tail,
@ -481,6 +488,7 @@ impl<P: ClapPlugin> Wrapper<P> {
num_output_channels: P::DEFAULT_NUM_OUTPUTS,
}),
current_buffer_config: AtomicCell::new(None),
current_process_mode: AtomicCell::new(ProcessMode::Realtime),
input_events: AtomicRefCell::new(VecDeque::with_capacity(512)),
output_events: AtomicRefCell::new(VecDeque::with_capacity(512)),
last_process_status: AtomicCell::new(ProcessStatus::Normal),
@ -556,6 +564,11 @@ impl<P: ClapPlugin> Wrapper<P> {
host_thread_check: AtomicRefCell::new(None),
clap_plugin_render: clap_plugin_render {
has_hard_realtime_requirement: Self::ext_render_has_hard_realtime_requirement,
set: Self::ext_render_set,
},
clap_plugin_state: clap_plugin_state {
save: Self::ext_state_save,
load: Self::ext_state_load,
@ -2620,6 +2633,33 @@ impl<P: ClapPlugin> Wrapper<P> {
}
}
unsafe extern "C" fn ext_render_has_hard_realtime_requirement(
_plugin: *const clap_plugin,
) -> bool {
// TODO: Add a constant on the CLapPlugin trait
false
}
unsafe extern "C" fn ext_render_set(
plugin: *const clap_plugin,
mode: clap_plugin_render_mode,
) -> bool {
check_null_ptr!(false, plugin);
let wrapper = &*(plugin as *const Self);
let mode = match mode {
CLAP_RENDER_REALTIME => ProcessMode::Realtime,
CLAP_RENDER_OFFLINE => ProcessMode::Offline,
n => {
nih_debug_assert_failure!("Unknown rendering mode '{}', defaulting to realtime", n);
ProcessMode::Realtime
}
};
wrapper.current_process_mode.store(mode);
true
}
unsafe extern "C" fn ext_state_save(
plugin: *const clap_plugin,
stream: *const clap_ostream,

View file

@ -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, PluginApi, ProcessContext, ProcessMode, Transport};
use crate::midi::NoteEvent;
use crate::param::internals::ParamPtr;
use crate::plugin::Plugin;
@ -81,6 +81,11 @@ impl<P: Plugin, B: Backend> ProcessContext for WrapperProcessContext<'_, P, B> {
&self.transport
}
fn process_mode(&self) -> ProcessMode {
// TODO: Detect JACK freewheeling and report it here
ProcessMode::Realtime
}
fn next_event(&mut self) -> Option<NoteEvent> {
nih_debug_assert_failure!("TODO: WrapperProcessContext::next_event()");

View file

@ -117,6 +117,10 @@ impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
&self.transport
}
fn process_mode(&self) -> crate::context::ProcessMode {
self.inner.current_process_mode.load()
}
fn next_event(&mut self) -> Option<NoteEvent> {
self.input_events_guard.pop_front()
}

View file

@ -16,7 +16,7 @@ use super::param_units::ParamUnits;
use super::util::{ObjectPtr, VstPtr, VST3_MIDI_PARAMS_END, VST3_MIDI_PARAMS_START};
use super::view::WrapperView;
use crate::buffer::Buffer;
use crate::context::Transport;
use crate::context::{ProcessMode, Transport};
use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
use crate::midi::{MidiConfig, NoteEvent};
use crate::param::internals::{ParamPtr, Params};
@ -67,6 +67,8 @@ pub(crate) struct WrapperInner<P: Vst3Plugin> {
/// The current buffer configuration, containing the sample rate and the maximum block size.
/// Will be set in `IAudioProcessor::setupProcessing()`.
pub current_buffer_config: AtomicCell<Option<BufferConfig>>,
/// The current audio processing mode. Set in `IAudioProcessor::setup_processing()`.
pub current_process_mode: AtomicCell<ProcessMode>,
/// The last process status returned by the plugin. This is used for tail handling.
pub last_process_status: AtomicCell<ProcessStatus>,
/// The current latency in samples, as set by the plugin through the [`ProcessContext`].
@ -277,6 +279,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
num_output_channels: P::DEFAULT_NUM_OUTPUTS,
}),
current_buffer_config: AtomicCell::new(None),
current_process_mode: AtomicCell::new(ProcessMode::Realtime),
last_process_status: AtomicCell::new(ProcessStatus::Normal),
current_latency: AtomicU32::new(0),
output_buffer: AtomicRefCell::new(Buffer::default()),

View file

@ -4,7 +4,7 @@ use std::mem::{self, MaybeUninit};
use std::ptr;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use vst3_com::vst::IProcessContextRequirementsFlags;
use vst3_com::vst::{IProcessContextRequirementsFlags, ProcessModes};
use vst3_sys::base::{kInvalidArgument, kNoInterface, kResultFalse, kResultOk, tresult, TBool};
use vst3_sys::base::{IBStream, IPluginBase};
use vst3_sys::utils::SharedVstPtr;
@ -23,7 +23,7 @@ use super::util::{
u16strlcpy, VstPtr, VST3_MIDI_CCS, VST3_MIDI_NUM_PARAMS, VST3_MIDI_PARAMS_START,
};
use super::view::WrapperView;
use crate::context::Transport;
use crate::context::{ProcessMode, Transport};
use crate::midi::{MidiConfig, NoteEvent};
use crate::param::ParamFlags;
use crate::plugin::{BufferConfig, BusConfig, ProcessStatus, Vst3Plugin};
@ -690,6 +690,17 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
max_buffer_size: setup.max_samples_per_block as u32,
}));
let mode = match setup.process_mode {
n if n == ProcessModes::kRealtime as i32 => ProcessMode::Realtime,
n if n == ProcessModes::kPrefetch as i32 => ProcessMode::Buffered,
n if n == ProcessModes::kOffline as i32 => ProcessMode::Offline,
n => {
nih_debug_assert_failure!("Unknown rendering mode '{}', defaulting to realtime", n);
ProcessMode::Realtime
}
};
self.inner.current_process_mode.store(mode);
// Initializing the plugin happens in `IAudioProcessor::set_active()` because the host may
// still change the channel layouts at this point