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 \
1401 an allocation."
1402 );
1403 }
1404 input_note_events.push_back(event);
1405 });
1406 }
1407 }
1408 }
1409 }
1410
1411 let result = if is_param_flush {
1412 kResultOk
1413 } else {
1414 let block_len = block_end - block_start;
1417
1418 let mut buffer_manager = self.inner.buffer_manager.borrow_mut();
1421 let buffers = unsafe {
1422 buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
1423 if data.numOutputs > 0
1424 && !data.outputs.is_null()
1425 && !(*data.outputs).__field0.channelBuffers32.is_null()
1426 && has_main_output
1427 {
1428 let audio_output = &*data.outputs;
1429 let ptrs =
1430 NonNull::new(audio_output.__field0.channelBuffers32).unwrap();
1431 let num_channels = audio_output.numChannels as usize;
1432
1433 *buffer_source.main_output_channel_pointers =
1434 Some(ChannelPointers { ptrs, num_channels });
1435 }
1436
1437 if data.numInputs > 0
1438 && !data.inputs.is_null()
1439 && !(*data.inputs).__field0.channelBuffers32.is_null()
1440 && has_main_input
1441 {
1442 let audio_input = &*data.inputs;
1443 let ptrs =
1444 NonNull::new(audio_input.__field0.channelBuffers32).unwrap();
1445 let num_channels = audio_input.numChannels as usize;
1446
1447 *buffer_source.main_input_channel_pointers =
1448 Some(ChannelPointers { ptrs, num_channels });
1449 }
1450
1451 if !data.inputs.is_null() {
1452 for (aux_input_no, aux_input_channel_pointers) in buffer_source
1453 .aux_input_channel_pointers
1454 .iter_mut()
1455 .enumerate()
1456 {
1457 let aux_input_idx = aux_input_no + aux_input_start_idx;
1458 if aux_input_idx > data.numOutputs as usize {
1459 break;
1460 }
1461
1462 let audio_input = &*data.inputs.add(aux_input_idx);
1463 match NonNull::new(audio_input.__field0.channelBuffers32) {
1464 Some(ptrs) => {
1465 let num_channels = audio_input.numChannels as usize;
1466
1467 *aux_input_channel_pointers =
1468 Some(ChannelPointers { ptrs, num_channels });
1469 }
1470 None => continue,
1471 }
1472 }
1473 }
1474
1475 if !data.outputs.is_null() {
1476 for (aux_output_no, aux_output_channel_pointers) in buffer_source
1477 .aux_output_channel_pointers
1478 .iter_mut()
1479 .enumerate()
1480 {
1481 let aux_output_idx = aux_output_no + aux_output_start_idx;
1482 if aux_output_idx > data.numOutputs as usize {
1483 break;
1484 }
1485
1486 let audio_output = &*data.outputs.add(aux_output_idx);
1487 match NonNull::new(audio_output.__field0.channelBuffers32) {
1488 Some(ptrs) => {
1489 let num_channels = audio_output.numChannels as usize;
1490
1491 *aux_output_channel_pointers =
1492 Some(ChannelPointers { ptrs, num_channels });
1493 }
1494 None => continue,
1495 }
1496 }
1497 }
1498 })
1499 };
1500
1501 let mut buffer_is_valid = true;
1506 for output_buffer_slice in
1507 buffers.main_buffer.as_slice_immutable().iter().chain(
1508 buffers
1509 .aux_outputs
1510 .iter()
1511 .flat_map(|buffer| buffer.as_slice_immutable().iter()),
1512 )
1513 {
1514 if output_buffer_slice.is_empty() {
1515 buffer_is_valid = false;
1516 break;
1517 }
1518 }
1519 crate::nice_debug_assert!(buffer_is_valid);
1520
1521 let mut transport = Transport::new(sample_rate);
1525 if !data.processContext.is_null() {
1526 let context = unsafe { &*data.processContext };
1527
1528 #[allow(clippy::unnecessary_cast)]
1529 {
1530 transport.playing = context.state & kPlaying as u32 != 0;
1531 transport.recording = context.state & kRecording as u32 != 0;
1532
1533 if context.state & kTempoValid as u32 != 0 {
1534 transport.tempo = Some(context.tempo);
1535 }
1536
1537 if context.state & kTimeSigValid as u32 != 0 {
1538 transport.time_sig_numerator = Some(context.timeSigNumerator);
1539 transport.time_sig_denominator = Some(context.timeSigDenominator);
1540 }
1541 }
1542
1543 transport.pos_samples =
1545 Some(context.projectTimeSamples + block_start as i64);
1546 #[allow(clippy::unnecessary_cast)]
1547 if context.state & kProjectTimeMusicValid as u32 != 0 {
1548 if P::SAMPLE_ACCURATE_AUTOMATION
1549 && block_start > 0
1550 && (context.state & kTempoValid as u32 != 0)
1551 {
1552 transport.pos_beats = Some(
1553 context.projectTimeMusic
1554 + (block_start as f64 / sample_rate as f64 / 60.0
1555 * context.tempo),
1556 );
1557 } else {
1558 transport.pos_beats = Some(context.projectTimeMusic);
1559 }
1560 }
1561
1562 #[allow(clippy::unnecessary_cast)]
1563 if context.state & kBarPositionValid as u32 != 0 {
1564 if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 {
1565 transport.bar_start_pos_beats =
1567 match transport.bar_start_pos_beats() {
1568 Some(updated) => Some(updated),
1569 None => Some(context.barPositionMusic),
1570 };
1571 } else {
1572 transport.bar_start_pos_beats = Some(context.barPositionMusic);
1573 }
1574 }
1575 #[allow(clippy::unnecessary_cast)]
1576 if context.state & kCycleActive as u32 != 0
1577 && context.state & kCycleValid as u32 != 0
1578 {
1579 transport.loop_range_beats =
1580 Some((context.cycleStartMusic, context.cycleEndMusic));
1581 }
1582 }
1583
1584 let result = if buffer_is_valid {
1585 if let Some(mut plugin) = self.inner.plugin.try_lock() {
1588 let mut aux = AuxiliaryBuffers {
1589 inputs: buffers.aux_inputs,
1590 outputs: buffers.aux_outputs,
1591 };
1592
1593 let host_out_events = unsafe { ComRef::from_raw(data.outputEvents) };
1594
1595 let mut context = self.inner.make_process_context(
1596 transport,
1597 host_out_events,
1598 total_buffer_len,
1599 block_start,
1600 );
1601
1602 let result =
1603 plugin.process(buffers.main_buffer, &mut aux, &mut context);
1604 self.inner.last_process_status.store(result);
1605 result
1606 } else {
1607 ProcessStatus::Error("Failed to acquire plugin lock")
1608 }
1609 } else {
1610 ProcessStatus::Normal
1611 };
1612
1613 match result {
1614 ProcessStatus::Error(err) => {
1615 crate::nice_debug_assert_failure!("Process error: {}", err);
1616
1617 return kResultFalse;
1618 }
1619 _ => kResultOk,
1620 }
1621 };
1622
1623 if block_end == total_buffer_len {
1627 break result;
1628 } else {
1629 block_start = block_end;
1630 }
1631 };
1632
1633 let updated_state = permit_alloc(|| self.inner.updated_state_receiver.try_recv());
1640 if let Ok(mut state) = updated_state {
1641 self.inner.set_state_inner(&mut state);
1642
1643 if let Err(err) = self.inner.updated_state_sender.send(state) {
1646 crate::nice_debug_assert_failure!(
1647 "Failed to send state object back to GUI thread: {}",
1648 err
1649 );
1650 };
1651 }
1652
1653 result
1654 })
1655 }
1656
1657 unsafe fn getTailSamples(&self) -> uint32 {
1658 match self.inner.last_process_status.load() {
1660 ProcessStatus::Tail(samples) => samples,
1661 ProcessStatus::KeepAlive => u32::MAX, _ => 0, }
1664 }
1665}
1666
1667impl<P: Vst3Plugin> IMidiMappingTrait for Wrapper<P> {
1668 unsafe fn getMidiControllerAssignment(
1669 &self,
1670 bus_index: int32,
1671 channel: int16,
1672 midi_cc_number: CtrlNumber,
1673 param_id: *mut ParamID,
1674 ) -> tresult {
1675 if P::MIDI_INPUT < MidiConfig::MidiCCs
1676 || bus_index != 0
1677 || !(0..VST3_MIDI_CHANNELS as i16).contains(&channel)
1678 || !(0..VST3_MIDI_CCS as i16).contains(&midi_cc_number)
1679 {
1680 return kResultFalse;
1681 }
1682
1683 check_null_ptr!(param_id);
1684
1685 unsafe {
1688 *param_id =
1689 VST3_MIDI_PARAMS_START + midi_cc_number as u32 + (channel as u32 * VST3_MIDI_CCS)
1690 };
1691
1692 kResultOk
1693 }
1694}
1695
1696impl<P: Vst3Plugin> INoteExpressionControllerTrait for Wrapper<P> {
1697 unsafe fn getNoteExpressionCount(&self, bus_idx: int32, _channel: int16) -> int32 {
1698 if P::MIDI_INPUT >= MidiConfig::Basic && bus_idx == 0 {
1700 note_expressions::KNOWN_NOTE_EXPRESSIONS.len() as i32
1701 } else {
1702 0
1703 }
1704 }
1705
1706 unsafe fn getNoteExpressionInfo(
1707 &self,
1708 bus_idx: int32,
1709 _channel: int16,
1710 note_expression_idx: int32,
1711 info: *mut NoteExpressionTypeInfo,
1712 ) -> tresult {
1713 if P::MIDI_INPUT < MidiConfig::Basic
1714 || bus_idx != 0
1715 || !(0..note_expressions::KNOWN_NOTE_EXPRESSIONS.len() as i32)
1716 .contains(¬e_expression_idx)
1717 {
1718 return kInvalidArgument;
1719 }
1720
1721 check_null_ptr!(info);
1722
1723 unsafe { *info = mem::zeroed() };
1724
1725 let info = unsafe { &mut *info };
1726 let note_expression_info =
1727 ¬e_expressions::KNOWN_NOTE_EXPRESSIONS[note_expression_idx as usize];
1728 info.typeId = note_expression_info.type_id;
1729 u16strlcpy(&mut info.title, note_expression_info.title);
1730 u16strlcpy(&mut info.shortTitle, note_expression_info.title);
1731 u16strlcpy(&mut info.units, note_expression_info.unit);
1732 info.unitId = kNoParentUnitId;
1733 info.valueDesc = NoteExpressionValueDescription {
1736 defaultValue: 0.5,
1737 minimum: 0.0,
1738 maximum: 1.0,
1739 stepCount: 0,
1740 };
1741 info.associatedParameterId = kNoParamId;
1742 info.flags = 1 << 2; kResultOk
1745 }
1746
1747 unsafe fn getNoteExpressionStringByValue(
1748 &self,
1749 _bus_idx: int32,
1750 _channel: int16,
1751 _id: NoteExpressionTypeID,
1752 _value: NoteExpressionValue,
1753 _string: *mut String128,
1754 ) -> tresult {
1755 kResultFalse
1756 }
1757
1758 unsafe fn getNoteExpressionValueByString(
1759 &self,
1760 _bus_idx: int32,
1761 _channel: int16,
1762 _id: NoteExpressionTypeID,
1763 _string: *const TChar,
1764 _value: *mut NoteExpressionValue,
1765 ) -> tresult {
1766 kResultFalse
1767 }
1768}
1769
1770impl<P: Vst3Plugin> IProcessContextRequirementsTrait for Wrapper<P> {
1771 #[allow(clippy::unnecessary_cast)]
1772 unsafe fn getProcessContextRequirements(&self) -> uint32 {
1773 (IProcessContextRequirements_::Flags_::kNeedProjectTimeMusic
1774 | IProcessContextRequirements_::Flags_::kNeedBarPositionMusic
1775 | IProcessContextRequirements_::Flags_::kNeedCycleMusic
1776 | IProcessContextRequirements_::Flags_::kNeedTimeSignature
1777 | IProcessContextRequirements_::Flags_::kNeedTempo
1778 | IProcessContextRequirements_::Flags_::kNeedTransportState) as u32
1779 }
1780}
1781
1782impl<P: Vst3Plugin> IUnitInfoTrait for Wrapper<P> {
1783 unsafe fn getUnitCount(&self) -> int32 {
1784 self.inner.param_units.len() as i32
1785 }
1786
1787 unsafe fn getUnitInfo(&self, unit_index: int32, info: *mut UnitInfo) -> tresult {
1788 check_null_ptr!(info);
1789
1790 match self.inner.param_units.info(unit_index as usize) {
1791 Some((unit_id, unit_info)) => {
1792 unsafe { *info = mem::zeroed() };
1793
1794 let info = unsafe { &mut *info };
1795 info.id = unit_id;
1796 info.parentUnitId = unit_info.parent_id;
1797 u16strlcpy(&mut info.name, &unit_info.name);
1798 info.programListId = kNoProgramListId;
1799
1800 kResultOk
1801 }
1802 None => kInvalidArgument,
1803 }
1804 }
1805
1806 unsafe fn getProgramListCount(&self) -> int32 {
1807 0
1809 }
1810
1811 unsafe fn getProgramListInfo(
1812 &self,
1813 _list_index: int32,
1814 _info: *mut ProgramListInfo,
1815 ) -> tresult {
1816 kInvalidArgument
1817 }
1818
1819 unsafe fn getProgramName(
1820 &self,
1821 _list_id: ProgramListID,
1822 _program_index: int32,
1823 _name: *mut String128,
1824 ) -> tresult {
1825 kInvalidArgument
1826 }
1827
1828 unsafe fn getProgramInfo(
1829 &self,
1830 _list_id: ProgramListID,
1831 _program_index: int32,
1832 _attribute_id: CString,
1833 _attribute_value: *mut String128,
1834 ) -> tresult {
1835 kInvalidArgument
1836 }
1837
1838 unsafe fn hasProgramPitchNames(&self, _id: ProgramListID, _index: int32) -> tresult {
1839 kInvalidArgument
1841 }
1842
1843 unsafe fn getProgramPitchName(
1844 &self,
1845 _id: ProgramListID,
1846 _index: int32,
1847 _pitch: int16,
1848 _name: *mut String128,
1849 ) -> tresult {
1850 kInvalidArgument
1851 }
1852
1853 unsafe fn getSelectedUnit(&self) -> UnitID {
1854 kRootUnitId
1856 }
1857
1858 unsafe fn selectUnit(&self, _id: UnitID) -> tresult {
1859 kResultFalse
1860 }
1861
1862 unsafe fn getUnitByBus(
1863 &self,
1864 _type_: MediaType,
1865 _dir: BusDirection,
1866 _bus_index: int32,
1867 _channel: int32,
1868 _unit_id: *mut UnitID,
1869 ) -> tresult {
1870 kResultFalse
1872 }
1873
1874 unsafe fn setUnitProgramData(
1875 &self,
1876 _list_or_unit: int32,
1877 _program_idx: int32,
1878 _data: *mut IBStream,
1879 ) -> tresult {
1880 kInvalidArgument
1881 }
1882}
1883
1884impl<P: Vst3Plugin> IInfoListenerTrait for Wrapper<P> {
1885 unsafe fn setChannelContextInfos(&self, list: *mut IAttributeList) -> tresult {
1886 #[cfg(not(feature = "editor"))]
1887 {
1888 let _ = list;
1889 return kResultOk;
1890 }
1891
1892 #[cfg(feature = "editor")]
1893 {
1894 use nice_plug_core::plugin::{TrackColor, TrackInfo};
1895 use vst3::Steinberg::Vst::{ChannelContext, IAttributeListTrait};
1896
1897 fn track_color_from_vst3_color(color: u32) -> TrackColor {
1898 TrackColor::new(
1899 ((color >> 16) & 0xFF) as u8,
1900 ((color >> 8) & 0xFF) as u8,
1901 (color & 0xFF) as u8,
1902 ((color >> 24) & 0xFF) as u8,
1903 )
1904 }
1905 check_null_ptr!(list);
1906
1907 let list = unsafe { ComRef::from_raw(list) };
1908 let Some(list) = list else {
1909 return kInvalidArgument;
1910 };
1911
1912 permit_alloc(|| {
1913 let mut current_track_info = self.inner.current_track_info.borrow_mut();
1914 let mut name = current_track_info.name().to_owned();
1915 let mut color = current_track_info.color();
1916
1917 let mut name_buf: String128 = [0; 128];
1918 if unsafe {
1919 list.getString(
1920 ChannelContext::kChannelNameKey,
1921 name_buf.as_mut_ptr(),
1922 mem::size_of::<String128>() as u32,
1923 )
1924 } == kResultOk
1925 && let Ok(cstr) = U16CStr::from_slice_truncate(&name_buf)
1926 {
1927 name = cstr.to_string_lossy();
1928 } let mut color_value = 0i64;
1931 if unsafe { list.getInt(ChannelContext::kChannelColorKey, &mut color_value) }
1932 == kResultOk
1933 {
1934 color = Some(track_color_from_vst3_color(color_value as u32));
1935 }
1936
1937 let track_info = TrackInfo::new(name, color);
1938 *current_track_info = track_info.clone();
1939
1940 if let Some(editor) = self.inner.editor.borrow().as_ref() {
1941 editor.lock().track_info_updated(track_info);
1942 }
1943 });
1944
1945 kResultOk
1946 }
1947 }
1948}