oximedia_edit/
multitrack.rs1#![allow(dead_code)]
7#![allow(clippy::module_name_repetitions)]
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TrackType {
12 Video,
14 Audio,
16 Title,
18 Effect,
20}
21
22impl TrackType {
23 #[must_use]
26 pub fn is_av(self) -> bool {
27 matches!(self, TrackType::Video | TrackType::Audio)
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct TrackLock {
34 pub track_id: u32,
36 pub locked: bool,
38 pub sync_locked: bool,
40}
41
42impl TrackLock {
43 #[must_use]
45 pub fn new(track_id: u32) -> Self {
46 Self {
47 track_id,
48 locked: false,
49 sync_locked: false,
50 }
51 }
52
53 #[must_use]
55 pub fn can_edit(&self) -> bool {
56 !self.locked
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct TrackVisibility {
63 pub track_id: u32,
65 pub visible: bool,
67 pub solo: bool,
69 pub muted: bool,
71}
72
73impl TrackVisibility {
74 #[must_use]
76 pub fn new(track_id: u32) -> Self {
77 Self {
78 track_id,
79 visible: true,
80 solo: false,
81 muted: false,
82 }
83 }
84
85 #[must_use]
91 pub fn is_audible(&self, any_solo: bool) -> bool {
92 if self.muted {
93 return false;
94 }
95 if any_solo {
96 return self.solo;
97 }
98 true
99 }
100}
101
102#[derive(Debug, Default)]
104pub struct MultitrackConfig {
105 pub tracks: Vec<(u32, TrackType)>,
107 pub locks: Vec<TrackLock>,
109 pub visibility: Vec<TrackVisibility>,
111}
112
113impl MultitrackConfig {
114 #[must_use]
116 pub fn new() -> Self {
117 Self::default()
118 }
119
120 pub fn add_track(&mut self, track_id: u32, track_type: TrackType) {
122 self.tracks.push((track_id, track_type));
123 self.locks.push(TrackLock::new(track_id));
124 self.visibility.push(TrackVisibility::new(track_id));
125 }
126
127 pub fn lock_track(&mut self, track_id: u32, locked: bool) {
129 if let Some(lock) = self.locks.iter_mut().find(|l| l.track_id == track_id) {
130 lock.locked = locked;
131 }
132 }
133
134 pub fn mute_track(&mut self, track_id: u32, muted: bool) {
136 if let Some(vis) = self.visibility.iter_mut().find(|v| v.track_id == track_id) {
137 vis.muted = muted;
138 }
139 }
140
141 pub fn solo_track(&mut self, track_id: u32, solo: bool) {
143 if let Some(vis) = self.visibility.iter_mut().find(|v| v.track_id == track_id) {
144 vis.solo = solo;
145 }
146 }
147
148 #[must_use]
150 pub fn audible_tracks(&self) -> Vec<u32> {
151 let any_solo = self.visibility.iter().any(|v| v.solo);
152 self.visibility
153 .iter()
154 .filter(|v| v.is_audible(any_solo))
155 .map(|v| v.track_id)
156 .collect()
157 }
158
159 #[must_use]
161 pub fn track_count(&self) -> usize {
162 self.tracks.len()
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
173 fn test_track_type_video_is_av() {
174 assert!(TrackType::Video.is_av());
175 }
176
177 #[test]
178 fn test_track_type_audio_is_av() {
179 assert!(TrackType::Audio.is_av());
180 }
181
182 #[test]
183 fn test_track_type_title_not_av() {
184 assert!(!TrackType::Title.is_av());
185 }
186
187 #[test]
188 fn test_track_type_effect_not_av() {
189 assert!(!TrackType::Effect.is_av());
190 }
191
192 #[test]
195 fn test_track_lock_new_unlocked() {
196 let lock = TrackLock::new(1);
197 assert!(!lock.locked);
198 assert!(lock.can_edit());
199 }
200
201 #[test]
202 fn test_track_lock_locked_cannot_edit() {
203 let mut lock = TrackLock::new(2);
204 lock.locked = true;
205 assert!(!lock.can_edit());
206 }
207
208 #[test]
211 fn test_track_visibility_defaults() {
212 let vis = TrackVisibility::new(1);
213 assert!(vis.visible);
214 assert!(!vis.solo);
215 assert!(!vis.muted);
216 }
217
218 #[test]
219 fn test_is_audible_no_solo_not_muted() {
220 let vis = TrackVisibility::new(1);
221 assert!(vis.is_audible(false));
222 }
223
224 #[test]
225 fn test_is_audible_muted() {
226 let mut vis = TrackVisibility::new(1);
227 vis.muted = true;
228 assert!(!vis.is_audible(false));
229 }
230
231 #[test]
232 fn test_is_audible_solo_mode_not_soloed() {
233 let vis = TrackVisibility::new(1); assert!(!vis.is_audible(true));
235 }
236
237 #[test]
238 fn test_is_audible_solo_mode_is_soloed() {
239 let mut vis = TrackVisibility::new(1);
240 vis.solo = true;
241 assert!(vis.is_audible(true));
242 }
243
244 #[test]
247 fn test_multitrack_add_track() {
248 let mut cfg = MultitrackConfig::new();
249 cfg.add_track(1, TrackType::Video);
250 cfg.add_track(2, TrackType::Audio);
251 assert_eq!(cfg.track_count(), 2);
252 }
253
254 #[test]
255 fn test_multitrack_lock_track() {
256 let mut cfg = MultitrackConfig::new();
257 cfg.add_track(1, TrackType::Video);
258 cfg.lock_track(1, true);
259 assert!(!cfg.locks[0].can_edit());
260 }
261
262 #[test]
263 fn test_multitrack_mute_track() {
264 let mut cfg = MultitrackConfig::new();
265 cfg.add_track(1, TrackType::Audio);
266 cfg.mute_track(1, true);
267 let audible = cfg.audible_tracks();
268 assert!(!audible.contains(&1));
269 }
270
271 #[test]
272 fn test_multitrack_solo_track() {
273 let mut cfg = MultitrackConfig::new();
274 cfg.add_track(1, TrackType::Audio);
275 cfg.add_track(2, TrackType::Audio);
276 cfg.solo_track(1, true);
277 let audible = cfg.audible_tracks();
278 assert!(audible.contains(&1));
280 assert!(!audible.contains(&2));
281 }
282
283 #[test]
284 fn test_multitrack_audible_tracks_all_active() {
285 let mut cfg = MultitrackConfig::new();
286 cfg.add_track(1, TrackType::Audio);
287 cfg.add_track(2, TrackType::Audio);
288 let audible = cfg.audible_tracks();
289 assert_eq!(audible.len(), 2);
290 }
291}