Skip to main content

nice_plug/wrapper/vst3/
wrapper.rs

1use nice_plug_core::audio_setup::{AuxiliaryBuffers, BufferConfig, ProcessMode};
2use nice_plug_core::context::process::Transport;
3use nice_plug_core::midi::sysex::SysExMessage;
4use nice_plug_core::midi::{MidiConfig, NoteEvent};
5use nice_plug_core::params::ParamFlags;
6use nice_plug_core::plugin::ProcessStatus;
7use std::borrow::Borrow;
8use std::ffi::c_void;
9use std::mem::{self, MaybeUninit};
10use std::num::NonZeroU32;
11use std::ptr::NonNull;
12use std::sync::Arc;
13use std::sync::atomic::Ordering;
14use vst3::Steinberg::Vst::ProcessContext_::StatesAndFlags_::{
15    kBarPositionValid, kCycleActive, kCycleValid, kPlaying, kProjectTimeMusicValid, kRecording,
16    kTempoValid, kTimeSigValid,
17};
18use vst3::Steinberg::Vst::{
19    BusDirection, CString, CtrlNumber, DataEvent, Event, Event_::EventTypes_, IAudioProcessor,
20    IAudioProcessorTrait, IComponent, IComponentHandler, IComponentTrait, IEditController,
21    IEditControllerTrait, IEventListTrait, IMidiMapping, IMidiMappingTrait,
22    INoteExpressionController, INoteExpressionControllerTrait, IParamValueQueueTrait,
23    IParameterChangesTrait, IProcessContextRequirements, IProcessContextRequirements_,
24    IProcessContextRequirementsTrait, IUnitInfo, IUnitInfoTrait, IoMode, LegacyMIDICCOutEvent,
25    MediaType, NoteExpressionTypeID, NoteExpressionTypeInfo, NoteExpressionValue,
26    NoteExpressionValueDescription, NoteOffEvent, NoteOnEvent, ParamID, ParamValue, ParameterInfo,
27    ParameterInfo_::ParameterFlags_, PolyPressureEvent, ProcessData, ProcessModes_, ProcessSetup,
28    ProgramListID, ProgramListInfo, SpeakerArrangement, String128, TChar, UnitID, UnitInfo,
29    kNoParamId, kNoParentUnitId, kNoProgramListId, kRootUnitId,
30};
31use vst3::Steinberg::{
32    FIDString, FUnknown, IBStream, IBStreamTrait, IPlugView, IPluginBaseTrait, TBool, TUID, int16,
33    int32, kInvalidArgument, kNoInterface, kResultFalse, kResultOk, tresult, uint32,
34};
35use vst3::{Class, ComRef, ComWrapper};
36use widestring::U16CStr;
37
38use super::inner::{ProcessEvent, WrapperInner};
39use super::note_expressions::{self, NoteExpressionController};
40use super::util::{VST3_MIDI_CCS, VST3_MIDI_NUM_PARAMS, VST3_MIDI_PARAMS_START, u16strlcpy};
41use super::util::{VST3_MIDI_CHANNELS, VST3_MIDI_PARAMS_END};
42use super::view::WrapperView;
43use crate::util::permit_alloc;
44use crate::wrapper::state;
45use crate::wrapper::util::buffer_management::{BufferManager, ChannelPointers};
46use crate::wrapper::util::{clamp_input_event_timing, clamp_output_event_timing, process_wrapper};
47use crate::wrapper::vst3::Vst3Plugin;
48
49#[allow(clippy::unnecessary_cast)]
50const K_SYMBOLIC_SAMPLE_SIZE_32: i32 = vst3::Steinberg::Vst::SymbolicSampleSizes_::kSample32 as i32;
51#[allow(clippy::unnecessary_cast)]
52const K_MEDIA_TYPE_AUDIO: i32 = vst3::Steinberg::Vst::MediaTypes_::kAudio as i32;
53#[allow(clippy::unnecessary_cast)]
54const K_MEDIA_TYPE_EVENT: i32 = vst3::Steinberg::Vst::MediaTypes_::kEvent as i32;
55#[allow(clippy::unnecessary_cast)]
56const K_BUS_DIRECTION_INPUT: i32 = vst3::Steinberg::Vst::BusDirections_::kInput as i32;
57#[allow(clippy::unnecessary_cast)]
58const K_BUS_DIRECTION_OUTPUT: i32 = vst3::Steinberg::Vst::BusDirections_::kOutput as i32;
59#[allow(clippy::unnecessary_cast)]
60const K_BUS_TYPE_MAIN: i32 = vst3::Steinberg::Vst::BusTypes_::kMain as i32;
61#[allow(clippy::unnecessary_cast)]
62const K_BUS_TYPE_AUX: i32 = vst3::Steinberg::Vst::BusTypes_::kAux as i32;
63
64pub struct Wrapper<P: Vst3Plugin> {
65    inner: Arc<WrapperInner<P>>,
66}
67
68impl<P: Vst3Plugin> Class for Wrapper<P> {
69    type Interfaces = (
70        IComponent,
71        IEditController,
72        IAudioProcessor,
73        IMidiMapping,
74        INoteExpressionController,
75        IProcessContextRequirements,
76        IUnitInfo,
77    );
78}
79
80impl<P: Vst3Plugin> Wrapper<P> {
81    pub fn new() -> Self {
82        Self {
83            inner: WrapperInner::new(),
84        }
85    }
86}
87
88impl<P: Vst3Plugin> Default for Wrapper<P> {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94impl<P: Vst3Plugin> Drop for Wrapper<P> {
95    fn drop(&mut self) {
96        crate::nice_debug_assert_eq!(Arc::strong_count(&self.inner), 1);
97    }
98}
99
100impl<P: Vst3Plugin> IPluginBaseTrait for Wrapper<P> {
101    unsafe fn initialize(&self, _context: *mut FUnknown) -> tresult {
102        // We currently don't need or allow any initialization logic
103        kResultOk
104    }
105
106    unsafe fn terminate(&self) -> tresult {
107        kResultOk
108    }
109}
110
111impl<P: Vst3Plugin> IComponentTrait for Wrapper<P> {
112    unsafe fn getControllerClassId(&self, _class_id: *mut TUID) -> tresult {
113        // We won't separate the edit controller to keep the implementation a bit smaller
114        kNoInterface
115    }
116
117    unsafe fn setIoMode(&self, _mode: IoMode) -> tresult {
118        // Not quite sure what the point of this is when the processing setup also receives similar
119        // information
120        kResultOk
121    }
122
123    unsafe fn getBusCount(
124        &self,
125        type_: vst3::Steinberg::Vst::MediaType,
126        dir: vst3::Steinberg::Vst::BusDirection,
127    ) -> int32 {
128        let current_audio_io_layout = self.inner.current_audio_io_layout.load();
129
130        // A plugin has a main input and output bus if the default number of channels is non-zero,
131        // and a plugin can also have auxiliary input and output busses
132        match type_ {
133            x if x == K_MEDIA_TYPE_AUDIO && dir == K_BUS_DIRECTION_INPUT => {
134                let main_busses = if current_audio_io_layout.main_input_channels.is_some() {
135                    1
136                } else {
137                    0
138                };
139                let aux_busses = current_audio_io_layout.aux_input_ports.len() as i32;
140
141                main_busses + aux_busses
142            }
143            x if x == K_MEDIA_TYPE_AUDIO && dir == K_BUS_DIRECTION_OUTPUT => {
144                let main_busses = if current_audio_io_layout.main_output_channels.is_some() {
145                    1
146                } else {
147                    0
148                };
149                let aux_busses = current_audio_io_layout.aux_output_ports.len() as i32;
150
151                main_busses + aux_busses
152            }
153            x if x == K_MEDIA_TYPE_EVENT
154                && dir == K_BUS_DIRECTION_INPUT
155                && P::MIDI_INPUT >= MidiConfig::Basic =>
156            {
157                1
158            }
159            x if x == K_MEDIA_TYPE_EVENT
160                && dir == K_BUS_DIRECTION_OUTPUT
161                && P::MIDI_OUTPUT >= MidiConfig::Basic =>
162            {
163                1
164            }
165            _ => 0,
166        }
167    }
168
169    unsafe fn getBusInfo(
170        &self,
171        type_: vst3::Steinberg::Vst::MediaType,
172        dir: vst3::Steinberg::Vst::BusDirection,
173        index: int32,
174        info: *mut vst3::Steinberg::Vst::BusInfo,
175    ) -> tresult {
176        check_null_ptr!(info);
177
178        let current_audio_io_layout = self.inner.current_audio_io_layout.load();
179
180        match (type_, dir, index) {
181            (t, d, _) if t == K_MEDIA_TYPE_AUDIO && d == K_BUS_DIRECTION_INPUT => {
182                unsafe { *info = mem::zeroed() };
183
184                let info = unsafe { &mut *info };
185                info.mediaType = K_MEDIA_TYPE_AUDIO;
186                info.direction = dir;
187                #[allow(clippy::unnecessary_cast)]
188                {
189                    info.flags = vst3::Steinberg::Vst::BusInfo_::BusFlags_::kDefaultActive as u32;
190                }
191
192                let has_main_input = current_audio_io_layout.main_input_channels.is_some();
193                let aux_input_start_idx = if has_main_input { 1 } else { 0 };
194                let aux_input_idx = (index - aux_input_start_idx).max(0) as usize;
195                if index == 0 && has_main_input {
196                    info.busType = K_BUS_TYPE_MAIN;
197                    info.channelCount =
198                        current_audio_io_layout.main_input_channels.unwrap().get() as i32;
199                    u16strlcpy(&mut info.name, &current_audio_io_layout.main_input_name());
200
201                    kResultOk
202                } else if aux_input_idx < current_audio_io_layout.aux_input_ports.len() {
203                    info.busType = K_BUS_TYPE_AUX;
204                    info.channelCount =
205                        current_audio_io_layout.aux_input_ports[aux_input_idx].get() as i32;
206                    u16strlcpy(
207                        &mut info.name,
208                        &current_audio_io_layout
209                            .aux_input_name(aux_input_idx)
210                            .expect("Out of bounds auxiliary input port"),
211                    );
212
213                    kResultOk
214                } else {
215                    kInvalidArgument
216                }
217            }
218            (t, d, _) if t == K_MEDIA_TYPE_AUDIO && d == K_BUS_DIRECTION_OUTPUT => {
219                unsafe { *info = mem::zeroed() };
220
221                let info = unsafe { &mut *info };
222                info.mediaType = K_MEDIA_TYPE_AUDIO;
223                info.direction = dir;
224                #[allow(clippy::unnecessary_cast)]
225                {
226                    info.flags = vst3::Steinberg::Vst::BusInfo_::BusFlags_::kDefaultActive as u32;
227                }
228
229                let has_main_output = current_audio_io_layout.main_output_channels.is_some();
230                let aux_output_start_idx = if has_main_output { 1 } else { 0 };
231                let aux_output_idx = (index - aux_output_start_idx).max(0) as usize;
232                if index == 0 && has_main_output {
233                    info.busType = K_BUS_TYPE_MAIN;
234                    // NOTE: See above, this becomes a 0 channel output if the plugin doesn't have a
235                    //       main output
236                    info.channelCount = current_audio_io_layout
237                        .main_output_channels
238                        .map(NonZeroU32::get)
239                        .unwrap_or_default() as i32;
240                    u16strlcpy(&mut info.name, &current_audio_io_layout.main_output_name());
241
242                    kResultOk
243                } else if aux_output_idx < current_audio_io_layout.aux_output_ports.len() {
244                    info.busType = K_BUS_TYPE_AUX;
245                    info.channelCount =
246                        current_audio_io_layout.aux_output_ports[aux_output_idx].get() as i32;
247                    u16strlcpy(
248                        &mut info.name,
249                        &current_audio_io_layout
250                            .aux_output_name(aux_output_idx)
251                            .expect("Out of bounds auxiliary output port"),
252                    );
253
254                    kResultOk
255                } else {
256                    kInvalidArgument
257                }
258            }
259            (t, d, 0)
260                if t == K_MEDIA_TYPE_EVENT
261                    && d == K_BUS_DIRECTION_INPUT
262                    && P::MIDI_INPUT >= MidiConfig::Basic =>
263            {
264                unsafe { *info = mem::zeroed() };
265
266                let info = unsafe { &mut *info };
267                info.mediaType = K_MEDIA_TYPE_EVENT;
268                info.direction = K_BUS_DIRECTION_INPUT;
269                info.channelCount = 16;
270                u16strlcpy(&mut info.name, "Note Input");
271                info.busType = K_BUS_TYPE_MAIN;
272                #[allow(clippy::unnecessary_cast)]
273                {
274                    info.flags = vst3::Steinberg::Vst::BusInfo_::BusFlags_::kDefaultActive as u32;
275                }
276                kResultOk
277            }
278            (t, d, 0)
279                if t == K_MEDIA_TYPE_EVENT
280                    && d == K_BUS_DIRECTION_OUTPUT
281                    && P::MIDI_OUTPUT >= MidiConfig::Basic =>
282            {
283                unsafe { *info = mem::zeroed() };
284
285                let info = unsafe { &mut *info };
286                info.mediaType = K_MEDIA_TYPE_EVENT;
287                info.direction = K_BUS_DIRECTION_OUTPUT;
288                info.channelCount = 16;
289                u16strlcpy(&mut info.name, "Note Output");
290                info.busType = K_BUS_TYPE_MAIN;
291                #[allow(clippy::unnecessary_cast)]
292                {
293                    info.flags = vst3::Steinberg::Vst::BusInfo_::BusFlags_::kDefaultActive as u32;
294                }
295                kResultOk
296            }
297            _ => kInvalidArgument,
298        }
299    }
300
301    unsafe fn getRoutingInfo(
302        &self,
303        in_info: *mut vst3::Steinberg::Vst::RoutingInfo,
304        out_info: *mut vst3::Steinberg::Vst::RoutingInfo,
305    ) -> tresult {
306        check_null_ptr!(in_info, out_info);
307
308        let current_audio_io_layout = self.inner.current_audio_io_layout.load();
309
310        unsafe { *out_info = mem::zeroed() };
311
312        let in_info = unsafe { &*in_info };
313        let out_info = unsafe { &mut *out_info };
314        match (in_info.mediaType, in_info.busIndex) {
315            (t, 0)
316                if t == K_MEDIA_TYPE_AUDIO
317                    // We only have an IO pair when the plugin has both a main input and a main output
318                    && current_audio_io_layout.main_input_channels.is_some()
319                    && current_audio_io_layout.main_output_channels.is_some() =>
320            {
321                out_info.mediaType = K_MEDIA_TYPE_AUDIO;
322                out_info.busIndex = in_info.busIndex;
323                out_info.channel = in_info.channel;
324
325                kResultOk
326            }
327            (t, 0)
328                if t == K_MEDIA_TYPE_EVENT
329                    && P::MIDI_INPUT >= MidiConfig::Basic
330                    && P::MIDI_OUTPUT >= MidiConfig::Basic =>
331            {
332                out_info.mediaType = K_MEDIA_TYPE_EVENT;
333                out_info.busIndex = in_info.busIndex;
334                out_info.channel = in_info.channel;
335
336                kResultOk
337            }
338            _ => kResultFalse,
339        }
340    }
341
342    unsafe fn activateBus(
343        &self,
344        type_: vst3::Steinberg::Vst::MediaType,
345        dir: vst3::Steinberg::Vst::BusDirection,
346        index: int32,
347        _state: TBool,
348    ) -> tresult {
349        let current_audio_io_layout = self.inner.current_audio_io_layout.load();
350
351        // We don't support this, but the validator will get very angry with us if we let it know
352        // that
353        match (type_, dir, index) {
354            (t, d, _) if t == K_MEDIA_TYPE_AUDIO && d == K_BUS_DIRECTION_INPUT => {
355                let main_busses = if current_audio_io_layout.main_input_channels.is_some() {
356                    1
357                } else {
358                    0
359                };
360                let aux_busses = current_audio_io_layout.aux_input_ports.len() as i32;
361
362                if (0..main_busses + aux_busses).contains(&index) {
363                    kResultOk
364                } else {
365                    kInvalidArgument
366                }
367            }
368            (t, d, _) if t == K_MEDIA_TYPE_AUDIO && d == K_BUS_DIRECTION_OUTPUT => {
369                let main_busses = if current_audio_io_layout.main_output_channels.is_some() {
370                    1
371                } else {
372                    0
373                };
374                let aux_busses = current_audio_io_layout.aux_output_ports.len() as i32;
375
376                if (0..main_busses + aux_busses).contains(&index) {
377                    kResultOk
378                } else {
379                    kInvalidArgument
380                }
381            }
382            (t, d, 0)
383                if t == K_MEDIA_TYPE_EVENT
384                    && d == K_BUS_DIRECTION_INPUT
385                    && P::MIDI_INPUT >= MidiConfig::Basic =>
386            {
387                kResultOk
388            }
389            (t, d, 0)
390                if t == K_MEDIA_TYPE_EVENT
391                    && d == K_BUS_DIRECTION_OUTPUT
392                    && P::MIDI_OUTPUT >= MidiConfig::Basic =>
393            {
394                kResultOk
395            }
396            _ => kInvalidArgument,
397        }
398    }
399
400    unsafe fn setActive(&self, state: TBool) -> tresult {
401        // We could call initialize in `IAudioProcessor::setup_processing()`, but REAPER will set
402        // the bus arrangements between that function and this function. So to be able to handle
403        // custom channel layout overrides we need to initialize here.
404        match (state != 0, self.inner.current_buffer_config.load()) {
405            (true, Some(buffer_config)) => {
406                // Before initializing the plugin, make sure all smoothers are set the the default values
407                for param in self.inner.param_by_hash.values() {
408                    unsafe { param._internal_update_smoother(buffer_config.sample_rate, true) };
409                }
410
411                // NOTE: This needs to be dropped after the `plugin` lock to avoid deadlocks
412                let mut init_context = self.inner.make_init_context();
413                let audio_io_layout = self.inner.current_audio_io_layout.load();
414                let mut plugin = self.inner.plugin.lock();
415                if plugin.initialize(&audio_io_layout, &buffer_config, &mut init_context) {
416                    // NOTE: We don't call `Plugin::reset()` here. The call is done in `set_process()`
417                    //       instead. Otherwise we would call the function twice, and `set_process()` needs
418                    //       to be called after this function before the plugin may process audio again.
419
420                    // This preallocates enough space so we can transform all of the host's raw
421                    // channel pointers into a set of `Buffer` objects for the plugin's main and
422                    // auxiliary IO
423                    *self.inner.buffer_manager.borrow_mut() = BufferManager::for_audio_io_layout(
424                        buffer_config.max_buffer_size as usize,
425                        audio_io_layout,
426                    );
427
428                    kResultOk
429                } else {
430                    kResultFalse
431                }
432            }
433            (true, None) => kResultFalse,
434            (false, _) => {
435                self.inner.plugin.lock().deactivate();
436
437                kResultOk
438            }
439        }
440    }
441
442    unsafe fn setState(&self, state: *mut IBStream) -> tresult {
443        use vst3::Steinberg::IBStream_::IStreamSeekMode_::*;
444
445        check_null_ptr!(state);
446
447        let state = unsafe { ComRef::from_raw(state).unwrap() };
448
449        // We need to know how large the state is before we can read it. The current position can be
450        // zero, but it can also be something else. Bitwig prepends the preset header in the stream,
451        // while some other hosts don't expose that to the plugin.
452        let mut current_pos = 0;
453        let mut eof_pos = 0;
454        if unsafe {
455            state.tell(&mut current_pos) != kResultOk
456                || state.seek(0, kIBSeekEnd as int32, &mut eof_pos) != kResultOk
457                || state.seek(current_pos, kIBSeekSet as int32, std::ptr::null_mut()) != kResultOk
458        } {
459            crate::nice_debug_assert_failure!("Could not get the stream length");
460            return kResultFalse;
461        }
462
463        let stream_byte_size = (eof_pos - current_pos) as i32;
464        let mut num_bytes_read = 0;
465        let mut read_buffer: Vec<u8> = Vec::with_capacity(stream_byte_size as usize);
466        unsafe {
467            state.read(
468                read_buffer.as_mut_ptr() as *mut c_void,
469                read_buffer.capacity() as i32,
470                &mut num_bytes_read,
471            );
472        }
473        unsafe { read_buffer.set_len(num_bytes_read as usize) };
474
475        // If the size is zero, some hosts will always return `kResultFalse` even if the read was
476        // 'successful', so we can't check the return value but we can check the number of bytes
477        // read.
478        if read_buffer.len() != stream_byte_size as usize {
479            crate::nice_debug_assert_failure!("Unexpected stream length");
480            return kResultFalse;
481        }
482
483        match unsafe { state::deserialize_json(&read_buffer) } {
484            Some(mut state) => {
485                if self.inner.set_state_inner(&mut state) {
486                    crate::nice_trace!("Loaded state ({} bytes)", read_buffer.len());
487                    kResultOk
488                } else {
489                    kResultFalse
490                }
491            }
492            None => kResultFalse,
493        }
494    }
495
496    unsafe fn getState(&self, state: *mut IBStream) -> tresult {
497        check_null_ptr!(state);
498
499        let state = unsafe { ComRef::from_raw(state).unwrap() };
500
501        let serialized = unsafe {
502            state::serialize_json::<P>(
503                self.inner.params.clone(),
504                state::make_params_iter(&self.inner.param_by_hash, &self.inner.param_id_to_hash),
505            )
506        };
507        match serialized {
508            Ok(serialized) => {
509                let mut num_bytes_written = 0;
510                let result = unsafe {
511                    state.write(
512                        serialized.as_ptr() as *mut c_void,
513                        serialized.len() as i32,
514                        &mut num_bytes_written,
515                    )
516                };
517
518                crate::nice_debug_assert_eq!(result, kResultOk);
519                crate::nice_debug_assert_eq!(num_bytes_written as usize, serialized.len());
520
521                crate::nice_trace!("Saved state ({} bytes)", serialized.len());
522
523                kResultOk
524            }
525            Err(err) => {
526                crate::nice_debug_assert_failure!("Could not save state: {:#}", err);
527                kResultFalse
528            }
529        }
530    }
531}
532
533impl<P: Vst3Plugin> IEditControllerTrait for Wrapper<P> {
534    unsafe fn setComponentState(&self, _state: *mut IBStream) -> tresult {
535        // We have a single file component, so we don't need to do anything here
536        kResultOk
537    }
538
539    unsafe fn setState(&self, _state: *mut IBStream) -> tresult {
540        // We don't store any separate state here. The plugin's state will have been restored
541        // through the component. Calling that same function here will likely lead to duplicate
542        // state restores
543        kResultOk
544    }
545
546    unsafe fn getState(&self, _state: *mut IBStream) -> tresult {
547        // Same for this function
548        kResultOk
549    }
550
551    unsafe fn getParameterCount(&self) -> int32 {
552        // We need to add a whole bunch of parameters if the plugin accepts MIDI CCs
553        if P::MIDI_INPUT >= MidiConfig::MidiCCs {
554            self.inner.param_hashes.len() as i32 + VST3_MIDI_NUM_PARAMS as i32
555        } else {
556            self.inner.param_hashes.len() as i32
557        }
558    }
559
560    unsafe fn getParameterInfo(&self, param_index: int32, info: *mut ParameterInfo) -> tresult {
561        check_null_ptr!(info);
562
563        if param_index < 0 || param_index > unsafe { self.getParameterCount() } {
564            return kInvalidArgument;
565        }
566
567        unsafe { *info = std::mem::zeroed() };
568        let info = unsafe { &mut *info };
569
570        // If the parameter is a generated MIDI CC/channel pressure/pitch bend then it needs to be
571        // handled separately
572        let num_actual_params = self.inner.param_hashes.len() as i32;
573        if P::MIDI_INPUT >= MidiConfig::MidiCCs && param_index >= num_actual_params {
574            let midi_param_relative_idx = (param_index - num_actual_params) as u32;
575            // This goes up to 130 for the 128 CCs followed by channel pressure and pitch bend
576            let midi_cc = midi_param_relative_idx % VST3_MIDI_CCS;
577            let midi_channel = midi_param_relative_idx / VST3_MIDI_CCS;
578            let name = match midi_cc {
579                // kAfterTouch
580                128 => format!("MIDI Ch. {} Channel Pressure", midi_channel + 1),
581                // kPitchBend
582                129 => format!("MIDI Ch. {} Pitch Bend", midi_channel + 1),
583                n => format!("MIDI Ch. {} CC {}", midi_channel + 1, n),
584            };
585
586            info.id = VST3_MIDI_PARAMS_START + midi_param_relative_idx;
587            u16strlcpy(&mut info.title, &name);
588            u16strlcpy(&mut info.shortTitle, &name);
589            info.flags = ParameterFlags_::kIsReadOnly | (1 << 4); // kIsHidden
590        } else {
591            let param_hash = &self.inner.param_hashes[param_index as usize];
592            let param_unit = &self
593                .inner
594                .param_units
595                .get_vst3_unit_id(*param_hash)
596                .expect("Inconsistent parameter data");
597            let param_ptr = &self.inner.param_by_hash[param_hash];
598            let default_value = unsafe { param_ptr.default_normalized_value() };
599            let flags = unsafe { param_ptr.flags() };
600            let automatable = !flags.contains(ParamFlags::NON_AUTOMATABLE);
601            let hidden = flags.contains(ParamFlags::HIDDEN);
602            let is_bypass = flags.contains(ParamFlags::BYPASS);
603
604            info.id = *param_hash;
605            u16strlcpy(&mut info.title, unsafe { param_ptr.name() });
606            u16strlcpy(&mut info.shortTitle, unsafe { param_ptr.name() });
607            u16strlcpy(&mut info.units, unsafe { param_ptr.unit() });
608            info.stepCount = unsafe { param_ptr.step_count().unwrap_or(0) } as i32;
609            info.defaultNormalizedValue = default_value as f64;
610            info.unitId = *param_unit;
611            info.flags = 0;
612            if automatable && !hidden {
613                info.flags |= ParameterFlags_::kCanAutomate;
614            }
615            if hidden {
616                info.flags |= ParameterFlags_::kIsReadOnly | (1 << 4); // kIsHidden
617            }
618            if is_bypass {
619                info.flags |= ParameterFlags_::kIsBypass;
620            }
621        }
622
623        kResultOk
624    }
625
626    unsafe fn getParamStringByValue(
627        &self,
628        id: ParamID,
629        value_normalized: ParamValue,
630        string: *mut String128,
631    ) -> tresult {
632        check_null_ptr!(string);
633
634        let dest = unsafe { &mut *(string) };
635
636        // TODO: We don't implement these methods at all for our generated MIDI CC parameters,
637        //       should be fine right? They should be hidden anyways.
638        match self.inner.param_by_hash.get(&id) {
639            Some(param_ptr) => {
640                unsafe {
641                    u16strlcpy(
642                        dest,
643                        &param_ptr.normalized_value_to_string(value_normalized as f32, false),
644                    );
645                }
646
647                kResultOk
648            }
649            _ => kInvalidArgument,
650        }
651    }
652
653    unsafe fn getParamValueByString(
654        &self,
655        id: ParamID,
656        string: *mut TChar,
657        value_normalized: *mut ParamValue,
658    ) -> tresult {
659        check_null_ptr!(string, value_normalized);
660
661        let string = match unsafe { U16CStr::from_ptr_str(string as *const u16).to_string() } {
662            Ok(s) => s,
663            Err(_) => return kInvalidArgument,
664        };
665
666        match self.inner.param_by_hash.get(&id) {
667            Some(param_ptr) => {
668                let value = match unsafe { param_ptr.string_to_normalized_value(&string) } {
669                    Some(v) => v as f64,
670                    None => return kResultFalse,
671                };
672                unsafe { *value_normalized = value };
673
674                kResultOk
675            }
676            _ => kInvalidArgument,
677        }
678    }
679
680    unsafe fn normalizedParamToPlain(
681        &self,
682        id: ParamID,
683        value_normalized: ParamValue,
684    ) -> ParamValue {
685        match self.inner.param_by_hash.get(&id) {
686            Some(param_ptr) => unsafe { param_ptr.preview_plain(value_normalized as f32) as f64 },
687            _ => value_normalized,
688        }
689    }
690
691    unsafe fn plainParamToNormalized(&self, id: ParamID, plain_value: ParamValue) -> ParamValue {
692        match self.inner.param_by_hash.get(&id) {
693            Some(param_ptr) => unsafe { param_ptr.preview_normalized(plain_value as f32) as f64 },
694            _ => plain_value,
695        }
696    }
697
698    unsafe fn getParamNormalized(&self, id: ParamID) -> ParamValue {
699        match self.inner.param_by_hash.get(&id) {
700            Some(param_ptr) => unsafe { param_ptr.modulated_normalized_value() as f64 },
701            _ => 0.5,
702        }
703    }
704
705    unsafe fn setParamNormalized(&self, id: ParamID, value: ParamValue) -> tresult {
706        // If the plugin is currently processing audio, then this parameter change will also be sent
707        // to the process function
708        if self.inner.is_processing.load(Ordering::SeqCst) {
709            return kResultOk;
710        }
711
712        let sample_rate = self
713            .inner
714            .current_buffer_config
715            .load()
716            .map(|c| c.sample_rate);
717        self.inner
718            .set_normalized_value_by_hash(id, value as f32, sample_rate)
719    }
720
721    unsafe fn setComponentHandler(&self, handler: *mut IComponentHandler) -> tresult {
722        *self.inner.component_handler.borrow_mut() =
723            unsafe { ComRef::from_raw(handler) }.map(|r| r.to_com_ptr());
724
725        kResultOk
726    }
727
728    unsafe fn createView(&self, _name: FIDString) -> *mut IPlugView {
729        // Without specialization this is the least redundant way to check if the plugin has an
730        // editor. The default implementation returns a None here.
731        match self.inner.editor.borrow().as_ref() {
732            Some(editor) => {
733                let view = ComWrapper::new(WrapperView::new(self.inner.clone(), editor.clone()));
734                let plug_view_ptr = view.to_com_ptr::<IPlugView>().unwrap().into_raw();
735                *self.inner.plug_view.write() = Some(view);
736                plug_view_ptr
737            }
738            None => std::ptr::null_mut(),
739        }
740    }
741}
742
743impl<P: Vst3Plugin> IAudioProcessorTrait for Wrapper<P> {
744    unsafe fn setBusArrangements(
745        &self,
746        inputs: *mut SpeakerArrangement,
747        num_ins: int32,
748        outputs: *mut SpeakerArrangement,
749        num_outs: int32,
750    ) -> tresult {
751        check_null_ptr!(inputs, outputs);
752
753        // Why are these signed integers again?
754        if num_ins < 0 || num_outs < 0 {
755            return kInvalidArgument;
756        }
757
758        // nice-plug no longer supports flexible IO layouts. Instead we'll try to find an audio IO
759        // layout that matches the host's requested layout.
760        let matching_layout = P::AUDIO_IO_LAYOUTS
761            .iter()
762            .find(|layout| {
763                // If the number of ports/busses doesn't match then we can immediately discard the
764                // layout. VST3 doesn't allow for optional switchable ports like CLAP does. Only the
765                // channel counts can change.
766                let num_layout_ins = if layout.main_input_channels.is_some() {
767                    1
768                } else {
769                    0
770                } + layout.aux_input_ports.len();
771                let num_layout_outs = if layout.main_output_channels.is_some() {
772                    1
773                } else {
774                    0
775                } + layout.aux_output_ports.len();
776                if num_ins as usize != num_layout_ins || num_outs as usize != num_layout_outs {
777                    return false;
778                }
779
780                // NOTE: We completely ignore the speaker arrangements and only look at the channel
781                //       counts here. This may cause issues at some point, but it works for now.
782                let has_main_input = layout.main_input_channels.is_some();
783                let aux_input_start_idx = if has_main_input { 0 } else { 1 };
784                if has_main_input
785                    && unsafe {
786                        (*inputs).count_ones() != layout.main_input_channels.unwrap().get()
787                    }
788                {
789                    return false;
790                }
791                for (aux_input_idx, channel_count) in layout.aux_input_ports.iter().enumerate() {
792                    if unsafe {
793                        (*inputs.add(aux_input_idx + aux_input_start_idx)).count_ones()
794                            != channel_count.get()
795                    } {
796                        return false;
797                    }
798                }
799
800                let has_main_output = layout.main_output_channels.is_some();
801                let aux_output_start_idx = if has_main_output { 0 } else { 1 };
802                if unsafe {
803                    (*outputs).count_ones()
804                        != layout
805                            .main_output_channels
806                            .map(NonZeroU32::get)
807                            .unwrap_or_default()
808                } {
809                    return false;
810                }
811                for (aux_output_idx, channel_count) in layout.aux_output_ports.iter().enumerate() {
812                    if unsafe {
813                        (*outputs.add(aux_output_idx + aux_output_start_idx)).count_ones()
814                            != channel_count.get()
815                    } {
816                        return false;
817                    }
818                }
819
820                true
821            })
822            .copied();
823
824        match matching_layout {
825            Some(layout) => {
826                // This layout is used from hereon onwards, at least until this function is called
827                // again
828                self.inner.current_audio_io_layout.store(layout);
829
830                kResultOk
831            }
832            None => kResultFalse,
833        }
834    }
835
836    unsafe fn getBusArrangement(
837        &self,
838        dir: BusDirection,
839        index: i32,
840        arr: *mut SpeakerArrangement,
841    ) -> tresult {
842        check_null_ptr!(arr);
843
844        let channel_count_to_map = |count| match count {
845            0 => vst3::Steinberg::Vst::SpeakerArr::kEmpty,
846            1 => vst3::Steinberg::Vst::SpeakerArr::kMono,
847            2 => vst3::Steinberg::Vst::SpeakerArr::kStereo,
848            5 => vst3::Steinberg::Vst::SpeakerArr::k50,
849            6 => vst3::Steinberg::Vst::SpeakerArr::k51,
850            7 => vst3::Steinberg::Vst::SpeakerArr::k70Cine,
851            8 => vst3::Steinberg::Vst::SpeakerArr::k71Cine,
852            n => {
853                crate::nice_debug_assert_failure!(
854                    "No defined layout for {} channels, making something up on the spot...",
855                    n
856                );
857                (1 << n) - 1
858            }
859        };
860
861        let current_audio_io_layout = self.inner.current_audio_io_layout.load();
862        let num_channels = if dir == K_BUS_DIRECTION_INPUT {
863            let has_main_input = current_audio_io_layout.main_input_channels.is_some();
864            let aux_input_start_idx = if has_main_input { 1 } else { 0 };
865            let aux_input_idx = (index - aux_input_start_idx).max(0) as usize;
866            if index == 0 && has_main_input {
867                current_audio_io_layout.main_input_channels.unwrap().get()
868            } else if aux_input_idx < current_audio_io_layout.aux_input_ports.len() {
869                current_audio_io_layout.aux_input_ports[aux_input_idx].get()
870            } else {
871                return kInvalidArgument;
872            }
873        } else if dir == K_BUS_DIRECTION_OUTPUT {
874            let has_main_output = current_audio_io_layout.main_output_channels.is_some();
875            let aux_output_start_idx = if has_main_output { 1 } else { 0 };
876            let aux_output_idx = (index - aux_output_start_idx).max(0) as usize;
877            if index == 0 && has_main_output {
878                current_audio_io_layout.main_output_channels.unwrap().get()
879            } else if aux_output_idx < current_audio_io_layout.aux_output_ports.len() {
880                current_audio_io_layout.aux_output_ports[aux_output_idx].get()
881            } else {
882                return kInvalidArgument;
883            }
884        } else {
885            return kInvalidArgument;
886        };
887        let channel_map = channel_count_to_map(num_channels);
888
889        crate::nice_debug_assert_eq!(num_channels, channel_map.count_ones());
890        unsafe { *arr = channel_map };
891
892        kResultOk
893    }
894
895    unsafe fn canProcessSampleSize(&self, symbolic_sample_size: int32) -> tresult {
896        if symbolic_sample_size == K_SYMBOLIC_SAMPLE_SIZE_32 {
897            kResultOk
898        } else {
899            kResultFalse
900        }
901    }
902
903    unsafe fn getLatencySamples(&self) -> uint32 {
904        self.inner.current_latency.load(Ordering::SeqCst)
905    }
906
907    unsafe fn setupProcessing(&self, setup: *mut ProcessSetup) -> tresult {
908        check_null_ptr!(setup);
909
910        // There's no special handling for offline processing at the moment
911        let setup = unsafe { &*setup };
912        crate::nice_debug_assert_eq!(setup.symbolicSampleSize, K_SYMBOLIC_SAMPLE_SIZE_32);
913
914        // This is needed when activating the plugin and when restoring state
915        self.inner.current_buffer_config.store(Some(BufferConfig {
916            sample_rate: setup.sampleRate as f32,
917            min_buffer_size: None,
918            max_buffer_size: setup.maxSamplesPerBlock as u32,
919            process_mode: self.inner.current_process_mode.load(),
920        }));
921
922        #[allow(clippy::unnecessary_cast)]
923        const K_REALTIME: i32 = ProcessModes_::kRealtime as i32;
924        #[allow(clippy::unnecessary_cast)]
925        const K_PREFETCH: i32 = ProcessModes_::kPrefetch as i32;
926        #[allow(clippy::unnecessary_cast)]
927        const K_OFFLINE: i32 = ProcessModes_::kOffline as i32;
928
929        let mode = match setup.processMode {
930            n if n == K_REALTIME => ProcessMode::Realtime,
931            n if n == K_PREFETCH => ProcessMode::Buffered,
932            n if n == K_OFFLINE => ProcessMode::Offline,
933            n => {
934                crate::nice_debug_assert_failure!(
935                    "Unknown rendering mode '{}', defaulting to realtime",
936                    n
937                );
938                ProcessMode::Realtime
939            }
940        };
941        self.inner.current_process_mode.store(mode);
942
943        // Initializing the plugin happens in `IAudioProcessor::set_active()` because the host may
944        // still change the channel layouts at this point
945
946        kResultOk
947    }
948
949    unsafe fn setProcessing(&self, state: TBool) -> tresult {
950        let state = state != 0;
951
952        // Always reset the processing status when the plugin gets activated or deactivated
953        self.inner.last_process_status.store(ProcessStatus::Normal);
954        self.inner.is_processing.store(state, Ordering::SeqCst);
955
956        // This function is also used to reset buffers on the plugin, so we should do the same
957        // thing. We don't call `reset()` in `setup_processing()` for that same reason.
958        if state {
959            // HACK: See the comment in `IComponent::setActive()`. This is needed to work around
960            //       Ardour bugs.
961            let mut plugin = match self.inner.plugin.try_lock() {
962                Some(plugin) => plugin,
963                None => {
964                    crate::nice_debug_assert_failure!(
965                        "The host tried to call IAudioProcessor::setProcessing(true) during a \
966                         reentrent call to IComponent::setActive(true), returning kResultOk. If \
967                         this is Ardour then it will still call \
968                         IAudioProcessor::setProcessing(true) later and everything will be fine. \
969                         Hopefully."
970                    );
971                    return kResultOk;
972                }
973            };
974
975            process_wrapper(|| plugin.reset());
976        }
977
978        // We don't have any special handling for suspending and resuming plugins, yet
979        kResultOk
980    }
981
982    // Clippy doesn't understand our `event_start_idx`
983    #[allow(clippy::mut_range_bound)]
984    unsafe fn process(&self, data: *mut ProcessData) -> tresult {
985        check_null_ptr!(data);
986
987        // Panic on allocations if the `assert_process_allocs` feature has been enabled, and make
988        // sure that FTZ is set up correctly
989        process_wrapper(|| {
990            // We need to handle incoming automation first
991            let data = unsafe { &*data };
992            let sample_rate = self
993                .inner
994                .current_buffer_config
995                .load()
996                .expect("Process call without prior setup call")
997                .sample_rate;
998
999            crate::nice_debug_assert!(data.numInputs >= 0 && data.numOutputs >= 0);
1000            crate::nice_debug_assert_eq!(data.symbolicSampleSize, K_SYMBOLIC_SAMPLE_SIZE_32);
1001            crate::nice_debug_assert!(data.numSamples >= 0);
1002
1003            let total_buffer_len = data.numSamples as usize;
1004
1005            let current_audio_io_layout = self.inner.current_audio_io_layout.load();
1006            let has_main_input = current_audio_io_layout.main_input_channels.is_some();
1007            let has_main_output = current_audio_io_layout.main_output_channels.is_some();
1008            let aux_input_start_idx = if has_main_input { 1 } else { 0 };
1009            let aux_output_start_idx = if has_main_output { 1 } else { 0 };
1010
1011            // NOTE: VST3 hosts may trigger a 'parameter flush' by calling the process function for
1012            //       0 input samples. If this is the case then we'll only handle events and skip all
1013            //       audio processing. Some hosts, like Ableton Live, implement this in a broken way
1014            //       and instead only set the number of channels to 0. In that case the
1015            //       'buffer_is_valid' check from below should still prevent audio processing.
1016            let mut is_param_flush = total_buffer_len == 0;
1017            if (data.numOutputs == 0 || data.outputs.is_null())
1018                && (has_main_output || !current_audio_io_layout.aux_output_ports.is_empty())
1019            {
1020                is_param_flush = true;
1021            }
1022
1023            // If `P::SAMPLE_ACCURATE_AUTOMATION` is set, then we'll split up the audio buffer into
1024            // chunks whenever a parameter change occurs. To do that, we'll store all of those
1025            // parameter changes in a vector. Otherwise all parameter changes are handled right here
1026            // and now. We'll also need to store the note events in the same vector because MIDI CC
1027            // messages are sent through parameter changes. This vector gets sorted at the end so we
1028            // can treat it as a sort of queue.
1029            let mut process_events = self.inner.process_events.borrow_mut();
1030            process_events.clear();
1031
1032            // First we'll go through the parameter changes. This may also include MIDI CC messages
1033            // if the plugin supports those
1034            if let Some(param_changes) = unsafe { ComRef::from_raw(data.inputParameterChanges) } {
1035                let num_param_queues = unsafe { param_changes.getParameterCount() };
1036                for change_queue_idx in 0..num_param_queues {
1037                    if let Some(param_change_queue) = unsafe {
1038                        ComRef::from_raw(param_changes.getParameterData(change_queue_idx))
1039                    } {
1040                        let param_hash = unsafe { param_change_queue.getParameterId() };
1041                        let num_changes = unsafe { param_change_queue.getPointCount() };
1042                        if num_changes <= 0 {
1043                            continue;
1044                        }
1045
1046                        let mut sample_offset = 0i32;
1047                        let mut value = 0.0f64;
1048                        for change_idx in 0..num_changes {
1049                            if unsafe {
1050                                param_change_queue.getPoint(
1051                                    change_idx,
1052                                    &mut sample_offset,
1053                                    &mut value,
1054                                ) == kResultOk
1055                            } {
1056                                // Later this timing will be compensated for block splits by calling
1057                                // `event.subtract_timing(block_start)` before it is passed to the
1058                                // plugin. Out of bounds events are clamped to the buffer>
1059                                let timing = clamp_input_event_timing(
1060                                    sample_offset as u32,
1061                                    total_buffer_len as u32,
1062                                );
1063                                let value = value as f32;
1064
1065                                // MIDI CC messages, channel pressure, and pitch bend are also sent
1066                                // as parameter changes
1067                                if P::MIDI_INPUT >= MidiConfig::MidiCCs
1068                                    && (VST3_MIDI_PARAMS_START..VST3_MIDI_PARAMS_END)
1069                                        .contains(&param_hash)
1070                                {
1071                                    let midi_param_relative_idx =
1072                                        param_hash - VST3_MIDI_PARAMS_START;
1073                                    // This goes up to 130 for the 128 CCs followed by channel pressure and pitch bend
1074                                    let midi_cc = (midi_param_relative_idx % VST3_MIDI_CCS) as u8;
1075                                    let midi_channel =
1076                                        (midi_param_relative_idx / VST3_MIDI_CCS) as u8;
1077                                    process_events.push(ProcessEvent::NoteEvent(match midi_cc {
1078                                        // kAfterTouch
1079                                        128 => NoteEvent::MidiChannelPressure {
1080                                            timing,
1081                                            channel: midi_channel,
1082                                            pressure: value,
1083                                        },
1084                                        // kPitchBend
1085                                        129 => NoteEvent::MidiPitchBend {
1086                                            timing,
1087                                            channel: midi_channel,
1088                                            value,
1089                                        },
1090                                        n => NoteEvent::MidiCC {
1091                                            timing,
1092                                            channel: midi_channel,
1093                                            cc: n,
1094                                            value,
1095                                        },
1096                                    }));
1097                                } else if P::SAMPLE_ACCURATE_AUTOMATION {
1098                                    process_events.push(ProcessEvent::ParameterChange {
1099                                        timing,
1100                                        hash: param_hash,
1101                                        normalized_value: value,
1102                                    });
1103                                } else {
1104                                    self.inner.set_normalized_value_by_hash(
1105                                        param_hash,
1106                                        value,
1107                                        Some(sample_rate),
1108                                    );
1109                                }
1110                            }
1111                        }
1112                    }
1113                }
1114            }
1115
1116            // Then we'll add all of our input events
1117            if P::MIDI_INPUT >= MidiConfig::Basic {
1118                let mut note_expression_controller =
1119                    self.inner.note_expression_controller.borrow_mut();
1120                if let Some(events) = unsafe { ComRef::from_raw(data.inputEvents) } {
1121                    let num_events = unsafe { events.getEventCount() };
1122
1123                    let mut event: MaybeUninit<_> = MaybeUninit::uninit();
1124                    for i in 0..num_events {
1125                        let result = unsafe { events.getEvent(i, event.as_mut_ptr()) };
1126                        crate::nice_debug_assert_eq!(result, kResultOk);
1127
1128                        let event = unsafe { event.assume_init() };
1129                        let timing = clamp_input_event_timing(
1130                            event.sampleOffset as u32,
1131                            total_buffer_len as u32,
1132                        );
1133
1134                        if event.r#type == EventTypes_::kNoteOnEvent as u16 {
1135                            let event = unsafe { event.__field0.noteOn };
1136
1137                            // We need to keep track of note IDs to be able to handle not
1138                            // expression value events
1139                            note_expression_controller.register_note(&event);
1140
1141                            process_events.push(ProcessEvent::NoteEvent(NoteEvent::NoteOn {
1142                                timing,
1143                                voice_id: if event.noteId != -1 {
1144                                    Some(event.noteId)
1145                                } else {
1146                                    None
1147                                },
1148                                channel: event.channel as u8,
1149                                note: event.pitch as u8,
1150                                velocity: event.velocity,
1151                            }));
1152                        } else if event.r#type == EventTypes_::kNoteOffEvent as u16 {
1153                            let event = unsafe { event.__field0.noteOff };
1154                            process_events.push(ProcessEvent::NoteEvent(NoteEvent::NoteOff {
1155                                timing,
1156                                voice_id: if event.noteId != -1 {
1157                                    Some(event.noteId)
1158                                } else {
1159                                    None
1160                                },
1161                                channel: event.channel as u8,
1162                                note: event.pitch as u8,
1163                                velocity: event.velocity,
1164                            }));
1165                        } else if event.r#type == EventTypes_::kPolyPressureEvent as u16 {
1166                            let event = unsafe { event.__field0.polyPressure };
1167                            process_events.push(ProcessEvent::NoteEvent(NoteEvent::PolyPressure {
1168                                timing,
1169                                voice_id: if event.noteId != -1 {
1170                                    Some(event.noteId)
1171                                } else {
1172                                    None
1173                                },
1174                                channel: event.channel as u8,
1175                                note: event.pitch as u8,
1176                                pressure: event.pressure,
1177                            }));
1178                        } else if event.r#type == EventTypes_::kNoteExpressionValueEvent as u16 {
1179                            let event = unsafe { event.__field0.noteExpressionValue };
1180                            match note_expression_controller.translate_event(timing, &event) {
1181                                Some(translated_event) => {
1182                                    process_events.push(ProcessEvent::NoteEvent(translated_event))
1183                                }
1184                                None => crate::nice_debug_assert_failure!(
1185                                    "Unhandled note expression type: {}",
1186                                    event.typeId
1187                                ),
1188                            }
1189                        } else if event.r#type == EventTypes_::kDataEvent as u16
1190                            && unsafe { event.__field0.data.r#type } == 0
1191                        {
1192                            // 0 = kMidiSysEx
1193                            let event = unsafe { event.__field0.data };
1194
1195                            // `NoteEvent::from_midi` prints some tracing if parsing fails, which is
1196                            // not necessarily an error
1197                            assert!(!event.bytes.is_null());
1198                            let sysex_buffer = unsafe {
1199                                std::slice::from_raw_parts(event.bytes, event.size as usize)
1200                            };
1201                            if let Ok(note_event) = NoteEvent::from_midi(timing, sysex_buffer) {
1202                                process_events.push(ProcessEvent::NoteEvent(note_event));
1203                            };
1204                        }
1205                    }
1206                }
1207            }
1208
1209            // And then we'll make sure everything is in the right order
1210            // NOTE: It's important that this sort is stable, because parameter changes need to be
1211            //       processed before note events. Otherwise you'll get out of bounds note events
1212            //       with block splitting when the note event occurs at one index after the end (or
1213            //       on the exclusive end index) of the block.
1214            // FIXME: Apparently stable sort allcoates if the slice is large enough. This should be
1215            //        fixed at some point.
1216            permit_alloc(|| {
1217                process_events.sort_by_key(|event| match event {
1218                    ProcessEvent::ParameterChange { timing, .. } => *timing,
1219                    ProcessEvent::NoteEvent(event) => event.timing(),
1220                })
1221            });
1222
1223            let mut block_start = 0usize;
1224            let mut block_end;
1225            let mut event_start_idx = 0;
1226            let result = loop {
1227                // In sample-accurate automation mode we'll handle all parameter changes from the
1228                // sorted process event array until we run into for the current sample, and then
1229                // process the block between the current sample and the sample containing the next
1230                // parameter change, if any. All timings also need to be compensated for this. As
1231                // mentioned above, for this to work correctly parameter changes need to be ordered
1232                // before note events at the same index.
1233                // The extra scope is here to make sure we release the borrow on input_events
1234                {
1235                    let mut input_events = self.inner.input_events.borrow_mut();
1236                    input_events.clear();
1237
1238                    block_end = total_buffer_len;
1239                    for event_idx in event_start_idx..process_events.len() {
1240                        match &process_events[event_idx] {
1241                            ProcessEvent::ParameterChange {
1242                                timing,
1243                                hash,
1244                                normalized_value,
1245                            } => {
1246                                // If this parameter change happens after the start of this block, then
1247                                // we'll split the block here and handle this parameter change after
1248                                // we've processed this block
1249                                if *timing != block_start as u32 {
1250                                    event_start_idx = event_idx;
1251                                    block_end = *timing as usize;
1252                                    break;
1253                                }
1254
1255                                self.inner.set_normalized_value_by_hash(
1256                                    *hash,
1257                                    *normalized_value,
1258                                    Some(sample_rate),
1259                                );
1260                            }
1261                            ProcessEvent::NoteEvent(event) => {
1262                                // We need to make sure to compensate the event for any block splitting,
1263                                // since we had to create the event object beforehand
1264                                let mut event = event.clone();
1265                                event.subtract_timing(block_start as u32);
1266                                input_events.push_back(event);
1267                            }
1268                        }
1269                    }
1270                }
1271
1272                let result = if is_param_flush {
1273                    kResultOk
1274                } else {
1275                    // After processing the events we now know where/if the block should be split,
1276                    // and we can start preparing audio processing
1277                    let block_len = block_end - block_start;
1278
1279                    // The buffer manager preallocated buffer slices for all the IO and storage for
1280                    // any axuiliary inputs.
1281                    let mut buffer_manager = self.inner.buffer_manager.borrow_mut();
1282                    let buffers = unsafe {
1283                        buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
1284                            if data.numOutputs > 0
1285                                && !data.outputs.is_null()
1286                                && !(*data.outputs).__field0.channelBuffers32.is_null()
1287                                && has_main_output
1288                            {
1289                                let audio_output = &*data.outputs;
1290                                let ptrs =
1291                                    NonNull::new(audio_output.__field0.channelBuffers32).unwrap();
1292                                let num_channels = audio_output.numChannels as usize;
1293
1294                                *buffer_source.main_output_channel_pointers =
1295                                    Some(ChannelPointers { ptrs, num_channels });
1296                            }
1297
1298                            if data.numInputs > 0
1299                                && !data.inputs.is_null()
1300                                && !(*data.inputs).__field0.channelBuffers32.is_null()
1301                                && has_main_input
1302                            {
1303                                let audio_input = &*data.inputs;
1304                                let ptrs =
1305                                    NonNull::new(audio_input.__field0.channelBuffers32).unwrap();
1306                                let num_channels = audio_input.numChannels as usize;
1307
1308                                *buffer_source.main_input_channel_pointers =
1309                                    Some(ChannelPointers { ptrs, num_channels });
1310                            }
1311
1312                            if !data.inputs.is_null() {
1313                                for (aux_input_no, aux_input_channel_pointers) in buffer_source
1314                                    .aux_input_channel_pointers
1315                                    .iter_mut()
1316                                    .enumerate()
1317                                {
1318                                    let aux_input_idx = aux_input_no + aux_input_start_idx;
1319                                    if aux_input_idx > data.numOutputs as usize {
1320                                        break;
1321                                    }
1322
1323                                    let audio_input = &*data.inputs.add(aux_input_idx);
1324                                    match NonNull::new(audio_input.__field0.channelBuffers32) {
1325                                        Some(ptrs) => {
1326                                            let num_channels = audio_input.numChannels as usize;
1327
1328                                            *aux_input_channel_pointers =
1329                                                Some(ChannelPointers { ptrs, num_channels });
1330                                        }
1331                                        None => continue,
1332                                    }
1333                                }
1334                            }
1335
1336                            if !data.outputs.is_null() {
1337                                for (aux_output_no, aux_output_channel_pointers) in buffer_source
1338                                    .aux_output_channel_pointers
1339                                    .iter_mut()
1340                                    .enumerate()
1341                                {
1342                                    let aux_output_idx = aux_output_no + aux_output_start_idx;
1343                                    if aux_output_idx > data.numOutputs as usize {
1344                                        break;
1345                                    }
1346
1347                                    let audio_output = &*data.outputs.add(aux_output_idx);
1348                                    match NonNull::new(audio_output.__field0.channelBuffers32) {
1349                                        Some(ptrs) => {
1350                                            let num_channels = audio_output.numChannels as usize;
1351
1352                                            *aux_output_channel_pointers =
1353                                                Some(ChannelPointers { ptrs, num_channels });
1354                                        }
1355                                        None => continue,
1356                                    }
1357                                }
1358                            }
1359                        })
1360                    };
1361
1362                    // We already checked whether the host has initiated a parameter flush, but in
1363                    // case it still did something unexpected that we did not catch we'll still try
1364                    // to prevent processing audio when the slices don't contain the values we
1365                    // expect.
1366                    let mut buffer_is_valid = true;
1367                    for output_buffer_slice in
1368                        buffers.main_buffer.as_slice_immutable().iter().chain(
1369                            buffers
1370                                .aux_outputs
1371                                .iter()
1372                                .flat_map(|buffer| buffer.as_slice_immutable().iter()),
1373                        )
1374                    {
1375                        if output_buffer_slice.is_empty() {
1376                            buffer_is_valid = false;
1377                            break;
1378                        }
1379                    }
1380                    crate::nice_debug_assert!(buffer_is_valid);
1381
1382                    // Some of the fields are left empty because VST3 does not provide this
1383                    // information, but the methods on [`Transport`] can reconstruct these values
1384                    // from the other fields
1385                    let mut transport = Transport::new(sample_rate);
1386                    if !data.processContext.is_null() {
1387                        let context = unsafe { &*data.processContext };
1388
1389                        #[allow(clippy::unnecessary_cast)]
1390                        {
1391                            transport.playing = context.state & kPlaying as u32 != 0;
1392                            transport.recording = context.state & kRecording as u32 != 0;
1393
1394                            if context.state & kTempoValid as u32 != 0 {
1395                                transport.tempo = Some(context.tempo);
1396                            }
1397
1398                            if context.state & kTimeSigValid as u32 != 0 {
1399                                transport.time_sig_numerator = Some(context.timeSigNumerator);
1400                                transport.time_sig_denominator = Some(context.timeSigDenominator);
1401                            }
1402                        }
1403
1404                        // We need to compensate for the block splitting here
1405                        transport.pos_samples =
1406                            Some(context.projectTimeSamples + block_start as i64);
1407                        #[allow(clippy::unnecessary_cast)]
1408                        if context.state & kProjectTimeMusicValid as u32 != 0 {
1409                            if P::SAMPLE_ACCURATE_AUTOMATION
1410                                && block_start > 0
1411                                && (context.state & kTempoValid as u32 != 0)
1412                            {
1413                                transport.pos_beats = Some(
1414                                    context.projectTimeMusic
1415                                        + (block_start as f64 / sample_rate as f64 / 60.0
1416                                            * context.tempo),
1417                                );
1418                            } else {
1419                                transport.pos_beats = Some(context.projectTimeMusic);
1420                            }
1421                        }
1422
1423                        #[allow(clippy::unnecessary_cast)]
1424                        if context.state & kBarPositionValid as u32 != 0 {
1425                            if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 {
1426                                // The transport object knows how to recompute this from the other information
1427                                transport.bar_start_pos_beats =
1428                                    match transport.bar_start_pos_beats() {
1429                                        Some(updated) => Some(updated),
1430                                        None => Some(context.barPositionMusic),
1431                                    };
1432                            } else {
1433                                transport.bar_start_pos_beats = Some(context.barPositionMusic);
1434                            }
1435                        }
1436                        #[allow(clippy::unnecessary_cast)]
1437                        if context.state & kCycleActive as u32 != 0
1438                            && context.state & kCycleValid as u32 != 0
1439                        {
1440                            transport.loop_range_beats =
1441                                Some((context.cycleStartMusic, context.cycleEndMusic));
1442                        }
1443                    }
1444
1445                    let result = if buffer_is_valid {
1446                        // NOTE: `parking_lot`'s mutexes sometimes allocate because of their use of
1447                        //       thread locals
1448                        let mut plugin = permit_alloc(|| self.inner.plugin.lock());
1449                        let mut aux = AuxiliaryBuffers {
1450                            inputs: buffers.aux_inputs,
1451                            outputs: buffers.aux_outputs,
1452                        };
1453                        let mut context = self.inner.make_process_context(transport);
1454                        let result = plugin.process(buffers.main_buffer, &mut aux, &mut context);
1455                        self.inner.last_process_status.store(result);
1456                        result
1457                    } else {
1458                        ProcessStatus::Normal
1459                    };
1460
1461                    match result {
1462                        ProcessStatus::Error(err) => {
1463                            crate::nice_debug_assert_failure!("Process error: {}", err);
1464
1465                            return kResultFalse;
1466                        }
1467                        _ => kResultOk,
1468                    }
1469                };
1470
1471                // Send any events output by the plugin during the process cycle
1472                if let Some(events) = unsafe { ComRef::from_raw(data.outputEvents) } {
1473                    let mut output_events = self.inner.output_events.borrow_mut();
1474                    while let Some(event) = output_events.pop_front() {
1475                        // We'll set the correct variant on this struct, or skip to the next loop
1476                        // iteration if we don't handle the event type
1477                        let mut vst3_event: Event = unsafe { mem::zeroed() };
1478                        vst3_event.busIndex = 0;
1479                        // There's also a ppqPos field, but uh how about no
1480                        vst3_event.sampleOffset = clamp_output_event_timing(
1481                            event.timing() + block_start as u32,
1482                            total_buffer_len as u32,
1483                        ) as i32;
1484
1485                        // `voice_id.unwrap_or(|| ...)` triggers
1486                        // https://github.com/rust-lang/rust-clippy/issues/8522
1487                        #[allow(clippy::unnecessary_lazy_evaluations)]
1488                        match event {
1489                            NoteEvent::NoteOn {
1490                                timing: _,
1491                                voice_id,
1492                                channel,
1493                                note,
1494                                velocity,
1495                            } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1496                                vst3_event.r#type = EventTypes_::kNoteOnEvent as u16;
1497                                vst3_event.__field0.noteOn = NoteOnEvent {
1498                                    channel: channel as i16,
1499                                    pitch: note as i16,
1500                                    tuning: 0.0,
1501                                    velocity,
1502                                    length: 0, // What?
1503                                    // We'll use this for our note IDs, that way we don't have to do
1504                                    // anything complicated here
1505                                    noteId: voice_id
1506                                        .unwrap_or_else(|| ((channel as i32) << 8) | note as i32),
1507                                };
1508                            }
1509                            NoteEvent::NoteOff {
1510                                timing: _,
1511                                voice_id,
1512                                channel,
1513                                note,
1514                                velocity,
1515                            } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1516                                vst3_event.r#type = EventTypes_::kNoteOffEvent as u16;
1517                                vst3_event.__field0.noteOff = NoteOffEvent {
1518                                    channel: channel as i16,
1519                                    pitch: note as i16,
1520                                    velocity,
1521                                    noteId: voice_id
1522                                        .unwrap_or_else(|| ((channel as i32) << 8) | note as i32),
1523                                    tuning: 0.0,
1524                                };
1525                            }
1526                            // VST3 does not support or need these events, but they should also not
1527                            // trigger a debug assertion failure in nice-plug. Also notes how this is
1528                            // gated by `P::MIDI_INPUT`.
1529                            NoteEvent::VoiceTerminated { .. }
1530                                if P::MIDI_INPUT >= MidiConfig::Basic =>
1531                            {
1532                                continue;
1533                            }
1534                            NoteEvent::PolyPressure {
1535                                timing: _,
1536                                voice_id,
1537                                channel,
1538                                note,
1539                                pressure,
1540                            } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1541                                vst3_event.r#type = EventTypes_::kPolyPressureEvent as u16;
1542                                vst3_event.__field0.polyPressure = PolyPressureEvent {
1543                                    channel: channel as i16,
1544                                    pitch: note as i16,
1545                                    noteId: voice_id
1546                                        .unwrap_or_else(|| ((channel as i32) << 8) | note as i32),
1547                                    pressure,
1548                                };
1549                            }
1550                            ref event @ (NoteEvent::PolyVolume {
1551                                voice_id,
1552                                channel,
1553                                note,
1554                                ..
1555                            }
1556                            | NoteEvent::PolyPan {
1557                                voice_id,
1558                                channel,
1559                                note,
1560                                ..
1561                            }
1562                            | NoteEvent::PolyTuning {
1563                                voice_id,
1564                                channel,
1565                                note,
1566                                ..
1567                            }
1568                            | NoteEvent::PolyVibrato {
1569                                voice_id,
1570                                channel,
1571                                note,
1572                                ..
1573                            }
1574                            | NoteEvent::PolyExpression {
1575                                voice_id,
1576                                channel,
1577                                note,
1578                                ..
1579                            }
1580                            | NoteEvent::PolyBrightness {
1581                                voice_id,
1582                                channel,
1583                                note,
1584                                ..
1585                            }) if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1586                                match NoteExpressionController::translate_event_reverse(
1587                                    voice_id
1588                                        .unwrap_or_else(|| ((channel as i32) << 8) | note as i32),
1589                                    event,
1590                                ) {
1591                                    Some(translated_event) => {
1592                                        vst3_event.r#type =
1593                                            EventTypes_::kNoteExpressionValueEvent as u16;
1594                                        vst3_event.__field0.noteExpressionValue = translated_event;
1595                                    }
1596                                    None => {
1597                                        crate::nice_debug_assert_failure!(
1598                                            "Mishandled note expression value event"
1599                                        );
1600                                    }
1601                                }
1602                            }
1603                            NoteEvent::MidiChannelPressure {
1604                                timing: _,
1605                                channel,
1606                                pressure,
1607                            } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => {
1608                                vst3_event.r#type = EventTypes_::kLegacyMIDICCOutEvent as u16;
1609                                vst3_event.__field0.midiCCOut = LegacyMIDICCOutEvent {
1610                                    controlNumber: 128, // kAfterTouch
1611                                    channel: channel as std::ffi::c_char,
1612                                    value: (pressure * 127.0).round() as std::ffi::c_char,
1613                                    value2: 0,
1614                                };
1615                            }
1616                            NoteEvent::MidiPitchBend {
1617                                timing: _,
1618                                channel,
1619                                value,
1620                            } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => {
1621                                let scaled = (value * ((1 << 14) - 1) as f32).round() as i32;
1622
1623                                vst3_event.r#type = EventTypes_::kLegacyMIDICCOutEvent as u16;
1624                                vst3_event.__field0.midiCCOut = LegacyMIDICCOutEvent {
1625                                    controlNumber: 129, // kPitchBend
1626                                    channel: channel as std::ffi::c_char,
1627                                    value: (scaled & 0b01111111) as std::ffi::c_char,
1628                                    value2: ((scaled >> 7) & 0b01111111) as std::ffi::c_char,
1629                                };
1630                            }
1631                            NoteEvent::MidiCC {
1632                                timing: _,
1633                                channel,
1634                                cc,
1635                                value,
1636                            } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => {
1637                                vst3_event.r#type = EventTypes_::kLegacyMIDICCOutEvent as u16;
1638                                vst3_event.__field0.midiCCOut = LegacyMIDICCOutEvent {
1639                                    controlNumber: cc,
1640                                    channel: channel as std::ffi::c_char,
1641                                    value: (value * 127.0).round() as std::ffi::c_char,
1642                                    value2: 0,
1643                                };
1644                            }
1645                            NoteEvent::MidiProgramChange {
1646                                timing: _,
1647                                channel,
1648                                program,
1649                            } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => {
1650                                vst3_event.r#type = EventTypes_::kLegacyMIDICCOutEvent as u16;
1651                                vst3_event.__field0.midiCCOut = LegacyMIDICCOutEvent {
1652                                    controlNumber: 130, // kCtrlProgramChange
1653                                    channel: channel as std::ffi::c_char,
1654                                    value: program as std::ffi::c_char,
1655                                    value2: 0,
1656                                };
1657                            }
1658                            NoteEvent::MidiSysEx { timing: _, message }
1659                                if P::MIDI_OUTPUT >= MidiConfig::Basic =>
1660                            {
1661                                let (padded_sysex_buffer, length) = message.to_buffer();
1662                                let padded_sysex_buffer = padded_sysex_buffer.borrow();
1663                                crate::nice_debug_assert!(padded_sysex_buffer.len() >= length);
1664                                let sysex_buffer = &padded_sysex_buffer[..length];
1665
1666                                vst3_event.r#type = EventTypes_::kDataEvent as u16;
1667                                vst3_event.__field0.data = DataEvent {
1668                                    size: sysex_buffer.len() as u32,
1669                                    r#type: 0, // kMidiSysEx
1670                                    bytes: sysex_buffer.as_ptr(),
1671                                };
1672
1673                                // NOTE: We need to have this call here while `sysex_buffer` is
1674                                //       still in scope since the event contains pointers to it
1675                                let result = unsafe { events.addEvent(&mut vst3_event) };
1676                                crate::nice_debug_assert_eq!(result, kResultOk);
1677                                continue;
1678                            }
1679                            _ => {
1680                                crate::nice_debug_assert_failure!(
1681                                    "Invalid output event for the current MIDI_OUTPUT setting"
1682                                );
1683                                continue;
1684                            }
1685                        };
1686
1687                        let result = unsafe { events.addEvent(&mut vst3_event) };
1688                        crate::nice_debug_assert_eq!(result, kResultOk);
1689                    }
1690                }
1691
1692                // If our block ends at the end of the buffer then that means there are no more
1693                // unprocessed (parameter) events. If there are more events, we'll just keep going
1694                // through this process until we've processed the entire buffer.
1695                if block_end == total_buffer_len {
1696                    break result;
1697                } else {
1698                    block_start = block_end;
1699                }
1700            };
1701
1702            // After processing audio, we'll check if the editor has sent us updated plugin state.
1703            // We'll restore that here on the audio thread to prevent changing the values during the
1704            // process call and also to prevent inconsistent state when the host also wants to load
1705            // plugin state.
1706            // FIXME: Zero capacity channels allocate on receiving, find a better alternative that
1707            //        doesn't do that
1708            let updated_state = permit_alloc(|| self.inner.updated_state_receiver.try_recv());
1709            if let Ok(mut state) = updated_state {
1710                self.inner.set_state_inner(&mut state);
1711
1712                // We'll pass the state object back to the GUI thread so deallocation can happen
1713                // there without potentially blocking the audio thread
1714                if let Err(err) = self.inner.updated_state_sender.send(state) {
1715                    crate::nice_debug_assert_failure!(
1716                        "Failed to send state object back to GUI thread: {}",
1717                        err
1718                    );
1719                };
1720            }
1721
1722            result
1723        })
1724    }
1725
1726    unsafe fn getTailSamples(&self) -> uint32 {
1727        // https://github.com/steinbergmedia/vst3_pluginterfaces/blob/2ad397ade5b51007860bedb3b01b8afd2c5f6fba/vst/ivstaudioprocessor.h#L145-L159
1728        match self.inner.last_process_status.load() {
1729            ProcessStatus::Tail(samples) => samples,
1730            ProcessStatus::KeepAlive => u32::MAX, // kInfiniteTail
1731            _ => 0,                               // kNoTail
1732        }
1733    }
1734}
1735
1736impl<P: Vst3Plugin> IMidiMappingTrait for Wrapper<P> {
1737    unsafe fn getMidiControllerAssignment(
1738        &self,
1739        bus_index: int32,
1740        channel: int16,
1741        midi_cc_number: CtrlNumber,
1742        param_id: *mut ParamID,
1743    ) -> tresult {
1744        if P::MIDI_INPUT < MidiConfig::MidiCCs
1745            || bus_index != 0
1746            || !(0..VST3_MIDI_CHANNELS as i16).contains(&channel)
1747            || !(0..VST3_MIDI_CCS as i16).contains(&midi_cc_number)
1748        {
1749            return kResultFalse;
1750        }
1751
1752        check_null_ptr!(param_id);
1753
1754        // We reserve a contiguous parameter range right at the end of the allowed parameter indices
1755        // for these MIDI CC parameters
1756        unsafe {
1757            *param_id =
1758                VST3_MIDI_PARAMS_START + midi_cc_number as u32 + (channel as u32 * VST3_MIDI_CCS)
1759        };
1760
1761        kResultOk
1762    }
1763}
1764
1765impl<P: Vst3Plugin> INoteExpressionControllerTrait for Wrapper<P> {
1766    unsafe fn getNoteExpressionCount(&self, bus_idx: int32, _channel: int16) -> int32 {
1767        // Apparently you need to define the predefined note expressions. Thanks VST3.
1768        if P::MIDI_INPUT >= MidiConfig::Basic && bus_idx == 0 {
1769            note_expressions::KNOWN_NOTE_EXPRESSIONS.len() as i32
1770        } else {
1771            0
1772        }
1773    }
1774
1775    unsafe fn getNoteExpressionInfo(
1776        &self,
1777        bus_idx: int32,
1778        _channel: int16,
1779        note_expression_idx: int32,
1780        info: *mut NoteExpressionTypeInfo,
1781    ) -> tresult {
1782        if P::MIDI_INPUT < MidiConfig::Basic
1783            || bus_idx != 0
1784            || !(0..note_expressions::KNOWN_NOTE_EXPRESSIONS.len() as i32)
1785                .contains(&note_expression_idx)
1786        {
1787            return kInvalidArgument;
1788        }
1789
1790        check_null_ptr!(info);
1791
1792        unsafe { *info = mem::zeroed() };
1793
1794        let info = unsafe { &mut *info };
1795        let note_expression_info =
1796            &note_expressions::KNOWN_NOTE_EXPRESSIONS[note_expression_idx as usize];
1797        info.typeId = note_expression_info.type_id;
1798        u16strlcpy(&mut info.title, note_expression_info.title);
1799        u16strlcpy(&mut info.shortTitle, note_expression_info.title);
1800        u16strlcpy(&mut info.units, note_expression_info.unit);
1801        info.unitId = kNoParentUnitId;
1802        // This should not be needed since they're predefined, but then again you'd think you also
1803        // wouldn't need to define predefined note expressions now do you?
1804        info.valueDesc = NoteExpressionValueDescription {
1805            defaultValue: 0.5,
1806            minimum: 0.0,
1807            maximum: 1.0,
1808            stepCount: 0,
1809        };
1810        info.associatedParameterId = kNoParamId;
1811        info.flags = 1 << 2; // kIsAbsolute
1812
1813        kResultOk
1814    }
1815
1816    unsafe fn getNoteExpressionStringByValue(
1817        &self,
1818        _bus_idx: int32,
1819        _channel: int16,
1820        _id: NoteExpressionTypeID,
1821        _value: NoteExpressionValue,
1822        _string: *mut String128,
1823    ) -> tresult {
1824        kResultFalse
1825    }
1826
1827    unsafe fn getNoteExpressionValueByString(
1828        &self,
1829        _bus_idx: int32,
1830        _channel: int16,
1831        _id: NoteExpressionTypeID,
1832        _string: *const TChar,
1833        _value: *mut NoteExpressionValue,
1834    ) -> tresult {
1835        kResultFalse
1836    }
1837}
1838
1839impl<P: Vst3Plugin> IProcessContextRequirementsTrait for Wrapper<P> {
1840    #[allow(clippy::unnecessary_cast)]
1841    unsafe fn getProcessContextRequirements(&self) -> uint32 {
1842        (IProcessContextRequirements_::Flags_::kNeedProjectTimeMusic
1843            | IProcessContextRequirements_::Flags_::kNeedBarPositionMusic
1844            | IProcessContextRequirements_::Flags_::kNeedCycleMusic
1845            | IProcessContextRequirements_::Flags_::kNeedTimeSignature
1846            | IProcessContextRequirements_::Flags_::kNeedTempo
1847            | IProcessContextRequirements_::Flags_::kNeedTransportState) as u32
1848    }
1849}
1850
1851impl<P: Vst3Plugin> IUnitInfoTrait for Wrapper<P> {
1852    unsafe fn getUnitCount(&self) -> int32 {
1853        self.inner.param_units.len() as i32
1854    }
1855
1856    unsafe fn getUnitInfo(&self, unit_index: int32, info: *mut UnitInfo) -> tresult {
1857        check_null_ptr!(info);
1858
1859        match self.inner.param_units.info(unit_index as usize) {
1860            Some((unit_id, unit_info)) => {
1861                unsafe { *info = mem::zeroed() };
1862
1863                let info = unsafe { &mut *info };
1864                info.id = unit_id;
1865                info.parentUnitId = unit_info.parent_id;
1866                u16strlcpy(&mut info.name, &unit_info.name);
1867                info.programListId = kNoProgramListId;
1868
1869                kResultOk
1870            }
1871            None => kInvalidArgument,
1872        }
1873    }
1874
1875    unsafe fn getProgramListCount(&self) -> int32 {
1876        // TODO: Do we want program lists? Probably not, CLAP doesn't even support them.
1877        0
1878    }
1879
1880    unsafe fn getProgramListInfo(
1881        &self,
1882        _list_index: int32,
1883        _info: *mut ProgramListInfo,
1884    ) -> tresult {
1885        kInvalidArgument
1886    }
1887
1888    unsafe fn getProgramName(
1889        &self,
1890        _list_id: ProgramListID,
1891        _program_index: int32,
1892        _name: *mut String128,
1893    ) -> tresult {
1894        kInvalidArgument
1895    }
1896
1897    unsafe fn getProgramInfo(
1898        &self,
1899        _list_id: ProgramListID,
1900        _program_index: int32,
1901        _attribute_id: CString,
1902        _attribute_value: *mut String128,
1903    ) -> tresult {
1904        kInvalidArgument
1905    }
1906
1907    unsafe fn hasProgramPitchNames(&self, _id: ProgramListID, _index: int32) -> tresult {
1908        // TODO: Support note names once someone requests it
1909        kInvalidArgument
1910    }
1911
1912    unsafe fn getProgramPitchName(
1913        &self,
1914        _id: ProgramListID,
1915        _index: int32,
1916        _pitch: int16,
1917        _name: *mut String128,
1918    ) -> tresult {
1919        kInvalidArgument
1920    }
1921
1922    unsafe fn getSelectedUnit(&self) -> UnitID {
1923        // No! Steinberg! I don't want any of this! I just want to group parameters!
1924        kRootUnitId
1925    }
1926
1927    unsafe fn selectUnit(&self, _id: UnitID) -> tresult {
1928        kResultFalse
1929    }
1930
1931    unsafe fn getUnitByBus(
1932        &self,
1933        _type_: MediaType,
1934        _dir: BusDirection,
1935        _bus_index: int32,
1936        _channel: int32,
1937        _unit_id: *mut UnitID,
1938    ) -> tresult {
1939        // Stahp it!
1940        kResultFalse
1941    }
1942
1943    unsafe fn setUnitProgramData(
1944        &self,
1945        _list_or_unit: int32,
1946        _program_idx: int32,
1947        _data: *mut IBStream,
1948    ) -> tresult {
1949        kInvalidArgument
1950    }
1951}