1use crate::error::{Error, Result};
2use crate::ffi;
3use core::ptr;
4
5pub mod fft_direction {
7 pub const FORWARD: i32 = 1;
8 pub const INVERSE: i32 = -1;
9}
10
11pub mod fft_radix {
13 pub const RADIX2: i32 = 0;
14 pub const RADIX3: i32 = 1;
15 pub const RADIX5: i32 = 2;
16}
17
18pub mod window_flags {
20 pub const HALF_WINDOW: i32 = 1;
21 pub const HANN_DENORM: i32 = 0;
22 pub const HANN_NORM: i32 = 2;
23}
24
25pub struct FftSetup {
27 ptr: ffi::FFTSetup,
28}
29
30unsafe impl Send for FftSetup {}
31unsafe impl Sync for FftSetup {}
32
33impl Drop for FftSetup {
34 fn drop(&mut self) {
35 if !self.ptr.is_null() {
36 unsafe { ffi::vDSP_destroy_fftsetup(self.ptr) };
38 self.ptr = ptr::null_mut();
39 }
40 }
41}
42
43impl FftSetup {
44 #[must_use]
45 pub fn new(log2n: usize, radix: i32) -> Option<Self> {
46 let ptr = unsafe { ffi::vDSP_create_fftsetup(log2n, radix) };
48 if ptr.is_null() {
49 None
50 } else {
51 Some(Self { ptr })
52 }
53 }
54
55 pub fn fft_zip(
56 &self,
57 real: &mut [f32],
58 imag: &mut [f32],
59 log2n: usize,
60 direction: i32,
61 ) -> Result<()> {
62 let shift = u32::try_from(log2n)
63 .map_err(|_| Error::OperationFailed("FFT log2 length exceeds u32"))?;
64 let expected = 1_usize
65 .checked_shl(shift)
66 .ok_or(Error::OperationFailed("FFT length overflowed"))?;
67 if real.len() != expected {
68 return Err(Error::InvalidLength {
69 expected,
70 actual: real.len(),
71 });
72 }
73 if imag.len() != expected {
74 return Err(Error::InvalidLength {
75 expected,
76 actual: imag.len(),
77 });
78 }
79
80 let split = ffi::DSPSplitComplex {
81 realp: real.as_mut_ptr(),
82 imagp: imag.as_mut_ptr(),
83 };
84 unsafe { ffi::vDSP_fft_zip(self.ptr, &split, 1, log2n, direction) };
86 Ok(())
87 }
88}
89
90pub struct BiquadSetup {
92 ptr: ffi::vDSP_biquad_Setup,
93}
94
95unsafe impl Send for BiquadSetup {}
96unsafe impl Sync for BiquadSetup {}
97
98impl Drop for BiquadSetup {
99 fn drop(&mut self) {
100 if !self.ptr.is_null() {
101 unsafe { ffi::vDSP_biquad_DestroySetup(self.ptr) };
103 self.ptr = ptr::null_mut();
104 }
105 }
106}
107
108impl BiquadSetup {
109 #[must_use]
110 pub fn new(coefficients: &[f64]) -> Option<Self> {
111 if coefficients.is_empty() || coefficients.len() % 5 != 0 {
112 return None;
113 }
114
115 let sections = coefficients.len() / 5;
116 let ptr = unsafe { ffi::vDSP_biquad_CreateSetup(coefficients.as_ptr(), sections) };
118 if ptr.is_null() {
119 None
120 } else {
121 Some(Self { ptr })
122 }
123 }
124
125 pub fn apply(&self, delay: &mut [f32], input: &[f32], output: &mut [f32]) -> Result<()> {
126 if input.len() != output.len() {
127 return Err(Error::InvalidLength {
128 expected: input.len(),
129 actual: output.len(),
130 });
131 }
132
133 unsafe {
135 ffi::vDSP_biquad(
136 self.ptr,
137 delay.as_mut_ptr(),
138 input.as_ptr(),
139 1,
140 output.as_mut_ptr(),
141 1,
142 input.len(),
143 );
144 }
145 Ok(())
146 }
147}
148
149fn binary_vector_op(
150 a: &[f32],
151 b: &[f32],
152 f: unsafe extern "C" fn(*const f32, isize, *const f32, isize, *mut f32, isize, usize),
153) -> Result<Vec<f32>> {
154 if a.len() != b.len() {
155 return Err(Error::InvalidLength {
156 expected: a.len(),
157 actual: b.len(),
158 });
159 }
160
161 let mut out = vec![0.0_f32; a.len()];
162 unsafe { f(a.as_ptr(), 1, b.as_ptr(), 1, out.as_mut_ptr(), 1, a.len()) };
164 Ok(out)
165}
166
167pub fn add_f32(a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
168 binary_vector_op(a, b, ffi::vDSP_vadd)
169}
170
171pub fn sub_f32(a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
172 if a.len() != b.len() {
173 return Err(Error::InvalidLength {
174 expected: a.len(),
175 actual: b.len(),
176 });
177 }
178
179 let mut out = vec![0.0_f32; a.len()];
180 unsafe { ffi::vDSP_vsub(b.as_ptr(), 1, a.as_ptr(), 1, out.as_mut_ptr(), 1, a.len()) };
182 Ok(out)
183}
184
185pub fn dot_f32(a: &[f32], b: &[f32]) -> Result<f32> {
186 if a.len() != b.len() {
187 return Err(Error::InvalidLength {
188 expected: a.len(),
189 actual: b.len(),
190 });
191 }
192
193 let mut out = 0.0_f32;
194 unsafe { ffi::vDSP_dotpr(a.as_ptr(), 1, b.as_ptr(), 1, &mut out, a.len()) };
196 Ok(out)
197}
198
199fn reduce_f32(
200 values: &[f32],
201 f: unsafe extern "C" fn(*const f32, isize, *mut f32, usize),
202) -> Result<f32> {
203 if values.is_empty() {
204 return Err(Error::InvalidLength {
205 expected: 1,
206 actual: 0,
207 });
208 }
209
210 let mut out = 0.0_f32;
211 unsafe { f(values.as_ptr(), 1, &mut out, values.len()) };
213 Ok(out)
214}
215
216pub fn max_f32(values: &[f32]) -> Result<f32> {
217 reduce_f32(values, ffi::vDSP_maxv)
218}
219
220pub fn min_f32(values: &[f32]) -> Result<f32> {
221 reduce_f32(values, ffi::vDSP_minv)
222}
223
224pub fn mean_f32(values: &[f32]) -> Result<f32> {
225 reduce_f32(values, ffi::vDSP_meanv)
226}
227
228pub fn sum_f32(values: &[f32]) -> Result<f32> {
229 reduce_f32(values, ffi::vDSP_sve)
230}
231
232#[must_use]
233pub fn hamming_window(length: usize, flags: i32) -> Vec<f32> {
234 let mut out = vec![0.0_f32; length];
235 if length > 0 {
236 unsafe { ffi::vDSP_hamm_window(out.as_mut_ptr(), length, flags) };
238 }
239 out
240}
241
242#[must_use]
243pub fn blackman_window(length: usize, flags: i32) -> Vec<f32> {
244 let mut out = vec![0.0_f32; length];
245 if length > 0 {
246 unsafe { ffi::vDSP_blkman_window(out.as_mut_ptr(), length, flags) };
248 }
249 out
250}