Skip to main content

truce_simd/
ops64.rs

1//! Block-rate audio ops at `f64`. Mirror of [`crate::ops`] (f32);
2//! see that module for the per-op contracts. The wider lane width
3//! means `f64x4` SIMD lanes for the same `wide` backend; chunk
4//! granularity is 4 instead of 8.
5
6#[inline]
7pub fn gain_block_scalar(buf: &mut [f64], gain: f64) {
8    for s in buf {
9        *s *= gain;
10    }
11}
12
13#[inline]
14pub fn gain_block(buf: &mut [f64], gain: f64) {
15    #[cfg(feature = "wide-backend")]
16    {
17        use wide::f64x4;
18        let g = f64x4::splat(gain);
19        let n = buf.len();
20        let n4 = n / 4 * 4;
21        let (head, tail) = buf.split_at_mut(n4);
22        for chunk in head.chunks_exact_mut(4) {
23            let v = f64x4::from(<[f64; 4]>::try_from(&chunk[..]).unwrap_or_default());
24            chunk.copy_from_slice((v * g).as_array_ref());
25        }
26        gain_block_scalar(tail, gain);
27    }
28    #[cfg(not(feature = "wide-backend"))]
29    gain_block_scalar(buf, gain);
30}
31
32#[inline]
33pub fn scale_block_scalar(out: &mut [f64], src: &[f64], scale: f64) {
34    let n = out.len().min(src.len());
35    for i in 0..n {
36        out[i] = src[i] * scale;
37    }
38}
39
40#[inline]
41pub fn scale_block(out: &mut [f64], src: &[f64], scale: f64) {
42    #[cfg(feature = "wide-backend")]
43    {
44        use wide::f64x4;
45        let n = out.len().min(src.len());
46        let n4 = n / 4 * 4;
47        let g = f64x4::splat(scale);
48        let (out_v, out_tail) = out[..n].split_at_mut(n4);
49        let src_v = &src[..n4];
50        let src_tail = &src[n4..n];
51        for (out_chunk, src_chunk) in out_v.chunks_exact_mut(4).zip(src_v.chunks_exact(4)) {
52            let v = f64x4::from(<[f64; 4]>::try_from(src_chunk).unwrap_or_default());
53            out_chunk.copy_from_slice((v * g).as_array_ref());
54        }
55        scale_block_scalar(out_tail, src_tail, scale);
56    }
57    #[cfg(not(feature = "wide-backend"))]
58    scale_block_scalar(out, src, scale);
59}
60
61#[inline]
62pub fn mul_block_scalar(out: &mut [f64], a: &[f64], b: &[f64]) {
63    let n = out.len().min(a.len()).min(b.len());
64    for i in 0..n {
65        out[i] = a[i] * b[i];
66    }
67}
68
69#[inline]
70pub fn mul_block(out: &mut [f64], a: &[f64], b: &[f64]) {
71    #[cfg(feature = "wide-backend")]
72    {
73        use wide::f64x4;
74        let n = out.len().min(a.len()).min(b.len());
75        let n4 = n / 4 * 4;
76        let (out_v, out_tail) = out[..n].split_at_mut(n4);
77        let a_v = &a[..n4];
78        let b_v = &b[..n4];
79        let a_tail = &a[n4..n];
80        let b_tail = &b[n4..n];
81        for ((out_chunk, a_chunk), b_chunk) in out_v
82            .chunks_exact_mut(4)
83            .zip(a_v.chunks_exact(4))
84            .zip(b_v.chunks_exact(4))
85        {
86            let av = f64x4::from(<[f64; 4]>::try_from(a_chunk).unwrap_or_default());
87            let bv = f64x4::from(<[f64; 4]>::try_from(b_chunk).unwrap_or_default());
88            out_chunk.copy_from_slice((av * bv).as_array_ref());
89        }
90        mul_block_scalar(out_tail, a_tail, b_tail);
91    }
92    #[cfg(not(feature = "wide-backend"))]
93    mul_block_scalar(out, a, b);
94}
95
96#[inline]
97pub fn mac_block_scalar(out: &mut [f64], src: &[f64], scale: f64) {
98    let n = out.len().min(src.len());
99    for i in 0..n {
100        out[i] += src[i] * scale;
101    }
102}
103
104#[inline]
105pub fn mac_block(out: &mut [f64], src: &[f64], scale: f64) {
106    #[cfg(feature = "wide-backend")]
107    {
108        use wide::f64x4;
109        let n = out.len().min(src.len());
110        let n4 = n / 4 * 4;
111        let (out_v, out_tail) = out[..n].split_at_mut(n4);
112        let src_v = &src[..n4];
113        let src_tail = &src[n4..n];
114        let s = f64x4::splat(scale);
115        for (out_chunk, src_chunk) in out_v.chunks_exact_mut(4).zip(src_v.chunks_exact(4)) {
116            let ov = f64x4::from(<[f64; 4]>::try_from(&out_chunk[..]).unwrap_or_default());
117            let sv = f64x4::from(<[f64; 4]>::try_from(src_chunk).unwrap_or_default());
118            out_chunk.copy_from_slice((ov + sv * s).as_array_ref());
119        }
120        mac_block_scalar(out_tail, src_tail, scale);
121    }
122    #[cfg(not(feature = "wide-backend"))]
123    mac_block_scalar(out, src, scale);
124}
125
126#[inline]
127pub fn mix_block_scalar(out: &mut [f64], a: &[f64], gain_a: f64, b: &[f64], gain_b: f64) {
128    let n = out.len().min(a.len()).min(b.len());
129    for i in 0..n {
130        out[i] = a[i] * gain_a + b[i] * gain_b;
131    }
132}
133
134#[inline]
135pub fn mix_block(out: &mut [f64], a: &[f64], gain_a: f64, b: &[f64], gain_b: f64) {
136    #[cfg(feature = "wide-backend")]
137    {
138        use wide::f64x4;
139        let n = out.len().min(a.len()).min(b.len());
140        let n4 = n / 4 * 4;
141        let (out_v, out_tail) = out[..n].split_at_mut(n4);
142        let a_v = &a[..n4];
143        let b_v = &b[..n4];
144        let a_tail = &a[n4..n];
145        let b_tail = &b[n4..n];
146        let ga = f64x4::splat(gain_a);
147        let gb = f64x4::splat(gain_b);
148        for ((out_chunk, a_chunk), b_chunk) in out_v
149            .chunks_exact_mut(4)
150            .zip(a_v.chunks_exact(4))
151            .zip(b_v.chunks_exact(4))
152        {
153            let av = f64x4::from(<[f64; 4]>::try_from(a_chunk).unwrap_or_default());
154            let bv = f64x4::from(<[f64; 4]>::try_from(b_chunk).unwrap_or_default());
155            out_chunk.copy_from_slice((av * ga + bv * gb).as_array_ref());
156        }
157        mix_block_scalar(out_tail, a_tail, gain_a, b_tail, gain_b);
158    }
159    #[cfg(not(feature = "wide-backend"))]
160    mix_block_scalar(out, a, gain_a, b, gain_b);
161}
162
163#[inline]
164pub fn copy_block(out: &mut [f64], src: &[f64]) {
165    let n = out.len().min(src.len());
166    out[..n].copy_from_slice(&src[..n]);
167}
168
169#[inline]
170pub fn zero_block(buf: &mut [f64]) {
171    buf.fill(0.0);
172}
173
174#[inline]
175#[must_use]
176pub fn abs_max_block(buf: &[f64]) -> f64 {
177    let mut peak = 0.0_f64;
178    for &v in buf {
179        if v.is_nan() {
180            return f64::NAN;
181        }
182        let a = v.abs();
183        if a > peak {
184            peak = a;
185        }
186    }
187    peak
188}