firewheel_core/dsp/
interleave.rs

1use crate::mask::SilenceMask;
2
3/// De-interleave audio channels
4pub fn deinterleave<V: AsMut<[f32]>>(
5    channels: &mut [V],
6    start_frame_in_channels: usize,
7    interleaved: &[f32],
8    num_interleaved_channels: usize,
9    calculate_silence_mask: bool,
10) -> SilenceMask {
11    if channels.is_empty() {
12        return SilenceMask::NONE_SILENT;
13    }
14
15    if num_interleaved_channels == 0 {
16        for ch in channels.iter_mut() {
17            ch.as_mut()[start_frame_in_channels..].fill(0.0);
18        }
19
20        return SilenceMask::new_all_silent(channels.len());
21    }
22
23    let mut silence_mask = SilenceMask::NONE_SILENT;
24
25    let (num_filled_channels, samples) = if num_interleaved_channels == 1 {
26        // Mono, no need to deinterleave.
27
28        let samples = interleaved.len();
29        let ch =
30            &mut channels[0].as_mut()[start_frame_in_channels..start_frame_in_channels + samples];
31
32        ch.copy_from_slice(interleaved);
33
34        if calculate_silence_mask {
35            if ch.iter().find(|&&s| s != 0.0).is_none() {
36                silence_mask.set_channel(0, true);
37            }
38        }
39
40        (1, samples)
41    } else if num_interleaved_channels == 2 && channels.len() >= 2 {
42        // Provide an optimized loop for stereo.
43
44        let samples = interleaved.len() / 2;
45
46        let (ch0, ch1) = channels.split_first_mut().unwrap();
47        let ch0 = &mut ch0.as_mut()[start_frame_in_channels..start_frame_in_channels + samples];
48        let ch1 = &mut ch1[0].as_mut()[start_frame_in_channels..start_frame_in_channels + samples];
49
50        for (in_chunk, (ch0_s, ch1_s)) in interleaved
51            .chunks_exact(2)
52            .zip(ch0.iter_mut().zip(ch1.iter_mut()))
53        {
54            *ch0_s = in_chunk[0];
55            *ch1_s = in_chunk[1];
56        }
57
58        if calculate_silence_mask {
59            for (ch_i, ch) in channels.iter_mut().enumerate() {
60                if ch.as_mut()[0..samples]
61                    .iter()
62                    .find(|&&s| s != 0.0)
63                    .is_none()
64                {
65                    silence_mask.set_channel(ch_i, true);
66                }
67            }
68        }
69
70        (2, samples)
71    } else {
72        let mut num_filled_channels = 0;
73        let samples = interleaved.len() / num_interleaved_channels;
74
75        for (ch_i, ch) in (0..num_interleaved_channels).zip(channels.iter_mut()) {
76            let ch = &mut ch.as_mut()[start_frame_in_channels..start_frame_in_channels + samples];
77
78            for (in_chunk, out_s) in interleaved
79                .chunks_exact(num_interleaved_channels)
80                .zip(ch.iter_mut())
81            {
82                *out_s = in_chunk[ch_i];
83            }
84
85            if calculate_silence_mask && ch_i < 64 {
86                if ch.iter().find(|&&s| s != 0.0).is_none() {
87                    silence_mask.set_channel(ch_i, true);
88                }
89            }
90
91            num_filled_channels += 1;
92        }
93
94        (num_filled_channels, samples)
95    };
96
97    if num_filled_channels < channels.len() {
98        for (ch_i, ch) in channels.iter_mut().enumerate().skip(num_filled_channels) {
99            ch.as_mut()[start_frame_in_channels..start_frame_in_channels + samples].fill(0.0);
100
101            if calculate_silence_mask && ch_i < 64 {
102                silence_mask.set_channel(ch_i, true);
103            }
104        }
105    }
106
107    silence_mask
108}
109
110/// Interleave audio channels
111pub fn interleave<V: AsRef<[f32]>>(
112    channels: &[V],
113    start_frame_in_channels: usize,
114    interleaved: &mut [f32],
115    num_interleaved_channels: usize,
116    silence_mask: Option<SilenceMask>,
117) {
118    if channels.is_empty() || num_interleaved_channels == 0 {
119        interleaved.fill(0.0);
120        return;
121    }
122
123    if let Some(silence_mask) = silence_mask {
124        if channels.len() <= 64 {
125            if silence_mask.all_channels_silent(channels.len()) {
126                interleaved.fill(0.0);
127                return;
128            }
129        }
130    }
131
132    if num_interleaved_channels == 1 {
133        // Mono, no need to interleave.
134        interleaved.copy_from_slice(
135            &channels[0].as_ref()
136                [start_frame_in_channels..start_frame_in_channels + interleaved.len()],
137        );
138        return;
139    }
140
141    if num_interleaved_channels == 2 && channels.len() >= 2 {
142        // Provide an optimized loop for stereo.
143        let samples = interleaved.len() / 2;
144
145        let ch1 = &channels[0].as_ref()[start_frame_in_channels..start_frame_in_channels + samples];
146        let ch2 = &channels[1].as_ref()[start_frame_in_channels..start_frame_in_channels + samples];
147
148        for (out_chunk, (&ch1_s, &ch2_s)) in interleaved
149            .chunks_exact_mut(2)
150            .zip(ch1.iter().zip(ch2.iter()))
151        {
152            out_chunk[0] = ch1_s;
153            out_chunk[1] = ch2_s;
154        }
155
156        return;
157    }
158
159    let any_channel_silent = if let Some(silence_mask) = silence_mask {
160        if channels.len() <= 64 {
161            silence_mask.any_channel_silent(channels.len())
162        } else {
163            true
164        }
165    } else {
166        false
167    };
168
169    if num_interleaved_channels > channels.len() || any_channel_silent {
170        interleaved.fill(0.0);
171    }
172
173    for (ch_i, ch) in (0..num_interleaved_channels).zip(channels.iter()) {
174        if let Some(silence_mask) = silence_mask {
175            if ch_i < 64 {
176                if silence_mask.is_channel_silent(ch_i) {
177                    continue;
178                }
179            }
180        }
181
182        for (out_chunk, &in_s) in interleaved
183            .chunks_exact_mut(num_interleaved_channels)
184            .zip(ch.as_ref()[start_frame_in_channels..].iter())
185        {
186            out_chunk[ch_i] = in_s;
187        }
188    }
189}