audio_samples/
iterators.rs

1//! Iterators and iterator-like helpers over audio samples.
2//!
3//! This module provides iterator types for structured access to audio data stored in
4//! [`AudioSamples`]. Depending on the iterator, items may be:
5//!
6//! - *Borrowed views* into the original audio (typically zero-allocation)
7//! - *Owned buffers* constructed per item (allocating) when a borrowed view is not possible or
8//!   when padding/ownership is required
9//!
10//! # Iterator Types
11//!
12//! - [`FrameIterator`] - Iterates over frames (one sample from each channel)
13//! - [`ChannelIterator`] - Iterates over complete channels sequentially
14//! - [`WindowIterator`] - Iterates over overlapping windows/blocks of data
15//!
16//! # Usage
17//!
18//! ```rust
19//! use audio_samples::{AudioSamples, iterators::AudioSampleIterators};
20//! use ndarray::array;
21//!
22//! let audio = AudioSamples::new_multi_channel(
23//!     array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
24//!     44100
25//! );
26//!
27//! // Iterate over frames
28//! for frame in audio.frames() {
29//!     println!("Frame: {:?}", frame);
30//! }
31//!
32//! // Iterate over channels
33//! for channel in audio.channels() {
34//!     println!("Channel samples: {:?}", channel);
35//! }
36//!
37//! // Iterate over overlapping windows
38//! for window in audio.windows(1024, 512) {
39//!     println!("Window: {} samples", window.len());
40//! }
41//! ```
42//!
43//! # Performance Notes
44//!
45//! - [`FrameIterator`] yields borrowed views and does not allocate.
46//! - [`ChannelIterator`] yields owned `AudioSamples` per channel (allocates) because
47//!   `AudioSamples::slice_channels` returns owned data.
48//! - [`WindowIterator`] yields owned `AudioSamples` per window (allocates), and can optionally
49//!   zero-pad windows.
50//! - Mutable window iteration cannot safely yield *overlapping* mutable windows; prefer
51//!   [`AudioSamples::apply_to_windows`] for overlapping in-place windowed processing.
52
53use crate::{AudioData, AudioSample, AudioSampleError, AudioSamples, ConvertTo, I24, LayoutError};
54
55#[cfg(feature = "editing")]
56use crate::AudioEditing;
57use std::marker::PhantomData;
58
59#[cfg(feature = "parallel-processing")]
60use rayon::prelude::*;
61
62#[cfg(feature = "parallel-processing")]
63use std::num::NonZeroU32;
64
65#[cfg(feature = "parallel-processing")]
66use ndarray::{Array1, Array2, s};
67
68/// Extension trait providing iterator methods for AudioSamples.
69pub trait AudioSampleIterators<'a, T: AudioSample> {
70    /// Returns an iterator over frames (one sample from each channel).
71    ///
72    /// For mono audio, each frame contains one sample.
73    /// For multi-channel audio, each frame contains one sample from each channel.
74    ///
75    /// # Inputs
76    /// - `&self`
77    ///
78    /// # Returns
79    /// An iterator yielding an `AudioSamples` view for each frame. Each yielded item contains
80    /// exactly one sample per channel.
81    ///
82    /// # Errors
83    /// This iterator does not report errors. Internally it relies on valid in-bounds slicing.
84    ///
85    /// # Panics
86    /// Does not panic.
87    ///
88    /// # Examples
89    ///
90    /// ```rust
91    /// # use audio_samples::{AudioSamples, iterators::AudioSampleIterators};
92    /// # use ndarray::array;
93    /// let audio = AudioSamples::new_multi_channel(
94    ///     array![[1.0f32, 2.0], [3.0, 4.0]],
95    ///     44100
96    /// );
97    ///
98    /// let frames: Vec<Vec<f32>> = audio.frames().map(|f| f.to_interleaved_vec()).collect();
99    /// assert_eq!(frames, vec![vec![1.0, 3.0], vec![2.0, 4.0]]);
100    /// ```
101    fn frames(&'a self) -> FrameIterator<'a, T>;
102
103    /// Returns an iterator over complete channels.
104    ///
105    /// Each iteration yields all samples from one channel before moving to the next.
106    ///
107    /// # Inputs
108    /// - `&self`
109    ///
110    /// # Returns
111    /// An iterator yielding owned `AudioSamples` values, one per channel.
112    ///
113    /// # Errors
114    /// This iterator does not report errors. Internally it relies on valid in-bounds slicing.
115    ///
116    /// # Panics
117    /// Does not panic.
118    ///
119    /// # Examples
120    ///
121    /// ```rust
122    /// # use audio_samples::{AudioSamples, iterators::AudioSampleIterators};
123    /// # use ndarray::array;
124    /// let audio = AudioSamples::new_multi_channel(
125    ///     array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
126    ///     44100
127    /// );
128    ///
129    /// let channels: Vec<Vec<f32>> = audio.channels().map(|c| c.to_interleaved_vec()).collect();
130    /// assert_eq!(channels[0], vec![1.0, 2.0, 3.0]);
131    /// assert_eq!(channels[1], vec![4.0, 5.0, 6.0]);
132    /// ```
133    fn channels<'iter>(&'iter self) -> ChannelIterator<'iter, 'a, T>;
134
135    /// Returns an iterator over overlapping windows of audio data.
136    ///
137    /// # Inputs
138    /// - `window_size`: size of each window in samples (per channel)
139    /// - `hop_size`: number of samples to advance between windows
140    ///
141    /// # Returns
142    /// An iterator yielding owned `AudioSamples` windows.
143    ///
144    /// # Padding
145    /// The default padding mode is [`PaddingMode::Zero`]. Use
146    /// [`WindowIterator::with_padding_mode`] to change it.
147    ///
148    /// # Panics
149    /// Does not panic. If `window_size == 0` or `hop_size == 0`, the iterator yields no items.
150    ///
151    /// # Examples
152    ///
153    /// ```rust
154    /// # use audio_samples::{AudioSamples, iterators::AudioSampleIterators};
155    /// # use ndarray::array;
156    /// let audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], 44100);
157    ///
158    /// let windows: Vec<Vec<f32>> = audio
159    ///     .windows(4, 2)
160    ///     .map(|w| w.to_interleaved_vec())
161    ///     .collect();
162    /// assert_eq!(windows, vec![
163    ///     vec![1.0, 2.0, 3.0, 4.0],
164    ///     vec![3.0, 4.0, 5.0, 6.0],
165    ///     vec![5.0, 6.0, 0.0, 0.0], // zero-padded tail
166    /// ]);
167    /// ```
168    fn windows(&'a self, window_size: usize, hop_size: usize) -> WindowIterator<'a, T>;
169
170    /// Returns a mutable iterator over frames (one sample from each channel).
171    ///
172    /// This allows in-place modification of frames. Each iteration yields mutable references
173    /// to samples that can be modified directly.
174    ///
175    /// # Inputs
176    /// - `&mut self`
177    ///
178    /// # Returns
179    /// A mutable iterator yielding one frame at a time.
180    ///
181    /// For multi-channel audio, each yielded [`FrameMut`] stores per-channel pointers and may
182    /// allocate a small `Vec` of pointers per frame.
183    ///
184    /// # Panics
185    /// Panics if the underlying multi-channel storage is not contiguous in memory.
186    ///
187    /// # Examples
188    ///
189    /// ```rust
190    /// # use audio_samples::{AudioSamples, iterators::AudioSampleIterators};
191    /// # use ndarray::array;
192    /// let mut audio = AudioSamples::new_multi_channel(
193    ///     array![[1.0f32, 2.0], [3.0, 4.0]],
194    ///     44100
195    /// );
196    ///
197    /// // Apply gain to each frame
198    /// for frame in audio.frames_mut() {
199    ///     frame.apply(|s| s * 0.5);
200    /// }
201    /// assert_eq!(audio.to_interleaved_vec(), vec![0.5, 1.5, 1.0, 2.0]);
202    /// ```
203    fn frames_mut(&'a mut self) -> FrameIteratorMut<'a, T>;
204
205    /// Returns a mutable iterator over complete channels.
206    ///
207    /// Each iteration yields mutable references to all samples in one channel,
208    /// allowing for efficient channel-wise processing.
209    ///
210    /// # Inputs
211    /// - `&mut self`
212    ///
213    /// # Returns
214    /// A mutable iterator yielding one mutable slice per channel.
215    ///
216    /// # Panics
217    /// Panics if the underlying multi-channel storage is not contiguous in memory.
218    ///
219    /// # Examples
220    ///
221    /// ```rust
222    /// # use audio_samples::{AudioSamples, iterators::AudioSampleIterators};
223    /// # use ndarray::array;
224    /// let mut audio = AudioSamples::new_multi_channel(
225    ///     array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
226    ///     44100
227    /// );
228    ///
229    /// // Apply different processing to each channel
230    /// for (ch, channel) in audio.channels_mut().enumerate() {
231    ///     let gain = if ch == 0 { 0.8 } else { 0.6 }; // Left/Right balance
232    ///     for sample in channel {
233    ///         *sample *= gain;
234    ///     }
235    /// }
236    /// ```
237    fn channels_mut(&'a mut self) -> ChannelIteratorMut<'a, T>;
238
239    /// Returns a mutable iterator over *non-overlapping* windows of audio data.
240    ///
241    /// This allows in-place modification of windows. For soundness, this iterator cannot yield
242    /// overlapping mutable windows.
243    ///
244    /// # Inputs
245    /// - `window_size`: size of each window in samples (per channel)
246    /// - `hop_size`: number of samples to advance between windows
247    ///
248    /// # Returns
249    /// A mutable iterator yielding one window at a time.
250    ///
251    /// # Panics
252    /// - Panics if `hop_size < window_size` **and** more than one window would be yielded
253    ///   (overlap would create aliasing mutable windows).
254    /// - Panics if the underlying multi-channel storage is not contiguous in memory.
255    ///
256    /// If `window_size == 0` or `hop_size == 0`, the iterator yields no items.
257    ///
258    /// # Examples
259    ///
260    /// ```rust
261    /// # use audio_samples::{AudioSamples, iterators::AudioSampleIterators};
262    /// # use ndarray::array;
263    /// let mut audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], 44100);
264    ///
265    /// // In-place non-overlapping window processing
266    /// for mut window in audio.windows_mut(4, 4) {
267    ///     window.apply_window_function(|i, n| {
268    ///         let pi = std::f32::consts::PI;
269    ///         // Hann window
270    ///         0.5 * (1.0 - (2.0 * pi * i as f32 / (n - 1) as f32).cos())
271    ///     });
272    /// }
273    /// ```
274    fn windows_mut(&'a mut self, window_size: usize, hop_size: usize) -> WindowIteratorMut<'a, T>;
275}
276
277/// Extension trait providing parallel iterator methods for AudioSamples.
278///
279/// This trait provides parallel versions of the standard audio iterators using rayon's
280/// parallel iterator framework. These methods are only available when the `parallel-processing`
281/// feature is enabled.
282///
283/// # Performance Notes
284///
285/// - Parallel iterators are most beneficial for CPU-intensive operations on large datasets
286/// - For simple operations or small audio samples, sequential iterators may be faster due to overhead
287/// - The parallel iterators automatically scale to available CPU cores
288/// - Consider using `.with_min_len()` on returned parallel iterators to tune work distribution
289///
290/// # Examples
291///
292/// ```rust
293/// use audio_samples::{AudioSamples, iterators::AudioSampleParallelIterators};
294/// use rayon::prelude::*;
295/// use ndarray::array;
296///
297/// let audio = AudioSamples::new_multi_channel(
298///     array![[1.0f32, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]],
299///     44100
300/// );
301///
302/// // Process frames in parallel
303/// let processed: Vec<f32> = audio.par_frames()
304///     .map(|frame| {
305///         // Expensive computation per frame
306///         frame.into_iter().map(|s| s.powi(2)).sum()
307///     })
308///     .collect();
309///
310/// // Process channels in parallel
311/// let channel_rms: Vec<f32> = audio.par_channels()
312///     .map(|channel| {
313///         let sum_squares: f32 = channel.into_iter().map(|s| s * s).sum();
314///         (sum_squares / channel.len() as f32).sqrt()
315///     })
316///     .collect();
317/// ```
318#[cfg(feature = "parallel-processing")]
319pub trait AudioSampleParallelIterators<T: AudioSample> {
320    /// Returns a parallel iterator over frames (one sample from each channel).
321    /// # Inputs
322    /// - `&self`
323    ///
324    /// # Returns
325    /// A parallel iterator yielding owned per-frame `AudioSamples` values.
326    ///
327    /// # Panics
328    /// Does not panic.
329    ///
330    /// Each frame is processed independently in parallel, making this ideal for
331    /// frame-wise audio processing operations.
332    ///
333    /// # Examples
334    ///
335    /// ```rust
336    /// # use audio_samples::{AudioSamples, iterators::AudioSampleParallelIterators};
337    /// # use rayon::prelude::*;
338    /// # use ndarray::array;
339    /// let audio = AudioSamples::new_multi_channel(
340    ///     array![[1.0f32, 2.0], [3.0, 4.0]],
341    ///     44100
342    /// );
343    ///
344    /// let frame_energies: Vec<f32> = audio.par_frames()
345    ///     .map(|frame| frame.into_iter().map(|s| s * s).sum())
346    ///     .collect();
347    /// ```
348    fn par_frames(&self) -> ParFrameIterator<T>;
349
350    /// Returns a parallel iterator over complete channels.
351    /// # Inputs
352    /// - `&self`
353    ///
354    /// # Returns
355    /// A parallel iterator yielding owned per-channel `AudioSamples` values.
356    ///
357    /// # Panics
358    /// Does not panic.
359    ///
360    /// Each channel is processed independently in parallel, making this ideal for
361    /// channel-wise audio processing operations.
362    ///
363    /// # Examples
364    ///
365    /// ```rust
366    /// # use audio_samples::{AudioSamples, iterators::AudioSampleParallelIterators};
367    /// # use rayon::prelude::*;
368    /// # use ndarray::array;
369    /// let audio = AudioSamples::new_multi_channel(
370    ///     array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
371    ///     44100
372    /// );
373    ///
374    /// let channel_maxes: Vec<f32> = audio.par_channels()
375    ///     .map(|channel| {
376    ///         channel.into_iter().fold(f32::NEG_INFINITY, |acc, x| acc.max(x))
377    ///     })
378    ///     .collect();
379    /// ```
380    fn par_channels(&self) -> ParChannelIterator<T>;
381
382    /// Returns a parallel iterator over overlapping windows of audio data.
383    ///
384    /// Each window is processed independently in parallel, making this ideal for
385    /// windowed audio processing operations like STFT.
386    ///
387    /// # Inputs
388    /// - `window_size`: size of each window in samples (per channel)
389    /// - `hop_size`: number of samples to advance between windows
390    ///
391    /// # Returns
392    /// A parallel iterator yielding owned window `AudioSamples` values.
393    ///
394    /// # Panics
395    /// Does not panic. If `window_size == 0` or `hop_size == 0`, yields no items.
396    ///
397    /// # Examples
398    ///
399    /// ```rust
400    /// # use audio_samples::{AudioSamples, iterators::AudioSampleParallelIterators};
401    /// # use rayon::prelude::*;
402    /// # use ndarray::array;
403    /// let audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], 44100);
404    ///
405    /// let window_energies: Vec<f32> = audio.par_windows(4, 2)
406    ///     .map(|window| window.into_iter().map(|s| s * s).sum())
407    ///     .collect();
408    /// ```
409    fn par_windows(&self, window_size: usize, hop_size: usize) -> ParWindowIterator<T>;
410}
411
412impl<'a, T: AudioSample> AudioSamples<'a, T> {
413    /// Returns an iterator over frames (one sample from each channel).
414    ///
415    /// # Returns
416    /// A [`FrameIterator`] yielding borrowed frame views.
417    ///
418    /// # Panics
419    /// Does not panic.
420    pub fn frames(&'a self) -> FrameIterator<'a, T> {
421        FrameIterator::new(self)
422    }
423
424    /// Returns an iterator over complete channels.
425    ///
426    /// # Returns
427    /// A [`ChannelIterator`] yielding owned `AudioSamples` values (one per channel).
428    ///
429    /// # Panics
430    /// Does not panic.
431    pub fn channels<'iter>(&'iter self) -> ChannelIterator<'iter, 'a, T> {
432        ChannelIterator::new(self)
433    }
434
435    /// Returns an iterator over windows of audio data.
436    ///
437    /// The default padding mode is [`PaddingMode::Zero`].
438    ///
439    /// # Inputs
440    /// - `window_size`: size of each window in samples (per channel)
441    /// - `hop_size`: number of samples to advance between windows
442    ///
443    /// # Returns
444    /// A [`WindowIterator`] yielding owned windows.
445    ///
446    /// # Panics
447    /// Does not panic. If `window_size == 0` or `hop_size == 0`, the iterator yields no items.
448    pub fn windows(&'a self, window_size: usize, hop_size: usize) -> WindowIterator<'a, T> {
449        WindowIterator::new(self, window_size, hop_size)
450    }
451
452    /// Returns a mutable iterator over frames (one sample from each channel).
453    ///
454    /// # Panics
455    /// Panics if the underlying multi-channel storage is not contiguous in memory.
456    pub fn frames_mut(&'a mut self) -> FrameIteratorMut<'a, T> {
457        FrameIteratorMut::new(self)
458    }
459
460    /// Returns a mutable iterator over complete channels.
461    ///
462    /// # Panics
463    /// Panics if the underlying multi-channel storage is not contiguous in memory.
464    pub fn channels_mut(&'a mut self) -> ChannelIteratorMut<'a, T> {
465        ChannelIteratorMut::new(self)
466    }
467
468    /// Returns a mutable iterator over *non-overlapping* windows of audio data.
469    ///
470    /// # Panics
471    /// - Panics if `hop_size < window_size` **and** more than one window would be yielded.
472    /// - Panics if the underlying multi-channel storage is not contiguous in memory.
473    ///
474    /// If `window_size == 0` or `hop_size == 0`, the iterator yields no items.
475    pub fn windows_mut(
476        &'a mut self,
477        window_size: usize,
478        hop_size: usize,
479    ) -> WindowIteratorMut<'a, T> {
480        WindowIteratorMut::new(self, window_size, hop_size)
481    }
482
483    /// Applies a function to each frame without borrowing issues.
484    ///
485    /// For mono audio, the callback receives a mutable slice of length 1.
486    ///
487    /// For multi-channel audio, the callback receives a temporary buffer containing exactly one
488    /// sample per channel. The buffer is ordered by channel index and is copied back into the
489    /// underlying audio after the callback returns.
490    ///
491    /// # Inputs
492    /// - `f(frame_index, frame_samples)`
493    ///
494    /// # Returns
495    /// `()`
496    ///
497    /// # Panics
498    /// Does not panic.
499    pub fn apply_to_frames<F>(&mut self, mut f: F)
500    where
501        F: FnMut(usize, &mut [T]), // (frame_index, frame_samples)
502    {
503        match &mut self.data {
504            AudioData::Mono(arr) => {
505                for (frame_idx, sample) in arr.iter_mut().enumerate() {
506                    f(frame_idx, std::slice::from_mut(sample));
507                }
508            }
509            AudioData::Multi(arr) => {
510                let (channels, samples_per_channel) = arr.dim();
511                for frame_idx in 0..samples_per_channel {
512                    let mut frame = Vec::with_capacity(channels);
513                    for ch in 0..channels {
514                        frame.push(arr[[ch, frame_idx]]);
515                    }
516
517                    f(frame_idx, &mut frame);
518
519                    for ch in 0..channels {
520                        arr[[ch, frame_idx]] = frame[ch];
521                    }
522                }
523            }
524        }
525    }
526
527    /// Applies a function to each channel's raw data without borrowing issues.
528    ///
529    /// # Inputs
530    /// - `f(channel_index, channel_samples)`
531    ///
532    /// # Returns
533    /// `Ok(())` on success.
534    ///
535    /// # Errors
536    /// Returns [`AudioSampleError::Layout`] if the underlying storage is not contiguous.
537    pub fn try_apply_to_channel_data<F>(&mut self, mut f: F) -> crate::AudioSampleResult<()>
538    where
539        F: FnMut(usize, &mut [T]), // (channel_index, channel_samples)
540    {
541        match &mut self.data {
542            AudioData::Mono(arr) => {
543                let slice = arr.as_slice_mut();
544                f(0, slice);
545            }
546            AudioData::Multi(arr) => {
547                let (channels, samples_per_channel) = arr.dim();
548                let slice = arr.as_slice_mut().ok_or_else(|| {
549                    AudioSampleError::Layout(LayoutError::NonContiguous {
550                        operation: "multi-channel iterator access".to_string(),
551                        layout_type: "non-contiguous multi-channel data".to_string(),
552                    })
553                })?;
554
555                for ch in 0..channels {
556                    let start_idx = ch * samples_per_channel;
557                    let channel_slice = &mut slice[start_idx..start_idx + samples_per_channel];
558                    f(ch, channel_slice);
559                }
560            }
561        }
562        Ok(())
563    }
564
565    /// Applies a function to each channel's raw data without borrowing issues.
566    ///
567    /// This is the infallible counterpart to [`AudioSamples::try_apply_to_channel_data`].
568    ///
569    /// # Panics
570    /// Panics if the underlying storage is not contiguous in memory.
571    pub fn apply_to_channel_data<F>(&mut self, mut f: F)
572    where
573        F: FnMut(usize, &mut [T]), // (channel_index, channel_samples)
574    {
575        self.try_apply_to_channel_data(|ch, data| f(ch, data))
576            .expect("apply_to_channel_data requires contiguous storage; use try_apply_to_channel_data to handle non-contiguous inputs");
577    }
578
579    /// Applies a function to each window without borrowing issues.
580    ///
581    /// For mono audio, the callback receives a mutable slice into the underlying buffer.
582    ///
583    /// For multi-channel audio, the callback receives a temporary interleaved buffer of length
584    /// `window_size * num_channels` that is copied back after the callback returns.
585    ///
586    /// # Inputs
587    /// - `window_size`: size of each window in samples (per channel)
588    /// - `hop_size`: number of samples to advance between windows
589    /// - `f(window_index, window_samples)`
590    ///
591    /// # Panics
592    /// Does not panic. If `window_size == 0` or the audio is empty, this method returns
593    /// immediately.
594    pub fn apply_to_windows<F>(&mut self, window_size: usize, hop_size: usize, mut f: F)
595    where
596        F: FnMut(usize, &mut [T]), // (window_index, window_samples)
597    {
598        let total_samples = self.samples_per_channel();
599        if total_samples == 0 || window_size == 0 {
600            return;
601        }
602
603        match &mut self.data {
604            AudioData::Mono(arr) => {
605                let mut window_idx = 0;
606                let mut pos = 0;
607
608                while pos + window_size <= total_samples {
609                    let slice = arr.as_slice_mut();
610                    let window_slice = &mut slice[pos..pos + window_size];
611                    f(window_idx, window_slice);
612                    let slice = arr.as_slice_mut();
613                    let window_slice = &mut slice[pos..pos + window_size];
614                    f(window_idx, window_slice);
615                    pos += hop_size;
616                    window_idx += 1;
617                }
618            }
619            AudioData::Multi(arr) => {
620                let (rows, cols) = arr.dim();
621                let samples_per_channel = cols;
622
623                let mut pos = 0;
624                let mut window_idx = 0;
625
626                while pos + window_size <= samples_per_channel {
627                    // Create a temporary buffer for the interleaved window
628                    let mut window_data = vec![T::zero(); window_size * rows];
629
630                    // Copy window data from each channel into interleaved buffer.
631                    for ch in 0..rows {
632                        for sample_idx in 0..window_size {
633                            let dst_idx = sample_idx * rows + ch; // Interleaved layout
634                            window_data[dst_idx] = arr[[ch, pos + sample_idx]];
635                        }
636                    }
637
638                    // Call the user function
639                    f(window_idx, &mut window_data);
640
641                    // Copy modified data back to original channels.
642                    for ch in 0..rows {
643                        for sample_idx in 0..window_size {
644                            let src_idx = sample_idx * rows + ch; // Interleaved layout
645                            arr[[ch, pos + sample_idx]] = window_data[src_idx];
646                        }
647                    }
648
649                    pos += hop_size;
650                    window_idx += 1;
651                }
652            }
653        }
654    }
655}
656
657#[cfg(feature = "parallel-processing")]
658impl<'a, T: AudioSample> AudioSampleParallelIterators<T> for AudioSamples<'a, T>
659where
660    i16: ConvertTo<T>,
661    I24: ConvertTo<T>,
662    i32: ConvertTo<T>,
663    f32: ConvertTo<T>,
664    f64: ConvertTo<T>,
665{
666    fn par_frames(&self) -> ParFrameIterator<T> {
667        ParFrameIterator::new(self)
668    }
669
670    fn par_channels(&self) -> ParChannelIterator<T> {
671        ParChannelIterator::new(self)
672    }
673
674    fn par_windows(&self, window_size: usize, hop_size: usize) -> ParWindowIterator<T> {
675        // Clone to create owned data to avoid lifetime issues
676        let owned_audio = self.clone().into_owned();
677        ParWindowIterator::new(owned_audio, window_size, hop_size)
678    }
679}
680
681/// Iterator over frames of audio data.
682///
683/// A frame contains one sample from each channel at a given time point.
684/// For mono audio, frames contain single samples. For multi-channel audio,
685/// frames contain one sample per channel.
686pub struct FrameIterator<'a, T: AudioSample> {
687    /// The source audio.
688    ///
689    /// This is an immutable borrow for the iterator lifetime.
690    audio: &'a AudioSamples<'a, T>,
691    current_frame: usize,
692    total_frames: usize,
693    _phantom: PhantomData<T>,
694}
695
696impl<'a, T: AudioSample> FrameIterator<'a, T> {
697    /// Creates a new [`FrameIterator`] for the given audio.
698    ///
699    /// # Inputs
700    /// - `audio`: the source audio
701    ///
702    /// # Returns
703    /// A frame iterator over `audio`.
704    ///
705    /// # Panics
706    /// Does not panic.
707    pub fn new(audio: &'a AudioSamples<'a, T>) -> Self {
708        let total_frames = audio.samples_per_channel();
709        Self {
710            audio,
711            current_frame: 0,
712            total_frames,
713            _phantom: PhantomData,
714        }
715    }
716}
717
718impl<'a, T: AudioSample> Iterator for FrameIterator<'a, T> {
719    type Item = AudioSamples<'a, T>;
720
721    fn next(&mut self) -> Option<Self::Item> {
722        if self.current_frame >= self.total_frames {
723            return None;
724        }
725
726        let frame_range = self.current_frame..self.current_frame + 1;
727        self.current_frame += 1;
728
729        // Return a view of a single frame
730        self.audio.slice_samples(frame_range).ok()
731    }
732
733    fn size_hint(&self) -> (usize, Option<usize>) {
734        let remaining = self.total_frames - self.current_frame;
735        (remaining, Some(remaining))
736    }
737}
738
739impl<'a, T: AudioSample> ExactSizeIterator for FrameIterator<'a, T> {}
740
741/// Iterator over complete channels of audio data.
742///
743/// Each iteration yields all samples from one channel before proceeding to the next channel.
744pub struct ChannelIterator<'iter, 'data, T: AudioSample> {
745    /// The source audio.
746    audio: &'iter AudioSamples<'data, T>,
747    current_channel: usize,
748    total_channels: usize,
749}
750
751impl<'iter, 'data, T: AudioSample> ChannelIterator<'iter, 'data, T> {
752    fn new(audio: &'iter AudioSamples<'data, T>) -> Self {
753        let total_channels = audio.num_channels();
754
755        Self {
756            audio,
757            current_channel: 0,
758            total_channels,
759        }
760    }
761}
762
763impl<'iter, 'data, T: AudioSample> Iterator for ChannelIterator<'iter, 'data, T> {
764    type Item = AudioSamples<'static, T>;
765
766    fn next(&mut self) -> Option<Self::Item> {
767        if self.current_channel >= self.total_channels {
768            return None;
769        }
770
771        let channel = match self
772            .audio
773            .clone()
774            .into_owned()
775            .slice_channels(self.current_channel..self.current_channel + 1)
776        {
777            Ok(ch) => ch,
778            Err(e) => {
779                eprintln!("Error slicing channel {}: {}", self.current_channel, e);
780                return None;
781            }
782        };
783
784        self.current_channel += 1;
785
786        Some(channel)
787    }
788
789    fn size_hint(&self) -> (usize, Option<usize>) {
790        let remaining = self.total_channels - self.current_channel;
791        (remaining, Some(remaining))
792    }
793}
794
795impl<'iter, 'data, T: AudioSample> ExactSizeIterator for ChannelIterator<'iter, 'data, T> {}
796
797/// Padding strategy for window iteration when the window extends beyond available data.
798#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
799pub enum PaddingMode {
800    /// Zero-pad incomplete windows to maintain consistent window size.
801    #[default]
802    Zero,
803    /// Return incomplete windows without padding (variable size).
804    None,
805    /// Skip incomplete windows entirely.
806    Skip,
807}
808
809/// Iterator over overlapping windows of audio data.
810///
811/// This iterator supports various windowing strategies including overlap and padding.
812/// It's optimized for audio processing operations like STFT that require consistent
813/// window sizes with configurable overlap.
814pub struct WindowIterator<'a, T: AudioSample> {
815    /// The source audio.
816    audio: &'a AudioSamples<'a, T>,
817    window_size: usize,
818    hop_size: usize,
819    current_position: usize,
820    total_samples: usize,
821    total_windows: usize,
822    current_window: usize,
823    padding_mode: PaddingMode,
824    _phantom: PhantomData<T>,
825}
826
827impl<'a, T: AudioSample> WindowIterator<'a, T> {
828    fn new(audio: &'a AudioSamples<'a, T>, window_size: usize, hop_size: usize) -> Self {
829        let total_samples = audio.samples_per_channel();
830
831        // Degenerate parameters: yield nothing.
832        let total_windows = if window_size == 0 || hop_size == 0 {
833            0
834        } else {
835            // Calculate total number of windows based on default padding mode (Zero)
836            Self::calculate_total_windows(total_samples, window_size, hop_size, PaddingMode::Zero)
837        };
838
839        Self {
840            audio,
841            window_size,
842            hop_size,
843            current_position: 0,
844            total_samples,
845            total_windows,
846            current_window: 0,
847            padding_mode: PaddingMode::Zero,
848            _phantom: PhantomData,
849        }
850    }
851
852    const fn calculate_total_windows(
853        total_samples: usize,
854        window_size: usize,
855        hop_size: usize,
856        padding_mode: PaddingMode,
857    ) -> usize {
858        if total_samples == 0 || window_size == 0 || hop_size == 0 {
859            return 0;
860        }
861
862        // Calculate the maximum number of windows we could have
863        // This is the ceiling of total_samples / hop_size
864        let max_windows = total_samples.div_ceil(hop_size);
865
866        match padding_mode {
867            PaddingMode::Zero => {
868                // With zero padding, we can always create max_windows
869                max_windows
870            }
871            PaddingMode::None => {
872                // With no padding, count windows that have at least some real data
873                let mut count = 0;
874                let mut pos = 0;
875                while pos < total_samples {
876                    count += 1;
877                    pos += hop_size;
878                }
879                count
880            }
881            PaddingMode::Skip => {
882                // With skip, only count complete windows
883                if total_samples < window_size {
884                    0
885                } else {
886                    1 + (total_samples - window_size) / hop_size
887                }
888            }
889        }
890    }
891
892    /// Set the padding mode for this iterator.
893    pub const fn with_padding_mode(mut self, mode: PaddingMode) -> Self {
894        self.padding_mode = mode;
895
896        // Recalculate total windows based on padding mode
897        self.total_windows = Self::calculate_total_windows(
898            self.total_samples,
899            self.window_size,
900            self.hop_size,
901            mode,
902        );
903
904        self
905    }
906}
907
908impl<'a, T: AudioSample> Iterator for WindowIterator<'a, T>
909where
910    i16: ConvertTo<T>,
911    I24: ConvertTo<T>,
912    i32: ConvertTo<T>,
913    f32: ConvertTo<T>,
914    f64: ConvertTo<T>,
915{
916    type Item = AudioSamples<'static, T>;
917
918    fn next(&mut self) -> Option<Self::Item> {
919        if self.current_window >= self.total_windows {
920            return None;
921        }
922
923        let start_pos = self.current_position;
924        let end_pos = start_pos + self.window_size;
925
926        let window = if end_pos <= self.total_samples {
927            // Complete window within bounds
928            self.audio
929                .slice_samples(start_pos..end_pos)
930                .ok()
931                .map(|w| w.into_owned())
932        } else {
933            // Window extends beyond available data
934            match self.padding_mode {
935                PaddingMode::Zero => {
936                    // Zero-pad to maintain consistent window size
937                    let available_samples = self.total_samples.saturating_sub(start_pos);
938                    match &self.audio.data {
939                        AudioData::Mono(_) => {
940                            // Add available samples
941                            let starting_slice = if available_samples > 0 {
942                                let slice = self
943                                    .audio
944                                    .slice_samples(start_pos..self.total_samples)
945                                    .ok()?
946                                    .into_owned();
947                                Some(slice)
948                            } else {
949                                None
950                            };
951
952                            let silence_samples = self.window_size - available_samples;
953                            let silence = if silence_samples > 0 {
954                                let silence = AudioSamples::<T>::zeros_mono(
955                                    silence_samples,
956                                    self.audio.sample_rate,
957                                );
958                                Some(silence)
959                            } else {
960                                return starting_slice;
961                            };
962
963                            match (starting_slice, silence) {
964                                (None, None) => None,
965                                (None, Some(silence)) => Some(silence),
966                                (Some(starting_slice), None) => Some(starting_slice),
967                                (Some(s), Some(z)) => {
968                                    let slices = vec![s, z];
969                                    Some(AudioSamples::concatenate_owned(slices).ok()?)
970                                }
971                            }
972                        }
973                        AudioData::Multi(_) => {
974                            let interleaved_slice = if available_samples > 0 {
975                                let slice = self
976                                    .audio
977                                    .slice_samples(start_pos..self.total_samples)
978                                    .ok()?
979                                    .into_owned();
980                                Some(slice)
981                            } else {
982                                None
983                            };
984
985                            // Zero-pad remainder
986                            let remaining_samples = self.window_size - available_samples;
987
988                            if remaining_samples == 0 {
989                                return interleaved_slice;
990                            }
991
992                            let silence = AudioSamples::<T>::zeros_multi_channel(
993                                self.audio.num_channels(),
994                                remaining_samples,
995                                self.audio.sample_rate,
996                            );
997
998                            match interleaved_slice {
999                                None => Some(silence),
1000                                Some(slice) => {
1001                                    AudioSamples::concatenate_owned(vec![slice, silence]).ok()
1002                                }
1003                            }
1004                        }
1005                    }
1006                }
1007                PaddingMode::None => {
1008                    // Return available samples without padding
1009                    let available_samples = self.total_samples.saturating_sub(start_pos);
1010                    if available_samples == 0 {
1011                        return None;
1012                    }
1013
1014                    self.audio
1015                        .slice_samples(start_pos..self.total_samples)
1016                        .ok()
1017                        .map(|w| w.into_owned())
1018                }
1019                PaddingMode::Skip => {
1020                    // Skip incomplete windows
1021                    return None;
1022                }
1023            }
1024        };
1025
1026        self.current_position += self.hop_size;
1027        self.current_window += 1;
1028        window
1029    }
1030
1031    fn size_hint(&self) -> (usize, Option<usize>) {
1032        let remaining = self.total_windows - self.current_window;
1033        (remaining, Some(remaining))
1034    }
1035}
1036
1037impl<'a, T: AudioSample> ExactSizeIterator for WindowIterator<'a, T>
1038where
1039    i16: ConvertTo<T>,
1040    I24: ConvertTo<T>,
1041    i32: ConvertTo<T>,
1042    f32: ConvertTo<T>,
1043    f64: ConvertTo<T>,
1044{
1045}
1046
1047// ==============================
1048// PARALLEL ITERATORS
1049// ==============================
1050
1051#[cfg(feature = "parallel-processing")]
1052/// Parallel iterator over frames of audio data.
1053///
1054/// This iterator processes frames in parallel using rayon's parallel iterator framework.
1055/// Each frame contains one sample from each channel at a given time point.
1056pub struct ParFrameIterator<T: AudioSample> {
1057    /// Owned audio data to ensure thread safety
1058    frames_data: Vec<Vec<T>>,
1059    sample_rate: NonZeroU32,
1060    num_channels: usize,
1061}
1062
1063#[cfg(feature = "parallel-processing")]
1064impl<T: AudioSample> ParFrameIterator<T> {
1065    fn new(audio: &AudioSamples<'_, T>) -> Self {
1066        let num_frames = audio.samples_per_channel();
1067        let num_channels = audio.num_channels();
1068        let mut frames_data = Vec::with_capacity(num_frames);
1069
1070        // Pre-compute all frames for parallel processing
1071        for frame_idx in 0..num_frames {
1072            let mut frame = Vec::with_capacity(num_channels);
1073            match &audio.data {
1074                AudioData::Mono(arr) => {
1075                    frame.push(arr[frame_idx]);
1076                }
1077                AudioData::Multi(arr) => {
1078                    for ch in 0..num_channels {
1079                        frame.push(arr[[ch, frame_idx]]);
1080                    }
1081                }
1082            }
1083            frames_data.push(frame);
1084        }
1085
1086        Self {
1087            frames_data,
1088            sample_rate: audio.sample_rate,
1089            num_channels,
1090        }
1091    }
1092}
1093
1094#[cfg(feature = "parallel-processing")]
1095impl<T: AudioSample> ParallelIterator for ParFrameIterator<T> {
1096    type Item = AudioSamples<'static, T>;
1097
1098    fn drive_unindexed<C>(self, consumer: C) -> C::Result
1099    where
1100        C: rayon::iter::plumbing::UnindexedConsumer<Self::Item>,
1101    {
1102        let num_channels = self.num_channels;
1103        let sample_rate = self.sample_rate;
1104        let par_iter = self.frames_data.into_par_iter().map(move |frame| {
1105            if num_channels == 1 {
1106                AudioSamples::new_mono(Array1::from_vec(frame), sample_rate)
1107            } else {
1108                // Create multi-channel frame with 1 sample per channel
1109                let mut frame_array = Array2::zeros((num_channels, 1));
1110                for (ch, &sample) in frame.iter().enumerate() {
1111                    frame_array[[ch, 0]] = sample;
1112                }
1113                AudioSamples::new_multi_channel(frame_array, sample_rate)
1114            }
1115        });
1116        par_iter.drive_unindexed(consumer)
1117    }
1118
1119    fn opt_len(&self) -> Option<usize> {
1120        Some(self.frames_data.len())
1121    }
1122}
1123
1124#[cfg(feature = "parallel-processing")]
1125impl<T: AudioSample> IndexedParallelIterator for ParFrameIterator<T> {
1126    fn drive<C>(self, consumer: C) -> C::Result
1127    where
1128        C: rayon::iter::plumbing::Consumer<Self::Item>,
1129    {
1130        let num_channels = self.num_channels;
1131        let sample_rate = self.sample_rate;
1132        let par_iter = self.frames_data.into_par_iter().map(move |frame| {
1133            if num_channels == 1 {
1134                AudioSamples::new_mono(Array1::from_vec(frame), sample_rate)
1135            } else {
1136                // Create multi-channel frame with 1 sample per channel
1137                let mut frame_array = Array2::zeros((num_channels, 1));
1138                for (ch, &sample) in frame.iter().enumerate() {
1139                    frame_array[[ch, 0]] = sample;
1140                }
1141                AudioSamples::new_multi_channel(frame_array, sample_rate)
1142            }
1143        });
1144        par_iter.drive(consumer)
1145    }
1146
1147    fn len(&self) -> usize {
1148        self.frames_data.len()
1149    }
1150
1151    fn with_producer<CB>(self, callback: CB) -> CB::Output
1152    where
1153        CB: rayon::iter::plumbing::ProducerCallback<Self::Item>,
1154    {
1155        let num_channels = self.num_channels;
1156        let sample_rate = self.sample_rate;
1157        let par_iter = self.frames_data.into_par_iter().map(move |frame| {
1158            if num_channels == 1 {
1159                AudioSamples::new_mono(Array1::from_vec(frame), sample_rate)
1160            } else {
1161                // Create multi-channel frame with 1 sample per channel
1162                let mut frame_array = Array2::zeros((num_channels, 1));
1163                for (ch, &sample) in frame.iter().enumerate() {
1164                    frame_array[[ch, 0]] = sample;
1165                }
1166                AudioSamples::new_multi_channel(frame_array, sample_rate)
1167            }
1168        });
1169        par_iter.with_producer(callback)
1170    }
1171}
1172
1173#[cfg(feature = "parallel-processing")]
1174/// Parallel iterator over complete channels of audio data.
1175///
1176/// This iterator processes channels in parallel using rayon's parallel iterator framework.
1177/// Each iteration yields all samples from one channel.
1178pub struct ParChannelIterator<T: AudioSample> {
1179    /// Owned channel data to ensure thread safety
1180    channels_data: Vec<Vec<T>>,
1181    sample_rate: NonZeroU32,
1182}
1183
1184#[cfg(feature = "parallel-processing")]
1185impl<T: AudioSample> ParChannelIterator<T> {
1186    fn new(audio: &AudioSamples<'_, T>) -> Self {
1187        let num_channels = audio.num_channels();
1188        let mut channels_data = Vec::with_capacity(num_channels);
1189
1190        // Pre-compute all channels for parallel processing
1191        for ch in 0..num_channels {
1192            let channel_data = match &audio.data {
1193                AudioData::Mono(arr) => arr.to_vec(),
1194                AudioData::Multi(arr) => arr.row(ch).to_vec(),
1195            };
1196            channels_data.push(channel_data);
1197        }
1198
1199        Self {
1200            channels_data,
1201            sample_rate: audio.sample_rate,
1202        }
1203    }
1204}
1205
1206#[cfg(feature = "parallel-processing")]
1207impl<T: AudioSample> ParallelIterator for ParChannelIterator<T> {
1208    type Item = AudioSamples<'static, T>;
1209
1210    fn drive_unindexed<C>(self, consumer: C) -> C::Result
1211    where
1212        C: rayon::iter::plumbing::UnindexedConsumer<Self::Item>,
1213    {
1214        let par_iter = self
1215            .channels_data
1216            .into_par_iter()
1217            .map(|channel| AudioSamples::new_mono(Array1::from_vec(channel), self.sample_rate));
1218        par_iter.drive_unindexed(consumer)
1219    }
1220
1221    fn opt_len(&self) -> Option<usize> {
1222        Some(self.channels_data.len())
1223    }
1224}
1225
1226#[cfg(feature = "parallel-processing")]
1227impl<T: AudioSample> IndexedParallelIterator for ParChannelIterator<T> {
1228    fn drive<C>(self, consumer: C) -> C::Result
1229    where
1230        C: rayon::iter::plumbing::Consumer<Self::Item>,
1231    {
1232        let par_iter = self
1233            .channels_data
1234            .into_par_iter()
1235            .map(|channel| AudioSamples::new_mono(Array1::from_vec(channel), self.sample_rate));
1236        par_iter.drive(consumer)
1237    }
1238
1239    fn len(&self) -> usize {
1240        self.channels_data.len()
1241    }
1242
1243    fn with_producer<CB>(self, callback: CB) -> CB::Output
1244    where
1245        CB: rayon::iter::plumbing::ProducerCallback<Self::Item>,
1246    {
1247        let par_iter = self
1248            .channels_data
1249            .into_par_iter()
1250            .map(|channel| AudioSamples::new_mono(Array1::from_vec(channel), self.sample_rate));
1251        par_iter.with_producer(callback)
1252    }
1253}
1254
1255#[cfg(feature = "parallel-processing")]
1256/// Parallel iterator over overlapping windows of audio data.
1257///
1258/// This iterator processes windows in parallel using rayon's parallel iterator framework.
1259/// Each window supports various windowing strategies including overlap and padding.
1260pub struct ParWindowIterator<T: AudioSample> {
1261    /// Owned window data to ensure thread safety
1262    windows_data: Vec<AudioSamples<'static, T>>,
1263}
1264
1265#[cfg(feature = "parallel-processing")]
1266impl<T: AudioSample> ParWindowIterator<T>
1267where
1268    i16: ConvertTo<T>,
1269    I24: ConvertTo<T>,
1270    i32: ConvertTo<T>,
1271    f32: ConvertTo<T>,
1272    f64: ConvertTo<T>,
1273{
1274    fn new(audio: AudioSamples<'static, T>, window_size: usize, hop_size: usize) -> Self {
1275        Self::new_with_mode(audio, window_size, hop_size, PaddingMode::Zero)
1276    }
1277
1278    /// Set the padding mode for this parallel iterator.
1279    ///
1280    /// # Inputs
1281    /// - `audio`: owned audio to window
1282    /// - `window_size`: size of each window in samples (per channel)
1283    /// - `hop_size`: number of samples to advance between windows
1284    /// - `mode`: padding mode
1285    ///
1286    /// # Returns
1287    /// A parallel iterator over windows.
1288    ///
1289    /// # Panics
1290    /// Does not panic. If `window_size == 0` or `hop_size == 0`, yields no items.
1291    pub fn with_padding_mode(
1292        audio: AudioSamples<'static, T>,
1293        window_size: usize,
1294        hop_size: usize,
1295        mode: PaddingMode,
1296    ) -> Self {
1297        Self::new_with_mode(audio, window_size, hop_size, mode)
1298    }
1299
1300    fn new_with_mode(
1301        audio: AudioSamples<'static, T>,
1302        window_size: usize,
1303        hop_size: usize,
1304        padding_mode: PaddingMode,
1305    ) -> Self {
1306        let mut windows_data = Vec::new();
1307
1308        let samples_per_channel = audio.samples_per_channel();
1309        let num_channels = audio.num_channels();
1310
1311        if samples_per_channel == 0 || window_size == 0 || hop_size == 0 {
1312            return Self { windows_data };
1313        }
1314
1315        // Calculate total windows based on padding mode
1316        let total_windows = match padding_mode {
1317            PaddingMode::Zero => samples_per_channel.div_ceil(hop_size),
1318            PaddingMode::None => {
1319                let mut count = 0;
1320                let mut pos = 0;
1321                while pos + window_size <= samples_per_channel {
1322                    count += 1;
1323                    pos += hop_size;
1324                }
1325                // Add partial window if there are remaining samples
1326                if pos < samples_per_channel {
1327                    count += 1;
1328                }
1329                count
1330            }
1331            PaddingMode::Skip => {
1332                let mut count = 0;
1333                let mut pos = 0;
1334                while pos + window_size <= samples_per_channel {
1335                    count += 1;
1336                    pos += hop_size;
1337                }
1338                count
1339            }
1340        };
1341
1342        for window_idx in 0..total_windows {
1343            let start = window_idx * hop_size;
1344            let end = start + window_size;
1345
1346            let window_data = match padding_mode {
1347                PaddingMode::Zero => {
1348                    // Always create full-size windows with zero padding
1349                    match &audio.data {
1350                        AudioData::Mono(data) => {
1351                            if end <= samples_per_channel {
1352                                let window_slice = data.slice(s![start..end]);
1353                                AudioData::Mono(window_slice.to_owned().into())
1354                            } else {
1355                                let mut window_vec = vec![T::default(); window_size];
1356                                let available_samples = samples_per_channel.saturating_sub(start);
1357                                if available_samples > 0 {
1358                                    let data_slice = data.slice(s![start..samples_per_channel]);
1359                                    if let Some(slice) = data_slice.as_slice() {
1360                                        window_vec[..available_samples].copy_from_slice(slice);
1361                                    } else {
1362                                        // Fall back to element-wise copy for non-contiguous arrays
1363                                        for (i, &val) in data_slice.iter().enumerate() {
1364                                            window_vec[i] = val;
1365                                        }
1366                                    }
1367                                }
1368                                AudioData::Mono(Array1::from(window_vec).into())
1369                            }
1370                        }
1371                        AudioData::Multi(data) => {
1372                            if end <= samples_per_channel {
1373                                let window_slice = data.slice(s![.., start..end]);
1374                                AudioData::Multi(window_slice.to_owned().into())
1375                            } else {
1376                                let mut window_array = Array2::zeros((num_channels, window_size));
1377                                let available_samples = samples_per_channel.saturating_sub(start);
1378                                if available_samples > 0 {
1379                                    let data_slice = data.slice(s![.., start..samples_per_channel]);
1380                                    window_array
1381                                        .slice_mut(s![.., ..available_samples])
1382                                        .assign(&data_slice);
1383                                }
1384                                AudioData::Multi(window_array.into())
1385                            }
1386                        }
1387                    }
1388                }
1389                PaddingMode::None => {
1390                    // Create windows with actual available samples, no padding
1391                    let actual_end = end.min(samples_per_channel);
1392
1393                    match &audio.data {
1394                        AudioData::Mono(data) => {
1395                            let window_slice = data.slice(s![start..actual_end]);
1396                            AudioData::Mono(window_slice.to_owned().into())
1397                        }
1398                        AudioData::Multi(data) => {
1399                            let window_slice = data.slice(s![.., start..actual_end]);
1400                            AudioData::Multi(window_slice.to_owned().into())
1401                        }
1402                    }
1403                }
1404                PaddingMode::Skip => {
1405                    // Only create full-size windows, skip incomplete ones
1406                    if end <= samples_per_channel {
1407                        match &audio.data {
1408                            AudioData::Mono(data) => {
1409                                let window_slice = data.slice(s![start..end]);
1410                                AudioData::Mono(window_slice.to_owned().into())
1411                            }
1412                            AudioData::Multi(data) => {
1413                                let window_slice = data.slice(s![.., start..end]);
1414                                AudioData::Multi(window_slice.to_owned().into())
1415                            }
1416                        }
1417                    } else {
1418                        continue; // Skip this incomplete window
1419                    }
1420                }
1421            };
1422
1423            let layout = match &window_data {
1424                AudioData::Mono(_) => crate::ChannelLayout::NonInterleaved,
1425                AudioData::Multi(_) => crate::ChannelLayout::Interleaved,
1426            };
1427
1428            let window_samples = AudioSamples {
1429                data: window_data,
1430                sample_rate: audio.sample_rate(),
1431                layout,
1432            };
1433
1434            windows_data.push(window_samples);
1435        }
1436
1437        Self { windows_data }
1438    }
1439}
1440
1441#[cfg(feature = "parallel-processing")]
1442impl<T: AudioSample> ParallelIterator for ParWindowIterator<T> {
1443    type Item = AudioSamples<'static, T>;
1444
1445    fn drive_unindexed<C>(self, consumer: C) -> C::Result
1446    where
1447        C: rayon::iter::plumbing::UnindexedConsumer<Self::Item>,
1448    {
1449        self.windows_data.into_par_iter().drive_unindexed(consumer)
1450    }
1451
1452    fn opt_len(&self) -> Option<usize> {
1453        Some(self.windows_data.len())
1454    }
1455}
1456
1457#[cfg(feature = "parallel-processing")]
1458impl<T: AudioSample> IndexedParallelIterator for ParWindowIterator<T> {
1459    fn drive<C>(self, consumer: C) -> C::Result
1460    where
1461        C: rayon::iter::plumbing::Consumer<Self::Item>,
1462    {
1463        self.windows_data.into_par_iter().drive(consumer)
1464    }
1465
1466    fn len(&self) -> usize {
1467        self.windows_data.len()
1468    }
1469
1470    fn with_producer<CB>(self, callback: CB) -> CB::Output
1471    where
1472        CB: rayon::iter::plumbing::ProducerCallback<Self::Item>,
1473    {
1474        self.windows_data.into_par_iter().with_producer(callback)
1475    }
1476}
1477
1478// ==============================
1479// MUTABLE ITERATORS
1480// ==============================
1481
1482/// Mutable iterator over frames of audio data.
1483///
1484/// This iterator provides mutable access to frames, allowing in-place modification
1485/// of audio samples. Each frame contains one sample from each channel at a given time point.
1486///
1487/// # Invariants
1488/// - For multi-channel audio, the underlying storage must be contiguous in memory.
1489/// - Each yielded frame refers to disjoint samples from all other yielded frames.
1490///
1491/// # Panics
1492/// Panics if multi-channel storage is not contiguous.
1493///
1494/// # Performance Notes
1495///
1496/// - Use this for frame-wise processing where you need to modify samples across channels
1497/// - For simple element-wise operations, prefer `AudioSamples::apply()` which uses optimized `mapv_inplace`
1498/// - This iterator is ideal for operations like panning, stereo effects, or cross-channel processing
1499pub struct FrameIteratorMut<'a, T: AudioSample> {
1500    /// Raw data pointer (extracted at creation time to avoid holding entire borrow)
1501    data_ptr: *mut T,
1502    /// Layout information
1503    layout: IteratorLayout,
1504    current_frame: usize,
1505    total_frames: usize,
1506    num_channels: usize,
1507    _phantom: PhantomData<&'a mut T>,
1508}
1509
1510/// Layout information for mutable iterators
1511#[derive(Clone)]
1512enum IteratorLayout {
1513    Mono {
1514        samples: usize,
1515    },
1516    MultiChannel {
1517        channels: usize,
1518        samples_per_channel: usize,
1519    },
1520}
1521
1522impl<'a, T: AudioSample> FrameIteratorMut<'a, T> {
1523    fn new(audio: &'a mut AudioSamples<'a, T>) -> Self {
1524        let total_frames = audio.samples_per_channel();
1525        let num_channels = audio.num_channels();
1526
1527        // Extract the raw pointer and layout info to avoid holding the borrow
1528        let (data_ptr, layout) = match &mut audio.data {
1529            AudioData::Mono(arr) => (
1530                arr.as_slice_mut().as_mut_ptr(),
1531                IteratorLayout::Mono {
1532                    samples: total_frames,
1533                },
1534            ),
1535            AudioData::Multi(arr) => {
1536                let slice = arr
1537                    .as_slice_mut()
1538                    .expect("mutable frame iteration requires contiguous multi-channel storage");
1539                (
1540                    slice.as_mut_ptr(),
1541                    IteratorLayout::MultiChannel {
1542                        channels: num_channels,
1543                        samples_per_channel: total_frames,
1544                    },
1545                )
1546            }
1547        };
1548
1549        Self {
1550            data_ptr,
1551            layout,
1552            current_frame: 0,
1553            total_frames,
1554            num_channels,
1555            _phantom: PhantomData,
1556        }
1557    }
1558}
1559
1560impl<'a, T: AudioSample> Iterator for FrameIteratorMut<'a, T> {
1561    type Item = FrameMut<'a, T>;
1562
1563    fn next(&mut self) -> Option<Self::Item> {
1564        if self.current_frame >= self.total_frames {
1565            return None;
1566        }
1567
1568        // SAFETY: We use unsafe here to extract multiple mutable references to different parts
1569        // of the audio data. This is safe because:
1570        // 1. Each frame accesses non-overlapping memory locations
1571        // 2. We ensure frame bounds are within the audio data
1572        // 3. The FrameMut lifetime is tied to the iterator lifetime
1573        // 4. The raw pointer was obtained from a valid mutable reference
1574        let frame = unsafe {
1575            match &self.layout {
1576                IteratorLayout::Mono { .. } => {
1577                    let ptr = self.data_ptr.add(self.current_frame);
1578                    FrameMut::Mono(std::slice::from_raw_parts_mut(ptr, 1))
1579                }
1580                IteratorLayout::MultiChannel {
1581                    channels: _,
1582                    samples_per_channel,
1583                } => {
1584                    let mut ptrs = Vec::with_capacity(self.num_channels);
1585                    for ch in 0..self.num_channels {
1586                        let ptr = self
1587                            .data_ptr
1588                            .add(ch * samples_per_channel + self.current_frame);
1589                        ptrs.push(ptr);
1590                    }
1591                    FrameMut::MultiChannel(ptrs, self.num_channels)
1592                }
1593            }
1594        };
1595
1596        self.current_frame += 1;
1597        Some(frame)
1598    }
1599
1600    fn size_hint(&self) -> (usize, Option<usize>) {
1601        let remaining = self.total_frames - self.current_frame;
1602        (remaining, Some(remaining))
1603    }
1604}
1605
1606impl<'a, T: AudioSample> ExactSizeIterator for FrameIteratorMut<'a, T> {}
1607
1608/// A mutable frame containing samples from all channels at a given time point.
1609pub enum FrameMut<'a, T: AudioSample> {
1610    /// Single-channel (mono) frame
1611    Mono(&'a mut [T]),
1612    /// Multi-channel frame with channel pointers and channel count
1613    MultiChannel(Vec<*mut T>, usize),
1614}
1615
1616impl<'a, T: AudioSample> FrameMut<'a, T> {
1617    /// Returns the number of channels (samples) in this frame.
1618    pub const fn len(&self) -> usize {
1619        match self {
1620            FrameMut::Mono(_) => 1,
1621            FrameMut::MultiChannel(_, channels) => *channels,
1622        }
1623    }
1624
1625    /// Returns true if the frame is empty (should not happen in practice).
1626    pub const fn is_empty(&self) -> bool {
1627        self.len() == 0
1628    }
1629
1630    /// Get a mutable reference to the sample for a specific channel.
1631    pub fn get_mut(&mut self, channel: usize) -> Option<&mut T> {
1632        match self {
1633            FrameMut::Mono(slice) => {
1634                if channel == 0 {
1635                    slice.get_mut(0)
1636                } else {
1637                    None
1638                }
1639            }
1640            FrameMut::MultiChannel(ptrs, channels) => {
1641                if channel < *channels {
1642                    // SAFETY: The pointers were created from valid mutable references to
1643                    // non-overlapping array elements in FrameIteratorMut::next(). The channel
1644                    // bound is verified above, and the FrameMut lifetime ensures the pointers
1645                    // remain valid. Each channel's pointer points to a distinct memory location.
1646                    Some(unsafe { &mut *ptrs[channel] })
1647                } else {
1648                    None
1649                }
1650            }
1651        }
1652    }
1653
1654    /// Apply a function to all samples in this frame.
1655    pub fn apply<F>(&mut self, f: F)
1656    where
1657        F: Fn(T) -> T,
1658    {
1659        match self {
1660            FrameMut::Mono(slice) => {
1661                for sample in slice.iter_mut() {
1662                    *sample = f(*sample);
1663                }
1664            }
1665            FrameMut::MultiChannel(ptrs, channels) => {
1666                for ptr in ptrs.iter_mut().take(*channels) {
1667                    // SAFETY: Pointers were created from valid mutable references in
1668                    // FrameIteratorMut::next(). Each pointer points to a distinct sample
1669                    // (different channel, same frame). We only access each pointer once per
1670                    // call to this function, so no aliasing occurs within the loop.
1671                    unsafe {
1672                        let sample_ref = &mut **ptr;
1673                        *sample_ref = f(*sample_ref);
1674                    }
1675                }
1676            }
1677        }
1678    }
1679
1680    /// Apply a function with channel index to all samples in this frame.
1681    pub fn apply_with_channel<F>(&mut self, f: F)
1682    where
1683        F: Fn(usize, T) -> T,
1684    {
1685        match self {
1686            FrameMut::Mono(slice) => {
1687                for sample in slice.iter_mut() {
1688                    *sample = f(0, *sample);
1689                }
1690            }
1691            FrameMut::MultiChannel(ptrs, channels) => {
1692                for (ch, ptr) in ptrs.iter_mut().enumerate().take(*channels) {
1693                    // SAFETY: Pointers are valid for the frame lifetime
1694                    unsafe {
1695                        let sample_ref = &mut **ptr;
1696                        *sample_ref = f(ch, *sample_ref);
1697                    }
1698                }
1699            }
1700        }
1701    }
1702}
1703
1704/// Mutable iterator over complete channels of audio data.
1705///
1706/// This iterator provides mutable access to entire channels, allowing efficient
1707/// channel-wise processing operations.
1708///
1709/// # Invariants
1710/// - For multi-channel audio, the underlying storage must be contiguous in memory.
1711/// - Each yielded channel slice is disjoint from all other yielded channel slices.
1712///
1713/// # Panics
1714/// Panics if multi-channel storage is not contiguous.
1715///
1716/// # Performance Notes
1717///
1718/// - Use this for channel-specific processing (e.g., different EQ per channel)
1719/// - For simple element-wise operations on all channels, prefer `AudioSamples::apply()`
1720/// - This iterator is ideal for operations like channel-specific effects or balance adjustments
1721pub struct ChannelIteratorMut<'a, T: AudioSample> {
1722    /// Raw data pointer (extracted at creation time to avoid holding entire borrow)
1723    data_ptr: *mut T,
1724    /// Layout information
1725    layout: IteratorLayout,
1726    current_channel: usize,
1727    total_channels: usize,
1728    _phantom: PhantomData<&'a mut T>,
1729}
1730
1731impl<'a, T: AudioSample> ChannelIteratorMut<'a, T> {
1732    fn new(audio: &'a mut AudioSamples<'a, T>) -> Self {
1733        let total_channels = audio.num_channels();
1734        let total_frames = audio.samples_per_channel();
1735
1736        // Extract the raw pointer and layout info to avoid holding the borrow
1737        let (data_ptr, layout) = match &mut audio.data {
1738            AudioData::Mono(arr) => (
1739                arr.as_slice_mut().as_mut_ptr(),
1740                IteratorLayout::Mono {
1741                    samples: total_frames,
1742                },
1743            ),
1744            AudioData::Multi(arr) => {
1745                let slice = arr
1746                    .as_slice_mut()
1747                    .expect("mutable channel iteration requires contiguous multi-channel storage");
1748                (
1749                    slice.as_mut_ptr(),
1750                    IteratorLayout::MultiChannel {
1751                        channels: total_channels,
1752                        samples_per_channel: total_frames,
1753                    },
1754                )
1755            }
1756        };
1757
1758        Self {
1759            data_ptr,
1760            layout,
1761            current_channel: 0,
1762            total_channels,
1763            _phantom: PhantomData,
1764        }
1765    }
1766}
1767
1768impl<'a, T: AudioSample> Iterator for ChannelIteratorMut<'a, T> {
1769    type Item = &'a mut [T];
1770
1771    fn next(&mut self) -> Option<Self::Item> {
1772        if self.current_channel >= self.total_channels {
1773            return None;
1774        }
1775
1776        // SAFETY: We use unsafe here to extract mutable references to different channels.
1777        // This is safe because:
1778        // 1. Each channel occupies non-overlapping memory
1779        // 2. We ensure channel bounds are within the audio data
1780        // 3. The returned slice lifetime is tied to the iterator lifetime
1781        // 4. The raw pointer was obtained from a valid mutable reference
1782        let channel_slice = unsafe {
1783            match &self.layout {
1784                IteratorLayout::Mono { samples } => {
1785                    // For mono, there's only one channel
1786                    if self.current_channel == 0 {
1787                        std::slice::from_raw_parts_mut(self.data_ptr, *samples)
1788                    } else {
1789                        // Mono only has one channel, so if we're asking for channel 1+, return None
1790                        return None;
1791                    }
1792                }
1793                IteratorLayout::MultiChannel {
1794                    samples_per_channel,
1795                    ..
1796                } => {
1797                    // Get mutable access to the specific channel row
1798                    let ptr = self
1799                        .data_ptr
1800                        .add(self.current_channel * samples_per_channel);
1801                    std::slice::from_raw_parts_mut(ptr, *samples_per_channel)
1802                }
1803            }
1804        };
1805
1806        self.current_channel += 1;
1807        Some(channel_slice)
1808    }
1809
1810    fn size_hint(&self) -> (usize, Option<usize>) {
1811        let remaining = self.total_channels - self.current_channel;
1812        (remaining, Some(remaining))
1813    }
1814}
1815
1816impl<'a, T: AudioSample> ExactSizeIterator for ChannelIteratorMut<'a, T> {}
1817
1818/// Mutable iterator over non-overlapping windows of audio data.
1819///
1820/// This iterator provides mutable access to windows of audio data, useful for
1821/// processing operations that work on blocks of samples like FFT, filtering, etc.
1822///
1823/// Unlike [`WindowIterator`], this mutable iterator does **not** support overlapping windows
1824/// or padding. It only yields complete, non-overlapping windows.
1825///
1826/// # Performance Notes
1827///
1828/// - Use this for windowed operations like STFT, filtering, or block-based effects
1829/// - For simple element-wise operations, prefer `AudioSamples::apply()`
1830/// - For overlapping in-place processing, use [`AudioSamples::apply_to_windows`]
1831///
1832/// # Panics
1833/// - Panics if `hop_size < window_size` **and** more than one window would be yielded.
1834/// - Panics if multi-channel storage is not contiguous.
1835pub struct WindowIteratorMut<'a, T: AudioSample> {
1836    /// Raw data pointer (extracted at creation time to avoid holding entire borrow)
1837    data_ptr: *mut T,
1838    /// Layout information
1839    layout: IteratorLayout,
1840    window_size: usize,
1841    hop_size: usize,
1842    current_position: usize,
1843    total_samples: usize,
1844    total_windows: usize,
1845    current_window: usize,
1846    padding_mode: PaddingMode,
1847    _phantom: PhantomData<&'a mut T>,
1848}
1849
1850impl<'a, T: AudioSample> WindowIteratorMut<'a, T> {
1851    fn new(audio: &'a mut AudioSamples<'a, T>, window_size: usize, hop_size: usize) -> Self {
1852        let total_samples = audio.samples_per_channel();
1853        let num_channels = audio.num_channels();
1854        let padding_mode = PaddingMode::Skip;
1855        let total_windows = if window_size == 0 || hop_size == 0 {
1856            0
1857        } else {
1858            Self::calculate_total_windows(total_samples, window_size, hop_size, padding_mode)
1859        };
1860
1861        if hop_size != 0 && window_size != 0 && hop_size < window_size && total_windows > 1 {
1862            panic!(
1863                "windows_mut requires hop_size >= window_size when more than one window would be yielded"
1864            );
1865        }
1866
1867        // Extract the raw pointer and layout info to avoid holding the borrow
1868        let (data_ptr, layout) = match &mut audio.data {
1869            AudioData::Mono(arr) => (
1870                arr.as_slice_mut().as_mut_ptr(),
1871                IteratorLayout::Mono {
1872                    samples: total_samples,
1873                },
1874            ),
1875            AudioData::Multi(arr) => {
1876                let slice = arr
1877                    .as_slice_mut()
1878                    .expect("mutable window iteration requires contiguous multi-channel storage");
1879                (
1880                    slice.as_mut_ptr(),
1881                    IteratorLayout::MultiChannel {
1882                        channels: num_channels,
1883                        samples_per_channel: total_samples,
1884                    },
1885                )
1886            }
1887        };
1888
1889        Self {
1890            data_ptr,
1891            layout,
1892            window_size,
1893            hop_size,
1894            current_position: 0,
1895            total_samples,
1896            total_windows,
1897            current_window: 0,
1898            padding_mode,
1899            _phantom: PhantomData,
1900        }
1901    }
1902
1903    const fn calculate_total_windows(
1904        total_samples: usize,
1905        window_size: usize,
1906        hop_size: usize,
1907        padding_mode: PaddingMode,
1908    ) -> usize {
1909        if total_samples == 0 || window_size == 0 || hop_size == 0 {
1910            return 0;
1911        }
1912
1913        let max_windows = total_samples.div_ceil(hop_size);
1914
1915        match padding_mode {
1916            PaddingMode::Zero => max_windows,
1917            PaddingMode::None => {
1918                let mut count = 0;
1919                let mut pos = 0;
1920                while pos < total_samples {
1921                    count += 1;
1922                    pos += hop_size;
1923                }
1924                count
1925            }
1926            PaddingMode::Skip => {
1927                if total_samples < window_size {
1928                    0
1929                } else {
1930                    1 + (total_samples - window_size) / hop_size
1931                }
1932            }
1933        }
1934    }
1935
1936    /// Set the padding mode for this iterator.
1937    ///
1938    /// Note: the mutable window iterator only yields complete windows (equivalent to
1939    /// [`PaddingMode::Skip`]). Other modes are accepted for API compatibility but are
1940    /// currently treated as `Skip`.
1941    pub const fn with_padding_mode(mut self, _mode: PaddingMode) -> Self {
1942        self.padding_mode = PaddingMode::Skip;
1943        self.total_windows = Self::calculate_total_windows(
1944            self.total_samples,
1945            self.window_size,
1946            self.hop_size,
1947            self.padding_mode,
1948        );
1949        self
1950    }
1951}
1952
1953impl<'a, T: AudioSample> Iterator for WindowIteratorMut<'a, T> {
1954    type Item = WindowMut<'a, T>;
1955
1956    fn next(&mut self) -> Option<Self::Item> {
1957        if self.current_window >= self.total_windows {
1958            return None;
1959        }
1960
1961        let start_pos = self.current_position;
1962        let end_pos = start_pos + self.window_size;
1963
1964        // For mutable windows, we need to be more careful about overlapping regions
1965        // For now, implement non-overlapping windows for safety
1966        if end_pos <= self.total_samples {
1967            // Complete window within bounds
1968            // SAFETY: We verify that end_pos <= total_samples, so all pointer arithmetic
1969            // stays within the bounds of the original array. The raw pointer data_ptr was
1970            // obtained from a valid mutable reference. For multi-channel data, each channel
1971            // is at offset ch * samples_per_channel, and we access start_pos..end_pos within
1972            // each channel's region, which is non-overlapping across channels.
1973            let window = unsafe {
1974                debug_assert!(!self.data_ptr.is_null(), "data pointer is null");
1975                debug_assert!(end_pos <= self.total_samples, "window end exceeds bounds");
1976                match &self.layout {
1977                    IteratorLayout::Mono { .. } => {
1978                        let ptr = self.data_ptr.add(start_pos);
1979                        let slice = std::slice::from_raw_parts_mut(ptr, self.window_size);
1980                        WindowMut::Mono(slice)
1981                    }
1982                    IteratorLayout::MultiChannel {
1983                        channels,
1984                        samples_per_channel,
1985                    } => {
1986                        let mut channel_ptrs = Vec::with_capacity(*channels);
1987                        for ch in 0..*channels {
1988                            let ptr = self.data_ptr.add(ch * samples_per_channel + start_pos);
1989                            channel_ptrs.push(ptr);
1990                        }
1991                        WindowMut::MultiChannel(channel_ptrs, *channels, self.window_size)
1992                    }
1993                }
1994            };
1995
1996            self.current_position += self.hop_size;
1997            self.current_window += 1;
1998            Some(window)
1999        } else {
2000            // Handle padding cases - for now, skip incomplete windows in mutable iterator
2001            // to avoid complexity of handling padding in mutable context
2002            None
2003        }
2004    }
2005
2006    fn size_hint(&self) -> (usize, Option<usize>) {
2007        let remaining = self.total_windows - self.current_window;
2008        (remaining, Some(remaining))
2009    }
2010}
2011
2012impl<'a, T: AudioSample> ExactSizeIterator for WindowIteratorMut<'a, T> {}
2013
2014/// A mutable window of audio data.
2015pub enum WindowMut<'a, T: AudioSample> {
2016    /// Single-channel (mono) window
2017    Mono(&'a mut [T]),
2018    /// Multi-channel window with channel pointers, channel count, and window size
2019    MultiChannel(Vec<*mut T>, usize, usize), // (channel_ptrs, num_channels, window_size)
2020}
2021
2022impl<'a, T: AudioSample> WindowMut<'a, T> {
2023    /// Returns the window size.
2024    pub const fn len(&self) -> usize {
2025        match self {
2026            WindowMut::Mono(slice) => slice.len(),
2027            WindowMut::MultiChannel(_, _, window_size) => *window_size,
2028        }
2029    }
2030
2031    /// Returns true if the window is empty.
2032    pub const fn is_empty(&self) -> bool {
2033        self.len() == 0
2034    }
2035
2036    /// Returns the number of channels in this window.
2037    pub const fn num_channels(&self) -> usize {
2038        match self {
2039            WindowMut::Mono(_) => 1,
2040            WindowMut::MultiChannel(_, channels, _) => *channels,
2041        }
2042    }
2043
2044    /// Returns a mutable slice for a specific channel.
2045    ///
2046    /// # Inputs
2047    /// - `channel`: channel index
2048    ///
2049    /// # Returns
2050    /// `Some(&mut [T])` for an in-bounds channel index, otherwise `None`.
2051    pub fn channel_mut(&mut self, channel: usize) -> Option<&mut [T]> {
2052        match self {
2053            WindowMut::Mono(slice) => {
2054                if channel == 0 {
2055                    Some(*slice)
2056                } else {
2057                    None
2058                }
2059            }
2060            WindowMut::MultiChannel(ptrs, channels, window_size) => {
2061                if channel < *channels {
2062                    // SAFETY: The pointers were created from valid mutable references in
2063                    // WindowIteratorMut::next(). Each channel's pointer points to a distinct
2064                    // region of the array (offset by ch * samples_per_channel). The window_size
2065                    // was verified during iterator construction to be within bounds. The
2066                    // WindowMut lifetime ensures the pointers remain valid.
2067                    Some(unsafe { std::slice::from_raw_parts_mut(ptrs[channel], *window_size) })
2068                } else {
2069                    None
2070                }
2071            }
2072        }
2073    }
2074
2075    /// Applies a function to all samples in this window.
2076    ///
2077    /// # Inputs
2078    /// - `f`: a pure function applied to each sample
2079    pub fn apply<F>(&mut self, f: F)
2080    where
2081        F: Fn(T) -> T + Copy,
2082    {
2083        match self {
2084            WindowMut::Mono(slice) => {
2085                for sample in slice.iter_mut() {
2086                    *sample = f(*sample);
2087                }
2088            }
2089            WindowMut::MultiChannel(ptrs, channels, window_size) => {
2090                for ch in ptrs.iter().take(*channels) {
2091                    // SAFETY: Pointers were obtained from valid mutable references in
2092                    // WindowIteratorMut::next(). Each channel's pointer is distinct
2093                    // (different ch * samples_per_channel offset). The window_size
2094                    // was validated during iterator construction.
2095                    unsafe {
2096                        let channel_slice = std::slice::from_raw_parts_mut(*ch, *window_size);
2097                        for sample in channel_slice.iter_mut() {
2098                            *sample = f(*sample);
2099                        }
2100                    }
2101                }
2102            }
2103        }
2104    }
2105
2106    /// Applies a window function to all samples.
2107    ///
2108    /// # Inputs
2109    /// - `window_fn(index, window_size) -> T`: returns the window multiplier
2110    pub fn apply_window_function<F>(&mut self, window_fn: F)
2111    where
2112        F: Fn(usize, usize) -> T + Copy, // (index, window_size) -> window_value
2113        T: std::ops::MulAssign,
2114    {
2115        let window_size = self.len();
2116        match self {
2117            WindowMut::Mono(slice) => {
2118                for (i, sample) in slice.iter_mut().enumerate() {
2119                    *sample *= window_fn(i, window_size);
2120                }
2121            }
2122            WindowMut::MultiChannel(ptrs, channels, _) => {
2123                for ch in ptrs.iter().take(*channels) {
2124                    // SAFETY: Same as apply() - pointers are from WindowIteratorMut::next()
2125                    // and point to distinct, non-overlapping channel regions. The window_size
2126                    // is captured at construction and remains valid.
2127                    unsafe {
2128                        let channel_slice = std::slice::from_raw_parts_mut(*ch, window_size);
2129                        for (i, sample) in channel_slice.iter_mut().enumerate() {
2130                            *sample *= window_fn(i, window_size);
2131                        }
2132                    }
2133                }
2134            }
2135        }
2136    }
2137}
2138
2139#[cfg(test)]
2140mod tests {
2141    use super::*;
2142    use crate::AudioSamples;
2143    use crate::sample_rate;
2144    use ndarray::{Array1, array};
2145
2146    #[test]
2147    fn test_frame_iterator_mono() {
2148        let audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0], sample_rate!(44100));
2149        audio
2150            .frames()
2151            .zip([1.0f32, 2.0, 3.0, 4.0, 5.0])
2152            .for_each(|(f, x)| {
2153                assert_eq!(f.to_interleaved_vec(), vec![x]);
2154            });
2155    }
2156
2157    #[test]
2158    fn test_frame_iterator_stereo() {
2159        let audio = AudioSamples::new_multi_channel(
2160            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2161            sample_rate!(44100),
2162        );
2163
2164        // Borrowing-first behavior: work with frames directly, don't collect into Vec
2165        let expected_frames = vec![vec![1.0, 4.0], vec![2.0, 5.0], vec![3.0, 6.0]];
2166
2167        for (i, frame) in audio.frames().enumerate() {
2168            assert_eq!(frame.to_interleaved_vec(), expected_frames[i]);
2169        }
2170    }
2171
2172    #[test]
2173    fn test_channel_iterator_mono() {
2174        let audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0], sample_rate!(44100));
2175
2176        // Borrowing-first behavior: work with channels directly
2177        let mut channel_count = 0;
2178        for channel in audio.channels() {
2179            channel_count += 1;
2180            assert_eq!(channel.to_interleaved_vec(), vec![1.0, 2.0, 3.0, 4.0]);
2181        }
2182        assert_eq!(channel_count, 1);
2183    }
2184
2185    #[test]
2186    fn test_channel_iterator_stereo() {
2187        let audio = AudioSamples::new_multi_channel(
2188            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2189            sample_rate!(44100),
2190        );
2191
2192        // Borrowing-first behavior: work with channels directly
2193        let expected_channels = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]];
2194
2195        for (i, channel) in audio.channels().enumerate() {
2196            assert_eq!(channel.as_mono().unwrap().to_vec(), expected_channels[i]);
2197        }
2198    }
2199
2200    #[test]
2201    fn test_window_iterator_no_overlap() {
2202        let audio =
2203            AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], sample_rate!(44100));
2204        let windows: Vec<AudioSamples<f32>> = audio.windows(3, 3).collect();
2205
2206        assert_eq!(windows.len(), 2);
2207        assert_eq!(windows[0].as_mono().unwrap().to_vec(), vec![1.0, 2.0, 3.0]);
2208        assert_eq!(windows[1].as_mono().unwrap().to_vec(), vec![4.0, 5.0, 6.0]);
2209    }
2210
2211    #[test]
2212    fn test_window_iterator_with_overlap() {
2213        let audio =
2214            AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], sample_rate!(44100));
2215        let windows: Vec<AudioSamples<f32>> = audio.windows(4, 2).collect();
2216
2217        // For 6 samples, window_size=4, hop_size=2:
2218        // Window 1: position 0-3 (samples 0,1,2,3)
2219        // Window 2: position 2-5 (samples 2,3,4,5)
2220        // Window 3: position 4-7 (samples 4,5 + 2 zeros for padding)
2221        assert_eq!(windows.len(), 3);
2222        assert_eq!(
2223            windows[0].as_mono().unwrap().to_vec(),
2224            vec![1.0, 2.0, 3.0, 4.0]
2225        );
2226        assert_eq!(
2227            windows[1].as_mono().unwrap().to_vec(),
2228            vec![3.0, 4.0, 5.0, 6.0]
2229        );
2230        assert_eq!(
2231            windows[2].as_mono().unwrap().to_vec(),
2232            vec![5.0, 6.0, 0.0, 0.0]
2233        );
2234    }
2235
2236    #[test]
2237    fn test_window_iterator_zero_padding() {
2238        let audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0], sample_rate!(44100));
2239        let windows: Vec<AudioSamples<f32>> = audio
2240            .windows(4, 3)
2241            .with_padding_mode(PaddingMode::Zero)
2242            .collect();
2243
2244        assert_eq!(windows.len(), 2);
2245        assert_eq!(
2246            windows[0].as_mono().unwrap().to_vec(),
2247            vec![1.0, 2.0, 3.0, 4.0]
2248        );
2249        assert_eq!(
2250            windows[1].as_mono().unwrap().to_vec(),
2251            vec![4.0, 5.0, 0.0, 0.0]
2252        ); // Zero-padded
2253    }
2254
2255    #[test]
2256    fn test_window_iterator_no_padding() {
2257        let audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0], sample_rate!(44100));
2258        let windows: Vec<AudioSamples<f32>> = audio
2259            .windows(4, 3)
2260            .with_padding_mode(PaddingMode::None)
2261            .collect();
2262
2263        assert_eq!(windows.len(), 2);
2264        assert_eq!(
2265            windows[0].as_mono().unwrap().to_vec(),
2266            vec![1.0, 2.0, 3.0, 4.0]
2267        );
2268        assert_eq!(windows[1].as_mono().unwrap().to_vec(), vec![4.0, 5.0]); // Incomplete window
2269    }
2270
2271    #[test]
2272    fn test_window_iterator_skip_padding() {
2273        let audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0], sample_rate!(44100));
2274        let windows: Vec<AudioSamples<f32>> = audio
2275            .windows(4, 3)
2276            .with_padding_mode(PaddingMode::Skip)
2277            .collect();
2278
2279        assert_eq!(windows.len(), 1);
2280        assert_eq!(
2281            windows[0].as_mono().unwrap().to_vec(),
2282            vec![1.0, 2.0, 3.0, 4.0]
2283        );
2284    }
2285
2286    #[test]
2287    fn test_window_iterator_stereo_interleaved() {
2288        let audio = AudioSamples::new_multi_channel(
2289            array![[1.0f32, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]],
2290            sample_rate!(44100),
2291        );
2292        let windows: Vec<AudioSamples<f32>> = audio.windows(2, 2).collect();
2293
2294        assert_eq!(windows.len(), 2);
2295        // First window: samples 0,1 interleaved across channels
2296        assert_eq!(windows[0].to_interleaved_vec(), vec![1.0, 5.0, 2.0, 6.0]);
2297        // Second window: samples 2,3 interleaved across channels
2298        assert_eq!(windows[1].to_interleaved_vec(), vec![3.0, 7.0, 4.0, 8.0]);
2299    }
2300
2301    #[test]
2302    fn test_exact_size_iterators() {
2303        let audio = AudioSamples::new_multi_channel(
2304            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2305            sample_rate!(44100),
2306        );
2307
2308        let frame_iter = audio.frames();
2309        assert_eq!(frame_iter.len(), 3);
2310
2311        let channel_iter = audio.channels();
2312        assert_eq!(channel_iter.len(), 2);
2313
2314        let window_iter = audio.windows(2, 1);
2315        assert_eq!(window_iter.len(), 3); // (3-2)/1 + 1 = 2, plus padding = 3
2316    }
2317
2318    #[test]
2319    fn test_multiple_iterators_from_same_audio() {
2320        // This test verifies that our raw pointer approach allows multiple iterators
2321        let audio = AudioSamples::new_multi_channel(
2322            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2323            sample_rate!(44100),
2324        );
2325
2326        // This should compile and work correctly
2327        let frames = audio.frames();
2328        let channels = audio.channels();
2329        let windows = audio.windows(2, 1);
2330
2331        // Verify they all work independently
2332        assert_eq!(frames.len(), 3);
2333        assert_eq!(channels.len(), 2);
2334        assert_eq!(windows.len(), 3);
2335    }
2336
2337    // ==============================
2338    // MUTABLE ITERATOR TESTS
2339    // ==============================
2340
2341    #[test]
2342    fn test_frame_iterator_mut_stereo() {
2343        let mut audio = AudioSamples::new_multi_channel(
2344            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2345            sample_rate!(44100),
2346        );
2347
2348        let expected = array![[0.5f32, 1.0, 1.5], [6.0, 7.5, 9.0]];
2349
2350        // Apply different processing to each channel
2351        audio.apply_to_channel_data(|ch, channel_data| {
2352            let gain = if ch == 0 { 0.5 } else { 1.5 };
2353            for sample in channel_data {
2354                *sample *= gain;
2355            }
2356        });
2357
2358        assert_eq!(audio.as_multi_channel().unwrap(), &expected);
2359    }
2360
2361    #[test]
2362    fn test_frame_iterator_mut_individual_access() {
2363        let mut audio =
2364            AudioSamples::new_multi_channel(array![[1.0f32, 2.0], [3.0, 4.0]], sample_rate!(44100));
2365
2366        let expected = array![[10.0f32, 20.0], [3.0, 4.0]];
2367
2368        // Modify only the left channel (channel 0)
2369        audio.apply_to_channel_data(|ch, channel_data| {
2370            if ch == 0 {
2371                for sample in channel_data {
2372                    *sample *= 10.0;
2373                }
2374            }
2375            // Leave right channel unchanged
2376        });
2377
2378        assert_eq!(audio.as_multi_channel().unwrap(), &expected);
2379    }
2380
2381    #[test]
2382    fn test_channel_iterator_mut_mono() {
2383        let mut audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0], sample_rate!(44100));
2384
2385        audio.apply_to_channel_data(|_ch, channel_data| {
2386            for sample in channel_data {
2387                *sample += 10.0;
2388            }
2389        });
2390
2391        assert_eq!(audio.as_mono().unwrap(), &array![11.0f32, 12.0, 13.0, 14.0]);
2392    }
2393
2394    #[test]
2395    fn test_channel_iterator_mut_stereo() {
2396        let mut audio = AudioSamples::new_multi_channel(
2397            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2398            sample_rate!(44100),
2399        );
2400
2401        let expected = array![[0.5f32, 1.0, 1.5], [8.0, 10.0, 12.0]];
2402
2403        // Apply different processing to each channel
2404        audio.apply_to_channel_data(|ch, channel_data| {
2405            let gain = if ch == 0 { 0.5 } else { 2.0 };
2406            for sample in channel_data {
2407                *sample *= gain;
2408            }
2409        });
2410
2411        assert_eq!(audio.as_multi_channel().unwrap(), &expected);
2412    }
2413
2414    #[test]
2415    fn test_window_iterator_mut_mono() {
2416        let mut audio =
2417            AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], sample_rate!(44100));
2418
2419        // Apply windowed processing (non-overlapping)
2420        audio.apply_to_windows(3, 3, |_window_idx, window_data| {
2421            for sample in window_data {
2422                *sample *= 0.5;
2423            }
2424        });
2425
2426        assert_eq!(
2427            audio.as_mono().unwrap(),
2428            &array![0.5f32, 1.0, 1.5, 2.0, 2.5, 3.0]
2429        );
2430    }
2431
2432    #[test]
2433    fn test_window_iterator_mut_stereo() {
2434        let mut audio = AudioSamples::new_multi_channel(
2435            array![[1.0f32, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]],
2436            sample_rate!(44100),
2437        );
2438
2439        let expected = array![[0.8f32, 1.6, 2.4, 3.2], [6.0, 7.2, 8.4, 9.6]];
2440
2441        // Apply windowed processing (non-overlapping)
2442        // For multi-channel, apply_to_windows provides interleaved data
2443        audio.apply_to_windows(2, 2, |_window_idx, window_data| {
2444            // window_data is interleaved: [L0, R0, L1, R1, ...] for 2-sample window
2445            let samples_per_channel = window_data.len() / 2; // 2 channels
2446            for sample_idx in 0..samples_per_channel {
2447                let left_idx = sample_idx * 2;
2448                let right_idx = sample_idx * 2 + 1;
2449                window_data[left_idx] *= 0.8; // Left channel gain
2450                window_data[right_idx] *= 1.2; // Right channel gain
2451            }
2452        });
2453
2454        let result = audio.as_multi_channel().unwrap();
2455        for (i, (&actual, &expected)) in result.iter().zip(expected.iter()).enumerate() {
2456            assert!(
2457                (actual - expected).abs() < 1e-6,
2458                "Mismatch at index {}: {} != {} (diff: {})",
2459                i,
2460                actual,
2461                expected,
2462                (actual - expected).abs()
2463            );
2464        }
2465    }
2466
2467    #[test]
2468    fn test_window_function_application() {
2469        let mut audio = AudioSamples::new_mono(array![1.0f32, 1.0, 1.0, 1.0], sample_rate!(44100));
2470
2471        // Apply Hann window function
2472        audio.apply_to_windows(4, 4, |_window_idx, window_data| {
2473            let window_size = window_data.len();
2474            for (i, sample) in window_data.iter_mut().enumerate() {
2475                let hann_weight = 0.5
2476                    * (1.0
2477                        - (2.0 * std::f32::consts::PI * i as f32 / (window_size - 1) as f32).cos());
2478                *sample *= hann_weight;
2479            }
2480        });
2481
2482        // Check that Hann window was applied (values should be different from 1.0)
2483        let result = audio.as_mono().unwrap();
2484        assert!(result[0] < 1.0); // Should be close to 0
2485        assert!(result[1] > 0.5); // Should be around 0.75
2486        assert!(result[2] > 0.5); // Should be around 0.75
2487        assert!(result[3] < 1.0); // Should be close to 0
2488    }
2489
2490    #[test]
2491    fn test_mutable_iterator_size_hints() {
2492        let mut audio = AudioSamples::new_multi_channel(
2493            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2494            sample_rate!(44100),
2495        );
2496
2497        let frame_iter = audio.frames_mut();
2498        assert_eq!(frame_iter.len(), 3);
2499
2500        // For channel iterator, we need a fresh audio instance since frame_iter consumed the mutable borrow
2501        let mut audio2 = AudioSamples::new_multi_channel(
2502            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2503            sample_rate!(44100),
2504        );
2505        let channel_iter = audio2.channels_mut();
2506        assert_eq!(channel_iter.len(), 2);
2507
2508        // For window iterator, we need another fresh audio instance
2509        let mut audio3 = AudioSamples::new_multi_channel(
2510            array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2511            sample_rate!(44100),
2512        );
2513        let window_iter = audio3.windows_mut(2, 2);
2514        assert_eq!(window_iter.len(), 1);
2515        let actual_windows: Vec<_> = window_iter.collect();
2516        assert_eq!(actual_windows.len(), 1);
2517    }
2518
2519    #[test]
2520    fn test_performance_comparison_apply_vs_iterator() {
2521        // This test demonstrates when to use each approach
2522        let mut audio1 = AudioSamples::new_mono(Array1::<f32>::ones(1000), sample_rate!(44100));
2523        let mut audio2 = audio1.clone();
2524
2525        // Method 1: Using optimized apply (recommended for simple operations)
2526        audio1.apply(|sample| sample * 0.5);
2527
2528        // Method 2: Using convenience method (alternative for complex operations)
2529        audio2.apply_to_frames(|_frame_idx, frame_data| {
2530            for sample in frame_data {
2531                *sample *= 0.5;
2532            }
2533        });
2534
2535        // Results should be identical
2536        assert_eq!(audio1.as_mono().unwrap(), audio2.as_mono().unwrap());
2537    }
2538
2539    #[test]
2540    fn test_frame_mut_edge_cases() {
2541        let mut audio = AudioSamples::new_mono(array![1.0f32], sample_rate!(44100));
2542
2543        for mut frame in audio.frames_mut() {
2544            assert_eq!(frame.len(), 1);
2545            assert!(!frame.is_empty());
2546            assert!(frame.get_mut(0).is_some());
2547            assert!(frame.get_mut(1).is_none());
2548        }
2549    }
2550
2551    #[test]
2552    fn test_window_mut_edge_cases() {
2553        let mut audio = AudioSamples::new_mono(array![1.0f32, 2.0], sample_rate!(44100));
2554
2555        // Window larger than audio
2556        let windows: Vec<_> = audio.windows_mut(5, 1).collect();
2557        assert_eq!(windows.len(), 0); // Should not produce any windows
2558
2559        // Test window bounds
2560        let mut audio2 = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0], sample_rate!(44100));
2561        for window in audio2.windows_mut(2, 2) {
2562            assert_eq!(window.len(), 2);
2563            assert!(!window.is_empty());
2564            assert_eq!(window.num_channels(), 1);
2565        }
2566    }
2567
2568    // ==============================
2569    // PARALLEL ITERATOR TESTS
2570    // ==============================
2571
2572    #[cfg(feature = "parallel-processing")]
2573    mod parallel_tests {
2574        use super::*;
2575        use crate::iterators::AudioSampleParallelIterators;
2576        use ndarray::Array2;
2577
2578        #[test]
2579        fn test_par_frame_iterator_mono() {
2580            let audio =
2581                AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0], sample_rate!(44100));
2582
2583            let frame_energies: Vec<f32> = audio
2584                .par_frames()
2585                .map(|frame| frame.as_mono().unwrap().iter().map(|&s| s * s).sum())
2586                .collect();
2587
2588            assert_eq!(frame_energies.len(), 5);
2589            assert_eq!(frame_energies, vec![1.0, 4.0, 9.0, 16.0, 25.0]);
2590        }
2591
2592        #[test]
2593        fn test_par_frame_iterator_stereo() {
2594            let audio = AudioSamples::new_multi_channel(
2595                array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2596                sample_rate!(44100),
2597            );
2598
2599            let frame_sums: Vec<f32> = audio
2600                .par_frames()
2601                .map(|frame| match &frame.data {
2602                    AudioData::Mono(m) => m.iter().sum(),
2603                    AudioData::Multi(m) => m.iter().sum(),
2604                })
2605                .collect();
2606
2607            assert_eq!(frame_sums.len(), 3);
2608            assert_eq!(frame_sums[0], 5.0); // 1.0 + 4.0
2609            assert_eq!(frame_sums[1], 7.0); // 2.0 + 5.0
2610            assert_eq!(frame_sums[2], 9.0); // 3.0 + 6.0
2611        }
2612
2613        #[test]
2614        fn test_par_channel_iterator_mono() {
2615            let audio = AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0], sample_rate!(44100));
2616
2617            let channel_rms: Vec<f32> = audio
2618                .par_channels()
2619                .map(|channel| {
2620                    let samples = channel.as_mono().unwrap();
2621                    let sum_squares: f32 = samples.iter().map(|&s| s * s).sum();
2622                    (sum_squares / samples.len() as f32).sqrt()
2623                })
2624                .collect();
2625
2626            assert_eq!(channel_rms.len(), 1);
2627            // RMS of [1, 2, 3, 4] = sqrt((1+4+9+16)/4) = sqrt(30/4) = sqrt(7.5)
2628            assert!((channel_rms[0] - (7.5f32).sqrt()).abs() < 1e-6);
2629        }
2630
2631        #[test]
2632        fn test_par_channel_iterator_stereo() {
2633            let audio = AudioSamples::new_multi_channel(
2634                array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2635                sample_rate!(44100),
2636            );
2637
2638            let channel_maxes: Vec<f32> = audio
2639                .par_channels()
2640                .map(|channel| {
2641                    channel
2642                        .as_mono()
2643                        .unwrap()
2644                        .iter()
2645                        .fold(f32::NEG_INFINITY, |acc, &x| acc.max(x))
2646                })
2647                .collect();
2648
2649            assert_eq!(channel_maxes.len(), 2);
2650            assert_eq!(channel_maxes[0], 3.0); // Max of [1, 2, 3]
2651            assert_eq!(channel_maxes[1], 6.0); // Max of [4, 5, 6]
2652        }
2653
2654        #[test]
2655        fn test_par_window_iterator_no_overlap() {
2656            let audio = AudioSamples::new_mono(
2657                array![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0],
2658                sample_rate!(44100),
2659            );
2660
2661            let window_sums: Vec<f32> = audio
2662                .par_windows(3, 3)
2663                .map(|window| window.as_mono().unwrap().iter().sum())
2664                .collect();
2665
2666            assert_eq!(window_sums.len(), 2);
2667            assert_eq!(window_sums[0], 6.0); // 1 + 2 + 3
2668            assert_eq!(window_sums[1], 15.0); // 4 + 5 + 6
2669        }
2670
2671        #[test]
2672        fn test_par_window_iterator_with_overlap() {
2673            let audio = AudioSamples::new_mono(
2674                array![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0],
2675                sample_rate!(44100),
2676            );
2677
2678            let window_sums: Vec<f32> = audio
2679                .par_windows(4, 2)
2680                .map(|window| window.as_mono().unwrap().iter().sum())
2681                .collect();
2682
2683            assert_eq!(window_sums.len(), 3);
2684            assert_eq!(window_sums[0], 10.0); // 1 + 2 + 3 + 4
2685            assert_eq!(window_sums[1], 18.0); // 3 + 4 + 5 + 6
2686            assert_eq!(window_sums[2], 11.0); // 5 + 6 + 0 + 0 (zero-padded)
2687        }
2688
2689        #[test]
2690        fn test_par_window_iterator_with_padding_modes() {
2691            let audio =
2692                AudioSamples::new_mono(array![1.0f32, 2.0, 3.0, 4.0, 5.0], sample_rate!(44100));
2693
2694            // Test with zero padding (default)
2695            let zero_padded: Vec<f32> = audio
2696                .par_windows(4, 3)
2697                .map(|window| window.as_mono().unwrap().len() as f32)
2698                .collect();
2699            assert_eq!(zero_padded, vec![4.0, 4.0]); // Both windows have size 4
2700
2701            // Test with no padding
2702            let no_padding =
2703                ParWindowIterator::with_padding_mode(audio.clone(), 4, 3, PaddingMode::None);
2704            let no_pad_lens: Vec<f32> = no_padding
2705                .map(|window| window.as_mono().unwrap().len() as f32)
2706                .collect();
2707            assert_eq!(no_pad_lens, vec![4.0, 2.0]); // Second window incomplete
2708
2709            // Test with skip padding
2710            let skip_padding =
2711                ParWindowIterator::with_padding_mode(audio.clone(), 4, 3, PaddingMode::Skip);
2712            let skip_lens: Vec<f32> = skip_padding
2713                .map(|window| window.as_mono().unwrap().len() as f32)
2714                .collect();
2715            assert_eq!(skip_lens, vec![4.0]); // Only complete windows
2716        }
2717
2718        #[test]
2719        fn test_parallel_vs_sequential_consistency() {
2720            let audio = AudioSamples::new_multi_channel(
2721                array![[1.0f32, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]],
2722                sample_rate!(44100),
2723            );
2724
2725            // Compare frame processing
2726            let seq_frame_sums: Vec<f32> = audio
2727                .frames()
2728                .map(|frame| {
2729                    // Sum all samples in the frame across all channels
2730                    match &frame.data {
2731                        AudioData::Mono(data) => data.iter().sum(),
2732                        AudioData::Multi(data) => data.iter().sum(),
2733                    }
2734                })
2735                .collect();
2736
2737            let par_frame_sums: Vec<f32> = audio
2738                .par_frames()
2739                .map(|frame| {
2740                    // Sum all samples in the frame across all channels
2741                    match &frame.data {
2742                        AudioData::Mono(data) => data.iter().sum(),
2743                        AudioData::Multi(data) => data.iter().sum(),
2744                    }
2745                })
2746                .collect();
2747
2748            assert_eq!(seq_frame_sums, par_frame_sums);
2749
2750            // Compare channel processing
2751            let seq_channel_sums: Vec<f32> = audio
2752                .channels()
2753                .map(|channel| {
2754                    // Channels should be mono, sum all samples
2755                    match &channel.data {
2756                        AudioData::Mono(data) => data.iter().sum(),
2757                        AudioData::Multi(data) => data.iter().sum(),
2758                    }
2759                })
2760                .collect();
2761
2762            let par_channel_sums: Vec<f32> = audio
2763                .par_channels()
2764                .map(|channel| {
2765                    // Channels should be mono, sum all samples
2766                    match &channel.data {
2767                        AudioData::Mono(data) => data.iter().sum(),
2768                        AudioData::Multi(data) => data.iter().sum(),
2769                    }
2770                })
2771                .collect();
2772
2773            assert_eq!(seq_channel_sums, par_channel_sums);
2774
2775            // Compare window processing
2776            let seq_window_sums: Vec<f32> = audio
2777                .windows(2, 1)
2778                .map(|window| match window.as_multi_channel() {
2779                    Some(arr) => arr.iter().sum(),
2780                    None => window.as_mono().unwrap().iter().sum(),
2781                })
2782                .collect();
2783
2784            let par_window_sums: Vec<f32> = audio
2785                .par_windows(2, 1)
2786                .map(|window| match window.as_multi_channel() {
2787                    Some(arr) => arr.iter().sum(),
2788                    None => window.as_mono().unwrap().iter().sum(),
2789                })
2790                .collect();
2791
2792            assert_eq!(seq_window_sums, par_window_sums);
2793        }
2794
2795        #[test]
2796        fn test_parallel_iterator_properties() {
2797            let audio = AudioSamples::new_multi_channel(
2798                array![[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0]],
2799                sample_rate!(44100),
2800            );
2801
2802            // Test that parallel iterators implement IndexedParallelIterator
2803            let frame_iter = audio.par_frames();
2804            assert_eq!(frame_iter.len(), 3);
2805
2806            let channel_iter = audio.par_channels();
2807            assert_eq!(channel_iter.len(), 2);
2808
2809            let window_iter = audio.par_windows(2, 1);
2810            assert_eq!(window_iter.len(), 3); // (3-2)/1 + 1 = 2, plus padding = 3
2811        }
2812
2813        #[test]
2814        fn test_parallel_iterator_with_min_len() {
2815            let audio = AudioSamples::new_mono(
2816                (0..1000).map(|i| i as f32).collect::<Array1<f32>>(),
2817                sample_rate!(44100),
2818            );
2819
2820            // Test that with_min_len works (rayon feature)
2821            let result: Vec<f32> = audio
2822                .par_frames()
2823                .with_min_len(10) // Minimum 10 items per thread
2824                .map(|frame| frame.as_mono().unwrap()[0] * 2.0)
2825                .collect();
2826
2827            assert_eq!(result.len(), 1000);
2828            assert_eq!(result[0], 0.0);
2829            assert_eq!(result[999], 1998.0);
2830        }
2831
2832        #[test]
2833        fn test_complex_parallel_processing() {
2834            let audio = AudioSamples::new_multi_channel(
2835                Array2::from_shape_fn((2, 1000), |(ch, sample)| {
2836                    (ch as f32 + 1.0) * (sample as f32 + 1.0)
2837                }),
2838                sample_rate!(44100),
2839            );
2840
2841            // Complex processing: compute spectral centroid per window in parallel
2842            let window_centroids: Vec<f32> = audio
2843                .par_windows(64, 32)
2844                .map(|window| {
2845                    let samples: Vec<f32> = match window.as_multi_channel() {
2846                        Some(arr) => arr.iter().copied().collect(),
2847                        None => window.as_mono().unwrap().to_vec(),
2848                    };
2849                    let mut weighted_sum = 0.0f32;
2850                    let mut magnitude_sum = 0.0f32;
2851
2852                    for (i, &sample) in samples.iter().enumerate() {
2853                        let magnitude = sample.abs();
2854                        weighted_sum += (i as f32) * magnitude;
2855                        magnitude_sum += magnitude;
2856                    }
2857
2858                    if magnitude_sum > 0.0 {
2859                        weighted_sum / magnitude_sum
2860                    } else {
2861                        0.0
2862                    }
2863                })
2864                .collect();
2865
2866            assert!(!window_centroids.is_empty());
2867            // Verify that we got reasonable centroid values
2868            // For multi-channel data, the effective window size is channels * window_size
2869            let max_centroid = 2.0 * 64.0; // 2 channels * 64 samples
2870            for &centroid in &window_centroids {
2871                assert!(
2872                    centroid >= 0.0 && centroid < max_centroid,
2873                    "Centroid {} not in range [0, {})",
2874                    centroid,
2875                    max_centroid
2876                );
2877            }
2878        }
2879    }
2880}