Skip to main content

max / audiofiles

3.9 KB · 131 lines History Blame Raw
1 //! Silence operations: insert silence at a position or remove a range of silence.
2
3 use crate::error::CoreError;
4
5 /// Insert silence (zeros) at the given frame position for the specified duration.
6 ///
7 /// Operates on interleaved sample data. Positions are in frame units.
8 pub fn apply_insert_silence(
9 samples: &mut Vec<f32>,
10 channels: u16,
11 start_frame: usize,
12 duration_frames: usize,
13 ) -> Result<(), CoreError> {
14 let ch = channels as usize;
15 if ch == 0 {
16 return Err(CoreError::Internal("insert_silence: channels must be > 0".to_string()));
17 }
18 let total_frames = samples.len() / ch;
19
20 if start_frame > total_frames {
21 return Err(CoreError::Internal(format!(
22 "insert_silence: start_frame ({start_frame}) exceeds total frames ({total_frames})"
23 )));
24 }
25 if duration_frames == 0 {
26 return Ok(());
27 }
28
29 let insert_at = start_frame * ch;
30 let insert_count = duration_frames * ch;
31 samples.splice(insert_at..insert_at, std::iter::repeat_n(0.0f32, insert_count));
32 Ok(())
33 }
34
35 /// Remove a range of samples (typically silence) between two frame positions.
36 ///
37 /// Operates on interleaved sample data. `start_frame` inclusive, `end_frame` exclusive.
38 pub fn apply_remove_range(
39 samples: &mut Vec<f32>,
40 channels: u16,
41 start_frame: usize,
42 end_frame: usize,
43 ) -> Result<(), CoreError> {
44 let ch = channels as usize;
45 if ch == 0 {
46 return Err(CoreError::Internal("remove_range: channels must be > 0".to_string()));
47 }
48 let total_frames = samples.len() / ch;
49
50 if start_frame >= end_frame {
51 return Err(CoreError::Internal(
52 "remove_range: start_frame must be less than end_frame".to_string(),
53 ));
54 }
55 if end_frame > total_frames {
56 return Err(CoreError::Internal(format!(
57 "remove_range: end_frame ({end_frame}) exceeds total frames ({total_frames})"
58 )));
59 }
60
61 let start_sample = start_frame * ch;
62 let end_sample = end_frame * ch;
63 samples.drain(start_sample..end_sample);
64 Ok(())
65 }
66
67 #[cfg(test)]
68 mod tests {
69 use super::*;
70
71 #[test]
72 fn insert_silence_mono_middle() {
73 let mut samples = vec![1.0, 2.0, 3.0];
74 apply_insert_silence(&mut samples, 1, 1, 2).unwrap();
75 assert_eq!(samples, vec![1.0, 0.0, 0.0, 2.0, 3.0]);
76 }
77
78 #[test]
79 fn insert_silence_stereo_start() {
80 let mut samples = vec![1.0, 2.0, 3.0, 4.0]; // 2 frames stereo
81 apply_insert_silence(&mut samples, 2, 0, 1).unwrap();
82 assert_eq!(samples, vec![0.0, 0.0, 1.0, 2.0, 3.0, 4.0]);
83 }
84
85 #[test]
86 fn insert_silence_at_end() {
87 let mut samples = vec![1.0, 2.0, 3.0];
88 apply_insert_silence(&mut samples, 1, 3, 2).unwrap();
89 assert_eq!(samples, vec![1.0, 2.0, 3.0, 0.0, 0.0]);
90 }
91
92 #[test]
93 fn insert_silence_zero_duration_noop() {
94 let mut samples = vec![1.0, 2.0, 3.0];
95 apply_insert_silence(&mut samples, 1, 1, 0).unwrap();
96 assert_eq!(samples, vec![1.0, 2.0, 3.0]);
97 }
98
99 #[test]
100 fn insert_silence_out_of_bounds() {
101 let mut samples = vec![1.0, 2.0, 3.0];
102 assert!(apply_insert_silence(&mut samples, 1, 10, 1).is_err());
103 }
104
105 #[test]
106 fn remove_range_mono() {
107 let mut samples = vec![1.0, 0.0, 0.0, 2.0, 3.0];
108 apply_remove_range(&mut samples, 1, 1, 3).unwrap();
109 assert_eq!(samples, vec![1.0, 2.0, 3.0]);
110 }
111
112 #[test]
113 fn remove_range_stereo() {
114 let mut samples = vec![1.0, 2.0, 0.0, 0.0, 3.0, 4.0]; // 3 stereo frames
115 apply_remove_range(&mut samples, 2, 1, 2).unwrap();
116 assert_eq!(samples, vec![1.0, 2.0, 3.0, 4.0]);
117 }
118
119 #[test]
120 fn remove_range_invalid() {
121 let mut samples = vec![1.0, 2.0, 3.0];
122 assert!(apply_remove_range(&mut samples, 1, 2, 1).is_err());
123 }
124
125 #[test]
126 fn remove_range_out_of_bounds() {
127 let mut samples = vec![1.0, 2.0, 3.0];
128 assert!(apply_remove_range(&mut samples, 1, 0, 10).is_err());
129 }
130 }
131