Skip to main content

truce_core/
config.rs

1//! Activation-time processing configuration.
2//!
3//! [`AudioConfig`] is the config struct handed to `reset` when the host
4//! (re)prepares the plugin. It carries the sample rate, the maximum block
5//! size, and the [`ProcessMode`] the host is driving audio with, so a
6//! plugin can size its buffers for an offline render before the first
7//! block arrives - allocation has to happen here, off the audio thread.
8
9/// How the host is driving audio through the plugin this activation.
10///
11/// Delivered two ways that answer two different questions.
12/// [`AudioConfig::process_mode`] at `reset` answers "how big should my
13/// buffers be for this render?" - the allocation-relevant question, since
14/// buffer sizing has to happen off the audio thread. The per-block
15/// `ProcessContext::process_mode` answers "may I skip the realtime
16/// discipline right now?" - it tracks host toggles that don't warrant a
17/// re-prepare (VST3 `kRealtime` <-> `kPrefetch`, an LV2 freewheel port).
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
19pub enum ProcessMode {
20    /// Fixed-rate realtime playback. Honor the no-alloc / no-lock rule.
21    #[default]
22    Realtime,
23    /// Real-time-like but processed ahead at irregular intervals to
24    /// loosen realtime pressure. Only VST3 (`kPrefetch`) produces this.
25    /// Treat it like [`Realtime`](Self::Realtime) unless there is a
26    /// specific reason to relax discipline.
27    Buffered,
28    /// Freewheeling offline render. No wall-clock deadline: a plugin may
29    /// allocate, raise oversampling, lengthen lookahead, and trade CPU
30    /// for quality.
31    Offline,
32}
33
34impl ProcessMode {
35    /// Whether the host is freewheeling with no realtime deadline. True
36    /// only for [`Offline`](Self::Offline) - the one mode where relaxing
37    /// the no-alloc / no-lock rule and raising quality is safe.
38    #[must_use]
39    pub fn is_offline(self) -> bool {
40        matches!(self, ProcessMode::Offline)
41    }
42
43    /// Discriminant for atomic storage. Wrappers whose offline signal
44    /// arrives on one thread (CLAP `render::set`, an AU property) and is
45    /// read on the audio thread stash the mode in an `AtomicU8`.
46    #[must_use]
47    pub fn as_u8(self) -> u8 {
48        match self {
49            ProcessMode::Realtime => 0,
50            ProcessMode::Buffered => 1,
51            ProcessMode::Offline => 2,
52        }
53    }
54
55    /// Inverse of [`Self::as_u8`]. Any unknown value maps to
56    /// [`Realtime`](Self::Realtime), the safe default.
57    #[must_use]
58    pub fn from_u8(v: u8) -> Self {
59        match v {
60            1 => ProcessMode::Buffered,
61            2 => ProcessMode::Offline,
62            _ => ProcessMode::Realtime,
63        }
64    }
65}
66
67/// Activation-time configuration handed to `reset`.
68///
69/// `#[non_exhaustive]` so future prepare-time fields (a bus layout, host
70/// latency budget) can be added without breaking the `reset` signature.
71/// Construct with [`Self::new`] (defaults to [`ProcessMode::Realtime`])
72/// plus [`Self::with_process_mode`]; read the fields directly.
73#[derive(Clone, Copy, Debug, PartialEq)]
74#[non_exhaustive]
75pub struct AudioConfig {
76    /// Host sample rate in Hz.
77    pub sample_rate: f64,
78    /// Largest block the host will hand `process`. Size preallocations
79    /// against this - the host never sends a bigger block this activation.
80    pub max_block_size: usize,
81    /// How the host drives audio this activation. See [`ProcessMode`].
82    pub process_mode: ProcessMode,
83}
84
85impl AudioConfig {
86    /// New config at the default [`ProcessMode::Realtime`]. Chain
87    /// [`Self::with_process_mode`] for an offline / buffered activation.
88    #[must_use]
89    pub fn new(sample_rate: f64, max_block_size: usize) -> Self {
90        Self {
91            sample_rate,
92            max_block_size,
93            process_mode: ProcessMode::Realtime,
94        }
95    }
96
97    /// Set the processing mode for this activation.
98    #[must_use]
99    pub fn with_process_mode(mut self, mode: ProcessMode) -> Self {
100        self.process_mode = mode;
101        self
102    }
103}