1use crate::basic::{
2 div_i16, div_i32, dot_i16, dot_i32, offset_i32, scale_i16, scale_i32, shift_i32,
3 vlog_i32_in_place,
4};
5use crate::complex::{cmplx_mag_i16, cmplx_mag_i32};
6use crate::matrix::{Matrix, mat_vec_mul_i16, mat_vec_mul_i32};
7use crate::statistics::{absmax_i16, absmax_i32};
8use crate::transform::{RfftI16, RfftI32};
9use crate::{sat_i16, sat_i32};
10
11const LOG2TOLOG_Q15: i32 = 0x02C5_C860;
12const MICRO_Q15: i64 = 0x0000_0219;
13const SHIFT_MELFILTER_SATURATION_Q15: i32 = 10;
14
15const LOG2TOLOG_Q31: i32 = 0x02C5_C860;
16const MICRO_Q31: i64 = 0x0863_7BD0;
17const SHIFT_MELFILTER_SATURATION_Q31: i32 = 10;
18
19#[inline]
20fn view_u16_as_i16(data: &[u16]) -> &[i16] {
21 unsafe { core::slice::from_raw_parts(data.as_ptr() as *const i16, data.len()) }
22}
23
24#[inline]
25fn view_u32_as_i32(data: &[u32]) -> &[i32] {
26 unsafe { core::slice::from_raw_parts(data.as_ptr() as *const i32, data.len()) }
27}
28
29#[inline]
30fn view_i16_as_i32_mut(data: &mut [i16], min_i32_len: usize) -> &mut [i32] {
31 let (_, body, _) = unsafe { data.align_to_mut::<i32>() };
32 assert!(
33 body.len() >= min_i32_len,
34 "tmp alignment/length must allow at least min_i32_len i32 values"
35 );
36 body
37}
38
39pub struct MfccI16 {
40 pub n_fft: usize,
41 pub n_mels: usize,
42 pub n_mfcc: usize,
43 pub dct: &'static [u16],
44 pub filter: &'static [u16],
45 pub filter_pos: &'static [u16],
46 pub filter_len: &'static [u16],
47 pub window: &'static [u16],
48 pub rfft: RfftI16,
49}
50
51impl MfccI16 {
52 pub const fn new(
53 n_fft: usize, n_mels: usize, n_mfcc: usize, dct: &'static [u16], filter: &'static [u16], filter_pos: &'static [u16], filter_len: &'static [u16], window: &'static [u16], ) -> Self {
62 assert!(
63 dct.len() == n_mfcc * n_mels,
64 "DCT matrix size must be n_mfcc * n_mels"
65 );
66 assert!(
67 filter_pos.len() == n_mels,
68 "Filter position array size must be n_mels"
69 );
70 assert!(
71 filter_len.len() == n_mels,
72 "Filter length array size must be n_mels"
73 );
74 assert!(window.len() == n_fft, "Window size must be equal to n_fft");
75
76 Self {
77 n_fft,
78 n_mels,
79 n_mfcc,
80 dct,
81 filter,
82 filter_pos,
83 filter_len,
84 window,
85 rfft: RfftI16::new(n_fft, false, true),
86 }
87 }
88
89 pub fn run(&self, input: &mut [i16], output: &mut [i16], tmp: &mut [i16]) {
90 assert!(
91 tmp.len() >= self.n_fft * 2,
92 "tmp length must be at least 2 * n_fft for q15 MFCC"
93 );
94
95 let dct = view_u16_as_i16(self.dct);
96 let filter = view_u16_as_i16(self.filter);
97
98 let (max_abs, _) = absmax_i16(input, self.n_fft);
100
101 if max_abs != 0 && max_abs != i16::MAX {
102 let (quotient, shift) = div_i16(i16::MAX, max_abs).expect("division must succeed");
103 scale_i16(input, quotient, shift as i8);
104 }
105
106 for (sample, &window) in input.iter_mut().zip(self.window) {
107 *sample = sat_i16(((*sample as i32) * (window as i32)) >> 15);
108 }
109
110 {
112 let tmp_fft = &mut tmp[..self.n_fft * 2];
113 self.rfft.run(input, tmp_fft);
114
115 let filter_limit = 1 + (self.n_fft >> 1);
116 cmplx_mag_i16(&tmp_fft[..filter_limit * 2], &mut input[..filter_limit]);
117 }
118
119 let tmp_q31 = view_i16_as_i32_mut(tmp, self.n_mels);
121
122 let mut packed_pos = 0usize;
123 for ((dst, &filter_pos), &filter_len) in tmp_q31
124 .iter_mut()
125 .zip(self.filter_pos)
126 .zip(self.filter_len)
127 .take(self.n_mels)
128 {
129 let filter_pos = usize::from(filter_pos);
130 let filter_len = usize::from(filter_len);
131 let filter_end = packed_pos + filter_len;
132
133 let acc = dot_i16(
134 &input[filter_pos..filter_pos + filter_len],
135 &filter[packed_pos..filter_end],
136 );
137 packed_pos = filter_end;
138
139 let acc = (acc + MICRO_Q15) >> SHIFT_MELFILTER_SATURATION_Q15;
140 *dst = sat_i32(acc);
141 }
142
143 if max_abs != 0 && max_abs != i16::MAX {
145 scale_i32(&mut tmp_q31[..self.n_mels], (max_abs as i32) << 16, 0);
146 }
147
148 vlog_i32_in_place(&mut tmp_q31[..self.n_mels]);
149
150 let fft_shift = self.n_fft.trailing_zeros() as i32;
151 let log_exponent =
152 (fft_shift + 2 + SHIFT_MELFILTER_SATURATION_Q15).wrapping_mul(LOG2TOLOG_Q15);
153 offset_i32(&mut tmp_q31[..self.n_mels], log_exponent);
154 shift_i32(&mut tmp_q31[..self.n_mels], -19);
155
156 for (dst, &src) in input.iter_mut().zip(tmp_q31.iter()).take(self.n_mels) {
157 *dst = sat_i16(src);
158 }
159
160 let dct = Matrix {
162 rows: self.n_mfcc,
163 cols: self.n_mels,
164 data: dct.as_ptr() as *mut i16,
165 };
166 mat_vec_mul_i16(dct, &input[..self.n_mels], output);
167 }
168}
169
170pub struct MfccI32 {
171 pub n_fft: usize,
172 pub n_mels: usize,
173 pub n_mfcc: usize,
174 pub dct: &'static [u32],
175 pub filter: &'static [u32],
176 pub filter_pos: &'static [u16],
177 pub filter_len: &'static [u16],
178 pub window: &'static [u32],
179 pub rfft: RfftI32,
180}
181
182impl MfccI32 {
183 pub const fn new(
184 n_fft: usize,
185 n_mels: usize,
186 n_mfcc: usize,
187 dct: &'static [u32],
188 filter: &'static [u32],
189 filter_pos: &'static [u16],
190 filter_len: &'static [u16],
191 window: &'static [u32],
192 ) -> Self {
193 assert!(
194 dct.len() == n_mfcc * n_mels,
195 "DCT matrix size must be n_mfcc * n_mels"
196 );
197 assert!(
198 filter_pos.len() == n_mels,
199 "Filter position array size must be n_mels"
200 );
201 assert!(
202 filter_len.len() == n_mels,
203 "Filter length array size must be n_mels"
204 );
205 assert!(window.len() == n_fft, "Window size must be equal to n_fft");
206
207 Self {
208 n_fft,
209 n_mels,
210 n_mfcc,
211 dct,
212 filter,
213 filter_pos,
214 filter_len,
215 window,
216 rfft: RfftI32::new(n_fft, false, true),
217 }
218 }
219
220 pub fn run(&self, input: &mut [i32], output: &mut [i32], tmp: &mut [i32]) {
221 let dct = view_u32_as_i32(self.dct);
222 let filter = view_u32_as_i32(self.filter);
223
224 assert!(
225 tmp.len() >= self.n_fft * 2,
226 "tmp length must be at least 2 * n_fft for q31 MFCC"
227 );
228
229 let (max_abs, _) = absmax_i32(input, self.n_fft);
231
232 if max_abs != 0 && max_abs != i32::MAX {
233 let (quotient, shift) = div_i32(i32::MAX, max_abs).expect("division must succeed");
234 scale_i32(input, quotient, shift as i8);
235 }
236
237 for (sample, &window) in input.iter_mut().zip(self.window) {
238 *sample = sat_i32((((*sample as i64) * (window as i64)) >> 32) << 1);
239 }
240
241 self.rfft.run(input, &mut tmp[..self.n_fft * 2]);
243
244 let filter_limit = 1 + (self.n_fft >> 1);
245 cmplx_mag_i32(&tmp[..filter_limit * 2], &mut input[..filter_limit]);
246
247 let mut packed_pos = 0usize;
249 for ((dst, &filter_pos), &filter_len) in tmp
250 .iter_mut()
251 .zip(self.filter_pos)
252 .zip(self.filter_len)
253 .take(self.n_mels)
254 {
255 let filter_pos = usize::from(filter_pos);
256 let filter_len = usize::from(filter_len);
257 let filter_end = packed_pos + filter_len;
258
259 let mut acc = dot_i32(
260 &input[filter_pos..filter_pos + filter_len],
261 &filter[packed_pos..filter_end],
262 );
263 packed_pos = filter_end;
264
265 acc = (acc + MICRO_Q31) >> (SHIFT_MELFILTER_SATURATION_Q31 + 18);
266 *dst = sat_i32(acc);
267 }
268
269 if max_abs != 0 && max_abs != i32::MAX {
271 scale_i32(&mut tmp[..self.n_mels], max_abs, 0);
272 }
273
274 vlog_i32_in_place(&mut tmp[..self.n_mels]);
275
276 let fft_shift = 31 - (self.n_fft as u32).leading_zeros() as i32;
277 let log_exponent =
278 (fft_shift + 2 + SHIFT_MELFILTER_SATURATION_Q31).wrapping_mul(LOG2TOLOG_Q31);
279 offset_i32(&mut tmp[..self.n_mels], log_exponent);
280 shift_i32(&mut tmp[..self.n_mels], -3);
281
282 let dct = Matrix {
284 rows: self.n_mfcc,
285 cols: self.n_mels,
286 data: dct.as_ptr() as *mut i32,
287 };
288 mat_vec_mul_i32(dct, &tmp[..self.n_mels], output);
289 }
290}