Skip to main content

embedded_dsp/
types.rs

1//! Data types, status codes, complex structures, and fixed-point helper types.
2
3pub use fixed::types::{I1F7 as q7, I1F15 as q15, I1F31 as q31};
4
5/// Q8.7 fixed-point type (8 integer bits, 7 fractional bits, `i16`-backed).
6///
7/// Used by results that don't fit `q15`'s `[-1.0, 1.0)` range, such as
8/// [`crate::audio::fast_log2_q15`]'s base-2 logarithm output.
9pub type Q8F7 = fixed::FixedI16<fixed::types::extra::U7>;
10
11/// Wide accumulator type used for dot products, sums-of-squares, and other
12/// reductions that need headroom beyond `i32`. This is a plain integer, not
13/// a Q1.63 fixed-point value: nothing here divides by a scale factor, and
14/// several call sites divide by an element count, which would be meaningless
15/// under a `[-1.0, 1.0)`-range fixed-point interpretation.
16#[allow(non_camel_case_types)]
17pub type q63 = i64;
18#[allow(non_camel_case_types)]
19pub type f32_t = f32;
20#[allow(non_camel_case_types)]
21pub type f64_t = f64;
22
23/// Error status returned by functions in `embedded-dsp`.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[repr(i8)]
26pub enum Status {
27    /// Operation succeeded without error.
28    Success = 0,
29    /// One or more arguments are invalid.
30    ArgumentError = -1,
31    /// Length of data buffer is invalid or mismatching.
32    LengthError = -2,
33    /// Matrix dimensions are incompatible.
34    SizeMismatch = -3,
35    /// NaN or Infinity was produced during computation.
36    NanInf = -4,
37    /// Matrix is singular and cannot be inverted.
38    Singular = -5,
39    /// Test or verification failed.
40    TestFailure = -6,
41    /// Matrix decomposition failed.
42    DecompositionFailure = -7,
43}
44
45/// Representation of a complex number with real and imaginary components.
46#[derive(Debug, Clone, Copy, PartialEq, Default)]
47#[repr(C)]
48pub struct Complex<T> {
49    pub real: T,
50    pub imag: T,
51}
52
53impl<T> Complex<T> {
54    #[inline(always)]
55    pub const fn new(real: T, imag: T) -> Self {
56        Self { real, imag }
57    }
58}
59
60/// Helper function for saturating multiplication in Q15 format.
61#[inline(always)]
62pub fn q15_mult(a: q15, b: q15) -> q15 {
63    a.saturating_mul(b)
64}
65
66/// Helper function for saturating multiplication in Q31 format.
67#[inline(always)]
68pub fn q31_mult(a: q31, b: q31) -> q31 {
69    a.saturating_mul(b)
70}
71
72/// Helper function for saturating multiplication in Q7 format.
73#[inline(always)]
74pub fn q7_mult(a: q7, b: q7) -> q7 {
75    a.saturating_mul(b)
76}
77
78/// Saturating division: returns `MAX`/`MIN` (matching the sign of `a`) on
79/// division by zero, rather than panicking.
80#[inline(always)]
81fn saturating_div_q<F>(a: F, b: F) -> F
82where
83    F: fixed::traits::Fixed + PartialOrd,
84{
85    match a.checked_div(b) {
86        Some(v) => v,
87        None if b == F::ZERO => {
88            if a >= F::ZERO {
89                F::MAX
90            } else {
91                F::MIN
92            }
93        }
94        None => {
95            if (a >= F::ZERO) == (b >= F::ZERO) {
96                F::MAX
97            } else {
98                F::MIN
99            }
100        }
101    }
102}
103
104// ─────────────────────────────────────────────────────────────────────────────
105// Unified DSP Sample Trait
106// ─────────────────────────────────────────────────────────────────────────────
107
108/// Unified numerical sample trait implemented for floating-point sample types.
109///
110/// Enables writing generic filters, delay lines, oscillators, and processing blocks that operate
111/// seamlessly with `f32` and `f64`.
112pub trait DspSample:
113    Copy
114    + Default
115    + PartialEq
116    + PartialOrd
117    + core::ops::Add<Output = Self>
118    + core::ops::Sub<Output = Self>
119    + core::ops::Mul<Output = Self>
120    + core::ops::Neg<Output = Self>
121{
122    /// Additive identity (`0.0`).
123    const ZERO: Self;
124    /// Multiplicative identity or normalized unity (`1.0`).
125    const ONE: Self;
126
127    /// Saturating addition.
128    fn sat_add(self, rhs: Self) -> Self;
129    /// Saturating subtraction.
130    fn sat_sub(self, rhs: Self) -> Self;
131    /// Saturating multiplication.
132    fn sat_mul(self, rhs: Self) -> Self;
133    /// Saturating division.
134    fn sat_div(self, rhs: Self) -> Self;
135    /// Absolute value.
136    fn abs_val(self) -> Self;
137    /// Convert to floating-point `f32`.
138    fn to_f32(self) -> f32;
139    /// Convert from floating-point `f32`.
140    fn from_f32(val: f32) -> Self;
141}
142
143impl DspSample for f32 {
144    const ZERO: Self = 0.0;
145    const ONE: Self = 1.0;
146
147    #[inline(always)]
148    fn sat_add(self, rhs: Self) -> Self {
149        self + rhs
150    }
151
152    #[inline(always)]
153    fn sat_sub(self, rhs: Self) -> Self {
154        self - rhs
155    }
156
157    #[inline(always)]
158    fn sat_mul(self, rhs: Self) -> Self {
159        self * rhs
160    }
161
162    #[inline(always)]
163    fn sat_div(self, rhs: Self) -> Self {
164        self / rhs
165    }
166
167    #[inline(always)]
168    fn abs_val(self) -> Self {
169        if self < 0.0 { -self } else { self }
170    }
171
172    #[inline(always)]
173    fn to_f32(self) -> f32 {
174        self
175    }
176
177    #[inline(always)]
178    fn from_f32(val: f32) -> Self {
179        val
180    }
181}
182
183impl DspSample for f64 {
184    const ZERO: Self = 0.0;
185    const ONE: Self = 1.0;
186
187    #[inline(always)]
188    fn sat_add(self, rhs: Self) -> Self {
189        self + rhs
190    }
191
192    #[inline(always)]
193    fn sat_sub(self, rhs: Self) -> Self {
194        self - rhs
195    }
196
197    #[inline(always)]
198    fn sat_mul(self, rhs: Self) -> Self {
199        self * rhs
200    }
201
202    #[inline(always)]
203    fn sat_div(self, rhs: Self) -> Self {
204        self / rhs
205    }
206
207    #[inline(always)]
208    fn abs_val(self) -> Self {
209        if self < 0.0 { -self } else { self }
210    }
211
212    #[inline(always)]
213    fn to_f32(self) -> f32 {
214        self as f32
215    }
216
217    #[inline(always)]
218    fn from_f32(val: f32) -> Self {
219        val as f64
220    }
221}
222
223impl DspSample for q15 {
224    const ZERO: Self = Self::ZERO;
225    const ONE: Self = Self::MAX;
226
227    #[inline(always)]
228    fn sat_add(self, rhs: Self) -> Self {
229        self.saturating_add(rhs)
230    }
231
232    #[inline(always)]
233    fn sat_sub(self, rhs: Self) -> Self {
234        self.saturating_sub(rhs)
235    }
236
237    #[inline(always)]
238    fn sat_mul(self, rhs: Self) -> Self {
239        self.saturating_mul(rhs)
240    }
241
242    #[inline(always)]
243    fn sat_div(self, rhs: Self) -> Self {
244        saturating_div_q(self, rhs)
245    }
246
247    #[inline(always)]
248    fn abs_val(self) -> Self {
249        self.saturating_abs()
250    }
251
252    #[inline(always)]
253    fn to_f32(self) -> f32 {
254        self.to_num()
255    }
256
257    #[inline(always)]
258    fn from_f32(val: f32) -> Self {
259        Self::saturating_from_num(val)
260    }
261}
262
263impl DspSample for q31 {
264    const ZERO: Self = Self::ZERO;
265    const ONE: Self = Self::MAX;
266
267    #[inline(always)]
268    fn sat_add(self, rhs: Self) -> Self {
269        self.saturating_add(rhs)
270    }
271
272    #[inline(always)]
273    fn sat_sub(self, rhs: Self) -> Self {
274        self.saturating_sub(rhs)
275    }
276
277    #[inline(always)]
278    fn sat_mul(self, rhs: Self) -> Self {
279        self.saturating_mul(rhs)
280    }
281
282    #[inline(always)]
283    fn sat_div(self, rhs: Self) -> Self {
284        saturating_div_q(self, rhs)
285    }
286
287    #[inline(always)]
288    fn abs_val(self) -> Self {
289        self.saturating_abs()
290    }
291
292    #[inline(always)]
293    fn to_f32(self) -> f32 {
294        self.to_num()
295    }
296
297    #[inline(always)]
298    fn from_f32(val: f32) -> Self {
299        Self::saturating_from_num(val)
300    }
301}
302
303// ─────────────────────────────────────────────────────────────────────────────
304// Complex Number Operations
305// ─────────────────────────────────────────────────────────────────────────────
306
307impl<T: core::ops::Add<Output = T>> core::ops::Add for Complex<T> {
308    type Output = Self;
309    #[inline(always)]
310    fn add(self, rhs: Self) -> Self {
311        Self {
312            real: self.real + rhs.real,
313            imag: self.imag + rhs.imag,
314        }
315    }
316}
317
318impl<T: core::ops::Sub<Output = T>> core::ops::Sub for Complex<T> {
319    type Output = Self;
320    #[inline(always)]
321    fn sub(self, rhs: Self) -> Self {
322        Self {
323            real: self.real - rhs.real,
324            imag: self.imag - rhs.imag,
325        }
326    }
327}
328
329impl<T: core::ops::Neg<Output = T>> core::ops::Neg for Complex<T> {
330    type Output = Self;
331    #[inline(always)]
332    fn neg(self) -> Self {
333        Self {
334            real: -self.real,
335            imag: -self.imag,
336        }
337    }
338}
339
340impl<T: Copy + core::ops::Add<Output = T> + core::ops::Sub<Output = T> + core::ops::Mul<Output = T>>
341    core::ops::Mul for Complex<T>
342{
343    type Output = Self;
344    #[inline(always)]
345    fn mul(self, rhs: Self) -> Self {
346        Self {
347            real: self.real * rhs.real - self.imag * rhs.imag,
348            imag: self.real * rhs.imag + self.imag * rhs.real,
349        }
350    }
351}
352
353impl<T: Copy + core::ops::Mul<Output = T>> core::ops::Mul<T> for Complex<T> {
354    type Output = Self;
355    #[inline(always)]
356    fn mul(self, scalar: T) -> Self {
357        Self {
358            real: self.real * scalar,
359            imag: self.imag * scalar,
360        }
361    }
362}