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