1use atomic_refcell::AtomicRefMut;
2use nice_plug_core::audio_setup::{AuxiliaryBuffers, BufferConfig, ProcessMode};
3use nice_plug_core::context::process::Transport;
4#[cfg(feature = "editor")]
5use nice_plug_core::editor::Editor;
6use nice_plug_core::midi::{Channel, Key, MidiConfig, NoteEvent, VoiceID};
7use nice_plug_core::params::ParamFlags;
8use nice_plug_core::plugin::ProcessStatus;
9use std::ffi::c_void;
10use std::mem::{self, MaybeUninit};
11use std::num::NonZeroU32;
12use std::ptr::NonNull;
13use std::sync::Arc;
14use std::sync::atomic::Ordering;
15use std::time::{Duration, Instant};
16use vst3::Steinberg::Vst::ProcessContext_::StatesAndFlags_::{
17 kBarPositionValid, kCycleActive, kCycleValid, kPlaying, kProjectTimeMusicValid, kRecording,
18 kTempoValid, kTimeSigValid,
19};
20use vst3::Steinberg::Vst::{
21 BusDirection, CString,
22 ChannelContext::{IInfoListener, IInfoListenerTrait},
23 CtrlNumber,
24 Event_::EventTypes_,
25 IAttributeList, IAudioProcessor, IAudioProcessorTrait, IComponent, IComponentHandler,
26 IComponentTrait, IEditController, IEditControllerTrait, IEventListTrait, IMidiMapping,
27 IMidiMappingTrait, INoteExpressionController, INoteExpressionControllerTrait,
28 IParamValueQueueTrait, IParameterChangesTrait, IProcessContextRequirements,
29 IProcessContextRequirements_, IProcessContextRequirementsTrait, IUnitInfo, IUnitInfoTrait,
30 IoMode, MediaType, NoteExpressionTypeID, NoteExpressionTypeInfo, NoteExpressionValue,
31 NoteExpressionValueDescription, ParamID, ParamValue, ParameterInfo,
32 ParameterInfo_::ParameterFlags_,
33 ProcessData, ProcessModes_, ProcessSetup, ProgramListID, ProgramListInfo, SpeakerArrangement,
34 String128, TChar, UnitID, UnitInfo, kNoParamId, kNoParentUnitId, 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;
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, 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();
417 let audio_io_layout = self.inner.current_audio_io_layout.load();
418
419 let now = Instant::now();
422 let mut result = kResultFalse;
423 loop {
424 if let Some(mut plugin) = self.inner.plugin.try_lock() {
425 if plugin.activate(&audio_io_layout, &buffer_config, &mut activate_context)
426 {
427 let now_2 = Instant::now();
434 loop {
435 if let Ok(mut buffer_manager) =
436 self.inner.buffer_manager.try_borrow_mut()
437 {
438 *buffer_manager = BufferManager::for_audio_io_layout(
442 buffer_config.max_buffer_size as usize,
443 audio_io_layout,
444 );
445
446 result = kResultOk;
447
448 break;
449 } else if now_2.elapsed() > Duration::from_secs(1) {
450 crate::nice_error!(
451 "Failed to acquire lock on buffers while activating"
452 );
453 break;
454 } else {
455 std::thread::sleep(Duration::from_millis(1));
456 }
457 }
458 }
459
460 break;
461 } else if now.elapsed() > Duration::from_secs(1) {
462 crate::nice_error!("Failed to acquire lock on plugin while activating");
463 break;
464 } else {
465 std::thread::sleep(Duration::from_millis(1));
466 }
467 }
468
469 drop(activate_context);
471
472 result
473 }
474 (true, None) => kResultFalse,
475 (false, _) => {
476 let now = Instant::now();
479 loop {
480 if let Some(mut plugin) = self.inner.plugin.try_lock() {
481 plugin.deactivate();
482 break;
483 } else if now.elapsed() > Duration::from_secs(1) {
484 crate::nice_error!("Failed to acquire lock on plugin while deactivating");
485 break;
486 } else {
487 std::thread::sleep(Duration::from_millis(1));
488 }
489 }
490
491 kResultOk
492 }
493 }
494 }
495
496 unsafe fn setState(&self, state: *mut IBStream) -> tresult {
497 use vst3::Steinberg::IBStream_::IStreamSeekMode_::*;
498
499 check_null_ptr!(state);
500
501 let state = unsafe { ComRef::from_raw(state).unwrap() };
502
503 let mut current_pos = 0;
507 let mut eof_pos = 0;
508 if unsafe {
509 state.tell(&mut current_pos) != kResultOk
510 || state.seek(0, kIBSeekEnd as int32, &mut eof_pos) != kResultOk
511 || state.seek(current_pos, kIBSeekSet as int32, std::ptr::null_mut()) != kResultOk
512 } {
513 crate::nice_debug_assert_failure!("Could not get the stream length");
514 return kResultFalse;
515 }
516
517 let stream_byte_size = (eof_pos - current_pos) as i32;
518 let mut num_bytes_read = 0;
519
520 let mut read_buffer: Vec<u8> = Vec::new();
521
522 if read_buffer
523 .try_reserve_exact(stream_byte_size as usize)
524 .is_err()
525 {
526 crate::nice_error!("Failed to load state: Failed to allocate buffer for state stream");
527 return kResultFalse;
528 }
529
530 unsafe {
531 state.read(
532 read_buffer.as_mut_ptr() as *mut c_void,
533 read_buffer.capacity() as i32,
534 &mut num_bytes_read,
535 );
536 }
537 unsafe { read_buffer.set_len(num_bytes_read as usize) };
538
539 if read_buffer.len() != stream_byte_size as usize {
543 crate::nice_debug_assert_failure!("Unexpected stream length");
544 return kResultFalse;
545 }
546
547 match unsafe { state::deserialize_json(&read_buffer) } {
548 Some(mut state) => {
549 if self.inner.set_state_inner(&mut state) {
550 crate::nice_trace!("Loaded state ({} bytes)", read_buffer.len());
551 kResultOk
552 } else {
553 kResultFalse
554 }
555 }
556 None => kResultFalse,
557 }
558 }
559
560 unsafe fn getState(&self, state: *mut IBStream) -> tresult {
561 check_null_ptr!(state);
562
563 let state = unsafe { ComRef::from_raw(state).unwrap() };
564
565 let serialized = unsafe {
566 state::serialize_json::<P>(
567 self.inner.params.clone(),
568 state::make_params_iter(&self.inner.param_by_hash, &self.inner.param_id_to_hash),
569 )
570 };
571 match serialized {
572 Ok(serialized) => {
573 let mut num_bytes_written = 0;
574 let result = unsafe {
575 state.write(
576 serialized.as_ptr() as *mut c_void,
577 serialized.len() as i32,
578 &mut num_bytes_written,
579 )
580 };
581
582 crate::nice_debug_assert_eq!(result, kResultOk);
583 crate::nice_debug_assert_eq!(num_bytes_written as usize, serialized.len());
584
585 crate::nice_trace!("Saved state ({} bytes)", serialized.len());
586
587 kResultOk
588 }
589 Err(err) => {
590 crate::nice_debug_assert_failure!("Could not save state: {:#}", err);
591 kResultFalse
592 }
593 }
594 }
595}
596
597impl<P: Vst3Plugin> IEditControllerTrait for Wrapper<P> {
598 unsafe fn setComponentState(&self, _state: *mut IBStream) -> tresult {
599 kResultOk
601 }
602
603 unsafe fn setState(&self, _state: *mut IBStream) -> tresult {
604 kResultOk
608 }
609
610 unsafe fn getState(&self, _state: *mut IBStream) -> tresult {
611 kResultOk
613 }
614
615 unsafe fn getParameterCount(&self) -> int32 {
616 if P::MIDI_INPUT >= MidiConfig::MidiCCs {
618 self.inner.param_hashes.len() as i32 + VST3_MIDI_NUM_PARAMS as i32
619 } else {
620 self.inner.param_hashes.len() as i32
621 }
622 }
623
624 unsafe fn getParameterInfo(&self, param_index: int32, info: *mut ParameterInfo) -> tresult {
625 check_null_ptr!(info);
626
627 if param_index < 0 || param_index > unsafe { self.getParameterCount() } {
628 return kInvalidArgument;
629 }
630
631 unsafe { *info = std::mem::zeroed() };
632 let info = unsafe { &mut *info };
633
634 let num_actual_params = self.inner.param_hashes.len() as i32;
637 if P::MIDI_INPUT >= MidiConfig::MidiCCs && param_index >= num_actual_params {
638 let midi_param_relative_idx = (param_index - num_actual_params) as u32;
639 let midi_cc = midi_param_relative_idx % VST3_MIDI_CCS;
641 let midi_channel = midi_param_relative_idx / VST3_MIDI_CCS;
642 let name = match midi_cc {
643 128 => format!("MIDI Ch. {} Channel Pressure", midi_channel + 1),
645 129 => format!("MIDI Ch. {} Pitch Bend", midi_channel + 1),
647 n => format!("MIDI Ch. {} CC {}", midi_channel + 1, n),
648 };
649
650 info.id = VST3_MIDI_PARAMS_START + midi_param_relative_idx;
651 u16strlcpy(&mut info.title, &name);
652 u16strlcpy(&mut info.shortTitle, &name);
653 info.flags = ParameterFlags_::kIsReadOnly | (1 << 4); } else {
655 let param_hash = &self.inner.param_hashes[param_index as usize];
656 let param_unit = &self
657 .inner
658 .param_units
659 .get_vst3_unit_id(*param_hash)
660 .expect("Inconsistent parameter data");
661 let param_ptr = &self.inner.param_by_hash[param_hash];
662 let default_value = unsafe { param_ptr.default_normalized_value() };
663 let flags = unsafe { param_ptr.flags() };
664 let automatable = !flags.contains(ParamFlags::NON_AUTOMATABLE);
665 let hidden = flags.contains(ParamFlags::HIDDEN);
666 let is_bypass = flags.contains(ParamFlags::BYPASS);
667
668 info.id = *param_hash;
669 u16strlcpy(&mut info.title, unsafe { param_ptr.name() });
670 u16strlcpy(&mut info.shortTitle, unsafe { param_ptr.name() });
671 u16strlcpy(&mut info.units, unsafe { param_ptr.unit() });
672 info.stepCount = unsafe { param_ptr.step_count().unwrap_or(0) } as i32;
673 info.defaultNormalizedValue = default_value as f64;
674 info.unitId = *param_unit;
675 info.flags = 0;
676 if automatable && !hidden {
677 info.flags |= ParameterFlags_::kCanAutomate;
678 }
679 if hidden {
680 info.flags |= ParameterFlags_::kIsReadOnly | (1 << 4); }
682 if is_bypass {
683 info.flags |= ParameterFlags_::kIsBypass;
684 }
685 }
686
687 kResultOk
688 }
689
690 unsafe fn getParamStringByValue(
691 &self,
692 id: ParamID,
693 value_normalized: ParamValue,
694 string: *mut String128,
695 ) -> tresult {
696 check_null_ptr!(string);
697
698 let dest = unsafe { &mut *(string) };
699
700 match self.inner.param_by_hash.get(&id) {
703 Some(param_ptr) => {
704 unsafe {
705 u16strlcpy(
706 dest,
707 ¶m_ptr.normalized_value_to_string(value_normalized as f32, false),
708 );
709 }
710
711 kResultOk
712 }
713 _ => kInvalidArgument,
714 }
715 }
716
717 unsafe fn getParamValueByString(
718 &self,
719 id: ParamID,
720 string: *mut TChar,
721 value_normalized: *mut ParamValue,
722 ) -> tresult {
723 check_null_ptr!(string, value_normalized);
724
725 let string = match unsafe { U16CStr::from_ptr_str(string as *const u16).to_string() } {
726 Ok(s) => s,
727 Err(_) => return kInvalidArgument,
728 };
729
730 match self.inner.param_by_hash.get(&id) {
731 Some(param_ptr) => {
732 let value = match unsafe { param_ptr.string_to_normalized_value(&string) } {
733 Some(v) => v as f64,
734 None => return kResultFalse,
735 };
736 unsafe { *value_normalized = value };
737
738 kResultOk
739 }
740 _ => kInvalidArgument,
741 }
742 }
743
744 unsafe fn normalizedParamToPlain(
745 &self,
746 id: ParamID,
747 value_normalized: ParamValue,
748 ) -> ParamValue {
749 match self.inner.param_by_hash.get(&id) {
750 Some(param_ptr) => unsafe { param_ptr.preview_plain(value_normalized as f32) as f64 },
751 _ => value_normalized,
752 }
753 }
754
755 unsafe fn plainParamToNormalized(&self, id: ParamID, plain_value: ParamValue) -> ParamValue {
756 match self.inner.param_by_hash.get(&id) {
757 Some(param_ptr) => unsafe { param_ptr.preview_normalized(plain_value as f32) as f64 },
758 _ => plain_value,
759 }
760 }
761
762 unsafe fn getParamNormalized(&self, id: ParamID) -> ParamValue {
763 match self.inner.param_by_hash.get(&id) {
764 Some(param_ptr) => unsafe { param_ptr.modulated_normalized_value() as f64 },
765 _ => 0.5,
766 }
767 }
768
769 unsafe fn setParamNormalized(&self, id: ParamID, value: ParamValue) -> tresult {
770 if self.inner.is_processing.load(Ordering::SeqCst) {
773 return kResultOk;
774 }
775
776 let sample_rate = self
777 .inner
778 .current_buffer_config
779 .load()
780 .map(|c| c.sample_rate);
781 self.inner
782 .set_normalized_value_by_hash(id, value as f32, sample_rate)
783 }
784
785 unsafe fn setComponentHandler(&self, handler: *mut IComponentHandler) -> tresult {
786 *self.inner.component_handler.borrow_mut() =
787 unsafe { ComRef::from_raw(handler) }.map(|r| r.to_com_ptr());
788
789 kResultOk
790 }
791
792 unsafe fn createView(&self, _name: FIDString) -> *mut IPlugView {
793 #[cfg(not(feature = "editor"))]
794 return std::ptr::null_mut();
795
796 #[cfg(feature = "editor")]
799 match self.inner.editor.borrow().as_ref() {
800 Some(editor) => {
801 use vst3::ComWrapper;
802
803 use crate::wrapper::vst3::view::WrapperView;
804
805 let view = ComWrapper::new(WrapperView::new(
806 Arc::downgrade(&self.inner),
807 Arc::downgrade(editor),
808 ));
809 let plug_view_ptr = view.to_com_ptr::<IPlugView>().unwrap().into_raw();
810 *self.inner.plug_view.write() = Some(view);
811 plug_view_ptr
812 }
813 None => std::ptr::null_mut(),
814 }
815 }
816}
817
818impl<P: Vst3Plugin> IAudioProcessorTrait for Wrapper<P> {
819 unsafe fn setBusArrangements(
820 &self,
821 inputs: *mut SpeakerArrangement,
822 num_ins: int32,
823 outputs: *mut SpeakerArrangement,
824 num_outs: int32,
825 ) -> tresult {
826 check_null_ptr!(inputs, outputs);
827
828 if num_ins < 0 || num_outs < 0 {
830 return kInvalidArgument;
831 }
832
833 let matching_layout = P::AUDIO_IO_LAYOUTS
836 .iter()
837 .find(|layout| {
838 let num_layout_ins = if layout.main_input_channels.is_some() {
842 1
843 } else {
844 0
845 } + layout.aux_input_ports.len();
846 let num_layout_outs = if layout.main_output_channels.is_some() {
847 1
848 } else {
849 0
850 } + layout.aux_output_ports.len();
851 if num_ins as usize != num_layout_ins || num_outs as usize != num_layout_outs {
852 return false;
853 }
854
855 let has_main_input = layout.main_input_channels.is_some();
858 let aux_input_start_idx = if has_main_input { 0 } else { 1 };
859 if has_main_input
860 && unsafe {
861 (*inputs).count_ones() != layout.main_input_channels.unwrap().get()
862 }
863 {
864 return false;
865 }
866 for (aux_input_idx, channel_count) in layout.aux_input_ports.iter().enumerate() {
867 if unsafe {
868 (*inputs.add(aux_input_idx + aux_input_start_idx)).count_ones()
869 != channel_count.get()
870 } {
871 return false;
872 }
873 }
874
875 let has_main_output = layout.main_output_channels.is_some();
876 let aux_output_start_idx = if has_main_output { 0 } else { 1 };
877 if unsafe {
878 (*outputs).count_ones()
879 != layout
880 .main_output_channels
881 .map(NonZeroU32::get)
882 .unwrap_or_default()
883 } {
884 return false;
885 }
886 for (aux_output_idx, channel_count) in layout.aux_output_ports.iter().enumerate() {
887 if unsafe {
888 (*outputs.add(aux_output_idx + aux_output_start_idx)).count_ones()
889 != channel_count.get()
890 } {
891 return false;
892 }
893 }
894
895 true
896 })
897 .copied();
898
899 match matching_layout {
900 Some(layout) => {
901 self.inner.current_audio_io_layout.store(layout);
904
905 kResultOk
906 }
907 None => kResultFalse,
908 }
909 }
910
911 unsafe fn getBusArrangement(
912 &self,
913 dir: BusDirection,
914 index: i32,
915 arr: *mut SpeakerArrangement,
916 ) -> tresult {
917 check_null_ptr!(arr);
918
919 let channel_count_to_map = |count| match count {
920 0 => vst3::Steinberg::Vst::SpeakerArr::kEmpty,
921 1 => vst3::Steinberg::Vst::SpeakerArr::kMono,
922 2 => vst3::Steinberg::Vst::SpeakerArr::kStereo,
923 5 => vst3::Steinberg::Vst::SpeakerArr::k50,
924 6 => vst3::Steinberg::Vst::SpeakerArr::k51,
925 7 => vst3::Steinberg::Vst::SpeakerArr::k70Cine,
926 8 => vst3::Steinberg::Vst::SpeakerArr::k71Cine,
927 n => {
928 crate::nice_debug_assert_failure!(
929 "No defined layout for {} channels, making something up on the spot...",
930 n
931 );
932 (1 << n) - 1
933 }
934 };
935
936 let current_audio_io_layout = self.inner.current_audio_io_layout.load();
937 let num_channels = if dir == K_BUS_DIRECTION_INPUT {
938 let has_main_input = current_audio_io_layout.main_input_channels.is_some();
939 let aux_input_start_idx = if has_main_input { 1 } else { 0 };
940 let aux_input_idx = (index - aux_input_start_idx).max(0) as usize;
941 if index == 0 && has_main_input {
942 current_audio_io_layout.main_input_channels.unwrap().get()
943 } else if aux_input_idx < current_audio_io_layout.aux_input_ports.len() {
944 current_audio_io_layout.aux_input_ports[aux_input_idx].get()
945 } else {
946 return kInvalidArgument;
947 }
948 } else if dir == K_BUS_DIRECTION_OUTPUT {
949 let has_main_output = current_audio_io_layout.main_output_channels.is_some();
950 let aux_output_start_idx = if has_main_output { 1 } else { 0 };
951 let aux_output_idx = (index - aux_output_start_idx).max(0) as usize;
952 if index == 0 && has_main_output {
953 current_audio_io_layout.main_output_channels.unwrap().get()
954 } else if aux_output_idx < current_audio_io_layout.aux_output_ports.len() {
955 current_audio_io_layout.aux_output_ports[aux_output_idx].get()
956 } else {
957 return kInvalidArgument;
958 }
959 } else {
960 return kInvalidArgument;
961 };
962 let channel_map = channel_count_to_map(num_channels);
963
964 crate::nice_debug_assert_eq!(num_channels, channel_map.count_ones());
965 unsafe { *arr = channel_map };
966
967 kResultOk
968 }
969
970 unsafe fn canProcessSampleSize(&self, symbolic_sample_size: int32) -> tresult {
971 if symbolic_sample_size == K_SYMBOLIC_SAMPLE_SIZE_32 {
972 kResultOk
973 } else {
974 kResultFalse
975 }
976 }
977
978 unsafe fn getLatencySamples(&self) -> uint32 {
979 self.inner.current_latency.load(Ordering::SeqCst)
980 }
981
982 unsafe fn setupProcessing(&self, setup: *mut ProcessSetup) -> tresult {
983 check_null_ptr!(setup);
984
985 let setup = unsafe { &*setup };
987 crate::nice_debug_assert_eq!(setup.symbolicSampleSize, K_SYMBOLIC_SAMPLE_SIZE_32);
988
989 self.inner.current_buffer_config.store(Some(BufferConfig {
991 sample_rate: setup.sampleRate as f32,
992 min_buffer_size: None,
993 max_buffer_size: setup.maxSamplesPerBlock as u32,
994 process_mode: self.inner.current_process_mode.load(),
995 }));
996
997 #[allow(clippy::unnecessary_cast)]
998 const K_REALTIME: i32 = ProcessModes_::kRealtime as i32;
999 #[allow(clippy::unnecessary_cast)]
1000 const K_PREFETCH: i32 = ProcessModes_::kPrefetch as i32;
1001 #[allow(clippy::unnecessary_cast)]
1002 const K_OFFLINE: i32 = ProcessModes_::kOffline as i32;
1003
1004 let mode = match setup.processMode {
1005 n if n == K_REALTIME => ProcessMode::Realtime,
1006 n if n == K_PREFETCH => ProcessMode::Buffered,
1007 n if n == K_OFFLINE => ProcessMode::Offline,
1008 n => {
1009 crate::nice_debug_assert_failure!(
1010 "Unknown rendering mode '{}', defaulting to realtime",
1011 n
1012 );
1013 ProcessMode::Realtime
1014 }
1015 };
1016 self.inner.current_process_mode.store(mode);
1017
1018 kResultOk
1022 }
1023
1024 unsafe fn setProcessing(&self, state: TBool) -> tresult {
1025 let state = state != 0;
1026
1027 self.inner.last_process_status.store(ProcessStatus::Normal);
1029 self.inner.is_processing.store(state, Ordering::SeqCst);
1030
1031 if state {
1034 let mut plugin = match self.inner.plugin.try_lock() {
1037 Some(plugin) => plugin,
1038 None => {
1039 crate::nice_debug_assert_failure!(
1040 "The host tried to call IAudioProcessor::setProcessing(true) during a \
1041 reentrent call to IComponent::setActive(true), returning kResultOk. If \
1042 this is Ardour then it will still call \
1043 IAudioProcessor::setProcessing(true) later and everything will be fine. \
1044 Hopefully."
1045 );
1046 return kResultOk;
1047 }
1048 };
1049
1050 process_wrapper(|| plugin.reset());
1051 }
1052
1053 kResultOk
1055 }
1056
1057 #[allow(clippy::mut_range_bound)]
1059 unsafe fn process(&self, data: *mut ProcessData) -> tresult {
1060 check_null_ptr!(data);
1061
1062 process_wrapper(|| {
1065 let data = unsafe { &*data };
1067 let sample_rate = self
1068 .inner
1069 .current_buffer_config
1070 .load()
1071 .expect("Process call without prior setup call")
1072 .sample_rate;
1073
1074 crate::nice_debug_assert!(data.numInputs >= 0 && data.numOutputs >= 0);
1075 crate::nice_debug_assert_eq!(data.symbolicSampleSize, K_SYMBOLIC_SAMPLE_SIZE_32);
1076 crate::nice_debug_assert!(data.numSamples >= 0);
1077
1078 let total_buffer_len = data.numSamples as usize;
1079
1080 let current_audio_io_layout = self.inner.current_audio_io_layout.load();
1081 let has_main_input = current_audio_io_layout.main_input_channels.is_some();
1082 let has_main_output = current_audio_io_layout.main_output_channels.is_some();
1083 let aux_input_start_idx = if has_main_input { 1 } else { 0 };
1084 let aux_output_start_idx = if has_main_output { 1 } else { 0 };
1085
1086 let mut is_param_flush = total_buffer_len == 0;
1092 if (data.numOutputs == 0 || data.outputs.is_null())
1093 && (has_main_output || !current_audio_io_layout.aux_output_ports.is_empty())
1094 {
1095 is_param_flush = true;
1096 }
1097
1098 let mut process_events = self.inner.process_events.borrow_mut();
1105 process_events.clear();
1106
1107 let push_process_event = |process_events: &mut AtomicRefMut<Vec<ProcessEvent<P>>>,
1108 event: ProcessEvent<P>| {
1109 permit_alloc(|| {
1110 if process_events.len() == process_events.capacity() {
1113 crate::nice_warn!(
1114 "Input event buffer filled up! This will cause an allocation."
1115 );
1116 }
1117
1118 if let Some(last_event) = process_events.last() {
1127 if event.timing() >= last_event.timing() {
1128 process_events.push(event);
1129 } else {
1130 let insert_i =
1131 process_events.partition_point(|x| x.timing() <= event.timing());
1132 process_events.insert(insert_i, event);
1133 }
1134 } else {
1135 process_events.push(event);
1136 }
1137 });
1138 };
1139
1140 if let Some(param_changes) = unsafe { ComRef::from_raw(data.inputParameterChanges) } {
1143 let num_param_queues = unsafe { param_changes.getParameterCount() };
1144 for change_queue_idx in 0..num_param_queues {
1145 if let Some(param_change_queue) = unsafe {
1146 ComRef::from_raw(param_changes.getParameterData(change_queue_idx))
1147 } {
1148 let param_hash = unsafe { param_change_queue.getParameterId() };
1149 let num_changes = unsafe { param_change_queue.getPointCount() };
1150 if num_changes <= 0 {
1151 continue;
1152 }
1153
1154 let mut sample_offset = 0i32;
1155 let mut value = 0.0f64;
1156 for change_idx in 0..num_changes {
1157 if unsafe {
1158 param_change_queue.getPoint(
1159 change_idx,
1160 &mut sample_offset,
1161 &mut value,
1162 ) == kResultOk
1163 } {
1164 let timing = clamp_input_event_timing(
1168 sample_offset as u32,
1169 total_buffer_len as u32,
1170 );
1171 let value = value as f32;
1172
1173 if P::MIDI_INPUT >= MidiConfig::MidiCCs
1176 && (VST3_MIDI_PARAMS_START..VST3_MIDI_PARAMS_END)
1177 .contains(¶m_hash)
1178 {
1179 let midi_param_relative_idx =
1180 param_hash - VST3_MIDI_PARAMS_START;
1181 let midi_cc = (midi_param_relative_idx % VST3_MIDI_CCS) as u8;
1183 let midi_channel =
1184 (midi_param_relative_idx / VST3_MIDI_CCS) as u8;
1185
1186 push_process_event(
1187 &mut process_events,
1188 ProcessEvent::NoteEvent(match midi_cc {
1189 128 => NoteEvent::MidiChannelPressure {
1191 timing,
1192 channel: midi_channel,
1193 pressure: value,
1194 },
1195 129 => NoteEvent::MidiPitchBend {
1197 timing,
1198 channel: midi_channel,
1199 value,
1200 },
1201 n => NoteEvent::MidiCC {
1202 timing,
1203 channel: midi_channel,
1204 cc: n,
1205 value,
1206 },
1207 }),
1208 );
1209 } else if P::SAMPLE_ACCURATE_AUTOMATION {
1210 push_process_event(
1211 &mut process_events,
1212 ProcessEvent::ParameterChange {
1213 timing,
1214 hash: param_hash,
1215 normalized_value: value,
1216 },
1217 );
1218 } else {
1219 self.inner.set_normalized_value_by_hash(
1220 param_hash,
1221 value,
1222 Some(sample_rate),
1223 );
1224 }
1225 }
1226 }
1227 }
1228 }
1229 }
1230
1231 if P::MIDI_INPUT >= MidiConfig::Basic {
1233 let mut note_expression_controller =
1234 self.inner.note_expression_controller.borrow_mut();
1235 if let Some(events) = unsafe { ComRef::from_raw(data.inputEvents) } {
1236 let num_events = unsafe { events.getEventCount() };
1237
1238 fn voice_from_i32(v: i32) -> VoiceID {
1239 if v >= 0 {
1240 VoiceID::ID(v)
1241 } else {
1242 VoiceID::Wildcard
1243 }
1244 }
1245 fn channel_from_i16(c: i16) -> Channel {
1246 if (0..=15).contains(&c) {
1247 Channel::Number(c as u8)
1248 } else {
1249 Channel::Wildcard
1250 }
1251 }
1252 fn key_from_i16(k: i16) -> Key {
1253 if (0..=127).contains(&k) {
1254 Key::Number(k as u8)
1255 } else {
1256 Key::Wildcard
1257 }
1258 }
1259
1260 let mut event: MaybeUninit<_> = MaybeUninit::uninit();
1261 for i in 0..num_events {
1262 let result = unsafe { events.getEvent(i, event.as_mut_ptr()) };
1263 crate::nice_debug_assert_eq!(result, kResultOk);
1264
1265 let event = unsafe { event.assume_init() };
1266 let timing = clamp_input_event_timing(
1267 event.sampleOffset as u32,
1268 total_buffer_len as u32,
1269 );
1270
1271 if event.r#type == EventTypes_::kNoteOnEvent as u16 {
1272 let event = unsafe { event.__field0.noteOn };
1273
1274 note_expression_controller.register_note(&event);
1277
1278 push_process_event(
1279 &mut process_events,
1280 ProcessEvent::NoteEvent(NoteEvent::NoteOn {
1281 timing,
1282 voice_id: voice_from_i32(event.noteId),
1283 channel: channel_from_i16(event.channel),
1284 key: key_from_i16(event.pitch),
1285 velocity: event.velocity,
1286 }),
1287 );
1288 } else if event.r#type == EventTypes_::kNoteOffEvent as u16 {
1289 let event = unsafe { event.__field0.noteOff };
1290
1291 push_process_event(
1292 &mut process_events,
1293 ProcessEvent::NoteEvent(NoteEvent::NoteOff {
1294 timing,
1295 voice_id: voice_from_i32(event.noteId),
1296 channel: channel_from_i16(event.channel),
1297 key: key_from_i16(event.pitch),
1298 velocity: event.velocity,
1299 }),
1300 );
1301 } else if event.r#type == EventTypes_::kPolyPressureEvent as u16 {
1302 let event = unsafe { event.__field0.polyPressure };
1303
1304 push_process_event(
1305 &mut process_events,
1306 ProcessEvent::NoteEvent(NoteEvent::PolyPressure {
1307 timing,
1308 voice_id: voice_from_i32(event.noteId),
1309 channel: channel_from_i16(event.channel),
1310 key: key_from_i16(event.pitch),
1311 pressure: event.pressure,
1312 }),
1313 );
1314 } else if event.r#type == EventTypes_::kNoteExpressionValueEvent as u16 {
1315 let event = unsafe { event.__field0.noteExpressionValue };
1316 match note_expression_controller.translate_event(timing, &event) {
1317 Some(translated_event) => {
1318 push_process_event(
1319 &mut process_events,
1320 ProcessEvent::NoteEvent(translated_event),
1321 );
1322 }
1323 None => crate::nice_trace!(
1324 "Unhandled note expression type: {}",
1325 event.typeId
1326 ),
1327 }
1328 } else if event.r#type == EventTypes_::kDataEvent as u16
1329 && unsafe { event.__field0.data.r#type } == 0
1330 {
1331 let event = unsafe { event.__field0.data };
1333
1334 assert!(!event.bytes.is_null());
1337 let sysex_buffer = unsafe {
1338 std::slice::from_raw_parts(event.bytes, event.size as usize)
1339 };
1340 if let Ok(note_event) = NoteEvent::from_midi(timing, sysex_buffer) {
1341 push_process_event(
1342 &mut process_events,
1343 ProcessEvent::NoteEvent(note_event),
1344 );
1345 };
1346 }
1347 }
1348 }
1349 }
1350
1351 let mut block_start = 0usize;
1352 let mut block_end;
1353 let mut event_start_idx = 0;
1354 let result = loop {
1355 {
1363 let mut input_note_events = self.inner.input_note_events.borrow_mut();
1364 input_note_events.clear();
1365
1366 block_end = total_buffer_len;
1367 for event_idx in event_start_idx..process_events.len() {
1368 match &process_events[event_idx] {
1369 ProcessEvent::ParameterChange {
1370 timing,
1371 hash,
1372 normalized_value,
1373 } => {
1374 if *timing != block_start as u32 {
1378 event_start_idx = event_idx;
1379 block_end = *timing as usize;
1380 break;
1381 }
1382
1383 self.inner.set_normalized_value_by_hash(
1384 *hash,
1385 *normalized_value,
1386 Some(sample_rate),
1387 );
1388 }
1389 ProcessEvent::NoteEvent(event) => {
1390 let mut event = event.clone();
1393 event.subtract_timing(block_start as u32);
1394
1395 permit_alloc(|| {
1396 if input_note_events.len() == input_note_events.capacity() {
1399 crate::nice_warn!(
1400 "Input note event buffer filled up! This will cause an allocation."
1401 );
1402 }
1403 input_note_events.push_back(event);
1404 });
1405 }
1406 }
1407 }
1408 }
1409
1410 let result = if is_param_flush {
1411 kResultOk
1412 } else {
1413 let block_len = block_end - block_start;
1416
1417 let mut buffer_manager = self.inner.buffer_manager.borrow_mut();
1420 let buffers = unsafe {
1421 buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
1422 if data.numOutputs > 0
1423 && !data.outputs.is_null()
1424 && !(*data.outputs).__field0.channelBuffers32.is_null()
1425 && has_main_output
1426 {
1427 let audio_output = &*data.outputs;
1428 let ptrs =
1429 NonNull::new(audio_output.__field0.channelBuffers32).unwrap();
1430 let num_channels = audio_output.numChannels as usize;
1431
1432 *buffer_source.main_output_channel_pointers =
1433 Some(ChannelPointers { ptrs, num_channels });
1434 }
1435
1436 if data.numInputs > 0
1437 && !data.inputs.is_null()
1438 && !(*data.inputs).__field0.channelBuffers32.is_null()
1439 && has_main_input
1440 {
1441 let audio_input = &*data.inputs;
1442 let ptrs =
1443 NonNull::new(audio_input.__field0.channelBuffers32).unwrap();
1444 let num_channels = audio_input.numChannels as usize;
1445
1446 *buffer_source.main_input_channel_pointers =
1447 Some(ChannelPointers { ptrs, num_channels });
1448 }
1449
1450 if !data.inputs.is_null() {
1451 for (aux_input_no, aux_input_channel_pointers) in buffer_source
1452 .aux_input_channel_pointers
1453 .iter_mut()
1454 .enumerate()
1455 {
1456 let aux_input_idx = aux_input_no + aux_input_start_idx;
1457 if aux_input_idx > data.numOutputs as usize {
1458 break;
1459 }
1460
1461 let audio_input = &*data.inputs.add(aux_input_idx);
1462 match NonNull::new(audio_input.__field0.channelBuffers32) {
1463 Some(ptrs) => {
1464 let num_channels = audio_input.numChannels as usize;
1465
1466 *aux_input_channel_pointers =
1467 Some(ChannelPointers { ptrs, num_channels });
1468 }
1469 None => continue,
1470 }
1471 }
1472 }
1473
1474 if !data.outputs.is_null() {
1475 for (aux_output_no, aux_output_channel_pointers) in buffer_source
1476 .aux_output_channel_pointers
1477 .iter_mut()
1478 .enumerate()
1479 {
1480 let aux_output_idx = aux_output_no + aux_output_start_idx;
1481 if aux_output_idx > data.numOutputs as usize {
1482 break;
1483 }
1484
1485 let audio_output = &*data.outputs.add(aux_output_idx);
1486 match NonNull::new(audio_output.__field0.channelBuffers32) {
1487 Some(ptrs) => {
1488 let num_channels = audio_output.numChannels as usize;
1489
1490 *aux_output_channel_pointers =
1491 Some(ChannelPointers { ptrs, num_channels });
1492 }
1493 None => continue,
1494 }
1495 }
1496 }
1497 })
1498 };
1499
1500 let mut buffer_is_valid = true;
1505 for output_buffer_slice in
1506 buffers.main_buffer.as_slice_immutable().iter().chain(
1507 buffers
1508 .aux_outputs
1509 .iter()
1510 .flat_map(|buffer| buffer.as_slice_immutable().iter()),
1511 )
1512 {
1513 if output_buffer_slice.is_empty() {
1514 buffer_is_valid = false;
1515 break;
1516 }
1517 }
1518 crate::nice_debug_assert!(buffer_is_valid);
1519
1520 let mut transport = Transport::new(sample_rate);
1524 if !data.processContext.is_null() {
1525 let context = unsafe { &*data.processContext };
1526
1527 #[allow(clippy::unnecessary_cast)]
1528 {
1529 transport.playing = context.state & kPlaying as u32 != 0;
1530 transport.recording = context.state & kRecording as u32 != 0;
1531
1532 if context.state & kTempoValid as u32 != 0 {
1533 transport.tempo = Some(context.tempo);
1534 }
1535
1536 if context.state & kTimeSigValid as u32 != 0 {
1537 transport.time_sig_numerator = Some(context.timeSigNumerator);
1538 transport.time_sig_denominator = Some(context.timeSigDenominator);
1539 }
1540 }
1541
1542 transport.pos_samples =
1544 Some(context.projectTimeSamples + block_start as i64);
1545 #[allow(clippy::unnecessary_cast)]
1546 if context.state & kProjectTimeMusicValid as u32 != 0 {
1547 if P::SAMPLE_ACCURATE_AUTOMATION
1548 && block_start > 0
1549 && (context.state & kTempoValid as u32 != 0)
1550 {
1551 transport.pos_beats = Some(
1552 context.projectTimeMusic
1553 + (block_start as f64 / sample_rate as f64 / 60.0
1554 * context.tempo),
1555 );
1556 } else {
1557 transport.pos_beats = Some(context.projectTimeMusic);
1558 }
1559 }
1560
1561 #[allow(clippy::unnecessary_cast)]
1562 if context.state & kBarPositionValid as u32 != 0 {
1563 if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 {
1564 transport.bar_start_pos_beats =
1566 match transport.bar_start_pos_beats() {
1567 Some(updated) => Some(updated),
1568 None => Some(context.barPositionMusic),
1569 };
1570 } else {
1571 transport.bar_start_pos_beats = Some(context.barPositionMusic);
1572 }
1573 }
1574 #[allow(clippy::unnecessary_cast)]
1575 if context.state & kCycleActive as u32 != 0
1576 && context.state & kCycleValid as u32 != 0
1577 {
1578 transport.loop_range_beats =
1579 Some((context.cycleStartMusic, context.cycleEndMusic));
1580 }
1581 }
1582
1583 let result = if buffer_is_valid {
1584 if let Some(mut plugin) = self.inner.plugin.try_lock() {
1587 let mut aux = AuxiliaryBuffers {
1588 inputs: buffers.aux_inputs,
1589 outputs: buffers.aux_outputs,
1590 };
1591
1592 let host_out_events = unsafe { ComRef::from_raw(data.outputEvents) };
1593
1594 let mut context = self.inner.make_process_context(
1595 transport,
1596 host_out_events,
1597 total_buffer_len,
1598 block_start,
1599 );
1600
1601 let result =
1602 plugin.process(buffers.main_buffer, &mut aux, &mut context);
1603 self.inner.last_process_status.store(result);
1604 result
1605 } else {
1606 ProcessStatus::Error("Failed to acquire plugin lock")
1607 }
1608 } else {
1609 ProcessStatus::Normal
1610 };
1611
1612 match result {
1613 ProcessStatus::Error(err) => {
1614 crate::nice_debug_assert_failure!("Process error: {}", err);
1615
1616 return kResultFalse;
1617 }
1618 _ => kResultOk,
1619 }
1620 };
1621
1622 if block_end == total_buffer_len {
1626 break result;
1627 } else {
1628 block_start = block_end;
1629 }
1630 };
1631
1632 let updated_state = permit_alloc(|| self.inner.updated_state_receiver.try_recv());
1639 if let Ok(mut state) = updated_state {
1640 self.inner.set_state_inner(&mut state);
1641
1642 if let Err(err) = self.inner.updated_state_sender.send(state) {
1645 crate::nice_debug_assert_failure!(
1646 "Failed to send state object back to GUI thread: {}",
1647 err
1648 );
1649 };
1650 }
1651
1652 result
1653 })
1654 }
1655
1656 unsafe fn getTailSamples(&self) -> uint32 {
1657 match self.inner.last_process_status.load() {
1659 ProcessStatus::Tail(samples) => samples,
1660 ProcessStatus::KeepAlive => u32::MAX, _ => 0, }
1663 }
1664}
1665
1666impl<P: Vst3Plugin> IMidiMappingTrait for Wrapper<P> {
1667 unsafe fn getMidiControllerAssignment(
1668 &self,
1669 bus_index: int32,
1670 channel: int16,
1671 midi_cc_number: CtrlNumber,
1672 param_id: *mut ParamID,
1673 ) -> tresult {
1674 if P::MIDI_INPUT < MidiConfig::MidiCCs
1675 || bus_index != 0
1676 || !(0..VST3_MIDI_CHANNELS as i16).contains(&channel)
1677 || !(0..VST3_MIDI_CCS as i16).contains(&midi_cc_number)
1678 {
1679 return kResultFalse;
1680 }
1681
1682 check_null_ptr!(param_id);
1683
1684 unsafe {
1687 *param_id =
1688 VST3_MIDI_PARAMS_START + midi_cc_number as u32 + (channel as u32 * VST3_MIDI_CCS)
1689 };
1690
1691 kResultOk
1692 }
1693}
1694
1695impl<P: Vst3Plugin> INoteExpressionControllerTrait for Wrapper<P> {
1696 unsafe fn getNoteExpressionCount(&self, bus_idx: int32, _channel: int16) -> int32 {
1697 if P::MIDI_INPUT >= MidiConfig::Basic && bus_idx == 0 {
1699 note_expressions::KNOWN_NOTE_EXPRESSIONS.len() as i32
1700 } else {
1701 0
1702 }
1703 }
1704
1705 unsafe fn getNoteExpressionInfo(
1706 &self,
1707 bus_idx: int32,
1708 _channel: int16,
1709 note_expression_idx: int32,
1710 info: *mut NoteExpressionTypeInfo,
1711 ) -> tresult {
1712 if P::MIDI_INPUT < MidiConfig::Basic
1713 || bus_idx != 0
1714 || !(0..note_expressions::KNOWN_NOTE_EXPRESSIONS.len() as i32)
1715 .contains(¬e_expression_idx)
1716 {
1717 return kInvalidArgument;
1718 }
1719
1720 check_null_ptr!(info);
1721
1722 unsafe { *info = mem::zeroed() };
1723
1724 let info = unsafe { &mut *info };
1725 let note_expression_info =
1726 ¬e_expressions::KNOWN_NOTE_EXPRESSIONS[note_expression_idx as usize];
1727 info.typeId = note_expression_info.type_id;
1728 u16strlcpy(&mut info.title, note_expression_info.title);
1729 u16strlcpy(&mut info.shortTitle, note_expression_info.title);
1730 u16strlcpy(&mut info.units, note_expression_info.unit);
1731 info.unitId = kNoParentUnitId;
1732 info.valueDesc = NoteExpressionValueDescription {
1735 defaultValue: 0.5,
1736 minimum: 0.0,
1737 maximum: 1.0,
1738 stepCount: 0,
1739 };
1740 info.associatedParameterId = kNoParamId;
1741 info.flags = 1 << 2; kResultOk
1744 }
1745
1746 unsafe fn getNoteExpressionStringByValue(
1747 &self,
1748 _bus_idx: int32,
1749 _channel: int16,
1750 _id: NoteExpressionTypeID,
1751 _value: NoteExpressionValue,
1752 _string: *mut String128,
1753 ) -> tresult {
1754 kResultFalse
1755 }
1756
1757 unsafe fn getNoteExpressionValueByString(
1758 &self,
1759 _bus_idx: int32,
1760 _channel: int16,
1761 _id: NoteExpressionTypeID,
1762 _string: *const TChar,
1763 _value: *mut NoteExpressionValue,
1764 ) -> tresult {
1765 kResultFalse
1766 }
1767}
1768
1769impl<P: Vst3Plugin> IProcessContextRequirementsTrait for Wrapper<P> {
1770 #[allow(clippy::unnecessary_cast)]
1771 unsafe fn getProcessContextRequirements(&self) -> uint32 {
1772 (IProcessContextRequirements_::Flags_::kNeedProjectTimeMusic
1773 | IProcessContextRequirements_::Flags_::kNeedBarPositionMusic
1774 | IProcessContextRequirements_::Flags_::kNeedCycleMusic
1775 | IProcessContextRequirements_::Flags_::kNeedTimeSignature
1776 | IProcessContextRequirements_::Flags_::kNeedTempo
1777 | IProcessContextRequirements_::Flags_::kNeedTransportState) as u32
1778 }
1779}
1780
1781impl<P: Vst3Plugin> IUnitInfoTrait for Wrapper<P> {
1782 unsafe fn getUnitCount(&self) -> int32 {
1783 self.inner.param_units.len() as i32
1784 }
1785
1786 unsafe fn getUnitInfo(&self, unit_index: int32, info: *mut UnitInfo) -> tresult {
1787 check_null_ptr!(info);
1788
1789 match self.inner.param_units.info(unit_index as usize) {
1790 Some((unit_id, unit_info)) => {
1791 unsafe { *info = mem::zeroed() };
1792
1793 let info = unsafe { &mut *info };
1794 info.id = unit_id;
1795 info.parentUnitId = unit_info.parent_id;
1796 u16strlcpy(&mut info.name, &unit_info.name);
1797 info.programListId = kNoProgramListId;
1798
1799 kResultOk
1800 }
1801 None => kInvalidArgument,
1802 }
1803 }
1804
1805 unsafe fn getProgramListCount(&self) -> int32 {
1806 0
1808 }
1809
1810 unsafe fn getProgramListInfo(
1811 &self,
1812 _list_index: int32,
1813 _info: *mut ProgramListInfo,
1814 ) -> tresult {
1815 kInvalidArgument
1816 }
1817
1818 unsafe fn getProgramName(
1819 &self,
1820 _list_id: ProgramListID,
1821 _program_index: int32,
1822 _name: *mut String128,
1823 ) -> tresult {
1824 kInvalidArgument
1825 }
1826
1827 unsafe fn getProgramInfo(
1828 &self,
1829 _list_id: ProgramListID,
1830 _program_index: int32,
1831 _attribute_id: CString,
1832 _attribute_value: *mut String128,
1833 ) -> tresult {
1834 kInvalidArgument
1835 }
1836
1837 unsafe fn hasProgramPitchNames(&self, _id: ProgramListID, _index: int32) -> tresult {
1838 kInvalidArgument
1840 }
1841
1842 unsafe fn getProgramPitchName(
1843 &self,
1844 _id: ProgramListID,
1845 _index: int32,
1846 _pitch: int16,
1847 _name: *mut String128,
1848 ) -> tresult {
1849 kInvalidArgument
1850 }
1851
1852 unsafe fn getSelectedUnit(&self) -> UnitID {
1853 kRootUnitId
1855 }
1856
1857 unsafe fn selectUnit(&self, _id: UnitID) -> tresult {
1858 kResultFalse
1859 }
1860
1861 unsafe fn getUnitByBus(
1862 &self,
1863 _type_: MediaType,
1864 _dir: BusDirection,
1865 _bus_index: int32,
1866 _channel: int32,
1867 _unit_id: *mut UnitID,
1868 ) -> tresult {
1869 kResultFalse
1871 }
1872
1873 unsafe fn setUnitProgramData(
1874 &self,
1875 _list_or_unit: int32,
1876 _program_idx: int32,
1877 _data: *mut IBStream,
1878 ) -> tresult {
1879 kInvalidArgument
1880 }
1881}
1882
1883impl<P: Vst3Plugin> IInfoListenerTrait for Wrapper<P> {
1884 unsafe fn setChannelContextInfos(&self, list: *mut IAttributeList) -> tresult {
1885 #[cfg(not(feature = "editor"))]
1886 {
1887 let _ = list;
1888 return kResultOk;
1889 }
1890
1891 #[cfg(feature = "editor")]
1892 {
1893 use nice_plug_core::plugin::{TrackColor, TrackInfo};
1894 use vst3::Steinberg::Vst::{ChannelContext, IAttributeListTrait};
1895
1896 fn track_color_from_vst3_color(color: u32) -> TrackColor {
1897 TrackColor::new(
1898 ((color >> 16) & 0xFF) as u8,
1899 ((color >> 8) & 0xFF) as u8,
1900 (color & 0xFF) as u8,
1901 ((color >> 24) & 0xFF) as u8,
1902 )
1903 }
1904 check_null_ptr!(list);
1905
1906 let list = unsafe { ComRef::from_raw(list) };
1907 let Some(list) = list else {
1908 return kInvalidArgument;
1909 };
1910
1911 permit_alloc(|| {
1912 let mut current_track_info = self.inner.current_track_info.borrow_mut();
1913 let mut name = current_track_info.name().to_owned();
1914 let mut color = current_track_info.color();
1915
1916 let mut name_buf: String128 = [0; 128];
1917 if unsafe {
1918 list.getString(
1919 ChannelContext::kChannelNameKey,
1920 name_buf.as_mut_ptr(),
1921 mem::size_of::<String128>() as u32,
1922 )
1923 } == kResultOk
1924 && let Ok(cstr) = U16CStr::from_slice_truncate(&name_buf)
1925 {
1926 name = cstr.to_string_lossy();
1927 } let mut color_value = 0i64;
1930 if unsafe { list.getInt(ChannelContext::kChannelColorKey, &mut color_value) }
1931 == kResultOk
1932 {
1933 color = Some(track_color_from_vst3_color(color_value as u32));
1934 }
1935
1936 let track_info = TrackInfo::new(name, color);
1937 *current_track_info = track_info.clone();
1938
1939 if let Some(editor) = self.inner.editor.borrow().as_ref() {
1940 editor.lock().track_info_updated(track_info);
1941 }
1942 });
1943
1944 kResultOk
1945 }
1946 }
1947}