use crate::error::{Error, Result};
use crate::ffi;
use core::ptr;
pub mod fft_direction {
pub const FORWARD: i32 = 1;
pub const INVERSE: i32 = -1;
}
pub mod fft_radix {
pub const RADIX2: i32 = 0;
pub const RADIX3: i32 = 1;
pub const RADIX5: i32 = 2;
}
pub mod window_flags {
pub const HALF_WINDOW: i32 = 1;
pub const HANN_DENORM: i32 = 0;
pub const HANN_NORM: i32 = 2;
}
pub struct FftSetup {
ptr: ffi::FFTSetup,
}
unsafe impl Send for FftSetup {}
unsafe impl Sync for FftSetup {}
impl Drop for FftSetup {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { ffi::vDSP_destroy_fftsetup(self.ptr) };
self.ptr = ptr::null_mut();
}
}
}
impl FftSetup {
#[must_use]
pub fn new(log2n: usize, radix: i32) -> Option<Self> {
let ptr = unsafe { ffi::vDSP_create_fftsetup(log2n, radix) };
if ptr.is_null() {
None
} else {
Some(Self { ptr })
}
}
pub fn fft_zip(
&self,
real: &mut [f32],
imag: &mut [f32],
log2n: usize,
direction: i32,
) -> Result<()> {
let shift = u32::try_from(log2n)
.map_err(|_| Error::OperationFailed("FFT log2 length exceeds u32"))?;
let expected = 1_usize
.checked_shl(shift)
.ok_or(Error::OperationFailed("FFT length overflowed"))?;
if real.len() != expected {
return Err(Error::InvalidLength {
expected,
actual: real.len(),
});
}
if imag.len() != expected {
return Err(Error::InvalidLength {
expected,
actual: imag.len(),
});
}
let split = ffi::DSPSplitComplex {
realp: real.as_mut_ptr(),
imagp: imag.as_mut_ptr(),
};
unsafe { ffi::vDSP_fft_zip(self.ptr, &split, 1, log2n, direction) };
Ok(())
}
}
pub struct BiquadSetup {
ptr: ffi::vDSP_biquad_Setup,
}
unsafe impl Send for BiquadSetup {}
unsafe impl Sync for BiquadSetup {}
impl Drop for BiquadSetup {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { ffi::vDSP_biquad_DestroySetup(self.ptr) };
self.ptr = ptr::null_mut();
}
}
}
impl BiquadSetup {
#[must_use]
pub fn new(coefficients: &[f64]) -> Option<Self> {
if coefficients.is_empty() || coefficients.len() % 5 != 0 {
return None;
}
let sections = coefficients.len() / 5;
let ptr = unsafe { ffi::vDSP_biquad_CreateSetup(coefficients.as_ptr(), sections) };
if ptr.is_null() {
None
} else {
Some(Self { ptr })
}
}
pub fn apply(&self, delay: &mut [f32], input: &[f32], output: &mut [f32]) -> Result<()> {
if input.len() != output.len() {
return Err(Error::InvalidLength {
expected: input.len(),
actual: output.len(),
});
}
unsafe {
ffi::vDSP_biquad(
self.ptr,
delay.as_mut_ptr(),
input.as_ptr(),
1,
output.as_mut_ptr(),
1,
input.len(),
);
}
Ok(())
}
}
fn binary_vector_op(
a: &[f32],
b: &[f32],
f: unsafe extern "C" fn(*const f32, isize, *const f32, isize, *mut f32, isize, usize),
) -> Result<Vec<f32>> {
if a.len() != b.len() {
return Err(Error::InvalidLength {
expected: a.len(),
actual: b.len(),
});
}
let mut out = vec![0.0_f32; a.len()];
unsafe { f(a.as_ptr(), 1, b.as_ptr(), 1, out.as_mut_ptr(), 1, a.len()) };
Ok(out)
}
pub fn add_f32(a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
binary_vector_op(a, b, ffi::vDSP_vadd)
}
pub fn sub_f32(a: &[f32], b: &[f32]) -> Result<Vec<f32>> {
if a.len() != b.len() {
return Err(Error::InvalidLength {
expected: a.len(),
actual: b.len(),
});
}
let mut out = vec![0.0_f32; a.len()];
unsafe { ffi::vDSP_vsub(b.as_ptr(), 1, a.as_ptr(), 1, out.as_mut_ptr(), 1, a.len()) };
Ok(out)
}
pub fn dot_f32(a: &[f32], b: &[f32]) -> Result<f32> {
if a.len() != b.len() {
return Err(Error::InvalidLength {
expected: a.len(),
actual: b.len(),
});
}
let mut out = 0.0_f32;
unsafe { ffi::vDSP_dotpr(a.as_ptr(), 1, b.as_ptr(), 1, &mut out, a.len()) };
Ok(out)
}
fn reduce_f32(
values: &[f32],
f: unsafe extern "C" fn(*const f32, isize, *mut f32, usize),
) -> Result<f32> {
if values.is_empty() {
return Err(Error::InvalidLength {
expected: 1,
actual: 0,
});
}
let mut out = 0.0_f32;
unsafe { f(values.as_ptr(), 1, &mut out, values.len()) };
Ok(out)
}
pub fn max_f32(values: &[f32]) -> Result<f32> {
reduce_f32(values, ffi::vDSP_maxv)
}
pub fn min_f32(values: &[f32]) -> Result<f32> {
reduce_f32(values, ffi::vDSP_minv)
}
pub fn mean_f32(values: &[f32]) -> Result<f32> {
reduce_f32(values, ffi::vDSP_meanv)
}
pub fn sum_f32(values: &[f32]) -> Result<f32> {
reduce_f32(values, ffi::vDSP_sve)
}
#[must_use]
pub fn hamming_window(length: usize, flags: i32) -> Vec<f32> {
let mut out = vec![0.0_f32; length];
if length > 0 {
unsafe { ffi::vDSP_hamm_window(out.as_mut_ptr(), length, flags) };
}
out
}
#[must_use]
pub fn blackman_window(length: usize, flags: i32) -> Vec<f32> {
let mut out = vec![0.0_f32; length];
if length > 0 {
unsafe { ffi::vDSP_blkman_window(out.as_mut_ptr(), length, flags) };
}
out
}