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