truce_core/process.rs
1use crate::config::ProcessMode;
2use crate::events::{EventList, TransportInfo};
3
4/// Per-block context handed to `process()`. Construct via
5/// [`Self::new`] + the `with_*` builders. Marked `#[non_exhaustive]`
6/// so adding host-populated fields in future (e.g. `host_latency`,
7/// `bus_routing`) isn't a `SemVer` break for downstream pre-1.0 callers.
8#[non_exhaustive]
9pub struct ProcessContext<'a> {
10 pub transport: &'a TransportInfo,
11 /// How the host is driving audio this block. Tracks host toggles
12 /// that don't force a re-prepare (VST3 `kRealtime` <-> `kPrefetch`,
13 /// an LV2 freewheel port). A plugin that reallocates for offline
14 /// keys off `AudioConfig::process_mode` at `reset` instead; this
15 /// field is for "may I relax realtime discipline right now?".
16 pub process_mode: ProcessMode,
17 pub sample_rate: f64,
18 pub block_size: usize,
19 pub output_events: &'a mut EventList,
20 params_fn: Option<&'a dyn Fn(u32) -> f64>,
21 meters_fn: Option<&'a dyn Fn(u32, f32)>,
22}
23
24impl<'a> ProcessContext<'a> {
25 pub fn new(
26 transport: &'a TransportInfo,
27 sample_rate: f64,
28 block_size: usize,
29 output_events: &'a mut EventList,
30 ) -> Self {
31 Self {
32 transport,
33 process_mode: ProcessMode::Realtime,
34 sample_rate,
35 block_size,
36 output_events,
37 params_fn: None,
38 meters_fn: None,
39 }
40 }
41
42 /// Set the processing mode for this block. Defaults to
43 /// [`ProcessMode::Realtime`]; wrappers stamp the live host mode.
44 #[must_use]
45 pub fn with_process_mode(mut self, mode: ProcessMode) -> Self {
46 self.process_mode = mode;
47 self
48 }
49
50 /// Set the parameter lookup callback.
51 #[must_use]
52 pub fn with_params(mut self, f: &'a dyn Fn(u32) -> f64) -> Self {
53 self.params_fn = Some(f);
54 self
55 }
56
57 /// Set the meter reporting callback.
58 #[must_use]
59 pub fn with_meters(mut self, f: &'a dyn Fn(u32, f32)) -> Self {
60 self.meters_fn = Some(f);
61 self
62 }
63
64 /// Read a parameter's plain value by ID.
65 ///
66 /// Returns `None` when no params callback is wired up (e.g. when a
67 /// plugin runs under the bare test driver without a `with_params`
68 /// closure). Callers that always run inside a real format wrapper
69 /// can `.unwrap_or_default()`. Distinguishing "no callback" from
70 /// "value is zero" lets test harnesses notice when they forgot to
71 /// wire up params rather than masking the misconfiguration as
72 /// "host set the value to zero".
73 #[must_use]
74 pub fn param(&self, id: u32) -> Option<f64> {
75 self.params_fn.map(|f| f(id))
76 }
77
78 /// Report a meter value (0.0 to 1.0).
79 pub fn set_meter(&self, id: impl Into<u32>, value: f32) {
80 let id = id.into();
81 if let Some(f) = self.meters_fn {
82 f(id, value);
83 }
84 }
85}
86
87#[derive(Clone, Copy, Debug, PartialEq)]
88pub enum ProcessStatus {
89 /// Plugin produced meaningful output.
90 Normal,
91 /// Plugin is producing tail. Value = remaining tail samples.
92 Tail(u32),
93 /// Keep alive even if input is silent.
94 KeepAlive,
95}