1use crate::bridge;
2use crate::error::{Error, Result};
3use core::ffi::c_void;
4use core::ptr;
5
6pub mod fft_direction {
8 pub const FORWARD: i32 = 1;
9 pub const INVERSE: i32 = -1;
10}
11
12pub mod fft_radix {
14 pub const RADIX2: i32 = 0;
15 pub const RADIX3: i32 = 1;
16 pub const RADIX5: i32 = 2;
17}
18
19pub mod window_flags {
21 pub const HALF_WINDOW: i32 = 1;
22 pub const HANN_DENORM: i32 = 0;
23 pub const HANN_NORM: i32 = 2;
24}
25
26pub struct FftSetup {
28 ptr: *mut c_void,
29}
30
31unsafe impl Send for FftSetup {}
32unsafe impl Sync for FftSetup {}
33
34impl Drop for FftSetup {
35 fn drop(&mut self) {
36 if !self.ptr.is_null() {
37 unsafe { bridge::acc_release_handle(self.ptr) };
39 self.ptr = ptr::null_mut();
40 }
41 }
42}
43
44impl FftSetup {
45 #[must_use]
46 pub fn new(log2n: usize, radix: i32) -> Option<Self> {
47 let ptr = unsafe { bridge::acc_vdsp_fft_setup_create(log2n, radix) };
49 if ptr.is_null() {
50 None
51 } else {
52 Some(Self { ptr })
53 }
54 }
55
56 pub fn fft_zip(
57 &self,
58 real: &mut [f32],
59 imag: &mut [f32],
60 log2n: usize,
61 direction: i32,
62 ) -> Result<()> {
63 let shift = u32::try_from(log2n)
64 .map_err(|_| Error::OperationFailed("FFT log2 length exceeds u32"))?;
65 let expected = 1_usize
66 .checked_shl(shift)
67 .ok_or(Error::OperationFailed("FFT length overflowed"))?;
68 if real.len() != expected {
69 return Err(Error::InvalidLength {
70 expected,
71 actual: real.len(),
72 });
73 }
74 if imag.len() != expected {
75 return Err(Error::InvalidLength {
76 expected,
77 actual: imag.len(),
78 });
79 }
80
81 let ok = unsafe {
83 bridge::acc_vdsp_fft_setup_apply(
84 self.ptr,
85 real.as_mut_ptr(),
86 imag.as_mut_ptr(),
87 log2n,
88 direction,
89 )
90 };
91 if ok {
92 Ok(())
93 } else {
94 Err(Error::OperationFailed("vDSP FFT operation failed"))
95 }
96 }
97}
98
99pub struct BiquadSetup {
101 ptr: *mut c_void,
102}
103
104unsafe impl Send for BiquadSetup {}
105unsafe impl Sync for BiquadSetup {}
106
107impl Drop for BiquadSetup {
108 fn drop(&mut self) {
109 if !self.ptr.is_null() {
110 unsafe { bridge::acc_release_handle(self.ptr) };
112 self.ptr = ptr::null_mut();
113 }
114 }
115}
116
117impl BiquadSetup {
118 #[must_use]
119 pub fn new(coefficients: &[f64]) -> Option<Self> {
120 if coefficients.is_empty() || coefficients.len() % 5 != 0 {
121 return None;
122 }
123
124 let ptr = unsafe {
126 bridge::acc_vdsp_biquad_setup_create(coefficients.as_ptr(), coefficients.len())
127 };
128 if ptr.is_null() {
129 None
130 } else {
131 Some(Self { ptr })
132 }
133 }
134
135 pub fn apply(&self, delay: &mut [f32], input: &[f32], output: &mut [f32]) -> Result<()> {
136 if delay.is_empty() {
137 return Err(Error::InvalidLength {
138 expected: 1,
139 actual: 0,
140 });
141 }
142 if input.len() != output.len() {
143 return Err(Error::InvalidLength {
144 expected: input.len(),
145 actual: output.len(),
146 });
147 }
148
149 let ok = unsafe {
151 bridge::acc_vdsp_biquad_setup_apply(
152 self.ptr,
153 delay.as_mut_ptr(),
154 input.as_ptr(),
155 output.as_mut_ptr(),
156 input.len(),
157 )
158 };
159 if ok {
160 Ok(())
161 } else {
162 Err(Error::OperationFailed("vDSP biquad operation failed"))
163 }
164 }
165}
166
167type BinaryVectorOpF32 = unsafe extern "C" fn(*const f32, *const f32, *mut f32, usize) -> bool;
168type BinaryVectorOpF64 = unsafe extern "C" fn(*const f64, *const f64, *mut f64, usize) -> bool;
169type ReduceOpF32 = unsafe extern "C" fn(*const f32, *mut f32, usize) -> bool;
170type ReduceOpF64 = unsafe extern "C" fn(*const f64, *mut f64, usize) -> bool;
171type WindowOpF32 = unsafe extern "C" fn(*mut f32, usize, i32) -> bool;
172type WindowOpF64 = unsafe extern "C" fn(*mut f64, usize, i32) -> bool;
173
174fn binary_vector_op_f32(a: &[f32], b: &[f32], f: BinaryVectorOpF32) -> Result<Vec<f32>> {
175 if a.len() != b.len() {
176 return Err(Error::InvalidLength {
177 expected: a.len(),
178 actual: b.len(),
179 });
180 }
181
182 let mut out = vec![0.0_f32; a.len()];
183 let ok = unsafe { f(a.as_ptr(), b.as_ptr(), out.as_mut_ptr(), a.len()) };
185 if ok {
186 Ok(out)
187 } else {
188 Err(Error::OperationFailed("vDSP vector operation failed"))
189 }
190}
191
192fn binary_vector_op_f64(a: &[f64], b: &[f64], f: BinaryVectorOpF64) -> Result<Vec<f64>> {
193 if a.len() != b.len() {
194 return Err(Error::InvalidLength {
195 expected: a.len(),
196 actual: b.len(),
197 });
198 }
199
200 let mut out = vec![0.0_f64; a.len()];
201 let ok = unsafe { f(a.as_ptr(), b.as_ptr(), out.as_mut_ptr(), a.len()) };
203 if ok {
204 Ok(out)
205 } else {
206 Err(Error::OperationFailed("vDSP vector operation failed"))
207 }
208}
209
210fn reduce_f32(values: &[f32], f: ReduceOpF32) -> Result<f32> {
211 if values.is_empty() {
212 return Err(Error::InvalidLength {
213 expected: 1,
214 actual: 0,
215 });
216 }
217
218 let mut out = 0.0_f32;
219 let ok = unsafe { f(values.as_ptr(), &mut out, values.len()) };
221 if ok {
222 Ok(out)
223 } else {
224 Err(Error::OperationFailed("vDSP reduction failed"))
225 }
226}
227
228fn reduce_f64(values: &[f64], f: ReduceOpF64) -> Result<f64> {
229 if values.is_empty() {
230 return Err(Error::InvalidLength {
231 expected: 1,
232 actual: 0,
233 });
234 }
235
236 let mut out = 0.0_f64;
237 let ok = unsafe { f(values.as_ptr(), &mut out, values.len()) };
239 if ok {
240 Ok(out)
241 } else {
242 Err(Error::OperationFailed("vDSP reduction failed"))
243 }
244}
245
246#[must_use]
247fn window_f32(length: usize, flags: i32, f: WindowOpF32) -> Vec<f32> {
248 let mut out = vec![0.0_f32; length];
249 if length == 0 {
250 return out;
251 }
252
253 let _ = unsafe { f(out.as_mut_ptr(), length, flags) };
255 out
256}
257
258#[must_use]
259fn window_f64(length: usize, flags: i32, f: WindowOpF64) -> Vec<f64> {
260 let mut out = vec![0.0_f64; length];
261 if length == 0 {
262 return out;
263 }
264
265 let _ = unsafe { f(out.as_mut_ptr(), length, flags) };
267 out
268}
269
270pub fn add_f32(a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
271 binary_vector_op_f32(a, b, bridge::acc_vdsp_add_f32)
272}
273
274pub fn add_f64(a: &[f64], b: &[f64]) -> Result<Vec<f64>> {
275 binary_vector_op_f64(a, b, bridge::acc_vdsp_add_f64)
276}
277
278pub fn sub_f32(a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
279 binary_vector_op_f32(a, b, bridge::acc_vdsp_sub_f32)
280}
281
282pub fn sub_f64(a: &[f64], b: &[f64]) -> Result<Vec<f64>> {
283 binary_vector_op_f64(a, b, bridge::acc_vdsp_sub_f64)
284}
285
286pub fn dot_f32(a: &[f32], b: &[f32]) -> Result<f32> {
287 if a.len() != b.len() {
288 return Err(Error::InvalidLength {
289 expected: a.len(),
290 actual: b.len(),
291 });
292 }
293
294 let mut out = 0.0_f32;
295 let ok = unsafe { bridge::acc_vdsp_dot_f32(a.as_ptr(), b.as_ptr(), &mut out, a.len()) };
297 if ok {
298 Ok(out)
299 } else {
300 Err(Error::OperationFailed("vDSP dot-product failed"))
301 }
302}
303
304pub fn dot_f64(a: &[f64], b: &[f64]) -> Result<f64> {
305 if a.len() != b.len() {
306 return Err(Error::InvalidLength {
307 expected: a.len(),
308 actual: b.len(),
309 });
310 }
311
312 let mut out = 0.0_f64;
313 let ok = unsafe { bridge::acc_vdsp_dot_f64(a.as_ptr(), b.as_ptr(), &mut out, a.len()) };
315 if ok {
316 Ok(out)
317 } else {
318 Err(Error::OperationFailed("vDSP dot-product failed"))
319 }
320}
321
322pub fn max_f32(values: &[f32]) -> Result<f32> {
323 reduce_f32(values, bridge::acc_vdsp_max_f32)
324}
325
326pub fn max_f64(values: &[f64]) -> Result<f64> {
327 reduce_f64(values, bridge::acc_vdsp_max_f64)
328}
329
330pub fn min_f32(values: &[f32]) -> Result<f32> {
331 reduce_f32(values, bridge::acc_vdsp_min_f32)
332}
333
334pub fn min_f64(values: &[f64]) -> Result<f64> {
335 reduce_f64(values, bridge::acc_vdsp_min_f64)
336}
337
338pub fn mean_f32(values: &[f32]) -> Result<f32> {
339 reduce_f32(values, bridge::acc_vdsp_mean_f32)
340}
341
342pub fn mean_f64(values: &[f64]) -> Result<f64> {
343 reduce_f64(values, bridge::acc_vdsp_mean_f64)
344}
345
346pub fn sum_f32(values: &[f32]) -> Result<f32> {
347 reduce_f32(values, bridge::acc_vdsp_sum_f32)
348}
349
350pub fn sum_f64(values: &[f64]) -> Result<f64> {
351 reduce_f64(values, bridge::acc_vdsp_sum_f64)
352}
353
354#[must_use]
355pub fn hamming_window(length: usize, flags: i32) -> Vec<f32> {
356 window_f32(length, flags, bridge::acc_vdsp_hamming_window)
357}
358
359#[must_use]
360pub fn hamming_window_f64(length: usize, flags: i32) -> Vec<f64> {
361 window_f64(length, flags, bridge::acc_vdsp_hamming_window_f64)
362}
363
364#[must_use]
365pub fn blackman_window(length: usize, flags: i32) -> Vec<f32> {
366 window_f32(length, flags, bridge::acc_vdsp_blackman_window)
367}
368
369#[must_use]
370pub fn blackman_window_f64(length: usize, flags: i32) -> Vec<f64> {
371 window_f64(length, flags, bridge::acc_vdsp_blackman_window_f64)
372}