1#[inline]
13pub fn gain_block_scalar(buf: &mut [f32], gain: f32) {
14 for s in buf {
15 *s *= gain;
16 }
17}
18
19#[inline]
22pub fn gain_block(buf: &mut [f32], gain: f32) {
23 #[cfg(feature = "wide-backend")]
24 {
25 use wide::f32x8;
26 let g = f32x8::splat(gain);
27 let n = buf.len();
28 let n8 = n / 8 * 8;
29 let (head, tail) = buf.split_at_mut(n8);
30 for chunk in head.chunks_exact_mut(8) {
31 let v = f32x8::from(<[f32; 8]>::try_from(&chunk[..]).unwrap_or_default());
32 chunk.copy_from_slice((v * g).as_array_ref());
33 }
34 gain_block_scalar(tail, gain);
35 }
36 #[cfg(not(feature = "wide-backend"))]
37 gain_block_scalar(buf, gain);
38}
39
40#[inline]
45pub fn scale_block_scalar(out: &mut [f32], src: &[f32], scale: f32) {
46 let n = out.len().min(src.len());
47 for i in 0..n {
48 out[i] = src[i] * scale;
49 }
50}
51
52#[inline]
54pub fn scale_block(out: &mut [f32], src: &[f32], scale: f32) {
55 #[cfg(feature = "wide-backend")]
56 {
57 use wide::f32x8;
58 let n = out.len().min(src.len());
59 let n8 = n / 8 * 8;
60 let g = f32x8::splat(scale);
61 let (out_v, out_tail) = out[..n].split_at_mut(n8);
62 let src_v = &src[..n8];
63 let src_tail = &src[n8..n];
64 for (out_chunk, src_chunk) in out_v.chunks_exact_mut(8).zip(src_v.chunks_exact(8)) {
65 let v = f32x8::from(<[f32; 8]>::try_from(src_chunk).unwrap_or_default());
66 out_chunk.copy_from_slice((v * g).as_array_ref());
67 }
68 scale_block_scalar(out_tail, src_tail, scale);
69 }
70 #[cfg(not(feature = "wide-backend"))]
71 scale_block_scalar(out, src, scale);
72}
73
74#[inline]
76pub fn mul_block_scalar(out: &mut [f32], a: &[f32], b: &[f32]) {
77 let n = out.len().min(a.len()).min(b.len());
78 for i in 0..n {
79 out[i] = a[i] * b[i];
80 }
81}
82
83#[inline]
85pub fn mul_block(out: &mut [f32], a: &[f32], b: &[f32]) {
86 #[cfg(feature = "wide-backend")]
87 {
88 use wide::f32x8;
89 let n = out.len().min(a.len()).min(b.len());
90 let n8 = n / 8 * 8;
91 let (out_v, out_tail) = out[..n].split_at_mut(n8);
92 let a_v = &a[..n8];
93 let b_v = &b[..n8];
94 let a_tail = &a[n8..n];
95 let b_tail = &b[n8..n];
96 for ((out_chunk, a_chunk), b_chunk) in out_v
97 .chunks_exact_mut(8)
98 .zip(a_v.chunks_exact(8))
99 .zip(b_v.chunks_exact(8))
100 {
101 let av = f32x8::from(<[f32; 8]>::try_from(a_chunk).unwrap_or_default());
104 let bv = f32x8::from(<[f32; 8]>::try_from(b_chunk).unwrap_or_default());
105 let mv = av * bv;
106 out_chunk.copy_from_slice(mv.as_array_ref());
107 }
108 mul_block_scalar(out_tail, a_tail, b_tail);
109 }
110 #[cfg(not(feature = "wide-backend"))]
111 mul_block_scalar(out, a, b);
112}
113
114#[inline]
116pub fn mac_block_scalar(out: &mut [f32], src: &[f32], scale: f32) {
117 let n = out.len().min(src.len());
118 for i in 0..n {
119 out[i] += src[i] * scale;
120 }
121}
122
123#[inline]
125pub fn mac_block(out: &mut [f32], src: &[f32], scale: f32) {
126 #[cfg(feature = "wide-backend")]
127 {
128 use wide::f32x8;
129 let n = out.len().min(src.len());
130 let n8 = n / 8 * 8;
131 let (out_v, out_tail) = out[..n].split_at_mut(n8);
132 let src_v = &src[..n8];
133 let src_tail = &src[n8..n];
134 let s = f32x8::splat(scale);
135 for (out_chunk, src_chunk) in out_v.chunks_exact_mut(8).zip(src_v.chunks_exact(8)) {
136 let ov = f32x8::from(<[f32; 8]>::try_from(&out_chunk[..]).unwrap_or_default());
137 let sv = f32x8::from(<[f32; 8]>::try_from(src_chunk).unwrap_or_default());
138 let r = ov + sv * s;
139 out_chunk.copy_from_slice(r.as_array_ref());
140 }
141 mac_block_scalar(out_tail, src_tail, scale);
142 }
143 #[cfg(not(feature = "wide-backend"))]
144 mac_block_scalar(out, src, scale);
145}
146
147#[inline]
149pub fn mix_block_scalar(out: &mut [f32], a: &[f32], gain_a: f32, b: &[f32], gain_b: f32) {
150 let n = out.len().min(a.len()).min(b.len());
151 for i in 0..n {
152 out[i] = a[i] * gain_a + b[i] * gain_b;
153 }
154}
155
156#[inline]
159pub fn mix_block(out: &mut [f32], a: &[f32], gain_a: f32, b: &[f32], gain_b: f32) {
160 #[cfg(feature = "wide-backend")]
161 {
162 use wide::f32x8;
163 let n = out.len().min(a.len()).min(b.len());
164 let n8 = n / 8 * 8;
165 let (out_v, out_tail) = out[..n].split_at_mut(n8);
166 let a_v = &a[..n8];
167 let b_v = &b[..n8];
168 let a_tail = &a[n8..n];
169 let b_tail = &b[n8..n];
170 let ga = f32x8::splat(gain_a);
171 let gb = f32x8::splat(gain_b);
172 for ((out_chunk, a_chunk), b_chunk) in out_v
173 .chunks_exact_mut(8)
174 .zip(a_v.chunks_exact(8))
175 .zip(b_v.chunks_exact(8))
176 {
177 let av = f32x8::from(<[f32; 8]>::try_from(a_chunk).unwrap_or_default());
178 let bv = f32x8::from(<[f32; 8]>::try_from(b_chunk).unwrap_or_default());
179 let r = av * ga + bv * gb;
180 out_chunk.copy_from_slice(r.as_array_ref());
181 }
182 mix_block_scalar(out_tail, a_tail, gain_a, b_tail, gain_b);
183 }
184 #[cfg(not(feature = "wide-backend"))]
185 mix_block_scalar(out, a, gain_a, b, gain_b);
186}
187
188#[inline]
192pub fn copy_block(out: &mut [f32], src: &[f32]) {
193 let n = out.len().min(src.len());
194 out[..n].copy_from_slice(&src[..n]);
195}
196
197#[inline]
199pub fn zero_block(buf: &mut [f32]) {
200 buf.fill(0.0);
201}
202
203#[inline]
207#[must_use]
208pub fn abs_max_block(buf: &[f32]) -> f32 {
209 let mut peak = 0.0_f32;
210 for &v in buf {
211 if v.is_nan() {
212 return f32::NAN;
213 }
214 let a = v.abs();
215 if a > peak {
216 peak = a;
217 }
218 }
219 peak
220}
221
222#[cfg(test)]
223mod tests {
224 #![allow(clippy::float_cmp, clippy::cast_precision_loss)]
228
229 use super::*;
230
231 #[test]
232 fn gain_block_matches_scalar() {
233 for n in [0, 1, 7, 8, 9, 16, 31, 32, 33, 128] {
234 let init: Vec<f32> = (0..n).map(|i| i as f32 * 0.5 - 1.0).collect();
235 let mut a = init.clone();
236 let mut b = init.clone();
237 gain_block_scalar(&mut a, 0.75);
238 gain_block(&mut b, 0.75);
239 assert_eq!(a, b, "mismatch at n={n}");
240 }
241 }
242
243 #[test]
244 fn scale_block_matches_scalar() {
245 for n in [0, 1, 7, 8, 9, 16, 33] {
246 let src: Vec<f32> = (0..n).map(|i| i as f32 * 0.3 - 1.0).collect();
247 let mut out_s = vec![0.0; n];
248 let mut out_v = vec![0.0; n];
249 scale_block_scalar(&mut out_s, &src, 0.5);
250 scale_block(&mut out_v, &src, 0.5);
251 assert_eq!(out_s, out_v, "mismatch at n={n}");
252 }
253 }
254
255 #[test]
256 fn mul_block_matches_scalar() {
257 for n in [0, 1, 7, 8, 33] {
258 let a: Vec<f32> = (0..n).map(|i| i as f32 * 0.1).collect();
259 let b: Vec<f32> = (0..n).map(|i| i as f32 * -0.2).collect();
260 let mut out_s = vec![0.0; n];
261 let mut out_v = vec![0.0; n];
262 mul_block_scalar(&mut out_s, &a, &b);
263 mul_block(&mut out_v, &a, &b);
264 assert_eq!(out_s, out_v, "mismatch at n={n}");
265 }
266 }
267
268 #[test]
269 fn mac_block_matches_scalar() {
270 for n in [0, 7, 16, 65] {
271 let src: Vec<f32> = (0..n).map(|i| i as f32).collect();
272 let mut a = vec![1.0; n];
273 let mut b = vec![1.0; n];
274 mac_block_scalar(&mut a, &src, 0.25);
275 mac_block(&mut b, &src, 0.25);
276 assert_eq!(a, b);
277 }
278 }
279
280 #[test]
281 fn mix_block_matches_scalar() {
282 let a: Vec<f32> = (0..32).map(|i| i as f32).collect();
283 let b: Vec<f32> = (0..32).map(|i| (i as f32) * 2.0).collect();
284 let mut out_s = vec![0.0; 32];
285 let mut out_v = vec![0.0; 32];
286 mix_block_scalar(&mut out_s, &a, 0.5, &b, 0.25);
287 mix_block(&mut out_v, &a, 0.5, &b, 0.25);
288 assert_eq!(out_s, out_v);
289 }
290
291 #[test]
292 fn abs_max_block_finds_peak() {
293 let buf = [-0.1, 0.7, -0.9, 0.3];
294 assert!((abs_max_block(&buf) - 0.9).abs() < 1e-6);
295 assert_eq!(abs_max_block(&[]), 0.0);
296 assert!(abs_max_block(&[1.0, f32::NAN, 2.0]).is_nan());
297 }
298}