stft_rs/utils.rs
1/// Utility functions for signal processing and multi-channel audio
2use num_traits::Float;
3
4#[cfg(not(feature = "std"))]
5use alloc::{vec, vec::Vec};
6
7#[cfg(feature = "std")]
8use std::vec;
9
10use crate::PadMode;
11
12/// Apply padding to a signal.
13/// Streaming applications should pad manually to match batch processing quality.
14pub fn apply_padding<T: Float>(signal: &[T], pad_amount: usize, mode: PadMode) -> Vec<T> {
15 let total_len = signal.len() + 2 * pad_amount;
16 let mut padded = vec![T::zero(); total_len];
17
18 padded[pad_amount..pad_amount + signal.len()].copy_from_slice(signal);
19
20 match mode {
21 PadMode::Reflect => {
22 for i in 0..pad_amount {
23 if i + 1 < signal.len() {
24 padded[pad_amount - 1 - i] = signal[i + 1];
25 }
26 }
27
28 let n = signal.len();
29 for i in 0..pad_amount {
30 if n >= 2 && n - 2 >= i {
31 padded[pad_amount + n + i] = signal[n - 2 - i];
32 }
33 }
34 }
35 PadMode::Zero => {}
36 PadMode::Edge => {
37 if !signal.is_empty() {
38 for i in 0..pad_amount {
39 padded[i] = signal[0];
40 }
41 for i in 0..pad_amount {
42 padded[pad_amount + signal.len() + i] = signal[signal.len() - 1];
43 }
44 }
45 }
46 }
47
48 padded
49}
50
51// ============================================================================
52// Multi-Channel Utilities
53// ============================================================================
54
55/// Deinterleave multi-channel audio data.
56///
57/// Converts interleaved format (e.g., `[L,R,L,R,L,R,...]` for stereo)
58/// into separate channels (`vec![vec![L,L,L,...], vec![R,R,R,...]]`).
59///
60/// # Arguments
61///
62/// * `data` - Interleaved audio data
63/// * `num_channels` - Number of channels
64///
65/// # Panics
66///
67/// Panics if `num_channels` is 0 or if `data.len()` is not divisible by `num_channels`.
68///
69/// # Example
70///
71/// ```
72/// use stft_rs::deinterleave;
73///
74/// let interleaved = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; // L,R,L,R,L,R
75/// let channels = deinterleave(&interleaved, 2);
76///
77/// assert_eq!(channels[0], vec![1.0, 3.0, 5.0]); // Left
78/// assert_eq!(channels[1], vec![2.0, 4.0, 6.0]); // Right
79/// ```
80pub fn deinterleave<T: Float>(data: &[T], num_channels: usize) -> Vec<Vec<T>> {
81 assert!(num_channels > 0, "num_channels must be greater than 0");
82 assert_eq!(
83 data.len() % num_channels,
84 0,
85 "data length ({}) must be divisible by num_channels ({})",
86 data.len(),
87 num_channels
88 );
89
90 if data.is_empty() {
91 return vec![Vec::new(); num_channels];
92 }
93
94 let samples_per_channel = data.len() / num_channels;
95 let mut channels = vec![Vec::with_capacity(samples_per_channel); num_channels];
96
97 for (i, &sample) in data.iter().enumerate() {
98 channels[i % num_channels].push(sample);
99 }
100
101 channels
102}
103
104/// Deinterleave multi-channel audio into a pre-allocated buffer (zero-allocation).
105///
106/// Converts interleaved format into separate channels without allocating new Vecs.
107/// The output buffer must contain exactly `num_channels` `Vec<T>` elements.
108/// Each Vec will be cleared and filled with samples.
109///
110/// # Arguments
111///
112/// * `data` - Interleaved audio data
113/// * `num_channels` - Number of channels
114/// * `output` - Pre-allocated output buffer with `num_channels` Vecs
115///
116/// # Panics
117///
118/// Panics if `num_channels` is 0, if `data.len()` is not divisible by `num_channels`,
119/// or if `output.len() != num_channels`.
120///
121/// # Example
122///
123/// ```
124/// use stft_rs::deinterleave_into;
125///
126/// let interleaved = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; // L,R,L,R,L,R
127/// let mut output = vec![Vec::new(), Vec::new()];
128///
129/// deinterleave_into(&interleaved, 2, &mut output);
130///
131/// assert_eq!(output[0], vec![1.0, 3.0, 5.0]); // Left
132/// assert_eq!(output[1], vec![2.0, 4.0, 6.0]); // Right
133/// ```
134pub fn deinterleave_into<T: Float>(data: &[T], num_channels: usize, output: &mut [Vec<T>]) {
135 assert!(num_channels > 0, "num_channels must be greater than 0");
136 assert_eq!(
137 output.len(),
138 num_channels,
139 "output must have exactly {} channels, got {}",
140 num_channels,
141 output.len()
142 );
143 assert_eq!(
144 data.len() % num_channels,
145 0,
146 "data length ({}) must be divisible by num_channels ({})",
147 data.len(),
148 num_channels
149 );
150
151 if data.is_empty() {
152 for channel in output.iter_mut() {
153 channel.clear();
154 }
155 return;
156 }
157
158 let samples_per_channel = data.len() / num_channels;
159
160 // Clear and reserve capacity
161 for channel in output.iter_mut() {
162 channel.clear();
163 channel.reserve(samples_per_channel);
164 }
165
166 // Deinterleave
167 for (i, &sample) in data.iter().enumerate() {
168 output[i % num_channels].push(sample);
169 }
170}
171
172/// Interleave multiple channels into a single buffer.
173///
174/// Converts separate channels (`vec![vec![L,L,L,...], vec![R,R,R,...]]`)
175/// into interleaved format (e.g., `[L,R,L,R,L,R,...]` for stereo).
176///
177/// # Arguments
178///
179/// * `channels` - Vector of audio channels
180///
181/// # Panics
182///
183/// Panics if channels is empty or if channels have different lengths.
184///
185/// # Example
186///
187/// ```
188/// use stft_rs::interleave;
189///
190/// let left = vec![1.0, 3.0, 5.0];
191/// let right = vec![2.0, 4.0, 6.0];
192/// let interleaved = interleave(&[left, right]);
193///
194/// assert_eq!(interleaved, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
195/// ```
196pub fn interleave<T: Float>(channels: &[Vec<T>]) -> Vec<T> {
197 assert!(!channels.is_empty(), "channels must not be empty");
198
199 if channels[0].is_empty() {
200 return Vec::new();
201 }
202
203 let num_channels = channels.len();
204 let samples_per_channel = channels[0].len();
205
206 // Validate all channels have same length
207 for (i, channel) in channels.iter().enumerate() {
208 assert_eq!(
209 channel.len(),
210 samples_per_channel,
211 "Channel {} has length {}, expected {}",
212 i,
213 channel.len(),
214 samples_per_channel
215 );
216 }
217
218 let mut interleaved = Vec::with_capacity(samples_per_channel * num_channels);
219
220 for sample_idx in 0..samples_per_channel {
221 for channel in channels {
222 interleaved.push(channel[sample_idx]);
223 }
224 }
225
226 interleaved
227}
228
229/// Interleave multiple channels into a pre-allocated buffer (zero-allocation).
230///
231/// Converts separate channels into interleaved format without allocating.
232/// The output buffer must have capacity for `samples_per_channel × num_channels`.
233///
234/// # Arguments
235///
236/// * `channels` - Slice of audio channels
237/// * `output` - Pre-allocated output buffer (will be cleared and filled)
238///
239/// # Panics
240///
241/// Panics if channels is empty or if channels have different lengths.
242///
243/// # Example
244///
245/// ```
246/// use stft_rs::interleave_into;
247///
248/// let left = vec![1.0, 3.0, 5.0];
249/// let right = vec![2.0, 4.0, 6.0];
250///
251/// let mut output = Vec::with_capacity(6);
252/// interleave_into(&[left, right], &mut output);
253///
254/// assert_eq!(output, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
255/// ```
256pub fn interleave_into<T: Float>(channels: &[Vec<T>], output: &mut Vec<T>) {
257 assert!(!channels.is_empty(), "channels must not be empty");
258
259 if channels[0].is_empty() {
260 output.clear();
261 return;
262 }
263
264 let num_channels = channels.len();
265 let samples_per_channel = channels[0].len();
266
267 // Validate all channels have same length
268 for (i, channel) in channels.iter().enumerate() {
269 assert_eq!(
270 channel.len(),
271 samples_per_channel,
272 "Channel {} has length {}, expected {}",
273 i,
274 channel.len(),
275 samples_per_channel
276 );
277 }
278
279 output.clear();
280 output.reserve(samples_per_channel * num_channels);
281
282 for sample_idx in 0..samples_per_channel {
283 for channel in channels {
284 output.push(channel[sample_idx]);
285 }
286 }
287}