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
63/// Resolve a format's display-name override. Each wrapper picks its
64/// own `<format>_name` field off `PluginInfo` and passes the result
65/// here along with the `PluginInfo::name` fallback. Empty overrides
66/// (unset or set to `""`) fall through to `fallback`.
67#[must_use]
68pub fn resolve_name_override(
69 override_value: Option<&'static str>,
70 fallback: &'static str,
71) -> &'static str {
72 match override_value {
73 Some(s) if !s.is_empty() => s,
74 _ => fallback,
75 }
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
79pub enum PluginCategory {
80 Effect,
81 Instrument,
82 /// MIDI note effect (e.g., transpose, arpeggiator). Processes MIDI events.
83 NoteEffect,
84 Analyzer,
85 Tool,
86}
87
88/// Convert a category string to [`PluginCategory`] at compile time.
89/// Used by the `plugin_info!()` macro.
90#[must_use]
91pub const fn category_from_str(s: &str) -> PluginCategory {
92 match s.as_bytes() {
93 b"Instrument" => PluginCategory::Instrument,
94 b"NoteEffect" => PluginCategory::NoteEffect,
95 b"Analyzer" => PluginCategory::Analyzer,
96 b"Tool" => PluginCategory::Tool,
97 _ => PluginCategory::Effect,
98 }
99}
100
101/// Helper to convert a 4-char string literal to `[u8; 4]` at compile time.
102/// Panics if the string is not exactly 4 ASCII bytes.
103///
104/// # Panics
105///
106/// Panics at compile time when used in a `const` context (preferred)
107/// or at runtime if `s.len() != 4`. ASCII-ness isn't checked here -
108/// callers that need it should validate separately.
109#[must_use]
110pub const fn fourcc(s: &[u8]) -> [u8; 4] {
111 assert!(s.len() == 4, "FourCC must be exactly 4 bytes");
112 [s[0], s[1], s[2], s[3]]
113}