Skip to main content

truce_core/
info.rs

1/// Static metadata about a plugin.
2#[derive(Clone, Debug)]
3pub struct PluginInfo {
4    pub name: &'static str,
5    pub vendor: &'static str,
6    pub url: &'static str,
7    pub version: &'static str,
8    pub category: PluginCategory,
9
10    /// Short identifier (`bundle_id` in `truce.toml`). Used to derive
11    /// the LV2 plugin URI (`{vendor.url}/lv2/{bundle_id}`); also a
12    /// stable, vendor-agnostic key for "this plugin" that doesn't
13    /// drift with display-name changes the way `clap_id` does.
14    pub bundle_id: &'static str,
15
16    // Format-specific IDs
17    pub vst3_id: &'static str,
18    pub clap_id: &'static str,
19    pub fourcc: [u8; 4],
20    pub au_type: [u8; 4],
21    pub au_manufacturer: [u8; 4],
22    pub aax_id: Option<&'static str>,
23    /// AAX plugin category string (e.g. "EQ", "Dynamics", "Reverb").
24    /// Maps to `AAX_ePlugInCategory` constants.
25    pub aax_category: Option<&'static str>,
26
27    /// Per-format display-name overrides, populated by
28    /// `truce::plugin_info!()` from the matching `truce.toml` keys.
29    /// Format wrappers fall back to `name` when the override is `None`.
30    /// Baked at compile time so back-to-back plugin builds with
31    /// different overrides don't invalidate the format wrapper's
32    /// build fingerprint.
33    ///
34    /// `au3_name` is exposed for parity with the other formats and
35    /// for user introspection, but `truce-au`'s `resolved_plugin_name`
36    /// reads `au_name` for both v2 and v3 builds - the v3 host's
37    /// displayed label comes from the appex `Info.plist`'s `AUNAME`
38    /// (which `cargo truce install --au3` populates from `au3_name`),
39    /// not from `g_descriptor->name`.
40    pub vst3_name: Option<&'static str>,
41    pub clap_name: Option<&'static str>,
42    pub vst2_name: Option<&'static str>,
43    pub au_name: Option<&'static str>,
44    pub au3_name: Option<&'static str>,
45    pub aax_name: Option<&'static str>,
46    pub lv2_name: Option<&'static str>,
47
48    /// Standalone-only. Format wrappers MUST NOT read this - it
49    /// exists for preview hosts (truce-standalone, the iOS `AUv3`
50    /// container app) that need a TOML-driven way to mute the
51    /// plug-in's audio output while keeping `process()` ticking, so
52    /// editors that visualise an input signal (analyzers, tuners,
53    /// spectrum displays) update from mic / file input without
54    /// closing a mic → speakers feedback loop. Set from
55    /// `mute_preview_output` in `truce.toml`. Real DAW hosts own
56    /// their own output graph; consulting this flag from a wrapper
57    /// would let plug-in authors silence the DAW's mix bus, which
58    /// is never what they want.
59    #[doc(hidden)]
60    pub mute_preview_output: bool,
61
62    /// Sample-accurate automation chunking tunables. Read by the
63    /// `chunked_process::process_chunked` helper that every format
64    /// wrapper routes `process()` through. Populated by
65    /// `truce::plugin_info!()` from `truce.toml`'s `[automation]`
66    /// table; defaults to [`AutomationConfig::DEFAULT`] when the
67    /// table is absent.
68    pub automation: AutomationConfig,
69}
70
71/// Sample-accurate chunking tunables baked into [`PluginInfo`] at
72/// compile time. Mirrors `truce_build::AutomationConfig` (the
73/// derive-time view of the same TOML key) but lives in `truce-core`
74/// so wrappers can read it without a `truce-build` dep.
75///
76/// See `truce-docs/docs/internal/parameter-dependent-chunking.md`.
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct AutomationConfig {
79    /// Smallest sub-block size in samples. The chunker only splits
80    /// the audio block at split-eligible events whose `sample_offset`
81    /// is at least `block_start + min_subblock_samples` past the
82    /// current sub-block start; closer events are coalesced. Default
83    /// 32 (set via [`AutomationConfig::DEFAULT`]).
84    pub min_subblock_samples: u32,
85}
86
87impl AutomationConfig {
88    /// Default used when `truce.toml` omits the `[automation]` table.
89    pub const DEFAULT: Self = Self {
90        min_subblock_samples: 32,
91    };
92}
93
94impl Default for AutomationConfig {
95    fn default() -> Self {
96        Self::DEFAULT
97    }
98}
99
100/// Resolve a format's display-name override. Each wrapper picks its
101/// own `<format>_name` field off `PluginInfo` and passes the result
102/// here along with the `PluginInfo::name` fallback. Empty overrides
103/// (unset or set to `""`) fall through to `fallback`.
104#[must_use]
105pub fn resolve_name_override(
106    override_value: Option<&'static str>,
107    fallback: &'static str,
108) -> &'static str {
109    match override_value {
110        Some(s) if !s.is_empty() => s,
111        _ => fallback,
112    }
113}
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116pub enum PluginCategory {
117    Effect,
118    Instrument,
119    /// MIDI note effect (e.g., transpose, arpeggiator). Processes MIDI events.
120    NoteEffect,
121    Analyzer,
122    Tool,
123}
124
125/// Convert a category string to [`PluginCategory`] at compile time.
126/// Used by the `plugin_info!()` macro.
127#[must_use]
128pub const fn category_from_str(s: &str) -> PluginCategory {
129    match s.as_bytes() {
130        b"Instrument" => PluginCategory::Instrument,
131        b"NoteEffect" => PluginCategory::NoteEffect,
132        b"Analyzer" => PluginCategory::Analyzer,
133        b"Tool" => PluginCategory::Tool,
134        _ => PluginCategory::Effect,
135    }
136}
137
138/// Helper to convert a 4-char string literal to `[u8; 4]` at compile time.
139/// Panics if the string is not exactly 4 ASCII bytes.
140///
141/// # Panics
142///
143/// Panics at compile time when used in a `const` context (preferred)
144/// or at runtime if `s.len() != 4`. ASCII-ness isn't checked here -
145/// callers that need it should validate separately.
146#[must_use]
147pub const fn fourcc(s: &[u8]) -> [u8; 4] {
148    assert!(s.len() == 4, "FourCC must be exactly 4 bytes");
149    [s[0], s[1], s[2], s[3]]
150}