Skip to main content

beamer_core/
config.rs

1//! Shared plugin configuration.
2//!
3//! This module provides format-agnostic plugin metadata that is shared
4//! across all plugin formats (AU, VST3).
5//!
6//! Format-specific configurations (UIDs, FourCC codes, etc.) are defined
7//! in their respective crates.
8//!
9//! # Example
10//!
11//! ```ignore
12//! use beamer_core::{Config, Category};
13//!
14//! pub static CONFIG: Config = Config::new("My Plugin", Category::Effect)
15//!     .with_vendor("My Company")
16//!     .with_version("1.0.0");
17//! ```
18
19/// Plugin subcategory for more specific classification.
20///
21/// These map directly to VST3 subcategories and AU tags.
22/// Use with `Config::with_subcategories()` to specify plugin characteristics.
23///
24/// # Example
25///
26/// ```ignore
27/// pub static CONFIG: Config = Config::new("My Compressor", Category::Effect)
28///     .with_subcategories(&[Subcategory::Dynamics]);
29/// ```
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum Subcategory {
32    // === Effect Subcategories ===
33    /// Scope, FFT-Display, Loudness Processing
34    Analyzer,
35    /// Tools dedicated to Bass Guitar
36    Bass,
37    /// Channel strip tools
38    ChannelStrip,
39    /// Delay, Multi-tap Delay, Ping-Pong Delay
40    Delay,
41    /// Amp Simulator, Sub-Harmonic, SoftClipper
42    Distortion,
43    /// Tools dedicated to Drums
44    Drums,
45    /// Compressor, Expander, Gate, Limiter, Maximizer
46    Dynamics,
47    /// Equalization, Graphical EQ
48    Eq,
49    /// WahWah, ToneBooster, Specific Filter
50    Filter,
51    /// Tone Generator, Noise Generator
52    Generator,
53    /// Tools dedicated to Guitar
54    Guitar,
55    /// Dither, Noise Shaping
56    Mastering,
57    /// Tools dedicated to Microphone
58    Microphone,
59    /// Phaser, Flanger, Chorus, Tremolo, Vibrato, AutoPan
60    Modulation,
61    /// Network-based effects
62    Network,
63    /// Pitch Processing, Pitch Correction, Vocal Tuning
64    PitchShift,
65    /// Denoiser, Declicker
66    Restoration,
67    /// Reverberation, Room Simulation, Convolution Reverb
68    Reverb,
69    /// MonoToStereo, StereoEnhancer
70    Spatial,
71    /// LFE Splitter, Bass Manager
72    Surround,
73    /// Volume, Mixer, Tuner
74    Tools,
75    /// Tools dedicated to Vocals
76    Vocals,
77
78    // === Instrument Subcategories ===
79    /// Instrument for Drum sounds
80    Drum,
81    /// External wrapped hardware
82    External,
83    /// Instrument for Piano sounds
84    Piano,
85    /// Instrument based on Samples
86    Sampler,
87    /// Instrument based on Synthesis
88    Synth,
89
90    // === Channel Configuration ===
91    /// Mono only plug-in
92    Mono,
93    /// Stereo only plug-in
94    Stereo,
95    /// Ambisonics channel
96    Ambisonics,
97    /// Mixconverter, Up-Mixer, Down-Mixer
98    UpDownMix,
99
100    // === Processing Constraints ===
101    /// Supports only realtime processing
102    OnlyRealTime,
103    /// Offline processing only
104    OnlyOfflineProcess,
105    /// Works as normal insert plug-in only (no offline)
106    NoOfflineProcess,
107}
108
109impl Subcategory {
110    /// Get the VST3 subcategory string.
111    pub const fn to_vst3(&self) -> &'static str {
112        match self {
113            // Effect subcategories
114            Subcategory::Analyzer => "Analyzer",
115            Subcategory::Bass => "Bass",
116            Subcategory::ChannelStrip => "Channel Strip",
117            Subcategory::Delay => "Delay",
118            Subcategory::Distortion => "Distortion",
119            Subcategory::Drums => "Drums",
120            Subcategory::Dynamics => "Dynamics",
121            Subcategory::Eq => "EQ",
122            Subcategory::Filter => "Filter",
123            Subcategory::Generator => "Generator",
124            Subcategory::Guitar => "Guitar",
125            Subcategory::Mastering => "Mastering",
126            Subcategory::Microphone => "Microphone",
127            Subcategory::Modulation => "Modulation",
128            Subcategory::Network => "Network",
129            Subcategory::PitchShift => "Pitch Shift",
130            Subcategory::Restoration => "Restoration",
131            Subcategory::Reverb => "Reverb",
132            Subcategory::Spatial => "Spatial",
133            Subcategory::Surround => "Surround",
134            Subcategory::Tools => "Tools",
135            Subcategory::Vocals => "Vocals",
136            // Instrument subcategories
137            Subcategory::Drum => "Drum",
138            Subcategory::External => "External",
139            Subcategory::Piano => "Piano",
140            Subcategory::Sampler => "Sampler",
141            Subcategory::Synth => "Synth",
142            // Channel configuration
143            Subcategory::Mono => "Mono",
144            Subcategory::Stereo => "Stereo",
145            Subcategory::Ambisonics => "Ambisonics",
146            Subcategory::UpDownMix => "Up-Downmix",
147            // Processing constraints
148            Subcategory::OnlyRealTime => "OnlyRT",
149            Subcategory::OnlyOfflineProcess => "OnlyOfflineProcess",
150            Subcategory::NoOfflineProcess => "NoOfflineProcess",
151        }
152    }
153
154    /// Get the AU tag string.
155    ///
156    /// AU tags are simpler and don't have all VST3 distinctions.
157    /// Returns `None` for subcategories that don't map to AU tags.
158    pub const fn to_au_tag(&self) -> Option<&'static str> {
159        match self {
160            Subcategory::Analyzer => Some("Analyzer"),
161            Subcategory::Delay => Some("Delay"),
162            Subcategory::Distortion => Some("Distortion"),
163            Subcategory::Dynamics => Some("Dynamics"),
164            Subcategory::Eq => Some("EQ"),
165            Subcategory::Filter => Some("Filter"),
166            Subcategory::Mastering => Some("Mastering"),
167            Subcategory::Modulation => Some("Modulation"),
168            Subcategory::PitchShift => Some("Pitch Shift"),
169            Subcategory::Restoration => Some("Restoration"),
170            Subcategory::Reverb => Some("Reverb"),
171            Subcategory::Drum => Some("Drums"),
172            Subcategory::Sampler => Some("Sampler"),
173            Subcategory::Synth => Some("Synth"),
174            Subcategory::Piano => Some("Piano"),
175            Subcategory::Generator => Some("Generator"),
176            // These don't have direct AU tag equivalents
177            _ => None,
178        }
179    }
180}
181
182/// Plugin type - determines how hosts categorize and use the plugin.
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
184pub enum Category {
185    /// Audio effect (EQ, compressor, reverb, delay)
186    Effect,
187    /// Virtual instrument (synth, sampler, drum machine)
188    Instrument,
189    /// MIDI processor (arpeggiator, chord generator)
190    MidiEffect,
191    /// Audio generator (test tones, noise, file player)
192    Generator,
193}
194
195impl Category {
196    /// Convert to AU component type code (FourCC as u32, big-endian)
197    pub const fn to_au_component_type(&self) -> u32 {
198        match self {
199            Category::Effect => u32::from_be_bytes(*b"aufx"),
200            Category::Instrument => u32::from_be_bytes(*b"aumu"),
201            Category::MidiEffect => u32::from_be_bytes(*b"aumi"),
202            Category::Generator => u32::from_be_bytes(*b"augn"),
203        }
204    }
205
206    /// Convert to VST3 base category string
207    pub const fn to_vst3_category(&self) -> &'static str {
208        match self {
209            Category::Effect | Category::MidiEffect => "Fx",
210            Category::Instrument => "Instrument",
211            Category::Generator => "Generator",
212        }
213    }
214
215    /// Check if this type accepts MIDI input
216    pub const fn accepts_midi(&self) -> bool {
217        matches!(self, Category::Instrument | Category::MidiEffect)
218    }
219
220    /// Check if this type can produce MIDI output
221    pub const fn produces_midi(&self) -> bool {
222        matches!(self, Category::Instrument | Category::MidiEffect)
223    }
224}
225
226/// Format-agnostic plugin configuration.
227///
228/// Contains metadata shared across all plugin formats. Format-specific
229/// configurations (like VST3 UIDs or AU FourCC codes) are defined separately.
230#[derive(Debug, Clone)]
231pub struct Config {
232    /// Plugin name displayed in the DAW.
233    pub name: &'static str,
234
235    /// Plugin category (effect, instrument, etc.)
236    pub category: Category,
237
238    /// Vendor/company name.
239    pub vendor: &'static str,
240
241    /// Vendor URL.
242    pub url: &'static str,
243
244    /// Vendor email.
245    pub email: &'static str,
246
247    /// Plugin version string.
248    pub version: &'static str,
249
250    /// Whether this plugin has an editor/GUI.
251    pub has_editor: bool,
252
253    /// Plugin subcategories for more specific classification.
254    pub subcategories: &'static [Subcategory],
255}
256
257impl Config {
258    /// Create a new plugin configuration with default values.
259    ///
260    /// # Example
261    ///
262    /// ```ignore
263    /// pub static CONFIG: Config = Config::new("My Plugin", Category::Effect)
264    ///     .with_vendor("My Company")
265    ///     .with_version(env!("CARGO_PKG_VERSION"));
266    /// ```
267    pub const fn new(name: &'static str, category: Category) -> Self {
268        Self {
269            name,
270            category,
271            vendor: "Unknown Vendor",
272            url: "",
273            email: "",
274            version: "1.0.0",
275            has_editor: false,
276            subcategories: &[],
277        }
278    }
279
280    /// Set the vendor name.
281    pub const fn with_vendor(mut self, vendor: &'static str) -> Self {
282        self.vendor = vendor;
283        self
284    }
285
286    /// Set the vendor URL.
287    pub const fn with_url(mut self, url: &'static str) -> Self {
288        self.url = url;
289        self
290    }
291
292    /// Set the vendor email.
293    pub const fn with_email(mut self, email: &'static str) -> Self {
294        self.email = email;
295        self
296    }
297
298    /// Set the version string.
299    pub const fn with_version(mut self, version: &'static str) -> Self {
300        self.version = version;
301        self
302    }
303
304    /// Enable the editor/GUI.
305    pub const fn with_editor(mut self) -> Self {
306        self.has_editor = true;
307        self
308    }
309
310    /// Set the plugin subcategories.
311    ///
312    /// Subcategories provide more specific classification beyond the main category.
313    /// They are used for VST3 subcategory strings and AU tags.
314    ///
315    /// # Example
316    ///
317    /// ```ignore
318    /// pub static CONFIG: Config = Config::new("My Compressor", Category::Effect)
319    ///     .with_subcategories(&[Subcategory::Dynamics]);
320    /// ```
321    pub const fn with_subcategories(mut self, subcategories: &'static [Subcategory]) -> Self {
322        self.subcategories = subcategories;
323        self
324    }
325
326    /// Build the VST3 subcategories string.
327    ///
328    /// Combines the main category with subcategories using pipe separators.
329    /// For example: `Category::Effect` with `[Subcategory::Dynamics]` becomes `"Fx|Dynamics"`.
330    ///
331    /// # Example
332    ///
333    /// ```ignore
334    /// let config = Config::new("My Plugin", Category::Effect)
335    ///     .with_subcategories(&[Subcategory::Dynamics, Subcategory::Eq]);
336    /// assert_eq!(config.vst3_subcategories(), "Fx|Dynamics|EQ");
337    /// ```
338    pub fn vst3_subcategories(&self) -> String {
339        let mut result = String::from(self.category.to_vst3_category());
340        for sub in self.subcategories {
341            result.push('|');
342            result.push_str(sub.to_vst3());
343        }
344        result
345    }
346
347    /// Get AU tags derived from subcategories.
348    ///
349    /// Returns tags for subcategories that have AU equivalents.
350    /// Subcategories without AU mappings are skipped.
351    ///
352    /// # Example
353    ///
354    /// ```ignore
355    /// let config = Config::new("My Plugin", Category::Effect)
356    ///     .with_subcategories(&[Subcategory::Dynamics, Subcategory::Eq]);
357    /// assert_eq!(config.au_tags(), vec!["Dynamics", "EQ"]);
358    /// ```
359    pub fn au_tags(&self) -> Vec<&'static str> {
360        self.subcategories
361            .iter()
362            .filter_map(|sub| sub.to_au_tag())
363            .collect()
364    }
365}