1use std::panic::{AssertUnwindSafe, catch_unwind};
7
8use bbx_dsp::PluginDsp;
9use bbx_midi::MidiEvent;
10
11use crate::handle::{BbxGraph, graph_from_handle};
12
13const MAX_SAMPLES: usize = 4096;
19
20const MAX_CHANNELS: usize = 2;
22
23#[allow(clippy::too_many_arguments)]
37pub unsafe fn process_audio<D: PluginDsp>(
38 handle: *mut BbxGraph,
39 inputs: *const *const f32,
40 outputs: *mut *mut f32,
41 num_channels: u32,
42 num_samples: u32,
43 params: *const f32,
44 num_params: u32,
45 midi_events: *const MidiEvent,
46 num_midi_events: u32,
47) {
48 if handle.is_null() || outputs.is_null() {
49 return;
50 }
51
52 let result = catch_unwind(AssertUnwindSafe(|| unsafe {
53 let inner = graph_from_handle::<D>(handle);
54
55 let num_channels = num_channels as usize;
56 let num_samples = num_samples as usize;
57
58 if !inner.prepared {
59 for i in 0..num_channels {
60 let output_ptr = *outputs.add(i);
61 if !output_ptr.is_null() {
62 std::ptr::write_bytes(output_ptr, 0, num_samples * size_of::<f32>());
63 }
64 }
65 return;
66 }
67
68 debug_assert!(
70 num_samples <= MAX_SAMPLES,
71 "Buffer size {num_samples} exceeds MAX_SAMPLES ({MAX_SAMPLES}). Only first {MAX_SAMPLES} samples processed."
72 );
73 debug_assert!(
74 num_channels <= MAX_CHANNELS,
75 "Channel count {num_channels} exceeds MAX_CHANNELS ({MAX_CHANNELS}). Only first {MAX_CHANNELS} channels processed."
76 );
77
78 let samples_to_process = num_samples.min(MAX_SAMPLES);
79 let channels_to_process = num_channels.min(MAX_CHANNELS);
80
81 if !params.is_null() && num_params > 0 {
83 let param_slice = std::slice::from_raw_parts(params, num_params as usize);
84 inner.dsp.apply_parameters(param_slice);
85 }
86
87 let midi_slice: &[MidiEvent] = if !midi_events.is_null() && num_midi_events > 0 {
89 std::slice::from_raw_parts(midi_events, num_midi_events as usize)
90 } else {
91 &[]
92 };
93
94 let silent_buffer: [f32; MAX_SAMPLES] = [0.0; MAX_SAMPLES];
97 let silent_slice: &[f32] = &silent_buffer[..samples_to_process];
98
99 let mut input_slices_storage: [&[f32]; MAX_CHANNELS] = [silent_slice; MAX_CHANNELS];
100
101 if !inputs.is_null() {
102 #[allow(clippy::needless_range_loop)] for ch in 0..channels_to_process {
104 let input_ptr = *inputs.add(ch);
105 if !input_ptr.is_null() {
106 input_slices_storage[ch] = std::slice::from_raw_parts(input_ptr, samples_to_process);
107 }
108 }
109 }
110
111 let output_ptr_0 = *outputs.add(0);
114 let output_ptr_1 = if channels_to_process > 1 {
115 *outputs.add(1)
116 } else {
117 output_ptr_0
118 };
119
120 if output_ptr_0.is_null() {
121 return;
122 }
123
124 let output_slice_0 = std::slice::from_raw_parts_mut(output_ptr_0, samples_to_process);
125
126 if channels_to_process == 1 {
127 let mut output_refs: [&mut [f32]; 1] = [output_slice_0];
128 inner
129 .dsp
130 .process(&input_slices_storage[..1], &mut output_refs, midi_slice, &inner.context);
131 } else {
132 if output_ptr_1.is_null() {
133 return;
134 }
135 let output_slice_1 = std::slice::from_raw_parts_mut(output_ptr_1, samples_to_process);
136 let mut output_refs: [&mut [f32]; 2] = [output_slice_0, output_slice_1];
137 inner.dsp.process(
138 &input_slices_storage[..channels_to_process],
139 &mut output_refs,
140 midi_slice,
141 &inner.context,
142 );
143 }
144 }));
145
146 if result.is_err() {
148 unsafe {
149 for i in 0..num_channels as usize {
150 let output_ptr = *outputs.add(i);
151 if !output_ptr.is_null() {
152 std::ptr::write_bytes(output_ptr, 0, num_samples as usize * size_of::<f32>());
153 }
154 }
155 }
156 }
157}