1use matroska::{Audio, Matroska, Settings};
4use rand::Rng;
5use symphonia::core::audio::Channels;
6
7use log::error;
8
9use crate::container::info::*;
10use crate::container::play_settings::{
11 ConvolutionReverbSettings, EffectSettings, PlaySettingsFile, PlaySettingsLegacy, SettingsTrack,
12};
13
14#[derive(Debug, Clone)]
16pub struct Prot {
17 pub info: Info,
18 file_path: Option<String>,
19 file_paths: Option<Vec<Vec<String>>>,
20 file_paths_dictionary: Option<Vec<String>>,
21 track_ids: Option<Vec<u32>>,
22 track_paths: Option<Vec<String>>,
23 duration: f64,
24 impulse_response_spec: Option<ImpulseResponseSpec>,
25 impulse_response_tail_db: Option<f32>,
26}
27
28#[derive(Debug, Clone)]
30pub enum ImpulseResponseSpec {
31 Attachment(String),
32 FilePath(String),
33}
34
35impl Prot {
36 pub fn new(file_path: &String) -> Self {
38 let info = Info::new(file_path.clone());
39
40 let mut this = Self {
41 info,
42 file_path: Some(file_path.clone()),
43 file_paths: None,
44 file_paths_dictionary: None,
45 track_ids: None,
46 track_paths: None,
47 duration: 0.0,
48 impulse_response_spec: None,
49 impulse_response_tail_db: None,
50 };
51
52 this.refresh_tracks();
53
54 this
55 }
56
57 pub fn new_from_file_paths(file_paths: &Vec<Vec<String>>) -> Self {
59 let mut file_paths_dictionary = Vec::new();
60 for file_path in file_paths {
63 for path in file_path {
64 if !file_paths_dictionary.contains(path) {
65 file_paths_dictionary.push(path.clone());
66 }
67 }
68 }
69
70 let info = Info::new_from_file_paths(file_paths_dictionary.clone());
71
72 let mut this = Self {
73 info,
74 file_path: None,
75 file_paths: Some(file_paths.clone()),
76 file_paths_dictionary: Some(file_paths_dictionary),
77 track_ids: None,
78 track_paths: None,
79 duration: 0.0,
80 impulse_response_spec: None,
81 impulse_response_tail_db: None,
82 };
83
84 this.refresh_tracks();
85
86 this
87 }
88
89 pub fn refresh_tracks(&mut self) {
96 let mut longest_duration = 0.0;
97 self.impulse_response_spec = None;
98 self.impulse_response_tail_db = None;
99
100 if let Some(file_paths) = &self.file_paths {
101 let mut track_paths: Vec<String> = Vec::new();
103 for file_path in file_paths {
104 let random_number = rand::thread_rng().gen_range(0..file_path.len());
105 let track_path = file_path[random_number].clone();
106
107 let index_in_dictionary = self
108 .file_paths_dictionary
109 .as_ref()
110 .unwrap()
111 .iter()
112 .position(|x| *x == track_path)
113 .unwrap();
114 let duration = self.info.get_duration(index_in_dictionary as u32).unwrap();
115
116 if duration > longest_duration {
117 longest_duration = duration;
118 self.duration = longest_duration;
119 }
120
121 track_paths.push(track_path);
122 }
123
124 self.track_paths = Some(track_paths);
125
126 return;
127 }
128
129 if !self.file_path.is_some() {
130 return;
131 }
132
133 let file_path = self.file_path.as_ref().unwrap();
134 let file = std::fs::File::open(file_path).unwrap();
135
136 let mka: Matroska = Matroska::open(file).expect("Could not open file");
137
138 let mut track_index_array: Vec<u32> = Vec::new();
139 mka.attachments.iter().for_each(|attachment| {
140 if attachment.name == "play_settings.json" {
142 let play_settings: PlaySettingsFile =
144 serde_json::from_slice(&attachment.data).unwrap();
145
146 self.impulse_response_spec = parse_impulse_response_spec(&play_settings);
147 self.impulse_response_tail_db = parse_impulse_response_tail_db(&play_settings);
148
149 match &play_settings {
150 PlaySettingsFile::Legacy(file) => {
151 collect_legacy_tracks(
152 file.settings.inner(),
153 &mut track_index_array,
154 &mut longest_duration,
155 &self.info,
156 &mut self.duration,
157 );
158 }
159 PlaySettingsFile::V1(file) => {
160 collect_tracks_from_ids(
161 &file.settings.inner().tracks,
162 &mut track_index_array,
163 &mut longest_duration,
164 &self.info,
165 &mut self.duration,
166 );
167 }
168 PlaySettingsFile::V2(file) => {
169 collect_tracks_from_ids(
170 &file.settings.inner().tracks,
171 &mut track_index_array,
172 &mut longest_duration,
173 &self.info,
174 &mut self.duration,
175 );
176 }
177 PlaySettingsFile::Unknown { .. } => {
178 error!("Unknown file format");
179 }
180 }
181 }
182 });
183
184 self.track_ids = Some(track_index_array);
185 }
186
187 fn get_audio_settings(file_path: &str) -> Audio {
188 let file = std::fs::File::open(file_path).unwrap();
189
190 let symph = get_probe_result_from_string(file_path);
191
192 symph.format.tracks();
193
194 let first_track = &symph.format.tracks().first().unwrap().codec_params;
195
196 let channels = {
197 let channels_option = first_track.channels.unwrap_or(Channels::FRONT_CENTRE);
198 channels_option.iter().count()
199 };
200
201 let mut bit_depth = None;
202
203 if let Some(bits) = first_track.bits_per_sample {
204 bit_depth = Some(bits as u64)
205 }
206
207 let audio = Audio {
208 sample_rate: first_track.sample_rate.unwrap() as f64,
209 channels: channels as u64,
210 bit_depth,
211 };
212
213 audio
214
215 }
231
232 pub fn get_impulse_response_spec(&self) -> Option<ImpulseResponseSpec> {
234 self.impulse_response_spec.clone()
235 }
236
237 pub fn get_impulse_response_tail_db(&self) -> Option<f32> {
239 self.impulse_response_tail_db
240 }
241
242 pub fn get_container_path(&self) -> Option<String> {
244 self.file_path.clone()
245 }
246
247 pub fn set_impulse_response_spec(&mut self, spec: ImpulseResponseSpec) {
249 self.impulse_response_spec = Some(spec);
250 }
251
252 pub fn set_impulse_response_tail_db(&mut self, tail_db: f32) {
254 self.impulse_response_tail_db = Some(tail_db);
255 }
256
257 pub fn get_keys(&self) -> Vec<u32> {
259 if let Some(track_paths) = &self.track_paths {
261 return (0..track_paths.len() as u32).collect();
262 }
263
264 if let Some(track_ids) = &self.track_ids {
265 return (0..track_ids.len() as u32).collect();
266 }
267
268 Vec::new()
269 }
270
271 pub fn get_ids(&self) -> Vec<String> {
273 if let Some(track_paths) = &self.track_paths {
274 return track_paths.clone();
275 }
276
277 if let Some(track_ids) = &self.track_ids {
278 return track_ids.into_iter().map(|id| format!("{}", id)).collect();
279 }
280
281 Vec::new()
282 }
283
284 pub fn enumerated_list(&self) -> Vec<(u16, String, Option<u32>)> {
286 let mut list: Vec<(u16, String, Option<u32>)> = Vec::new();
287 if let Some(track_paths) = &self.track_paths {
288 for (index, file_path) in track_paths.iter().enumerate() {
289 list.push((index as u16, String::from(file_path), None));
290 }
291
292 return list;
293 }
294
295 if let Some(track_ids) = &self.track_ids {
296 for (index, track_id) in track_ids.iter().enumerate() {
297 list.push((
298 index as u16,
299 String::from(self.file_path.as_ref().unwrap()),
300 Some(*track_id),
301 ));
302 }
303
304 return list;
305 }
306
307 list
308 }
309
310 pub fn container_track_entries(&self) -> Option<(String, Vec<(u16, u32)>)> {
312 let file_path = self.file_path.as_ref()?;
313 let track_ids = self.track_ids.as_ref()?;
314 let mut entries = Vec::new();
315 for (index, track_id) in track_ids.iter().enumerate() {
316 entries.push((index as u16, *track_id));
317 }
318 Some((file_path.clone(), entries))
319 }
320
321 pub fn get_duration(&self) -> &f64 {
323 &self.duration
324 }
325
326 pub fn get_length(&self) -> usize {
328 if let Some(file_paths) = &self.file_paths {
329 return file_paths.len();
330 }
331
332 if let Some(track_ids) = &self.track_ids {
333 return track_ids.len();
334 }
335
336 0
337 }
338
339 pub fn get_file_paths_dictionary(&self) -> Vec<String> {
341 match &self.file_paths_dictionary {
342 Some(dictionary) => dictionary.to_vec(),
343 None => Vec::new(),
344 }
345 }
346}
347
348fn parse_impulse_response_spec(play_settings: &PlaySettingsFile) -> Option<ImpulseResponseSpec> {
349 if let Some(settings) = parse_convolution_settings(play_settings) {
350 if let Some(spec) = parse_impulse_response_string_or_struct(&settings) {
351 return Some(spec);
352 }
353 }
354
355 None
356}
357
358fn parse_impulse_response_tail_db(play_settings: &PlaySettingsFile) -> Option<f32> {
359 if let Some(settings) = parse_convolution_settings(play_settings) {
360 if let Some(value) = settings.impulse_response_tail_db {
361 return Some(value);
362 }
363 if let Some(value) = settings.impulse_response_tail {
364 return Some(value);
365 }
366 }
367
368 None
369}
370
371fn parse_convolution_settings(
372 play_settings: &PlaySettingsFile,
373) -> Option<ConvolutionReverbSettings> {
374 let effects = match play_settings {
375 PlaySettingsFile::V1(file) => &file.settings.inner().effects,
376 PlaySettingsFile::V2(file) => &file.settings.inner().effects,
377 _ => return None,
378 };
379
380 for effect in effects {
381 if let EffectSettings::ConvolutionReverbSettings(settings) = effect {
382 return Some(settings.clone());
383 }
384 }
385
386 None
387}
388
389fn parse_impulse_response_string_or_struct(
390 settings: &ConvolutionReverbSettings,
391) -> Option<ImpulseResponseSpec> {
392 if let Some(value) = settings.impulse_response.as_deref() {
393 return parse_impulse_response_string(value);
394 }
395 if let Some(value) = settings.impulse_response_attachment.as_deref() {
396 return parse_impulse_response_string(value);
397 }
398 if let Some(value) = settings.impulse_response_path.as_deref() {
399 return parse_impulse_response_string(value);
400 }
401 None
402}
403
404pub fn parse_impulse_response_string(value: &str) -> Option<ImpulseResponseSpec> {
410 if let Some(attachment) = value.strip_prefix("attachment:") {
411 return Some(ImpulseResponseSpec::Attachment(
412 attachment.trim().to_string(),
413 ));
414 }
415
416 if let Some(path) = value.strip_prefix("file:") {
417 return Some(ImpulseResponseSpec::FilePath(path.trim().to_string()));
418 }
419
420 Some(ImpulseResponseSpec::FilePath(value.trim().to_string()))
421}
422
423fn collect_tracks_from_ids(
424 tracks: &[SettingsTrack],
425 track_index_array: &mut Vec<u32>,
426 longest_duration: &mut f64,
427 info: &Info,
428 total_duration: &mut f64,
429) {
430 for track in tracks {
431 if track.ids.is_empty() {
432 continue;
433 }
434 let random_number = rand::thread_rng().gen_range(0..track.ids.len());
435 let index = track.ids[random_number];
436 if let Some(track_duration) = info.get_duration(index) {
437 if track_duration > *longest_duration {
438 *longest_duration = track_duration;
439 *total_duration = *longest_duration;
440 }
441 }
442 track_index_array.push(index);
443 }
444}
445
446fn collect_legacy_tracks(
447 settings: &PlaySettingsLegacy,
448 track_index_array: &mut Vec<u32>,
449 longest_duration: &mut f64,
450 info: &Info,
451 total_duration: &mut f64,
452) {
453 for track in &settings.tracks {
454 let (Some(starting_index), Some(length)) = (track.starting_index, track.length) else {
455 continue;
456 };
457 let starting_index = starting_index + 1;
458 let index = rand::thread_rng().gen_range(starting_index..(starting_index + length));
459 if let Some(track_duration) = info.get_duration(index) {
460 if track_duration > *longest_duration {
461 *longest_duration = track_duration;
462 *total_duration = *longest_duration;
463 }
464 }
465 track_index_array.push(index);
466 }
467}