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 /// VST3 "Plugin Type Categories" secondary token. The wrapper
27 /// emits this after the primary token (`Fx|<sub>`,
28 /// `Instrument|<sub>`) so hosts like Cubase route to the right
29 /// submenu. Values from the VST3 SDK list: `Delay`, `Distortion`,
30 /// `Dynamics`, `EQ`, `Filter`, `Mastering`, `Modulation`,
31 /// `Pitch Shift`, `Restoration`, `Reverb`, `Analyzer`, `Tools`,
32 /// `Surround`. Optional; when `None` only the primary token is
33 /// emitted and Cubase will fall back to "Other".
34 pub vst3_subcategory: Option<&'static str>,
35
36 /// Per-format display-name overrides, populated by
37 /// `truce::plugin_info!()` from the matching `truce.toml` keys.
38 /// Format wrappers fall back to `name` when the override is `None`.
39 /// Baked at compile time so back-to-back plugin builds with
40 /// different overrides don't invalidate the format wrapper's
41 /// build fingerprint.
42 ///
43 /// `au3_name` is exposed for parity with the other formats and
44 /// for user introspection, but `truce-au`'s `resolved_plugin_name`
45 /// reads `au_name` for both v2 and v3 builds - the v3 host's
46 /// displayed label comes from the appex `Info.plist`'s `AUNAME`
47 /// (which `cargo truce install --au3` populates from `au3_name`),
48 /// not from `g_descriptor->name`.
49 /// `[plugin.presets]` `user_dir` from `truce.toml`: replaces the
50 /// `truce/<vendor>/<plugin>` subpath of the user-scope preset
51 /// root. `truce_utils::presets::user_preset_root` documents
52 /// where the path resolves on each OS. Effectively permanent
53 /// once a plugin ships - changing it orphans previously saved
54 /// user presets and packs.
55 pub preset_user_dir: Option<&'static str>,
56
57 pub vst3_name: Option<&'static str>,
58 pub clap_name: Option<&'static str>,
59 pub vst2_name: Option<&'static str>,
60 pub au_name: Option<&'static str>,
61 pub au3_name: Option<&'static str>,
62 pub aax_name: Option<&'static str>,
63 pub lv2_name: Option<&'static str>,
64
65 /// Standalone-only. Format wrappers MUST NOT read this - it
66 /// exists for preview hosts (truce-standalone, the iOS `AUv3`
67 /// container app) that need a TOML-driven way to mute the
68 /// plug-in's audio output while keeping `process()` ticking, so
69 /// editors that visualise an input signal (analyzers, tuners,
70 /// spectrum displays) update from mic / file input without
71 /// closing a mic → speakers feedback loop. Set from
72 /// `mute_preview_output` in `truce.toml`. Real DAW hosts own
73 /// their own output graph; consulting this flag from a wrapper
74 /// would let plug-in authors silence the DAW's mix bus, which
75 /// is never what they want.
76 #[doc(hidden)]
77 pub mute_preview_output: bool,
78
79 /// Sample-accurate automation chunking tunables. Read by the
80 /// `chunked_process::process_chunked` helper that every format
81 /// wrapper routes `process()` through. Populated by
82 /// `truce::plugin_info!()` from `truce.toml`'s `[automation]`
83 /// table; defaults to [`AutomationConfig::DEFAULT`] when the
84 /// table is absent.
85 pub automation: AutomationConfig,
86}
87
88/// Sample-accurate chunking tunables baked into [`PluginInfo`] at
89/// compile time. Mirrors `truce_build::AutomationConfig` (the
90/// derive-time view of the same TOML key) but lives in `truce-core`
91/// so wrappers can read it without a `truce-build` dep.
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub struct AutomationConfig {
94 /// Smallest sub-block size in samples. The chunker only splits
95 /// the audio block at split-eligible events whose `sample_offset`
96 /// is at least `block_start + min_subblock_samples` past the
97 /// current sub-block start; closer events are coalesced. Default
98 /// 32 (set via [`AutomationConfig::DEFAULT`]).
99 pub min_subblock_samples: u32,
100}
101
102impl AutomationConfig {
103 /// Default used when `truce.toml` omits the `[automation]` table.
104 pub const DEFAULT: Self = Self {
105 min_subblock_samples: 32,
106 };
107}
108
109impl Default for AutomationConfig {
110 fn default() -> Self {
111 Self::DEFAULT
112 }
113}
114
115/// Resolve a format's display-name override. Each wrapper picks its
116/// own `<format>_name` field off `PluginInfo` and passes the result
117/// here along with the `PluginInfo::name` fallback. Empty overrides
118/// (unset or set to `""`) fall through to `fallback`.
119#[must_use]
120pub fn resolve_name_override(
121 override_value: Option<&'static str>,
122 fallback: &'static str,
123) -> &'static str {
124 match override_value {
125 Some(s) if !s.is_empty() => s,
126 _ => fallback,
127 }
128}
129
130#[derive(Clone, Copy, Debug, PartialEq, Eq)]
131pub enum PluginCategory {
132 Effect,
133 Instrument,
134 /// MIDI note effect (e.g., transpose, arpeggiator). Processes MIDI events.
135 NoteEffect,
136 Analyzer,
137 Tool,
138}
139
140/// Convert a category string to [`PluginCategory`] at compile time.
141/// Used by the `plugin_info!()` macro.
142#[must_use]
143pub const fn category_from_str(s: &str) -> PluginCategory {
144 match s.as_bytes() {
145 b"Instrument" => PluginCategory::Instrument,
146 b"NoteEffect" => PluginCategory::NoteEffect,
147 b"Analyzer" => PluginCategory::Analyzer,
148 b"Tool" => PluginCategory::Tool,
149 _ => PluginCategory::Effect,
150 }
151}
152
153/// Helper to convert a 4-char string literal to `[u8; 4]` at compile time.
154/// Panics if the string is not exactly 4 ASCII bytes.
155///
156/// # Panics
157///
158/// Panics at compile time when used in a `const` context (preferred)
159/// or at runtime if `s.len() != 4`. ASCII-ness isn't checked here -
160/// callers that need it should validate separately.
161#[must_use]
162pub const fn fourcc(s: &[u8]) -> [u8; 4] {
163 assert!(s.len() == 4, "FourCC must be exactly 4 bytes");
164 [s[0], s[1], s[2], s[3]]
165}