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}