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 BinaryVectorOp = unsafe extern "C" fn(*const f32, *const f32, *mut f32, usize) -> bool;
168type ReduceOp = unsafe extern "C" fn(*const f32, *mut f32, usize) -> bool;
169
170fn binary_vector_op(a: &[f32], b: &[f32], f: BinaryVectorOp) -> Result<Vec<f32>> {
171 if a.len() != b.len() {
172 return Err(Error::InvalidLength {
173 expected: a.len(),
174 actual: b.len(),
175 });
176 }
177
178 let mut out = vec![0.0_f32; a.len()];
179 let ok = unsafe { f(a.as_ptr(), b.as_ptr(), out.as_mut_ptr(), a.len()) };
181 if ok {
182 Ok(out)
183 } else {
184 Err(Error::OperationFailed("vDSP vector operation failed"))
185 }
186}
187
188fn reduce_f32(values: &[f32], f: ReduceOp) -> Result<f32> {
189 if values.is_empty() {
190 return Err(Error::InvalidLength {
191 expected: 1,
192 actual: 0,
193 });
194 }
195
196 let mut out = 0.0_f32;
197 let ok = unsafe { f(values.as_ptr(), &mut out, values.len()) };
199 if ok {
200 Ok(out)
201 } else {
202 Err(Error::OperationFailed("vDSP reduction failed"))
203 }
204}
205
206pub fn add_f32(a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
207 binary_vector_op(a, b, bridge::acc_vdsp_add_f32)
208}
209
210pub fn sub_f32(a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
211 binary_vector_op(a, b, bridge::acc_vdsp_sub_f32)
212}
213
214pub fn dot_f32(a: &[f32], b: &[f32]) -> Result<f32> {
215 if a.len() != b.len() {
216 return Err(Error::InvalidLength {
217 expected: a.len(),
218 actual: b.len(),
219 });
220 }
221
222 let mut out = 0.0_f32;
223 let ok = unsafe { bridge::acc_vdsp_dot_f32(a.as_ptr(), b.as_ptr(), &mut out, a.len()) };
225 if ok {
226 Ok(out)
227 } else {
228 Err(Error::OperationFailed("vDSP dot-product failed"))
229 }
230}
231
232pub fn max_f32(values: &[f32]) -> Result<f32> {
233 reduce_f32(values, bridge::acc_vdsp_max_f32)
234}
235
236pub fn min_f32(values: &[f32]) -> Result<f32> {
237 reduce_f32(values, bridge::acc_vdsp_min_f32)
238}
239
240pub fn mean_f32(values: &[f32]) -> Result<f32> {
241 reduce_f32(values, bridge::acc_vdsp_mean_f32)
242}
243
244pub fn sum_f32(values: &[f32]) -> Result<f32> {
245 reduce_f32(values, bridge::acc_vdsp_sum_f32)
246}
247
248#[must_use]
249pub fn hamming_window(length: usize, flags: i32) -> Vec<f32> {
250 let mut out = vec![0.0_f32; length];
251 if length == 0 {
252 return out;
253 }
254
255 let _ = unsafe { bridge::acc_vdsp_hamming_window(out.as_mut_ptr(), length, flags) };
257 out
258}
259
260#[must_use]
261pub fn blackman_window(length: usize, flags: i32) -> Vec<f32> {
262 let mut out = vec![0.0_f32; length];
263 if length == 0 {
264 return out;
265 }
266
267 let _ = unsafe { bridge::acc_vdsp_blackman_window(out.as_mut_ptr(), length, flags) };
269 out
270}