diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index b9c96a1e..2ba9abc8 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -6,6 +6,14 @@ new and what's changed, this document lists all breaking changes in reverse chronological order. If a new feature did not require any changes to existing code then it will not be listed here. +## [2023-02-01] + +- The `Vst3Plugin::VST3_CATEGORIES` string constant has been replaced by a + `Vst3Plugin::VST3_SUBCATEGORIES` constant of type `&[Vst3SubCategory]`. + `Vst3SubCategory` is an enum containing all of VST3's predefined categories, + and it behaves similarly to the `ClapFeature` enum used for CLAP plugins. This + makes defining subcategories for VST3 plugins easier and less error prone. + ## [2023-01-31] - NIH-plug has gained support MIDI SysEx in a simple, type-safe, and diff --git a/plugins/buffr_glitch/src/lib.rs b/plugins/buffr_glitch/src/lib.rs index 62c8589c..5112d52a 100644 --- a/plugins/buffr_glitch/src/lib.rs +++ b/plugins/buffr_glitch/src/lib.rs @@ -431,6 +431,7 @@ impl ClapPlugin for BuffrGlitch { const CLAP_SUPPORT_URL: Option<&'static str> = None; const CLAP_FEATURES: &'static [ClapFeature] = &[ ClapFeature::AudioEffect, + ClapFeature::Synthesizer, ClapFeature::Stereo, ClapFeature::Glitch, ]; @@ -438,7 +439,11 @@ impl ClapPlugin for BuffrGlitch { impl Vst3Plugin for BuffrGlitch { const VST3_CLASS_ID: [u8; 16] = *b"BuffrGlitch.RvdH"; - const VST3_CATEGORIES: &'static str = "Fx"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[ + Vst3SubCategory::Fx, + Vst3SubCategory::Synth, + Vst3SubCategory::Custom("Glitch"), + ]; } nih_export_clap!(BuffrGlitch); diff --git a/plugins/crisp/src/lib.rs b/plugins/crisp/src/lib.rs index e37f0ad8..8f4b51ea 100644 --- a/plugins/crisp/src/lib.rs +++ b/plugins/crisp/src/lib.rs @@ -507,7 +507,11 @@ impl ClapPlugin for Crisp { impl Vst3Plugin for Crisp { const VST3_CLASS_ID: [u8; 16] = *b"CrispPluginRvdH."; - const VST3_CATEGORIES: &'static str = "Fx|Filter|Distortion"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[ + Vst3SubCategory::Fx, + Vst3SubCategory::Filter, + Vst3SubCategory::Distortion, + ]; } nih_export_clap!(Crisp); diff --git a/plugins/crossover/src/lib.rs b/plugins/crossover/src/lib.rs index 6cacb21a..670946cd 100644 --- a/plugins/crossover/src/lib.rs +++ b/plugins/crossover/src/lib.rs @@ -401,7 +401,8 @@ impl ClapPlugin for Crossover { impl Vst3Plugin for Crossover { const VST3_CLASS_ID: [u8; 16] = *b"CrossoverRvdH..."; - const VST3_CATEGORIES: &'static str = "Fx|Tools"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::Tools]; } nih_export_clap!(Crossover); diff --git a/plugins/diopser/src/lib.rs b/plugins/diopser/src/lib.rs index 1e6e7a3c..041c16bb 100644 --- a/plugins/diopser/src/lib.rs +++ b/plugins/diopser/src/lib.rs @@ -355,7 +355,8 @@ impl ClapPlugin for Diopser { impl Vst3Plugin for Diopser { const VST3_CLASS_ID: [u8; 16] = *b"DiopserPlugRvdH."; - const VST3_CATEGORIES: &'static str = "Fx|Filter"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::Filter]; } nih_export_clap!(Diopser); diff --git a/plugins/examples/gain/src/lib.rs b/plugins/examples/gain/src/lib.rs index 09a3b569..b53403a5 100644 --- a/plugins/examples/gain/src/lib.rs +++ b/plugins/examples/gain/src/lib.rs @@ -195,7 +195,8 @@ impl ClapPlugin for Gain { impl Vst3Plugin for Gain { const VST3_CLASS_ID: [u8; 16] = *b"GainMoistestPlug"; - const VST3_CATEGORIES: &'static str = "Fx|Dynamics"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::Tools]; } nih_export_clap!(Gain); diff --git a/plugins/examples/gain_gui_egui/src/lib.rs b/plugins/examples/gain_gui_egui/src/lib.rs index abe4fb17..99e91bcb 100644 --- a/plugins/examples/gain_gui_egui/src/lib.rs +++ b/plugins/examples/gain_gui_egui/src/lib.rs @@ -225,7 +225,8 @@ impl ClapPlugin for Gain { impl Vst3Plugin for Gain { const VST3_CLASS_ID: [u8; 16] = *b"GainGuiYeahBoyyy"; - const VST3_CATEGORIES: &'static str = "Fx|Dynamics"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::Tools]; } nih_export_clap!(Gain); diff --git a/plugins/examples/gain_gui_iced/src/lib.rs b/plugins/examples/gain_gui_iced/src/lib.rs index 2653e3b8..63de5197 100644 --- a/plugins/examples/gain_gui_iced/src/lib.rs +++ b/plugins/examples/gain_gui_iced/src/lib.rs @@ -167,7 +167,8 @@ impl ClapPlugin for Gain { impl Vst3Plugin for Gain { const VST3_CLASS_ID: [u8; 16] = *b"GainGuiIcedAaAAa"; - const VST3_CATEGORIES: &'static str = "Fx|Dynamics"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::Tools]; } nih_export_clap!(Gain); diff --git a/plugins/examples/gain_gui_vizia/src/lib.rs b/plugins/examples/gain_gui_vizia/src/lib.rs index a7ad83f9..0e1cfc3e 100644 --- a/plugins/examples/gain_gui_vizia/src/lib.rs +++ b/plugins/examples/gain_gui_vizia/src/lib.rs @@ -166,7 +166,8 @@ impl ClapPlugin for Gain { impl Vst3Plugin for Gain { const VST3_CLASS_ID: [u8; 16] = *b"GainGuiVIIIZIAAA"; - const VST3_CATEGORIES: &'static str = "Fx|Dynamics"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::Tools]; } nih_export_clap!(Gain); diff --git a/plugins/examples/midi_inverter/src/lib.rs b/plugins/examples/midi_inverter/src/lib.rs index d87bd0e0..271494d2 100644 --- a/plugins/examples/midi_inverter/src/lib.rs +++ b/plugins/examples/midi_inverter/src/lib.rs @@ -227,7 +227,8 @@ impl ClapPlugin for MidiInverter { impl Vst3Plugin for MidiInverter { const VST3_CLASS_ID: [u8; 16] = *b"M1d1Inv3r70rzAaA"; - const VST3_CATEGORIES: &'static str = "Instrument|Tools"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Instrument, Vst3SubCategory::Tools]; } nih_export_clap!(MidiInverter); diff --git a/plugins/examples/poly_mod_synth/src/lib.rs b/plugins/examples/poly_mod_synth/src/lib.rs index 94c6c0c1..5527ae1c 100644 --- a/plugins/examples/poly_mod_synth/src/lib.rs +++ b/plugins/examples/poly_mod_synth/src/lib.rs @@ -612,7 +612,8 @@ impl ClapPlugin for PolyModSynth { // modulation impl Vst3Plugin for PolyModSynth { const VST3_CLASS_ID: [u8; 16] = *b"PolyM0dSynth1337"; - const VST3_CATEGORIES: &'static str = "Instrument|Synth"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Instrument, Vst3SubCategory::Synth]; } nih_export_clap!(PolyModSynth); diff --git a/plugins/examples/sine/src/lib.rs b/plugins/examples/sine/src/lib.rs index 4936c2fd..4d2d4597 100644 --- a/plugins/examples/sine/src/lib.rs +++ b/plugins/examples/sine/src/lib.rs @@ -213,7 +213,11 @@ impl ClapPlugin for Sine { impl Vst3Plugin for Sine { const VST3_CLASS_ID: [u8; 16] = *b"SineMoistestPlug"; - const VST3_CATEGORIES: &'static str = "Instrument|Synth|Tools"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[ + Vst3SubCategory::Instrument, + Vst3SubCategory::Synth, + Vst3SubCategory::Tools, + ]; } nih_export_clap!(Sine); diff --git a/plugins/examples/stft/src/lib.rs b/plugins/examples/stft/src/lib.rs index 066e2805..cf106817 100644 --- a/plugins/examples/stft/src/lib.rs +++ b/plugins/examples/stft/src/lib.rs @@ -179,7 +179,8 @@ impl ClapPlugin for Stft { impl Vst3Plugin for Stft { const VST3_CLASS_ID: [u8; 16] = *b"StftMoistestPlug"; - const VST3_CATEGORIES: &'static str = "Fx|Tools"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::Tools]; } nih_export_clap!(Stft); diff --git a/plugins/examples/sysex/src/lib.rs b/plugins/examples/sysex/src/lib.rs index ee7b7149..8aa7aff3 100644 --- a/plugins/examples/sysex/src/lib.rs +++ b/plugins/examples/sysex/src/lib.rs @@ -102,7 +102,11 @@ impl ClapPlugin for SysEx { impl Vst3Plugin for SysEx { const VST3_CLASS_ID: [u8; 16] = *b"SysExCoolPluginn"; - const VST3_CATEGORIES: &'static str = "Fx|Instrument|Tools"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[ + Vst3SubCategory::Fx, + Vst3SubCategory::Instrument, + Vst3SubCategory::Tools, + ]; } nih_export_clap!(SysEx); diff --git a/plugins/loudness_war_winner/src/lib.rs b/plugins/loudness_war_winner/src/lib.rs index ee710fcd..f20d01f9 100644 --- a/plugins/loudness_war_winner/src/lib.rs +++ b/plugins/loudness_war_winner/src/lib.rs @@ -261,7 +261,12 @@ impl ClapPlugin for LoudnessWarWinner { impl Vst3Plugin for LoudnessWarWinner { const VST3_CLASS_ID: [u8; 16] = *b"LoudnessWar.RvdH"; - const VST3_CATEGORIES: &'static str = "Fx|Dynamics|Distortion"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[ + Vst3SubCategory::Fx, + Vst3SubCategory::Dynamics, + Vst3SubCategory::Distortion, + Vst3SubCategory::Custom("Pain"), + ]; } nih_export_clap!(LoudnessWarWinner); diff --git a/plugins/puberty_simulator/src/lib.rs b/plugins/puberty_simulator/src/lib.rs index 72c10cb2..a049f022 100644 --- a/plugins/puberty_simulator/src/lib.rs +++ b/plugins/puberty_simulator/src/lib.rs @@ -431,7 +431,8 @@ impl ClapPlugin for PubertySimulator { impl Vst3Plugin for PubertySimulator { const VST3_CLASS_ID: [u8; 16] = *b"PubertySim..RvdH"; - const VST3_CATEGORIES: &'static str = "Fx|Pitch Shift"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::PitchShift]; } nih_export_clap!(PubertySimulator); diff --git a/plugins/safety_limiter/src/lib.rs b/plugins/safety_limiter/src/lib.rs index 876598b8..0c949753 100644 --- a/plugins/safety_limiter/src/lib.rs +++ b/plugins/safety_limiter/src/lib.rs @@ -340,7 +340,8 @@ impl ClapPlugin for SafetyLimiter { impl Vst3Plugin for SafetyLimiter { const VST3_CLASS_ID: [u8; 16] = *b"SafetyLimtrRvdH."; - const VST3_CATEGORIES: &'static str = "Fx|Tools"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = + &[Vst3SubCategory::Fx, Vst3SubCategory::Tools]; } nih_export_clap!(SafetyLimiter); diff --git a/plugins/spectral_compressor/src/lib.rs b/plugins/spectral_compressor/src/lib.rs index a73e2c1a..d0fbf87e 100644 --- a/plugins/spectral_compressor/src/lib.rs +++ b/plugins/spectral_compressor/src/lib.rs @@ -581,7 +581,11 @@ impl ClapPlugin for SpectralCompressor { impl Vst3Plugin for SpectralCompressor { const VST3_CLASS_ID: [u8; 16] = *b"SpectrlComprRvdH"; - const VST3_CATEGORIES: &'static str = "Fx|Dynamics|Spectral"; + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[ + Vst3SubCategory::Fx, + Vst3SubCategory::Dynamics, + Vst3SubCategory::Custom("Spectral"), + ]; } nih_export_clap!(SpectralCompressor); diff --git a/src/plugin.rs b/src/plugin.rs index 7ae04a6b..ce90336d 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -12,6 +12,7 @@ use crate::midi::MidiConfig; use crate::params::Params; use crate::wrapper::clap::features::ClapFeature; use crate::wrapper::state::PluginState; +pub use crate::wrapper::vst3::subcategories::Vst3SubCategory; /// A function that can execute a plugin's [`BackgroundTask`][Plugin::BackgroundTask]s. A plugin can /// dispatch these tasks from the `initialize()` function, the `process()` function, or the GUI, so @@ -249,12 +250,11 @@ pub trait Vst3Plugin: Plugin { /// /// This will be shuffled into a different byte order on Windows for project-compatibility. const VST3_CLASS_ID: [u8; 16]; - /// One or more categories, separated by pipe characters (`|`), up to 127 characters. Anything - /// longer than that will be truncated. See the VST3 SDK for examples of common categories: - /// - // - // TODO: Create a category enum similar to ClapFeature - const VST3_CATEGORIES: &'static str; + /// One or more subcategories. The host may use these to categorize the plugin. Internally this + /// slice will be converted to a string where each character is separated by a pipe character + /// (`|`). This string has a limit of 127 characters, and anything longer than that will be + /// truncated. + const VST3_SUBCATEGORIES: &'static [Vst3SubCategory]; /// [`VST3_CLASS_ID`][Self::VST3_CLASS_ID`] in the correct order for the current platform so /// projects and presets can be shared between platforms. This should not be overridden. diff --git a/src/prelude.rs b/src/prelude.rs index 9884ab3e..5d2112bf 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -30,3 +30,4 @@ pub use crate::plugin::{ }; pub use crate::wrapper::clap::features::ClapFeature; pub use crate::wrapper::state::PluginState; +pub use crate::wrapper::vst3::subcategories::Vst3SubCategory; diff --git a/src/wrapper/clap/features.rs b/src/wrapper/clap/features.rs index ab95e8b2..79e81896 100644 --- a/src/wrapper/clap/features.rs +++ b/src/wrapper/clap/features.rs @@ -4,6 +4,7 @@ /// A keyword for a CLAP plugin. See /// for more /// information. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ClapFeature { // These are the main categories, every plugin should have at least one of these Instrument, diff --git a/src/wrapper/vst3/factory.rs b/src/wrapper/vst3/factory.rs index ee9d38e6..9e66e7e9 100644 --- a/src/wrapper/vst3/factory.rs +++ b/src/wrapper/vst3/factory.rs @@ -8,6 +8,7 @@ use vst3_sys::VST3; // Alias needed for the VST3 attribute macro use vst3_sys as vst3_com; +use super::subcategories::Vst3SubCategory; use super::util::u16strlcpy; use super::wrapper::Wrapper; use crate::plugin::Vst3Plugin; @@ -102,20 +103,7 @@ impl IPluginFactory2 for Factory

