dasp_rs/features/
manipulation.rs1use ndarray::{Array1, Array2, Axis};
2
3pub fn stack_memory(
20 data: &Array2<f32>,
21 n_steps: Option<usize>,
22 delay: Option<usize>,
23) -> Array2<f32> {
24 let n_steps = n_steps.unwrap_or(2);
25 let delay = delay.unwrap_or(1);
26 let n_frames = data.shape()[1];
27 let n_features = data.shape()[0];
28 let mut stacked = Array2::zeros((n_features * n_steps, n_frames));
29 for step in 0..n_steps {
30 let offset = step * delay;
31 for t in 0..n_frames {
32 let src_t = (t as isize - offset as isize).max(0) as usize;
33 for f in 0..n_features {
34 stacked[[f + step * n_features, t]] = data[[f, src_t]];
35 }
36 }
37 }
38 stacked
39}
40
41pub fn temporal_kurtosis(
63 y: Option<&[f32]>,
64 s: Option<&Array2<f32>>,
65 frame_length: Option<usize>,
66 hop_length: Option<usize>,
67) -> Array1<f32> {
68 let frame_len = frame_length.unwrap_or(2048);
69 let hop = hop_length.unwrap_or(frame_len / 4);
70 match (y, s) {
71 (Some(y), None) => {
72 let n_frames = (y.len() - frame_len) / hop + 1;
73 let mut kurtosis = Array1::zeros(n_frames);
74 for i in 0..n_frames {
75 let start = i * hop;
76 let frame = &y[start..(start + frame_len).min(y.len())];
77 let mean = frame.iter().sum::<f32>() / frame.len() as f32;
78 let m2 = frame.iter().map(|&x| (x - mean).powi(2)).sum::<f32>() / frame.len() as f32;
79 let m4 = frame.iter().map(|&x| (x - mean).powi(4)).sum::<f32>() / frame.len() as f32;
80 kurtosis[i] = if m2 > 1e-10 { m4 / m2.powi(2) - 3.0 } else { 0.0 };
81 }
82 kurtosis
83 }
84 (None, Some(s)) => s.axis_iter(Axis(1)).map(|frame| {
85 let mean = frame.mean().unwrap_or(0.0);
86 let m2 = frame.mapv(|x| (x - mean).powi(2)).mean().unwrap_or(0.0);
87 let m4 = frame.mapv(|x| (x - mean).powi(4)).mean().unwrap_or(0.0);
88 if m2 > 1e-10 { m4 / m2.powi(2) - 3.0 } else { 0.0 }
89 }).collect(),
90 _ => panic!("Must provide either y or S"),
91 }
92}
93
94pub fn zero_crossing_rate(
112 y: &[f32],
113 frame_length: Option<usize>,
114 hop_length: Option<usize>,
115) -> Array1<f32> {
116 let frame_len = frame_length.unwrap_or(2048);
117 let hop = hop_length.unwrap_or(frame_len / 4);
118 let n_frames = (y.len() - frame_len) / hop + 1;
119 let mut zcr = Array1::zeros(n_frames);
120 for i in 0..n_frames {
121 let start = i * hop;
122 let slice = &y[start..(start + frame_len).min(y.len())];
123 let count = slice.windows(2).filter(|w| w[0] * w[1] < 0.0).count();
124 zcr[i] = count as f32 / frame_len as f32;
125 }
126 zcr
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use ndarray::array;
133
134 #[test]
135 fn test_stack_memory() {
136 let data = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
137 let result = stack_memory(&data, Some(2), Some(1));
138 let expected = array![
139 [1.0, 2.0, 3.0],
140 [4.0, 5.0, 6.0],
141 [1.0, 1.0, 2.0],
142 [4.0, 4.0, 5.0]
143 ];
144 assert_eq!(result, expected);
145 }
146
147 #[test]
148 fn test_temporal_kurtosis_y() {
149 let y = vec![1.0, -1.0, 1.0, -1.0];
150 let result = temporal_kurtosis(Some(&y), None, Some(4), Some(4));
151 let expected = array![-2.0];
152 assert_eq!(result, expected);
153 }
154
155 #[test]
156 fn test_temporal_kurtosis_s() {
157 let s = array![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]];
158 let result = temporal_kurtosis(None, Some(&s), None, None);
159 let expected = array![0.0, 0.0, 0.0];
160 assert_eq!(result, expected);
161 }
162
163 #[test]
164 fn test_zero_crossing_rate() {
165 let y = vec![1.0, -1.0, 2.0, -2.0, 1.0];
166 let result = zero_crossing_rate(&y, Some(2), Some(1));
167 let expected = array![0.5, 0.5, 0.5, 0.5];
168 assert_eq!(result, expected);
169 }
170
171 #[test]
172 fn test_zero_crossing_rate_full() {
173 let y = vec![1.0, -1.0, 2.0, -2.0, 1.0];
174 let result = zero_crossing_rate(&y, Some(5), Some(5));
175 let expected = array![0.8];
176 assert_eq!(result, expected);
177 }
178}