{
- pub fn new() -> Box {
- unsafe fn initialize(&self, _context: *mut c_void) -> tresult {
- // We currently don't need or allow any initialization logic
- kResultOk
- }
-
- unsafe fn terminate(&self) -> tresult {
- kResultOk
- }
-}
-
-impl {
- unsafe fn get_controller_class_id(&self, _tuid: *mut vst3_sys::IID) -> tresult {
- // We won't separate the edit controller to keep the implemetnation a bit smaller
- kNoInterface
- }
-
- unsafe fn set_io_mode(&self, _mode: vst3_sys::vst::IoMode) -> tresult {
- // This would need to integrate with the GUI, which we currently don't have
- kResultOk
- }
-
- unsafe fn get_bus_count(
- &self,
- type_: vst3_sys::vst::MediaType,
- dir: vst3_sys::vst::BusDirection,
- ) -> i32 {
- // All plugins currently only have a single input and a single output bus
- match type_ {
- x if x == vst3_sys::vst::MediaTypes::kAudio as i32 => 1,
- x if x == vst3_sys::vst::MediaTypes::kEvent as i32
- && dir == vst3_sys::vst::BusDirections::kInput as i32
- && P::ACCEPTS_MIDI =>
- {
- 1
- }
- _ => 0,
- }
- }
-
- unsafe fn get_bus_info(
- &self,
- type_: vst3_sys::vst::MediaType,
- dir: vst3_sys::vst::BusDirection,
- index: i32,
- info: *mut vst3_sys::vst::BusInfo,
- ) -> tresult {
- check_null_ptr!(info);
-
- match (type_, dir, index) {
- (t, _, _) if t == vst3_sys::vst::MediaTypes::kAudio as i32 => {
- *info = mem::zeroed();
-
- let info = &mut *info;
- info.media_type = vst3_sys::vst::MediaTypes::kAudio as i32;
- info.bus_type = vst3_sys::vst::BusTypes::kMain as i32;
- info.flags = vst3_sys::vst::BusFlags::kDefaultActive as u32;
- match (dir, index) {
- (d, 0) if d == vst3_sys::vst::BusDirections::kInput as i32 => {
- info.direction = vst3_sys::vst::BusDirections::kInput as i32;
- info.channel_count =
- self.inner.current_bus_config.load().num_input_channels as i32;
- u16strlcpy(&mut info.name, "Input");
-
- kResultOk
- }
- (d, 0) if d == vst3_sys::vst::BusDirections::kOutput as i32 => {
- info.direction = vst3_sys::vst::BusDirections::kOutput as i32;
- info.channel_count =
- self.inner.current_bus_config.load().num_output_channels as i32;
- u16strlcpy(&mut info.name, "Output");
-
- kResultOk
- }
- _ => kInvalidArgument,
- }
- }
- (t, d, 0)
- if t == vst3_sys::vst::MediaTypes::kEvent as i32
- && d == vst3_sys::vst::BusDirections::kInput as i32
- && P::ACCEPTS_MIDI =>
- {
- *info = mem::zeroed();
-
- let info = &mut *info;
- info.media_type = vst3_sys::vst::MediaTypes::kEvent as i32;
- info.direction = vst3_sys::vst::BusDirections::kInput as i32;
- info.channel_count = 16;
- u16strlcpy(&mut info.name, "MIDI");
- info.bus_type = vst3_sys::vst::BusTypes::kMain as i32;
- info.flags = vst3_sys::vst::BusFlags::kDefaultActive as u32;
- kResultOk
- }
- _ => kInvalidArgument,
- }
- }
-
- unsafe fn get_routing_info(
- &self,
- in_info: *mut vst3_sys::vst::RoutingInfo,
- out_info: *mut vst3_sys::vst::RoutingInfo,
- ) -> tresult {
- check_null_ptr!(in_info, out_info);
-
- *out_info = mem::zeroed();
-
- let in_info = &*in_info;
- let out_info = &mut *out_info;
- match (in_info.media_type, in_info.bus_index) {
- (t, 0) if t == vst3_sys::vst::MediaTypes::kAudio as i32 => {
- out_info.media_type = vst3_sys::vst::MediaTypes::kAudio as i32;
- out_info.bus_index = in_info.bus_index;
- out_info.channel = in_info.channel;
-
- kResultOk
- }
- _ => kInvalidArgument,
- }
- }
-
- unsafe fn activate_bus(
- &self,
- type_: vst3_sys::vst::MediaType,
- dir: vst3_sys::vst::BusDirection,
- index: i32,
- _state: vst3_sys::base::TBool,
- ) -> tresult {
- // We don't need any special handling here
- match (type_, dir, index) {
- (t, _, 0) if t == vst3_sys::vst::MediaTypes::kAudio as i32 => kResultOk,
- (t, d, 0)
- if t == vst3_sys::vst::MediaTypes::kEvent as i32
- && d == vst3_sys::vst::BusDirections::kInput as i32
- && P::ACCEPTS_MIDI =>
- {
- kResultOk
- }
- _ => kInvalidArgument,
- }
- }
-
- unsafe fn set_active(&self, _state: TBool) -> tresult {
- // We don't need any special handling here
- kResultOk
- }
-
- unsafe fn set_state(&self, state: SharedVstPtr {
- unsafe fn set_component_state(&self, _state: SharedVstPtr {
- unsafe fn set_bus_arrangements(
- &self,
- inputs: *mut vst3_sys::vst::SpeakerArrangement,
- num_ins: i32,
- outputs: *mut vst3_sys::vst::SpeakerArrangement,
- num_outs: i32,
- ) -> tresult {
- check_null_ptr!(inputs, outputs);
-
- // We currently only do single audio bus IO configurations
- if num_ins != 1 || num_outs != 1 {
- return kInvalidArgument;
- }
-
- let input_channel_map = &*inputs;
- let output_channel_map = &*outputs;
- let proposed_config = BusConfig {
- num_input_channels: input_channel_map.count_ones(),
- num_output_channels: output_channel_map.count_ones(),
- };
- if self
- .inner
- .plugin
- .read()
- .accepts_bus_config(&proposed_config)
- {
- self.inner.current_bus_config.store(proposed_config);
-
- kResultOk
- } else {
- kResultFalse
- }
- }
-
- unsafe fn get_bus_arrangement(
- &self,
- dir: vst3_sys::vst::BusDirection,
- index: i32,
- arr: *mut vst3_sys::vst::SpeakerArrangement,
- ) -> tresult {
- check_null_ptr!(arr);
-
- let channel_count_to_map = |count| match count {
- 0 => vst3_sys::vst::kEmpty,
- 1 => vst3_sys::vst::kMono,
- 2 => vst3_sys::vst::kStereo,
- 5 => vst3_sys::vst::k50,
- 6 => vst3_sys::vst::k51,
- 7 => vst3_sys::vst::k70Cine,
- 8 => vst3_sys::vst::k71Cine,
- n => {
- nih_debug_assert_failure!(
- "No defined layout for {} channels, making something up on the spot...",
- n
- );
- (1 << n) - 1
- }
- };
-
- let config = self.inner.current_bus_config.load();
- let num_channels = match (dir, index) {
- (d, 0) if d == vst3_sys::vst::BusDirections::kInput as i32 => config.num_input_channels,
- (d, 0) if d == vst3_sys::vst::BusDirections::kOutput as i32 => {
- config.num_output_channels
- }
- _ => return kInvalidArgument,
- };
- let channel_map = channel_count_to_map(num_channels);
-
- nih_debug_assert_eq!(num_channels, channel_map.count_ones());
- *arr = channel_map;
-
- kResultOk
- }
-
- unsafe fn can_process_sample_size(&self, symbolic_sample_size: i32) -> tresult {
- if symbolic_sample_size == vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32 {
- kResultOk
- } else {
- kResultFalse
- }
- }
-
- unsafe fn get_latency_samples(&self) -> u32 {
- self.inner.current_latency.load(Ordering::SeqCst)
- }
-
- unsafe fn setup_processing(&self, setup: *const vst3_sys::vst::ProcessSetup) -> tresult {
- check_null_ptr!(setup);
-
- // There's no special handling for offline processing at the moment
- let setup = &*setup;
- nih_debug_assert_eq!(
- setup.symbolic_sample_size,
- vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32
- );
-
- let bus_config = self.inner.current_bus_config.load();
- let buffer_config = BufferConfig {
- sample_rate: setup.sample_rate as f32,
- max_buffer_size: setup.max_samples_per_block as u32,
- };
-
- // Befure initializing the plugin, make sure all smoothers are set the the default values
- for param in self.inner.param_by_hash.values() {
- param.update_smoother(buffer_config.sample_rate, true);
- }
-
- if self.inner.plugin.write().initialize(
- &bus_config,
- &buffer_config,
- &mut self.inner.make_process_context(),
- ) {
- // Preallocate enough room in the output slices vector so we can convert a `*mut *mut
- // f32` to a `&mut [&mut f32]` in the process call
- self.inner
- .output_buffer
- .write()
- .as_raw_vec()
- .resize_with(bus_config.num_output_channels as usize, || &mut []);
-
- // Also store this for later, so we can reinitialize the plugin after restoring state
- self.inner.current_buffer_config.store(Some(buffer_config));
-
- kResultOk
- } else {
- kResultFalse
- }
- }
-
- unsafe fn set_processing(&self, state: TBool) -> tresult {
- // Always reset the processing status when the plugin gets activated or deactivated
- self.inner.last_process_status.store(ProcessStatus::Normal);
- self.inner.is_processing.store(state != 0, Ordering::SeqCst);
-
- // We don't have any special handling for suspending and resuming plugins, yet
- kResultOk
- }
-
- unsafe fn process(&self, data: *mut vst3_sys::vst::ProcessData) -> tresult {
- check_null_ptr!(data);
-
- // Panic on allocations if the `assert_process_allocs` feature has been enabled, and make
- // sure that FTZ is set up correctly
- process_wrapper(|| {
- // We need to handle incoming automation first
- let data = &*data;
- let sample_rate = self
- .inner
- .current_buffer_config
- .load()
- .map(|c| c.sample_rate);
- if let Some(param_changes) = data.input_param_changes.upgrade() {
- let num_param_queues = param_changes.get_parameter_count();
- for change_queue_idx in 0..num_param_queues {
- if let Some(param_change_queue) =
- param_changes.get_parameter_data(change_queue_idx).upgrade()
- {
- let param_hash = param_change_queue.get_parameter_id();
- let num_changes = param_change_queue.get_point_count();
-
- // TODO: Handle sample accurate parameter changes, possibly in a similar way
- // to the smoothing
- let mut sample_offset = 0i32;
- let mut value = 0.0f64;
- if num_changes > 0
- && param_change_queue.get_point(
- num_changes - 1,
- &mut sample_offset,
- &mut value,
- ) == kResultOk
- {
- self.inner.set_normalized_value_by_hash(
- param_hash,
- value as f32,
- sample_rate,
- );
- }
- }
- }
- }
-
- // And also incoming note events if the plugin accepts MDII
- if P::ACCEPTS_MIDI {
- let mut input_events = self.inner.input_events.write();
- if let Some(events) = data.input_events.upgrade() {
- let num_events = events.get_event_count();
-
- input_events.clear();
- let mut event: MaybeUninit<_> = MaybeUninit::uninit();
- for i in 0..num_events {
- nih_debug_assert_eq!(events.get_event(i, event.as_mut_ptr()), kResultOk);
- let event = event.assume_init();
- let timing = event.sample_offset as u32;
- if event.type_ == vst3_sys::vst::EventTypes::kNoteOnEvent as u16 {
- let event = event.event.note_on;
- input_events.push_back(NoteEvent::NoteOn {
- timing,
- channel: event.channel as u8,
- note: event.pitch as u8,
- velocity: (event.velocity * 127.0).round() as u8,
- });
- } else if event.type_ == vst3_sys::vst::EventTypes::kNoteOffEvent as u16 {
- let event = event.event.note_off;
- input_events.push_back(NoteEvent::NoteOff {
- timing,
- channel: event.channel as u8,
- note: event.pitch as u8,
- velocity: (event.velocity * 127.0).round() as u8,
- });
- }
- }
- }
- }
-
- // It's possible the host only wanted to send new parameter values
- if data.num_outputs == 0 {
- nih_log!("VST3 parameter flush");
- return kResultOk;
- }
-
- // The setups we suppport are:
- // - 1 input bus
- // - 1 output bus
- // - 1 input bus and 1 output bus
- nih_debug_assert!(
- data.num_inputs >= 0
- && data.num_inputs <= 1
- && data.num_outputs >= 0
- && data.num_outputs <= 1,
- "The host provides more than one input or output bus"
- );
- nih_debug_assert_eq!(
- data.symbolic_sample_size,
- vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32
- );
- nih_debug_assert!(data.num_samples >= 0);
-
- let num_output_channels = (*data.outputs).num_channels as usize;
- check_null_ptr_msg!(
- "Process output pointer is null",
- data.outputs,
- (*data.outputs).buffers,
- );
-
- // This vector has been reallocated to contain enough slices as there are output
- // channels
- let mut output_buffer = self.inner.output_buffer.write();
- {
- let output_slices = output_buffer.as_raw_vec();
- nih_debug_assert_eq!(num_output_channels, output_slices.len());
- for (output_channel_idx, output_channel_slice) in
- output_slices.iter_mut().enumerate()
- {
- // SAFETY: These pointers may not be valid outside of this function even though
- // their lifetime is equal to this structs. This is still safe because they are
- // only dereferenced here later as part of this process function.
- *output_channel_slice = std::slice::from_raw_parts_mut(
- *((*data.outputs).buffers as *mut *mut f32).add(output_channel_idx),
- data.num_samples as usize,
- );
- }
- }
-
- // Most hosts process data in place, in which case we don't need to do any copying
- // ourselves. If the pointers do not alias, then we'll do the copy here and then the
- // plugin can just do normal in place processing.
- if !data.inputs.is_null() {
- let num_input_channels = (*data.inputs).num_channels as usize;
- nih_debug_assert!(
- num_input_channels <= num_output_channels,
- "Stereo to mono and similar configurations are not supported"
- );
- for input_channel_idx in 0..cmp::min(num_input_channels, num_output_channels) {
- let output_channel_ptr =
- *((*data.outputs).buffers as *mut *mut f32).add(input_channel_idx);
- let input_channel_ptr =
- *((*data.inputs).buffers as *const *const f32).add(input_channel_idx);
- if input_channel_ptr != output_channel_ptr {
- ptr::copy_nonoverlapping(
- input_channel_ptr,
- output_channel_ptr,
- data.num_samples as usize,
- );
- }
- }
- }
-
- let plugin = &mut *self.inner.plugin.data_ptr();
- let mut context = self.inner.make_process_context();
- match plugin.process(&mut output_buffer, &mut context) {
- ProcessStatus::Error(err) => {
- nih_debug_assert_failure!("Process error: {}", err);
-
- kResultFalse
- }
- _ => kResultOk,
- }
- })
- }
-
- unsafe fn get_tail_samples(&self) -> u32 {
- // https://github.com/steinbergmedia/vst3_pluginterfaces/blob/2ad397ade5b51007860bedb3b01b8afd2c5f6fba/vst/ivstaudioprocessor.h#L145-L159
- match self.inner.last_process_status.load() {
- ProcessStatus::Tail(samples) => samples,
- ProcessStatus::KeepAlive => u32::MAX, // kInfiniteTail
- _ => 0, // kNoTail
- }
- }
-}
-
#[doc(hidden)]
#[VST3(implements(IPluginFactory, IPluginFactory2, IPluginFactory3))]
pub struct Factory {
+ pub fn new() -> Box {
+ unsafe fn initialize(&self, _context: *mut c_void) -> tresult {
+ // We currently don't need or allow any initialization logic
+ kResultOk
+ }
+
+ unsafe fn terminate(&self) -> tresult {
+ kResultOk
+ }
+}
+
+impl {
+ unsafe fn get_controller_class_id(&self, _tuid: *mut vst3_sys::IID) -> tresult {
+ // We won't separate the edit controller to keep the implemetnation a bit smaller
+ kNoInterface
+ }
+
+ unsafe fn set_io_mode(&self, _mode: vst3_sys::vst::IoMode) -> tresult {
+ // This would need to integrate with the GUI, which we currently don't have
+ kResultOk
+ }
+
+ unsafe fn get_bus_count(
+ &self,
+ type_: vst3_sys::vst::MediaType,
+ dir: vst3_sys::vst::BusDirection,
+ ) -> i32 {
+ // All plugins currently only have a single input and a single output bus
+ match type_ {
+ x if x == vst3_sys::vst::MediaTypes::kAudio as i32 => 1,
+ x if x == vst3_sys::vst::MediaTypes::kEvent as i32
+ && dir == vst3_sys::vst::BusDirections::kInput as i32
+ && P::ACCEPTS_MIDI =>
+ {
+ 1
+ }
+ _ => 0,
+ }
+ }
+
+ unsafe fn get_bus_info(
+ &self,
+ type_: vst3_sys::vst::MediaType,
+ dir: vst3_sys::vst::BusDirection,
+ index: i32,
+ info: *mut vst3_sys::vst::BusInfo,
+ ) -> tresult {
+ check_null_ptr!(info);
+
+ match (type_, dir, index) {
+ (t, _, _) if t == vst3_sys::vst::MediaTypes::kAudio as i32 => {
+ *info = mem::zeroed();
+
+ let info = &mut *info;
+ info.media_type = vst3_sys::vst::MediaTypes::kAudio as i32;
+ info.bus_type = vst3_sys::vst::BusTypes::kMain as i32;
+ info.flags = vst3_sys::vst::BusFlags::kDefaultActive as u32;
+ match (dir, index) {
+ (d, 0) if d == vst3_sys::vst::BusDirections::kInput as i32 => {
+ info.direction = vst3_sys::vst::BusDirections::kInput as i32;
+ info.channel_count =
+ self.inner.current_bus_config.load().num_input_channels as i32;
+ u16strlcpy(&mut info.name, "Input");
+
+ kResultOk
+ }
+ (d, 0) if d == vst3_sys::vst::BusDirections::kOutput as i32 => {
+ info.direction = vst3_sys::vst::BusDirections::kOutput as i32;
+ info.channel_count =
+ self.inner.current_bus_config.load().num_output_channels as i32;
+ u16strlcpy(&mut info.name, "Output");
+
+ kResultOk
+ }
+ _ => kInvalidArgument,
+ }
+ }
+ (t, d, 0)
+ if t == vst3_sys::vst::MediaTypes::kEvent as i32
+ && d == vst3_sys::vst::BusDirections::kInput as i32
+ && P::ACCEPTS_MIDI =>
+ {
+ *info = mem::zeroed();
+
+ let info = &mut *info;
+ info.media_type = vst3_sys::vst::MediaTypes::kEvent as i32;
+ info.direction = vst3_sys::vst::BusDirections::kInput as i32;
+ info.channel_count = 16;
+ u16strlcpy(&mut info.name, "MIDI");
+ info.bus_type = vst3_sys::vst::BusTypes::kMain as i32;
+ info.flags = vst3_sys::vst::BusFlags::kDefaultActive as u32;
+ kResultOk
+ }
+ _ => kInvalidArgument,
+ }
+ }
+
+ unsafe fn get_routing_info(
+ &self,
+ in_info: *mut vst3_sys::vst::RoutingInfo,
+ out_info: *mut vst3_sys::vst::RoutingInfo,
+ ) -> tresult {
+ check_null_ptr!(in_info, out_info);
+
+ *out_info = mem::zeroed();
+
+ let in_info = &*in_info;
+ let out_info = &mut *out_info;
+ match (in_info.media_type, in_info.bus_index) {
+ (t, 0) if t == vst3_sys::vst::MediaTypes::kAudio as i32 => {
+ out_info.media_type = vst3_sys::vst::MediaTypes::kAudio as i32;
+ out_info.bus_index = in_info.bus_index;
+ out_info.channel = in_info.channel;
+
+ kResultOk
+ }
+ _ => kInvalidArgument,
+ }
+ }
+
+ unsafe fn activate_bus(
+ &self,
+ type_: vst3_sys::vst::MediaType,
+ dir: vst3_sys::vst::BusDirection,
+ index: i32,
+ _state: vst3_sys::base::TBool,
+ ) -> tresult {
+ // We don't need any special handling here
+ match (type_, dir, index) {
+ (t, _, 0) if t == vst3_sys::vst::MediaTypes::kAudio as i32 => kResultOk,
+ (t, d, 0)
+ if t == vst3_sys::vst::MediaTypes::kEvent as i32
+ && d == vst3_sys::vst::BusDirections::kInput as i32
+ && P::ACCEPTS_MIDI =>
+ {
+ kResultOk
+ }
+ _ => kInvalidArgument,
+ }
+ }
+
+ unsafe fn set_active(&self, _state: TBool) -> tresult {
+ // We don't need any special handling here
+ kResultOk
+ }
+
+ unsafe fn set_state(&self, state: SharedVstPtr {
+ unsafe fn set_component_state(&self, _state: SharedVstPtr {
+ unsafe fn set_bus_arrangements(
+ &self,
+ inputs: *mut vst3_sys::vst::SpeakerArrangement,
+ num_ins: i32,
+ outputs: *mut vst3_sys::vst::SpeakerArrangement,
+ num_outs: i32,
+ ) -> tresult {
+ check_null_ptr!(inputs, outputs);
+
+ // We currently only do single audio bus IO configurations
+ if num_ins != 1 || num_outs != 1 {
+ return kInvalidArgument;
+ }
+
+ let input_channel_map = &*inputs;
+ let output_channel_map = &*outputs;
+ let proposed_config = BusConfig {
+ num_input_channels: input_channel_map.count_ones(),
+ num_output_channels: output_channel_map.count_ones(),
+ };
+ if self
+ .inner
+ .plugin
+ .read()
+ .accepts_bus_config(&proposed_config)
+ {
+ self.inner.current_bus_config.store(proposed_config);
+
+ kResultOk
+ } else {
+ kResultFalse
+ }
+ }
+
+ unsafe fn get_bus_arrangement(
+ &self,
+ dir: vst3_sys::vst::BusDirection,
+ index: i32,
+ arr: *mut vst3_sys::vst::SpeakerArrangement,
+ ) -> tresult {
+ check_null_ptr!(arr);
+
+ let channel_count_to_map = |count| match count {
+ 0 => vst3_sys::vst::kEmpty,
+ 1 => vst3_sys::vst::kMono,
+ 2 => vst3_sys::vst::kStereo,
+ 5 => vst3_sys::vst::k50,
+ 6 => vst3_sys::vst::k51,
+ 7 => vst3_sys::vst::k70Cine,
+ 8 => vst3_sys::vst::k71Cine,
+ n => {
+ nih_debug_assert_failure!(
+ "No defined layout for {} channels, making something up on the spot...",
+ n
+ );
+ (1 << n) - 1
+ }
+ };
+
+ let config = self.inner.current_bus_config.load();
+ let num_channels = match (dir, index) {
+ (d, 0) if d == vst3_sys::vst::BusDirections::kInput as i32 => config.num_input_channels,
+ (d, 0) if d == vst3_sys::vst::BusDirections::kOutput as i32 => {
+ config.num_output_channels
+ }
+ _ => return kInvalidArgument,
+ };
+ let channel_map = channel_count_to_map(num_channels);
+
+ nih_debug_assert_eq!(num_channels, channel_map.count_ones());
+ *arr = channel_map;
+
+ kResultOk
+ }
+
+ unsafe fn can_process_sample_size(&self, symbolic_sample_size: i32) -> tresult {
+ if symbolic_sample_size == vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32 {
+ kResultOk
+ } else {
+ kResultFalse
+ }
+ }
+
+ unsafe fn get_latency_samples(&self) -> u32 {
+ self.inner.current_latency.load(Ordering::SeqCst)
+ }
+
+ unsafe fn setup_processing(&self, setup: *const vst3_sys::vst::ProcessSetup) -> tresult {
+ check_null_ptr!(setup);
+
+ // There's no special handling for offline processing at the moment
+ let setup = &*setup;
+ nih_debug_assert_eq!(
+ setup.symbolic_sample_size,
+ vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32
+ );
+
+ let bus_config = self.inner.current_bus_config.load();
+ let buffer_config = BufferConfig {
+ sample_rate: setup.sample_rate as f32,
+ max_buffer_size: setup.max_samples_per_block as u32,
+ };
+
+ // Befure initializing the plugin, make sure all smoothers are set the the default values
+ for param in self.inner.param_by_hash.values() {
+ param.update_smoother(buffer_config.sample_rate, true);
+ }
+
+ if self.inner.plugin.write().initialize(
+ &bus_config,
+ &buffer_config,
+ &mut self.inner.make_process_context(),
+ ) {
+ // Preallocate enough room in the output slices vector so we can convert a `*mut *mut
+ // f32` to a `&mut [&mut f32]` in the process call
+ self.inner
+ .output_buffer
+ .write()
+ .as_raw_vec()
+ .resize_with(bus_config.num_output_channels as usize, || &mut []);
+
+ // Also store this for later, so we can reinitialize the plugin after restoring state
+ self.inner.current_buffer_config.store(Some(buffer_config));
+
+ kResultOk
+ } else {
+ kResultFalse
+ }
+ }
+
+ unsafe fn set_processing(&self, state: TBool) -> tresult {
+ // Always reset the processing status when the plugin gets activated or deactivated
+ self.inner.last_process_status.store(ProcessStatus::Normal);
+ self.inner.is_processing.store(state != 0, Ordering::SeqCst);
+
+ // We don't have any special handling for suspending and resuming plugins, yet
+ kResultOk
+ }
+
+ unsafe fn process(&self, data: *mut vst3_sys::vst::ProcessData) -> tresult {
+ check_null_ptr!(data);
+
+ // Panic on allocations if the `assert_process_allocs` feature has been enabled, and make
+ // sure that FTZ is set up correctly
+ process_wrapper(|| {
+ // We need to handle incoming automation first
+ let data = &*data;
+ let sample_rate = self
+ .inner
+ .current_buffer_config
+ .load()
+ .map(|c| c.sample_rate);
+ if let Some(param_changes) = data.input_param_changes.upgrade() {
+ let num_param_queues = param_changes.get_parameter_count();
+ for change_queue_idx in 0..num_param_queues {
+ if let Some(param_change_queue) =
+ param_changes.get_parameter_data(change_queue_idx).upgrade()
+ {
+ let param_hash = param_change_queue.get_parameter_id();
+ let num_changes = param_change_queue.get_point_count();
+
+ // TODO: Handle sample accurate parameter changes, possibly in a similar way
+ // to the smoothing
+ let mut sample_offset = 0i32;
+ let mut value = 0.0f64;
+ if num_changes > 0
+ && param_change_queue.get_point(
+ num_changes - 1,
+ &mut sample_offset,
+ &mut value,
+ ) == kResultOk
+ {
+ self.inner.set_normalized_value_by_hash(
+ param_hash,
+ value as f32,
+ sample_rate,
+ );
+ }
+ }
+ }
+ }
+
+ // And also incoming note events if the plugin accepts MDII
+ if P::ACCEPTS_MIDI {
+ let mut input_events = self.inner.input_events.write();
+ if let Some(events) = data.input_events.upgrade() {
+ let num_events = events.get_event_count();
+
+ input_events.clear();
+ let mut event: MaybeUninit<_> = MaybeUninit::uninit();
+ for i in 0..num_events {
+ nih_debug_assert_eq!(events.get_event(i, event.as_mut_ptr()), kResultOk);
+ let event = event.assume_init();
+ let timing = event.sample_offset as u32;
+ if event.type_ == vst3_sys::vst::EventTypes::kNoteOnEvent as u16 {
+ let event = event.event.note_on;
+ input_events.push_back(NoteEvent::NoteOn {
+ timing,
+ channel: event.channel as u8,
+ note: event.pitch as u8,
+ velocity: (event.velocity * 127.0).round() as u8,
+ });
+ } else if event.type_ == vst3_sys::vst::EventTypes::kNoteOffEvent as u16 {
+ let event = event.event.note_off;
+ input_events.push_back(NoteEvent::NoteOff {
+ timing,
+ channel: event.channel as u8,
+ note: event.pitch as u8,
+ velocity: (event.velocity * 127.0).round() as u8,
+ });
+ }
+ }
+ }
+ }
+
+ // It's possible the host only wanted to send new parameter values
+ if data.num_outputs == 0 {
+ nih_log!("VST3 parameter flush");
+ return kResultOk;
+ }
+
+ // The setups we suppport are:
+ // - 1 input bus
+ // - 1 output bus
+ // - 1 input bus and 1 output bus
+ nih_debug_assert!(
+ data.num_inputs >= 0
+ && data.num_inputs <= 1
+ && data.num_outputs >= 0
+ && data.num_outputs <= 1,
+ "The host provides more than one input or output bus"
+ );
+ nih_debug_assert_eq!(
+ data.symbolic_sample_size,
+ vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32
+ );
+ nih_debug_assert!(data.num_samples >= 0);
+
+ let num_output_channels = (*data.outputs).num_channels as usize;
+ check_null_ptr_msg!(
+ "Process output pointer is null",
+ data.outputs,
+ (*data.outputs).buffers,
+ );
+
+ // This vector has been reallocated to contain enough slices as there are output
+ // channels
+ let mut output_buffer = self.inner.output_buffer.write();
+ {
+ let output_slices = output_buffer.as_raw_vec();
+ nih_debug_assert_eq!(num_output_channels, output_slices.len());
+ for (output_channel_idx, output_channel_slice) in
+ output_slices.iter_mut().enumerate()
+ {
+ // SAFETY: These pointers may not be valid outside of this function even though
+ // their lifetime is equal to this structs. This is still safe because they are
+ // only dereferenced here later as part of this process function.
+ *output_channel_slice = std::slice::from_raw_parts_mut(
+ *((*data.outputs).buffers as *mut *mut f32).add(output_channel_idx),
+ data.num_samples as usize,
+ );
+ }
+ }
+
+ // Most hosts process data in place, in which case we don't need to do any copying
+ // ourselves. If the pointers do not alias, then we'll do the copy here and then the
+ // plugin can just do normal in place processing.
+ if !data.inputs.is_null() {
+ let num_input_channels = (*data.inputs).num_channels as usize;
+ nih_debug_assert!(
+ num_input_channels <= num_output_channels,
+ "Stereo to mono and similar configurations are not supported"
+ );
+ for input_channel_idx in 0..cmp::min(num_input_channels, num_output_channels) {
+ let output_channel_ptr =
+ *((*data.outputs).buffers as *mut *mut f32).add(input_channel_idx);
+ let input_channel_ptr =
+ *((*data.inputs).buffers as *const *const f32).add(input_channel_idx);
+ if input_channel_ptr != output_channel_ptr {
+ ptr::copy_nonoverlapping(
+ input_channel_ptr,
+ output_channel_ptr,
+ data.num_samples as usize,
+ );
+ }
+ }
+ }
+
+ let plugin = &mut *self.inner.plugin.data_ptr();
+ let mut context = self.inner.make_process_context();
+ match plugin.process(&mut output_buffer, &mut context) {
+ ProcessStatus::Error(err) => {
+ nih_debug_assert_failure!("Process error: {}", err);
+
+ kResultFalse
+ }
+ _ => kResultOk,
+ }
+ })
+ }
+
+ unsafe fn get_tail_samples(&self) -> u32 {
+ // https://github.com/steinbergmedia/vst3_pluginterfaces/blob/2ad397ade5b51007860bedb3b01b8afd2c5f6fba/vst/ivstaudioprocessor.h#L145-L159
+ match self.inner.last_process_status.load() {
+ ProcessStatus::Tail(samples) => samples,
+ ProcessStatus::KeepAlive => u32::MAX, // kInfiniteTail
+ _ => 0, // kNoTail
+ }
+ }
+}