ot_tools_io/samples/
configs.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Helper / Grouped configs for sample attribute files (`SampleAttributes`).
7
8use crate::RBoxErr;
9use crate::{
10    samples::options::SampleAttributeLoopMode, samples::SampleAttributes, OptionEnumValueConvert,
11};
12
13/// An OT Sample's Trim settings
14
15#[derive(PartialEq, Debug, Clone, Copy)]
16pub struct SampleTrimConfig {
17    /// Start of full audio sample (n samples)
18    pub start: u32,
19    /// End of full audio sample (n samples)
20    pub end: u32,
21    /// Length of audio sample to play before stopping/looping playback
22    /// NOTE: This is measured in number of bars.
23    pub length: u32,
24}
25
26impl SampleTrimConfig {
27    pub fn from_decoded(decoded: &SampleAttributes) -> RBoxErr<Self> {
28        let new = SampleTrimConfig {
29            start: decoded.trim_start,
30            end: decoded.trim_end,
31            length: decoded.trim_len,
32        };
33        Ok(new)
34    }
35}
36
37/// An OT Sample's Loop settings
38
39#[derive(PartialEq, Debug, Clone, Copy)]
40pub struct SampleLoopConfig {
41    /// Loop start position for the audio sample (n samples).
42    pub start: u32,
43
44    /// Length of the loop for the audio sample (n samples).
45    pub length: u32,
46
47    /// Type of looping mode.
48    pub mode: SampleAttributeLoopMode,
49}
50
51impl SampleLoopConfig {
52    pub fn new(start: u32, length: u32, mode: SampleAttributeLoopMode) -> RBoxErr<Self> {
53        Ok(Self {
54            start,
55            length,
56            mode,
57        })
58    }
59
60    pub fn from_decoded(decoded: &SampleAttributes) -> RBoxErr<Self> {
61        Self::new(
62            decoded.loop_start,
63            decoded.loop_len,
64            SampleAttributeLoopMode::from_value(&decoded.loop_mode)
65                .unwrap_or(SampleAttributeLoopMode::Off),
66        )
67    }
68}
69
70#[cfg(test)]
71#[allow(unused_imports)]
72mod tests {
73
74    mod test_sample_loop_config {
75        mod test_new {
76
77            use crate::samples::configs::{SampleAttributeLoopMode, SampleLoopConfig};
78
79            #[test]
80            fn test_new_sample_loop_config_loop_off() {
81                assert_eq!(
82                    SampleLoopConfig::new(0, 10, SampleAttributeLoopMode::Off).unwrap(),
83                    SampleLoopConfig {
84                        start: 0,
85                        length: 10,
86                        mode: SampleAttributeLoopMode::Off
87                    }
88                );
89            }
90
91            #[test]
92            fn test_new_sample_loop_config_umin_start_umax_length() {
93                assert_eq!(
94                    SampleLoopConfig::new(u32::MIN, u32::MAX, SampleAttributeLoopMode::Off)
95                        .unwrap(),
96                    SampleLoopConfig {
97                        start: u32::MIN,
98                        length: u32::MAX,
99                        mode: SampleAttributeLoopMode::Off
100                    }
101                );
102            }
103
104            #[test]
105            fn test_new_sample_loop_config_loop_normal() {
106                assert_eq!(
107                    SampleLoopConfig::new(0, 10, SampleAttributeLoopMode::Normal).unwrap(),
108                    SampleLoopConfig {
109                        start: 0,
110                        length: 10,
111                        mode: SampleAttributeLoopMode::Normal
112                    }
113                );
114            }
115
116            #[test]
117            fn test_new_sample_loop_config_loop_pingpong() {
118                assert_eq!(
119                    SampleLoopConfig::new(0, 10, SampleAttributeLoopMode::PingPong).unwrap(),
120                    SampleLoopConfig {
121                        start: 0,
122                        length: 10,
123                        mode: SampleAttributeLoopMode::PingPong
124                    }
125                );
126            }
127        }
128    }
129
130    mod test_from_decoded {
131
132        use crate::{
133            samples::{
134                configs::{SampleAttributeLoopMode, SampleLoopConfig},
135                slices::Slice,
136                SampleAttributes, SAMPLES_FILE_VERSION, SAMPLES_HEADER,
137            },
138            OptionEnumValueConvert,
139        };
140
141        #[test]
142        fn test_umin_start_umax_len() {
143            let decoded = SampleAttributes {
144                header: SAMPLES_HEADER,
145                datatype_version: SAMPLES_FILE_VERSION,
146                unknown: 0,
147                tempo: 128000,
148                trim_len: 0,
149                loop_len: u32::MAX,
150                stretch: 0,
151                loop_mode: SampleAttributeLoopMode::Off.value().unwrap(),
152                gain: 0,
153                quantization: 0,
154                trim_start: 0,
155                trim_end: 0,
156                loop_start: u32::MIN,
157                slices: [Slice {
158                    trim_start: 0,
159                    trim_end: 0,
160                    loop_start: 0,
161                }; 64],
162                slices_len: 0,
163                checksum: 0,
164            };
165
166            assert_eq!(
167                SampleLoopConfig::from_decoded(&decoded).unwrap(),
168                SampleLoopConfig {
169                    start: u32::MIN,
170                    length: u32::MAX,
171                    mode: SampleAttributeLoopMode::Off
172                }
173            );
174        }
175
176        #[test]
177        fn test_loop_off() {
178            let decoded = SampleAttributes {
179                header: SAMPLES_HEADER,
180                datatype_version: SAMPLES_FILE_VERSION,
181                unknown: 0,
182                tempo: 128000,
183                trim_len: 0,
184                loop_len: 10,
185                stretch: 0,
186                loop_mode: SampleAttributeLoopMode::Off.value().unwrap(),
187                gain: 0,
188                quantization: 0,
189                trim_start: 0,
190                trim_end: 0,
191                loop_start: 0,
192                slices: [Slice {
193                    trim_start: 0,
194                    trim_end: 0,
195                    loop_start: 0,
196                }; 64],
197                slices_len: 0,
198                checksum: 0,
199            };
200
201            assert_eq!(
202                SampleLoopConfig::from_decoded(&decoded).unwrap(),
203                SampleLoopConfig {
204                    start: 0,
205                    length: 10,
206                    mode: SampleAttributeLoopMode::Off
207                }
208            );
209        }
210
211        #[test]
212        fn test_loop_normal() {
213            let decoded = SampleAttributes {
214                header: SAMPLES_HEADER,
215                datatype_version: SAMPLES_FILE_VERSION,
216                unknown: 0,
217                tempo: 128000,
218                trim_len: 0,
219                loop_len: 10,
220                stretch: 0,
221                loop_mode: SampleAttributeLoopMode::Normal.value().unwrap(),
222                gain: 0,
223                quantization: 0,
224                trim_start: 0,
225                trim_end: 0,
226                loop_start: 0,
227                slices: [Slice {
228                    trim_start: 0,
229                    trim_end: 0,
230                    loop_start: 0,
231                }; 64],
232                slices_len: 0,
233                checksum: 0,
234            };
235
236            assert_eq!(
237                SampleLoopConfig::from_decoded(&decoded).unwrap(),
238                SampleLoopConfig {
239                    start: 0,
240                    length: 10,
241                    mode: SampleAttributeLoopMode::Normal
242                }
243            );
244        }
245
246        #[test]
247        fn test_loop_pingpong() {
248            let decoded = SampleAttributes {
249                header: SAMPLES_HEADER,
250                datatype_version: SAMPLES_FILE_VERSION,
251                unknown: 0,
252                tempo: 128000,
253                trim_len: 0,
254                loop_len: 10,
255                stretch: 0,
256                loop_mode: SampleAttributeLoopMode::PingPong.value().unwrap(),
257                gain: 0,
258                quantization: 0,
259                trim_start: 0,
260                trim_end: 0,
261                loop_start: 0,
262                slices: [Slice {
263                    trim_start: 0,
264                    trim_end: 0,
265                    loop_start: 0,
266                }; 64],
267                slices_len: 0,
268                checksum: 0,
269            };
270
271            assert_eq!(
272                SampleLoopConfig::from_decoded(&decoded).unwrap(),
273                SampleLoopConfig {
274                    start: 0,
275                    length: 10,
276                    mode: SampleAttributeLoopMode::PingPong
277                }
278            );
279        }
280    }
281}