1use 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
68pub trait AudioSampleIterators<'a, T: AudioSample> {
70 fn frames(&'a self) -> FrameIterator<'a, T>;
102
103 fn channels<'iter>(&'iter self) -> ChannelIterator<'iter, 'a, T>;
134
135 fn windows(&'a self, window_size: usize, hop_size: usize) -> WindowIterator<'a, T>;
169
170 fn frames_mut(&'a mut self) -> FrameIteratorMut<'a, T>;
204
205 fn channels_mut(&'a mut self) -> ChannelIteratorMut<'a, T>;
238
239 fn windows_mut(&'a mut self, window_size: usize, hop_size: usize) -> WindowIteratorMut<'a, T>;
275}
276
277#[cfg(feature = "parallel-processing")]
319pub trait AudioSampleParallelIterators<T: AudioSample> {
320 fn par_frames(&self) -> ParFrameIterator<T>;
349
350 fn par_channels(&self) -> ParChannelIterator<T>;
381
382 fn par_windows(&self, window_size: usize, hop_size: usize) -> ParWindowIterator<T>;
410}
411
412impl<'a, T: AudioSample> AudioSamples<'a, T> {
413 pub fn frames(&'a self) -> FrameIterator<'a, T> {
421 FrameIterator::new(self)
422 }
423
424 pub fn channels<'iter>(&'iter self) -> ChannelIterator<'iter, 'a, T> {
432 ChannelIterator::new(self)
433 }
434
435 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 pub fn frames_mut(&'a mut self) -> FrameIteratorMut<'a, T> {
457 FrameIteratorMut::new(self)
458 }
459
460 pub fn channels_mut(&'a mut self) -> ChannelIteratorMut<'a, T> {
465 ChannelIteratorMut::new(self)
466 }
467
468 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 pub fn apply_to_frames<F>(&mut self, mut f: F)
500 where
501 F: FnMut(usize, &mut [T]), {
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 pub fn try_apply_to_channel_data<F>(&mut self, mut f: F) -> crate::AudioSampleResult<()>
538 where
539 F: FnMut(usize, &mut [T]), {
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 pub fn apply_to_channel_data<F>(&mut self, mut f: F)
572 where
573 F: FnMut(usize, &mut [T]), {
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 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]), {
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 let mut window_data = vec![T::zero(); window_size * rows];
629
630 for ch in 0..rows {
632 for sample_idx in 0..window_size {
633 let dst_idx = sample_idx * rows + ch; window_data[dst_idx] = arr[[ch, pos + sample_idx]];
635 }
636 }
637
638 f(window_idx, &mut window_data);
640
641 for ch in 0..rows {
643 for sample_idx in 0..window_size {
644 let src_idx = sample_idx * rows + ch; 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 let owned_audio = self.clone().into_owned();
677 ParWindowIterator::new(owned_audio, window_size, hop_size)
678 }
679}
680
681pub struct FrameIterator<'a, T: AudioSample> {
687 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 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 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
741pub struct ChannelIterator<'iter, 'data, T: AudioSample> {
745 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
799pub enum PaddingMode {
800 #[default]
802 Zero,
803 None,
805 Skip,
807}
808
809pub struct WindowIterator<'a, T: AudioSample> {
815 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 let total_windows = if window_size == 0 || hop_size == 0 {
833 0
834 } else {
835 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 let max_windows = total_samples.div_ceil(hop_size);
865
866 match padding_mode {
867 PaddingMode::Zero => {
868 max_windows
870 }
871 PaddingMode::None => {
872 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 if total_samples < window_size {
884 0
885 } else {
886 1 + (total_samples - window_size) / hop_size
887 }
888 }
889 }
890 }
891
892 pub const fn with_padding_mode(mut self, mode: PaddingMode) -> Self {
894 self.padding_mode = mode;
895
896 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 self.audio
929 .slice_samples(start_pos..end_pos)
930 .ok()
931 .map(|w| w.into_owned())
932 } else {
933 match self.padding_mode {
935 PaddingMode::Zero => {
936 let available_samples = self.total_samples.saturating_sub(start_pos);
938 match &self.audio.data {
939 AudioData::Mono(_) => {
940 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 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 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 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#[cfg(feature = "parallel-processing")]
1052pub struct ParFrameIterator<T: AudioSample> {
1057 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 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 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 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 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")]
1174pub struct ParChannelIterator<T: AudioSample> {
1179 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 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")]
1256pub struct ParWindowIterator<T: AudioSample> {
1261 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 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 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 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 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 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 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 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; }
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
1478pub struct FrameIteratorMut<'a, T: AudioSample> {
1500 data_ptr: *mut T,
1502 layout: IteratorLayout,
1504 current_frame: usize,
1505 total_frames: usize,
1506 num_channels: usize,
1507 _phantom: PhantomData<&'a mut T>,
1508}
1509
1510#[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 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 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
1608pub enum FrameMut<'a, T: AudioSample> {
1610 Mono(&'a mut [T]),
1612 MultiChannel(Vec<*mut T>, usize),
1614}
1615
1616impl<'a, T: AudioSample> FrameMut<'a, T> {
1617 pub const fn len(&self) -> usize {
1619 match self {
1620 FrameMut::Mono(_) => 1,
1621 FrameMut::MultiChannel(_, channels) => *channels,
1622 }
1623 }
1624
1625 pub const fn is_empty(&self) -> bool {
1627 self.len() == 0
1628 }
1629
1630 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 Some(unsafe { &mut *ptrs[channel] })
1647 } else {
1648 None
1649 }
1650 }
1651 }
1652 }
1653
1654 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 unsafe {
1672 let sample_ref = &mut **ptr;
1673 *sample_ref = f(*sample_ref);
1674 }
1675 }
1676 }
1677 }
1678 }
1679
1680 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 unsafe {
1695 let sample_ref = &mut **ptr;
1696 *sample_ref = f(ch, *sample_ref);
1697 }
1698 }
1699 }
1700 }
1701 }
1702}
1703
1704pub struct ChannelIteratorMut<'a, T: AudioSample> {
1722 data_ptr: *mut T,
1724 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 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 let channel_slice = unsafe {
1783 match &self.layout {
1784 IteratorLayout::Mono { samples } => {
1785 if self.current_channel == 0 {
1787 std::slice::from_raw_parts_mut(self.data_ptr, *samples)
1788 } else {
1789 return None;
1791 }
1792 }
1793 IteratorLayout::MultiChannel {
1794 samples_per_channel,
1795 ..
1796 } => {
1797 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
1818pub struct WindowIteratorMut<'a, T: AudioSample> {
1836 data_ptr: *mut T,
1838 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 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 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 if end_pos <= self.total_samples {
1967 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 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
2014pub enum WindowMut<'a, T: AudioSample> {
2016 Mono(&'a mut [T]),
2018 MultiChannel(Vec<*mut T>, usize, usize), }
2021
2022impl<'a, T: AudioSample> WindowMut<'a, T> {
2023 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 pub const fn is_empty(&self) -> bool {
2033 self.len() == 0
2034 }
2035
2036 pub const fn num_channels(&self) -> usize {
2038 match self {
2039 WindowMut::Mono(_) => 1,
2040 WindowMut::MultiChannel(_, channels, _) => *channels,
2041 }
2042 }
2043
2044 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 Some(unsafe { std::slice::from_raw_parts_mut(ptrs[channel], *window_size) })
2068 } else {
2069 None
2070 }
2071 }
2072 }
2073 }
2074
2075 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 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 pub fn apply_window_function<F>(&mut self, window_fn: F)
2111 where
2112 F: Fn(usize, usize) -> T + Copy, 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 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 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 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 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 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 ); }
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]); }
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 assert_eq!(windows[0].to_interleaved_vec(), vec![1.0, 5.0, 2.0, 6.0]);
2297 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); }
2317
2318 #[test]
2319 fn test_multiple_iterators_from_same_audio() {
2320 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 let frames = audio.frames();
2328 let channels = audio.channels();
2329 let windows = audio.windows(2, 1);
2330
2331 assert_eq!(frames.len(), 3);
2333 assert_eq!(channels.len(), 2);
2334 assert_eq!(windows.len(), 3);
2335 }
2336
2337 #[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 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 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 });
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 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 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 audio.apply_to_windows(2, 2, |_window_idx, window_data| {
2444 let samples_per_channel = window_data.len() / 2; 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; window_data[right_idx] *= 1.2; }
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 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 let result = audio.as_mono().unwrap();
2484 assert!(result[0] < 1.0); assert!(result[1] > 0.5); assert!(result[2] > 0.5); assert!(result[3] < 1.0); }
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 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 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 let mut audio1 = AudioSamples::new_mono(Array1::<f32>::ones(1000), sample_rate!(44100));
2523 let mut audio2 = audio1.clone();
2524
2525 audio1.apply(|sample| sample * 0.5);
2527
2528 audio2.apply_to_frames(|_frame_idx, frame_data| {
2530 for sample in frame_data {
2531 *sample *= 0.5;
2532 }
2533 });
2534
2535 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 let windows: Vec<_> = audio.windows_mut(5, 1).collect();
2557 assert_eq!(windows.len(), 0); 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 #[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); assert_eq!(frame_sums[1], 7.0); assert_eq!(frame_sums[2], 9.0); }
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 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); assert_eq!(channel_maxes[1], 6.0); }
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); assert_eq!(window_sums[1], 15.0); }
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); assert_eq!(window_sums[1], 18.0); assert_eq!(window_sums[2], 11.0); }
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 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]); 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]); 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]); }
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 let seq_frame_sums: Vec<f32> = audio
2727 .frames()
2728 .map(|frame| {
2729 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 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 let seq_channel_sums: Vec<f32> = audio
2752 .channels()
2753 .map(|channel| {
2754 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 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 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 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); }
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 let result: Vec<f32> = audio
2822 .par_frames()
2823 .with_min_len(10) .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 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 let max_centroid = 2.0 * 64.0; for ¢roid 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}