This repository has no description
1use super::remote_probe::RemoteProbe;
2use nih_plug::prelude::*;
3use rand::Rng;
4use std::sync::Arc;
5
6pub struct ShapemakerVST {
7 params: Arc<ShapemakerVSTParams>,
8 probe: RemoteProbe,
9}
10
11#[derive(Params)]
12struct ShapemakerVSTParams {
13 /// Used to send automation data to Shapemaker
14 #[id = "param1"]
15 pub param1: FloatParam,
16 #[id = "param2"]
17 pub param2: FloatParam,
18 #[id = "param3"]
19 pub param3: FloatParam,
20 #[id = "param4"]
21 pub param4: FloatParam,
22 #[id = "param5"]
23 pub param5: FloatParam,
24 #[id = "param6"]
25 pub param6: FloatParam,
26 #[id = "param7"]
27 pub param7: FloatParam,
28 #[id = "param8"]
29 pub param8: FloatParam,
30 #[id = "param9"]
31 pub param9: FloatParam,
32}
33
34impl Default for ShapemakerVST {
35 fn default() -> Self {
36 let probe_id = rand::rng().random_range(1..=u32::MAX);
37 Self {
38 params: Arc::new(ShapemakerVSTParams::default()),
39 probe: RemoteProbe::new(probe_id),
40 }
41 }
42}
43
44impl Default for ShapemakerVSTParams {
45 fn default() -> Self {
46 let paramdef = |id: usize| {
47 FloatParam::new(
48 format!("Param #{}", id),
49 0.0,
50 FloatRange::Linear { min: 0.0, max: 1.1 },
51 )
52 };
53 Self {
54 param1: paramdef(1),
55 param2: paramdef(2),
56 param3: paramdef(3),
57 param4: paramdef(4),
58 param5: paramdef(5),
59 param6: paramdef(6),
60 param7: paramdef(7),
61 param8: paramdef(8),
62 param9: paramdef(9),
63 }
64 }
65}
66
67impl Plugin for ShapemakerVST {
68 const NAME: &'static str = "Shapemaker VST b3";
69 const VENDOR: &'static str = "Gwenn Le Bihan";
70 const URL: &'static str = env!("CARGO_PKG_HOMEPAGE");
71 const EMAIL: &'static str = "gwenn.lebihan7@gmail.com";
72
73 const VERSION: &'static str = env!("CARGO_PKG_VERSION");
74
75 // The first audio IO layout is used as the default. The other layouts may be selected either
76 // explicitly or automatically by the host or the user depending on the plugin API/backend.
77 const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[AudioIOLayout {
78 main_input_channels: NonZeroU32::new(2),
79 main_output_channels: NonZeroU32::new(2),
80
81 aux_input_ports: &[],
82 aux_output_ports: &[],
83
84 // Individual ports and the layout as a whole can be named here. By default these names
85 // are generated as needed. This layout will be called 'Stereo', while a layout with
86 // only one input and output channel would be called 'Mono'.
87 names: PortNames::const_default(),
88 }];
89
90 const MIDI_INPUT: MidiConfig = MidiConfig::MidiCCs;
91 const MIDI_OUTPUT: MidiConfig = MidiConfig::None;
92
93 const SAMPLE_ACCURATE_AUTOMATION: bool = true;
94
95 // If the plugin can send or receive SysEx messages, it can define a type to wrap around those
96 // messages here. The type implements the `SysExMessage` trait, which allows conversion to and
97 // from plain byte buffers.
98 type SysExMessage = ();
99 // More advanced plugins can use this to run expensive background tasks. See the field's
100 // documentation for more information. `()` means that the plugin does not have any background
101 // tasks.
102 type BackgroundTask = ();
103
104 fn params(&self) -> Arc<dyn Params> {
105 self.params.clone()
106 }
107
108 fn initialize(
109 &mut self,
110 _audio_io_layout: &AudioIOLayout,
111 _buffer_config: &BufferConfig,
112 _context: &mut impl InitContext<Self>,
113 ) -> bool {
114 let _ = self.probe.register();
115 true
116 }
117
118 fn reset(&mut self) {
119 // Reset buffers and envelopes here. This can be called from the audio thread and may not
120 // allocate. You can remove this function if you do not need it.
121 }
122
123 fn deactivate(&mut self) {
124 // probe should be removed from beacon thanks to the Drop impl
125 }
126
127 fn process(
128 &mut self,
129 _buffer: &mut Buffer,
130 _aux: &mut AuxiliaryBuffers,
131 _context: &mut impl ProcessContext<Self>,
132 ) -> ProcessStatus {
133 let ts = RemoteProbe::timestamp();
134 // self.probe.say(format!("{} sending data", ts));
135 let _ = self.probe.store_automation(ts, 1, &self.params.param1);
136 let _ = self.probe.store_automation(ts, 2, &self.params.param2);
137 let _ = self.probe.store_automation(ts, 3, &self.params.param3);
138 let _ = self.probe.store_automation(ts, 4, &self.params.param4);
139 let _ = self.probe.store_automation(ts, 4, &self.params.param4);
140 let _ = self.probe.store_automation(ts, 5, &self.params.param5);
141 let _ = self.probe.store_automation(ts, 6, &self.params.param6);
142 let _ = self.probe.store_automation(ts, 7, &self.params.param7);
143 let _ = self.probe.store_automation(ts, 8, &self.params.param8);
144 let _ = self.probe.store_automation(ts, 9, &self.params.param9);
145 // self.probe.say(format!("{} sent automation", ts));
146
147 // self.probe.store_audio(
148 // ts,
149 // buffer
150 // .iter_samples()
151 // .flatten()
152 // .map(|f| *f)
153 // .collect::<Vec<f32>>(),
154 // );
155 // self.probe.say(format!("{} sent audio", ts));
156
157 ProcessStatus::Normal
158 }
159}
160
161impl ClapPlugin for ShapemakerVST {
162 const CLAP_ID: &'static str = "works.gwen.shapemakervst";
163 const CLAP_DESCRIPTION: Option<&'static str> = Some(
164 "A VST plugin for Shapemaker, an experimental audiovisual SVG-based rendering engine",
165 );
166 const CLAP_MANUAL_URL: Option<&'static str> = Some(Self::URL);
167 const CLAP_SUPPORT_URL: Option<&'static str> = None;
168
169 // Don't forget to change these features
170 const CLAP_FEATURES: &'static [ClapFeature] =
171 &[ClapFeature::AudioEffect, ClapFeature::Stereo];
172}
173
174impl Vst3Plugin for ShapemakerVST {
175 const VST3_CLASS_ID: [u8; 16] = *b"gwennlbhshapemak";
176
177 // And also don't forget to change these categories
178 const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] =
179 &[Vst3SubCategory::Fx, Vst3SubCategory::Dynamics];
180}