1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5use std::cell::Cell;
6use std::ffi::{c_char, c_void, CString};
7use std::str::FromStr;
8use std::sync::atomic::{AtomicU64, Ordering};
9use std::{ptr, slice};
10
11use vst3::{uid, Class, ComRef, ComWrapper, Steinberg::Vst::*, Steinberg::*};
12
13fn copy_cstring(src: &str, dst: &mut [c_char]) {
14 let c_string = CString::new(src).unwrap_or_else(|_| CString::default());
15 let bytes = c_string.as_bytes_with_nul();
16
17 for (src, dst) in bytes.iter().zip(dst.iter_mut()) {
18 *dst = *src as c_char;
19 }
20
21 if bytes.len() > dst.len() {
22 if let Some(last) = dst.last_mut() {
23 *last = 0;
24 }
25 }
26}
27
28fn copy_wstring(src: &str, dst: &mut [TChar]) {
29 let mut len = 0;
30 for (src, dst) in src.encode_utf16().zip(dst.iter_mut()) {
31 *dst = src as TChar;
32 len += 1;
33 }
34
35 if len < dst.len() {
36 dst[len] = 0;
37 } else if let Some(last) = dst.last_mut() {
38 *last = 0;
39 }
40}
41
42unsafe fn len_wstring(string: *const TChar) -> usize {
43 let mut len = 0;
44
45 while *string.offset(len) != 0 {
46 len += 1;
47 }
48
49 len as usize
50}
51
52const PLUGIN_NAME: &'static str = "Gain (vst3-rs example plugin)";
53
54struct GainProcessor {
55 gain: AtomicU64,
56}
57
58impl Class for GainProcessor {
59 type Interfaces = (IComponent, IAudioProcessor, IProcessContextRequirements);
60}
61
62impl GainProcessor {
63 const CID: TUID = uid(0x6E332252, 0x54224A00, 0xAA69301A, 0xF318797D);
64
65 fn new() -> GainProcessor {
66 GainProcessor {
67 gain: AtomicU64::new(1.0f64.to_bits()),
68 }
69 }
70}
71
72impl IPluginBaseTrait for GainProcessor {
73 unsafe fn initialize(&self, _context: *mut FUnknown) -> tresult {
74 kResultOk
75 }
76
77 unsafe fn terminate(&self) -> tresult {
78 kResultOk
79 }
80}
81
82impl IComponentTrait for GainProcessor {
83 unsafe fn getControllerClassId(&self, class_id: *mut TUID) -> tresult {
84 *class_id = GainController::CID;
85 kResultOk
86 }
87
88 unsafe fn setIoMode(&self, _mode: IoMode) -> tresult {
89 kResultOk
90 }
91
92 unsafe fn getBusCount(&self, mediaType: MediaType, dir: BusDirection) -> i32 {
93 match mediaType as BusDirections {
94 MediaTypes_::kAudio => match dir as BusDirections {
95 BusDirections_::kInput => 1,
96 BusDirections_::kOutput => 1,
97 _ => 0,
98 },
99 MediaTypes_::kEvent => 0,
100 _ => 0,
101 }
102 }
103
104 unsafe fn getBusInfo(
105 &self,
106 mediaType: MediaType,
107 dir: BusDirection,
108 index: i32,
109 bus: *mut BusInfo,
110 ) -> tresult {
111 match mediaType as MediaTypes {
112 MediaTypes_::kAudio => match dir as BusDirections {
113 BusDirections_::kInput => match index {
114 0 => {
115 let bus = &mut *bus;
116
117 bus.mediaType = MediaTypes_::kAudio as MediaType;
118 bus.direction = BusDirections_::kInput as BusDirection;
119 bus.channelCount = 2;
120 copy_wstring("Input", &mut bus.name);
121 bus.busType = BusTypes_::kMain as BusType;
122 bus.flags = BusInfo_::BusFlags_::kDefaultActive as u32;
123
124 kResultOk
125 }
126 _ => kInvalidArgument,
127 },
128 BusDirections_::kOutput => match index {
129 0 => {
130 let bus = &mut *bus;
131
132 bus.mediaType = MediaTypes_::kAudio as MediaType;
133 bus.direction = BusDirections_::kOutput as BusDirection;
134 bus.channelCount = 2;
135 copy_wstring("Output", &mut bus.name);
136 bus.busType = BusTypes_::kMain as BusType;
137 bus.flags = BusInfo_::BusFlags_::kDefaultActive as u32 as uint32;
138
139 kResultOk
140 }
141 _ => kInvalidArgument,
142 },
143 _ => kInvalidArgument,
144 },
145 MediaTypes_::kEvent => kInvalidArgument,
146 _ => kInvalidArgument,
147 }
148 }
149
150 unsafe fn getRoutingInfo(
151 &self,
152 _in_info: *mut RoutingInfo,
153 _out_info: *mut RoutingInfo,
154 ) -> tresult {
155 kNotImplemented
156 }
157
158 unsafe fn activateBus(
159 &self,
160 _media_type: MediaType,
161 _dir: BusDirection,
162 _index: i32,
163 _state: TBool,
164 ) -> tresult {
165 kResultOk
166 }
167
168 unsafe fn setActive(&self, _state: TBool) -> tresult {
169 kResultOk
170 }
171
172 unsafe fn setState(&self, _state: *mut IBStream) -> tresult {
173 kResultOk
174 }
175
176 unsafe fn getState(&self, _state: *mut IBStream) -> tresult {
177 kResultOk
178 }
179}
180
181impl IAudioProcessorTrait for GainProcessor {
182 unsafe fn setBusArrangements(
183 &self,
184 inputs: *mut SpeakerArrangement,
185 num_ins: i32,
186 outputs: *mut SpeakerArrangement,
187 num_outs: i32,
188 ) -> tresult {
189 if num_ins != 1 || num_outs != 1 {
190 return kResultFalse;
191 }
192
193 if *inputs != SpeakerArr::kStereo || *outputs != SpeakerArr::kStereo {
194 return kResultFalse;
195 }
196
197 kResultTrue
198 }
199
200 unsafe fn getBusArrangement(
201 &self,
202 dir: BusDirection,
203 index: i32,
204 arr: *mut SpeakerArrangement,
205 ) -> tresult {
206 match dir as BusDirections {
207 BusDirections_::kInput => {
208 if index == 0 {
209 *arr = SpeakerArr::kStereo;
210 kResultOk
211 } else {
212 kInvalidArgument
213 }
214 }
215 BusDirections_::kOutput => {
216 if index == 0 {
217 *arr = SpeakerArr::kStereo;
218 kResultOk
219 } else {
220 kInvalidArgument
221 }
222 }
223 _ => kInvalidArgument,
224 }
225 }
226
227 unsafe fn canProcessSampleSize(&self, symbolic_sample_size: i32) -> tresult {
228 match symbolic_sample_size as SymbolicSampleSizes {
229 SymbolicSampleSizes_::kSample32 => kResultOk as i32,
230 SymbolicSampleSizes_::kSample64 => kNotImplemented as i32,
231 _ => kInvalidArgument,
232 }
233 }
234
235 unsafe fn getLatencySamples(&self) -> u32 {
236 0
237 }
238
239 unsafe fn setupProcessing(&self, _setup: *mut ProcessSetup) -> tresult {
240 kResultOk
241 }
242
243 unsafe fn setProcessing(&self, _state: TBool) -> tresult {
244 kResultOk
245 }
246
247 unsafe fn process(&self, data: *mut ProcessData) -> tresult {
248 let process_data = &*data;
249
250 if let Some(param_changes) = ComRef::from_raw(process_data.inputParameterChanges) {
251 let param_count = param_changes.getParameterCount();
252 for param_index in 0..param_count {
253 if let Some(param_queue) =
254 ComRef::from_raw(param_changes.getParameterData(param_index))
255 {
256 let param_id = param_queue.getParameterId();
257 let point_count = param_queue.getPointCount();
258
259 match param_id {
260 0 => {
261 let mut sample_offset = 0;
262 let mut value = 0.0;
263 let result = param_queue.getPoint(
264 point_count - 1,
265 &mut sample_offset,
266 &mut value,
267 );
268
269 if result == kResultTrue {
270 self.gain.store(value.to_bits(), Ordering::Relaxed);
271 }
272 }
273 _ => {}
274 }
275 }
276 }
277 }
278
279 let gain = f64::from_bits(self.gain.load(Ordering::Relaxed)) as f32;
280
281 let num_samples = process_data.numSamples as usize;
282
283 if process_data.numInputs != 1 || process_data.numOutputs != 1 {
284 return kResultOk;
285 }
286
287 let input_buses =
288 slice::from_raw_parts(process_data.inputs, process_data.numInputs as usize);
289 let output_buses =
290 slice::from_raw_parts(process_data.outputs, process_data.numOutputs as usize);
291
292 if input_buses[0].numChannels != 2 || output_buses[0].numChannels != 2 {
293 return kResultOk;
294 }
295
296 let input_channels = slice::from_raw_parts(
297 input_buses[0].__field0.channelBuffers32,
298 input_buses[0].numChannels as usize,
299 );
300 let output_channels = slice::from_raw_parts(
301 output_buses[0].__field0.channelBuffers32,
302 output_buses[0].numChannels as usize,
303 );
304
305 let input_l = slice::from_raw_parts(input_channels[0], num_samples);
306 let input_r = slice::from_raw_parts(input_channels[1], num_samples);
307 let output_l = slice::from_raw_parts_mut(output_channels[0], num_samples);
308 let output_r = slice::from_raw_parts_mut(output_channels[1], num_samples);
309
310 for i in 0..num_samples {
311 output_l[i] = gain * input_l[i];
312 output_r[i] = gain * input_r[i];
313 }
314
315 kResultOk
316 }
317
318 unsafe fn getTailSamples(&self) -> u32 {
319 0
320 }
321}
322
323impl IProcessContextRequirementsTrait for GainProcessor {
324 unsafe fn getProcessContextRequirements(&self) -> u32 {
325 0
326 }
327}
328
329struct GainController {
330 gain: Cell<f64>,
331}
332
333impl Class for GainController {
334 type Interfaces = (IEditController,);
335}
336
337impl GainController {
338 const CID: TUID = uid(0x1BA8A477, 0xEE0A4A2D, 0x80F50D14, 0x13D2EAA0);
339
340 fn new() -> GainController {
341 GainController {
342 gain: Cell::new(1.0),
343 }
344 }
345}
346
347impl IPluginBaseTrait for GainController {
348 unsafe fn initialize(&self, _context: *mut FUnknown) -> tresult {
349 kResultOk
350 }
351
352 unsafe fn terminate(&self) -> tresult {
353 kResultOk
354 }
355}
356
357impl IEditControllerTrait for GainController {
358 unsafe fn setComponentState(&self, _state: *mut IBStream) -> tresult {
359 kNotImplemented
360 }
361
362 unsafe fn setState(&self, _state: *mut IBStream) -> tresult {
363 kResultOk
364 }
365
366 unsafe fn getState(&self, _state: *mut IBStream) -> tresult {
367 kResultOk
368 }
369
370 unsafe fn getParameterCount(&self) -> i32 {
371 1
372 }
373
374 unsafe fn getParameterInfo(&self, param_index: i32, info: *mut ParameterInfo) -> tresult {
375 match param_index {
376 0 => {
377 let info = &mut *info;
378
379 info.id = 0;
380 copy_wstring("Gain", &mut info.title);
381 copy_wstring("Gain", &mut info.shortTitle);
382 copy_wstring("", &mut info.units);
383 info.stepCount = 0;
384 info.defaultNormalizedValue = 1.0;
385 info.unitId = 0;
386 info.flags = ParameterInfo_::ParameterFlags_::kCanAutomate as i32;
387
388 kResultOk
389 }
390 _ => kInvalidArgument,
391 }
392 }
393
394 unsafe fn getParamStringByValue(
395 &self,
396 id: u32,
397 value_normalized: f64,
398 string: *mut String128,
399 ) -> tresult {
400 let slice = unsafe { &mut *string };
401
402 match id {
403 0 => {
404 let display = value_normalized.to_string();
405 copy_wstring(&display, slice);
406 kResultOk
407 }
408 _ => kInvalidArgument,
409 }
410 }
411
412 unsafe fn getParamValueByString(
413 &self,
414 id: u32,
415 string: *mut TChar,
416 value_normalized: *mut f64,
417 ) -> tresult {
418 match id {
419 0 => {
420 let len = len_wstring(string as *const TChar);
421 if let Ok(string) =
422 String::from_utf16(slice::from_raw_parts(string as *const u16, len))
423 {
424 if let Ok(value) = f64::from_str(&string) {
425 *value_normalized = value;
426 return kResultOk;
427 }
428 }
429 kInvalidArgument
430 }
431 _ => kInvalidArgument,
432 }
433 }
434
435 unsafe fn normalizedParamToPlain(&self, id: u32, value_normalized: f64) -> f64 {
436 match id {
437 0 => value_normalized,
438 _ => 0.0,
439 }
440 }
441
442 unsafe fn plainParamToNormalized(&self, id: u32, plain_value: f64) -> f64 {
443 match id {
444 0 => plain_value,
445 _ => 0.0,
446 }
447 }
448
449 unsafe fn getParamNormalized(&self, id: u32) -> f64 {
450 match id {
451 0 => self.gain.get(),
452 _ => 0.0,
453 }
454 }
455
456 unsafe fn setParamNormalized(&self, id: u32, value: f64) -> tresult {
457 match id {
458 0 => {
459 self.gain.set(value);
460 kResultOk
461 }
462 _ => kInvalidArgument,
463 }
464 }
465
466 unsafe fn setComponentHandler(&self, _handler: *mut IComponentHandler) -> tresult {
467 kResultOk
468 }
469
470 unsafe fn createView(&self, _name: *const c_char) -> *mut IPlugView {
471 ptr::null_mut()
472 }
473}
474
475struct Factory {}
476
477impl Class for Factory {
478 type Interfaces = (IPluginFactory,);
479}
480
481impl IPluginFactoryTrait for Factory {
482 unsafe fn getFactoryInfo(&self, info: *mut PFactoryInfo) -> tresult {
483 let info = &mut *info;
484
485 copy_cstring("Vendor", &mut info.vendor);
486 copy_cstring("https://example.com", &mut info.url);
487 copy_cstring("someone@example.com", &mut info.email);
488 info.flags = PFactoryInfo_::FactoryFlags_::kUnicode as int32;
489
490 kResultOk
491 }
492
493 unsafe fn countClasses(&self) -> i32 {
494 2
495 }
496
497 unsafe fn getClassInfo(&self, index: i32, info: *mut PClassInfo) -> tresult {
498 match index {
499 0 => {
500 let info = &mut *info;
501 info.cid = GainProcessor::CID;
502 info.cardinality = PClassInfo_::ClassCardinality_::kManyInstances as int32;
503 copy_cstring("Audio Module Class", &mut info.category);
504 copy_cstring(PLUGIN_NAME, &mut info.name);
505
506 kResultOk
507 }
508 1 => {
509 let info = &mut *info;
510 info.cid = GainController::CID;
511 info.cardinality = PClassInfo_::ClassCardinality_::kManyInstances as int32;
512 copy_cstring("Component Controller Class", &mut info.category);
513 copy_cstring(PLUGIN_NAME, &mut info.name);
514
515 kResultOk
516 }
517 _ => kInvalidArgument,
518 }
519 }
520
521 unsafe fn createInstance(
522 &self,
523 cid: FIDString,
524 iid: FIDString,
525 obj: *mut *mut c_void,
526 ) -> tresult {
527 let instance = match *(cid as *const TUID) {
528 GainProcessor::CID => Some(
529 ComWrapper::new(GainProcessor::new())
530 .to_com_ptr::<FUnknown>()
531 .unwrap(),
532 ),
533 GainController::CID => Some(
534 ComWrapper::new(GainController::new())
535 .to_com_ptr::<FUnknown>()
536 .unwrap(),
537 ),
538 _ => None,
539 };
540
541 if let Some(instance) = instance {
542 let ptr = instance.as_ptr();
543 ((*(*ptr).vtbl).queryInterface)(ptr, iid as *mut TUID, obj)
544 } else {
545 kInvalidArgument
546 }
547 }
548}
549
550#[cfg(target_os = "windows")]
551#[no_mangle]
552extern "system" fn InitDll() -> bool {
553 true
554}
555
556#[cfg(target_os = "windows")]
557#[no_mangle]
558extern "system" fn ExitDll() -> bool {
559 true
560}
561
562#[cfg(target_os = "macos")]
563#[no_mangle]
564extern "system" fn BundleEntry(_bundle_ref: *mut c_void) -> bool {
565 true
566}
567
568#[cfg(target_os = "macos")]
569#[no_mangle]
570extern "system" fn BundleExit() -> bool {
571 true
572}
573
574#[cfg(target_os = "linux")]
575#[no_mangle]
576extern "system" fn ModuleEntry(_library_handle: *mut c_void) -> bool {
577 true
578}
579
580#[cfg(target_os = "linux")]
581#[no_mangle]
582extern "system" fn ModuleExit() -> bool {
583 true
584}
585
586#[no_mangle]
587extern "system" fn GetPluginFactory() -> *mut IPluginFactory {
588 ComWrapper::new(Factory {})
589 .to_com_ptr::<IPluginFactory>()
590 .unwrap()
591 .into_raw()
592}