{ strlcpy(&mut info.category, "Audio Module Class"); strlcpy(&mut info.name, P::NAME); info.class_flags = 1 << 1; // kSimpleModeSupported - - // No idea if any hosts do something with this, but it's part of VST3's example categories - // list - if P::HARD_REALTIME_ONLY { - nih_debug_assert!(!P::VST3_CATEGORIES.ends_with('|')); - nih_debug_assert!(!P::VST3_CATEGORIES.contains("OnlyRT")); - strlcpy( - &mut info.subcategories, - &format!("{}|OnlyRT", P::VST3_CATEGORIES), - ); - } else { - strlcpy(&mut info.subcategories, P::VST3_CATEGORIES); - }; - + strlcpy(&mut info.subcategories, &make_subcategories_string::

()); strlcpy(&mut info.vendor, P::VENDOR); strlcpy(&mut info.version, P::VERSION); strlcpy(&mut info.sdk_version, VST3_SDK_VERSION); @@ -142,7 +130,7 @@ impl IPluginFactory3 for Factory

{ strlcpy(&mut info.category, "Audio Module Class"); u16strlcpy(&mut info.name, P::NAME); info.class_flags = 1 << 1; // kSimpleModeSupported - strlcpy(&mut info.subcategories, P::VST3_CATEGORIES); + strlcpy(&mut info.subcategories, &make_subcategories_string::

()); u16strlcpy(&mut info.vendor, P::VENDOR); u16strlcpy(&mut info.version, P::VERSION); u16strlcpy(&mut info.sdk_version, VST3_SDK_VERSION); @@ -155,3 +143,20 @@ impl IPluginFactory3 for Factory

{ kResultOk } } + +fn make_subcategories_string() -> String { + // No idea if any hosts do something with OnlyRT, but it's part of VST3's example categories + // list. Plugins should not be adding this feature manually + nih_debug_assert!(!P::VST3_SUBCATEGORIES.contains(&Vst3SubCategory::Custom("OnlyRT"))); + let category_string = P::VST3_SUBCATEGORIES + .iter() + .map(Vst3SubCategory::as_str) + .collect::>() + .join("|"); + + if P::HARD_REALTIME_ONLY { + format!("{category_string}|OnlyRT") + } else { + category_string + } +} diff --git a/src/wrapper/vst3/subcategories.rs b/src/wrapper/vst3/subcategories.rs index f32fd648..a1f95e88 100644 --- a/src/wrapper/vst3/subcategories.rs +++ b/src/wrapper/vst3/subcategories.rs @@ -5,6 +5,7 @@ /// /// for a list of all predefined subcategories. Multiple subcategories are concatenated to a string /// separated by pipe characters, and the total length of this string may not exceed 127 characters. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Vst3SubCategory { // These are the main categories, every plugin should have at least one of these, I think Fx,