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