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 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 kNoInterface
120 }
121
122 unsafe fn setIoMode(&self, _mode: IoMode) -> tresult {
123 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 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, ¤t_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 ¤t_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 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, ¤t_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 ¤t_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 && 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 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 match (state != 0, self.inner.current_buffer_config.load()) {
410 (true, Some(buffer_config)) => {
411 for param in self.inner.param_by_hash.values() {
413 unsafe { param._internal_update_smoother(buffer_config.sample_rate, true) };
414 }
415
416 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 *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 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 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 kResultOk
542 }
543
544 unsafe fn setState(&self, _state: *mut IBStream) -> tresult {
545 kResultOk
549 }
550
551 unsafe fn getState(&self, _state: *mut IBStream) -> tresult {
552 kResultOk
554 }
555
556 unsafe fn getParameterCount(&self) -> int32 {
557 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 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 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 128 => format!("MIDI Ch. {} Channel Pressure", midi_channel + 1),
586 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); } 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); }
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 match self.inner.param_by_hash.get(&id) {
644 Some(param_ptr) => {
645 unsafe {
646 u16strlcpy(
647 dest,
648 ¶m_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 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 #[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 if num_ins < 0 || num_outs < 0 {
771 return kInvalidArgument;
772 }
773
774 let matching_layout = P::AUDIO_IO_LAYOUTS
777 .iter()
778 .find(|layout| {
779 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 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 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 let setup = unsafe { &*setup };
928 crate::nice_debug_assert_eq!(setup.symbolicSampleSize, K_SYMBOLIC_SAMPLE_SIZE_32);
929
930 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 kResultOk
963 }
964
965 unsafe fn setProcessing(&self, state: TBool) -> tresult {
966 let state = state != 0;
967
968 self.inner.last_process_status.store(ProcessStatus::Normal);
970 self.inner.is_processing.store(state, Ordering::SeqCst);
971
972 if state {
975 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 kResultOk
996 }
997
998 #[allow(clippy::mut_range_bound)]
1000 unsafe fn process(&self, data: *mut ProcessData) -> tresult {
1001 check_null_ptr!(data);
1002
1003 process_wrapper(|| {
1006 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 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 let mut process_events = self.inner.process_events.borrow_mut();
1046 process_events.clear();
1047
1048 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 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 if P::MIDI_INPUT >= MidiConfig::MidiCCs
1084 && (VST3_MIDI_PARAMS_START..VST3_MIDI_PARAMS_END)
1085 .contains(¶m_hash)
1086 {
1087 let midi_param_relative_idx =
1088 param_hash - VST3_MIDI_PARAMS_START;
1089 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 128 => NoteEvent::MidiChannelPressure {
1096 timing,
1097 channel: midi_channel,
1098 pressure: value,
1099 },
1100 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 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 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 let event = unsafe { event.__field0.data };
1210
1211 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 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 {
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 *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 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 let block_len = block_end - block_start;
1294
1295 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 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 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 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 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 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 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 let mut vst3_event: Event = unsafe { mem::zeroed() };
1494 vst3_event.busIndex = 0;
1495 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 #[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, 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 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, 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, 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, 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, bytes: sysex_buffer.as_ptr(),
1687 };
1688
1689 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 block_end == total_buffer_len {
1712 break result;
1713 } else {
1714 block_start = block_end;
1715 }
1716 };
1717
1718 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 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 match self.inner.last_process_status.load() {
1745 ProcessStatus::Tail(samples) => samples,
1746 ProcessStatus::KeepAlive => u32::MAX, _ => 0, }
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 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 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(¬e_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 ¬e_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 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; 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 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 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 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 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 } 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}