1use matroska::{Audio, Matroska, Settings};
4use rand::Rng;
5use symphonia::core::audio::Channels;
6use symphonia::core::sample::SampleFormat;
7
8use log::{error, warn};
9
10use crate::container::info::*;
11use crate::container::play_settings::{PlaySettingsFile, PlaySettingsLegacy, SettingsTrack};
12use crate::dsp::effects::convolution_reverb::{
13 parse_impulse_response_spec, parse_impulse_response_tail_db, ImpulseResponseSpec,
14};
15
16#[derive(Debug, Clone)]
18pub struct Prot {
19 pub info: Info,
20 file_path: Option<String>,
21 file_paths: Option<Vec<Vec<String>>>,
22 file_paths_dictionary: Option<Vec<String>>,
23 track_ids: Option<Vec<u32>>,
24 track_paths: Option<Vec<String>>,
25 duration: f64,
26 impulse_response_spec: Option<ImpulseResponseSpec>,
27 impulse_response_tail_db: Option<f32>,
28}
29
30impl Prot {
31 pub fn new(file_path: &String) -> Self {
33 let info = Info::new(file_path.clone());
34
35 let mut this = Self {
36 info,
37 file_path: Some(file_path.clone()),
38 file_paths: None,
39 file_paths_dictionary: None,
40 track_ids: None,
41 track_paths: None,
42 duration: 0.0,
43 impulse_response_spec: None,
44 impulse_response_tail_db: None,
45 };
46
47 this.refresh_tracks();
48
49 this
50 }
51
52 pub fn new_from_file_paths(file_paths: &Vec<Vec<String>>) -> Self {
54 let mut file_paths_dictionary = Vec::new();
55 for file_path in file_paths {
58 for path in file_path {
59 if !file_paths_dictionary.contains(path) {
60 file_paths_dictionary.push(path.clone());
61 }
62 }
63 }
64
65 let info = Info::new_from_file_paths(file_paths_dictionary.clone());
66
67 let mut this = Self {
68 info,
69 file_path: None,
70 file_paths: Some(file_paths.clone()),
71 file_paths_dictionary: Some(file_paths_dictionary),
72 track_ids: None,
73 track_paths: None,
74 duration: 0.0,
75 impulse_response_spec: None,
76 impulse_response_tail_db: None,
77 };
78
79 this.refresh_tracks();
80
81 this
82 }
83
84 pub fn refresh_tracks(&mut self) {
91 let mut longest_duration = 0.0;
92 self.impulse_response_spec = None;
93 self.impulse_response_tail_db = None;
94
95 if let Some(file_paths) = &self.file_paths {
96 let mut track_paths: Vec<String> = Vec::new();
98 for file_path in file_paths {
99 let random_number = rand::thread_rng().gen_range(0..file_path.len());
100 let track_path = file_path[random_number].clone();
101
102 let index_in_dictionary = self
103 .file_paths_dictionary
104 .as_ref()
105 .unwrap()
106 .iter()
107 .position(|x| *x == track_path)
108 .unwrap();
109 let duration = self.info.get_duration(index_in_dictionary as u32).unwrap();
110
111 if duration > longest_duration {
112 longest_duration = duration;
113 self.duration = longest_duration;
114 }
115
116 track_paths.push(track_path);
117 }
118
119 self.track_paths = Some(track_paths);
120
121 return;
122 }
123
124 if !self.file_path.is_some() {
125 return;
126 }
127
128 let file_path = self.file_path.as_ref().unwrap();
129 let file = std::fs::File::open(file_path).unwrap();
130
131 let mka: Matroska = Matroska::open(file).expect("Could not open file");
132
133 let mut track_index_array: Vec<u32> = Vec::new();
134 mka.attachments.iter().for_each(|attachment| {
135 if attachment.name == "play_settings.json" {
137 let play_settings: PlaySettingsFile =
139 serde_json::from_slice(&attachment.data).unwrap();
140
141 self.impulse_response_spec = parse_impulse_response_spec(&play_settings);
142 self.impulse_response_tail_db = parse_impulse_response_tail_db(&play_settings);
143
144 match &play_settings {
145 PlaySettingsFile::Legacy(file) => {
146 collect_legacy_tracks(
147 file.settings.inner(),
148 &mut track_index_array,
149 &mut longest_duration,
150 &self.info,
151 &mut self.duration,
152 );
153 }
154 PlaySettingsFile::V1(file) => {
155 collect_tracks_from_ids(
156 &file.settings.inner().tracks,
157 &mut track_index_array,
158 &mut longest_duration,
159 &self.info,
160 &mut self.duration,
161 );
162 }
163 PlaySettingsFile::V2(file) => {
164 collect_tracks_from_ids(
165 &file.settings.inner().tracks,
166 &mut track_index_array,
167 &mut longest_duration,
168 &self.info,
169 &mut self.duration,
170 );
171 }
172 PlaySettingsFile::Unknown { .. } => {
173 error!("Unknown file format");
174 }
175 }
176 }
177 });
178
179 self.track_ids = Some(track_index_array);
180 }
181
182 fn get_audio_settings(file_path: &str) -> Audio {
183 let file = std::fs::File::open(file_path).unwrap();
184
185 let symph = match get_probe_result_from_string(file_path) {
186 Ok(probed) => probed,
187 Err(err) => {
188 warn!("Failed to probe audio settings: {}", err);
189 return Audio {
190 sample_rate: 0.0,
191 channels: 0,
192 bit_depth: None,
193 };
194 }
195 };
196
197 let first_track = match symph.format.tracks().first() {
198 Some(track) => &track.codec_params,
199 None => {
200 warn!("No audio tracks found in {}", file_path);
201 return Audio {
202 sample_rate: 0.0,
203 channels: 0,
204 bit_depth: None,
205 };
206 }
207 };
208
209 let channels = {
210 let channels_option = first_track.channels.unwrap_or(Channels::FRONT_CENTRE);
211 channels_option.iter().count()
212 };
213
214 let mut bit_depth = None;
215
216 let bits_per_sample = first_track
217 .bits_per_sample
218 .or_else(|| sample_format_bits(first_track.sample_format));
219 if let Some(bits) = bits_per_sample {
220 bit_depth = Some(bits as u64);
221 }
222
223 let audio = Audio {
224 sample_rate: first_track.sample_rate.unwrap_or(0) as f64,
225 channels: channels as u64,
226 bit_depth,
227 };
228
229 audio
230
231 }
247
248 pub fn get_impulse_response_spec(&self) -> Option<ImpulseResponseSpec> {
250 self.impulse_response_spec.clone()
251 }
252
253 pub fn get_impulse_response_tail_db(&self) -> Option<f32> {
255 self.impulse_response_tail_db
256 }
257
258 pub fn get_container_path(&self) -> Option<String> {
260 self.file_path.clone()
261 }
262
263 pub fn set_impulse_response_spec(&mut self, spec: ImpulseResponseSpec) {
265 self.impulse_response_spec = Some(spec);
266 }
267
268 pub fn set_impulse_response_tail_db(&mut self, tail_db: f32) {
270 self.impulse_response_tail_db = Some(tail_db);
271 }
272
273 pub fn get_keys(&self) -> Vec<u32> {
275 if let Some(track_paths) = &self.track_paths {
277 return (0..track_paths.len() as u32).collect();
278 }
279
280 if let Some(track_ids) = &self.track_ids {
281 return (0..track_ids.len() as u32).collect();
282 }
283
284 Vec::new()
285 }
286
287 pub fn get_ids(&self) -> Vec<String> {
289 if let Some(track_paths) = &self.track_paths {
290 return track_paths.clone();
291 }
292
293 if let Some(track_ids) = &self.track_ids {
294 return track_ids.into_iter().map(|id| format!("{}", id)).collect();
295 }
296
297 Vec::new()
298 }
299
300 pub fn enumerated_list(&self) -> Vec<(u16, String, Option<u32>)> {
302 let mut list: Vec<(u16, String, Option<u32>)> = Vec::new();
303 if let Some(track_paths) = &self.track_paths {
304 for (index, file_path) in track_paths.iter().enumerate() {
305 list.push((index as u16, String::from(file_path), None));
306 }
307
308 return list;
309 }
310
311 if let Some(track_ids) = &self.track_ids {
312 for (index, track_id) in track_ids.iter().enumerate() {
313 list.push((
314 index as u16,
315 String::from(self.file_path.as_ref().unwrap()),
316 Some(*track_id),
317 ));
318 }
319
320 return list;
321 }
322
323 list
324 }
325
326 pub fn container_track_entries(&self) -> Option<(String, Vec<(u16, u32)>)> {
328 let file_path = self.file_path.as_ref()?;
329 let track_ids = self.track_ids.as_ref()?;
330 let mut entries = Vec::new();
331 for (index, track_id) in track_ids.iter().enumerate() {
332 entries.push((index as u16, *track_id));
333 }
334 Some((file_path.clone(), entries))
335 }
336
337 pub fn get_duration(&self) -> &f64 {
339 &self.duration
340 }
341
342 pub fn get_length(&self) -> usize {
344 if let Some(file_paths) = &self.file_paths {
345 return file_paths.len();
346 }
347
348 if let Some(track_ids) = &self.track_ids {
349 return track_ids.len();
350 }
351
352 0
353 }
354
355 pub fn get_file_paths_dictionary(&self) -> Vec<String> {
357 match &self.file_paths_dictionary {
358 Some(dictionary) => dictionary.to_vec(),
359 None => Vec::new(),
360 }
361 }
362}
363
364fn collect_tracks_from_ids(
365 tracks: &[SettingsTrack],
366 track_index_array: &mut Vec<u32>,
367 longest_duration: &mut f64,
368 info: &Info,
369 total_duration: &mut f64,
370) {
371 for track in tracks {
372 if track.ids.is_empty() {
373 continue;
374 }
375 let random_number = rand::thread_rng().gen_range(0..track.ids.len());
376 let index = track.ids[random_number];
377 if let Some(track_duration) = info.get_duration(index) {
378 if track_duration > *longest_duration {
379 *longest_duration = track_duration;
380 *total_duration = *longest_duration;
381 }
382 }
383 track_index_array.push(index);
384 }
385}
386
387fn collect_legacy_tracks(
388 settings: &PlaySettingsLegacy,
389 track_index_array: &mut Vec<u32>,
390 longest_duration: &mut f64,
391 info: &Info,
392 total_duration: &mut f64,
393) {
394 for track in &settings.tracks {
395 let (Some(starting_index), Some(length)) = (track.starting_index, track.length) else {
396 continue;
397 };
398 let starting_index = starting_index + 1;
399 let index = rand::thread_rng().gen_range(starting_index..(starting_index + length));
400 if let Some(track_duration) = info.get_duration(index) {
401 if track_duration > *longest_duration {
402 *longest_duration = track_duration;
403 *total_duration = *longest_duration;
404 }
405 }
406 track_index_array.push(index);
407 }
408}
409
410fn sample_format_bits(sample_format: Option<SampleFormat>) -> Option<u32> {
411 match sample_format {
412 Some(SampleFormat::U8 | SampleFormat::S8) => Some(8),
413 Some(SampleFormat::U16 | SampleFormat::S16) => Some(16),
414 Some(SampleFormat::U24 | SampleFormat::S24) => Some(24),
415 Some(SampleFormat::U32 | SampleFormat::S32 | SampleFormat::F32) => Some(32),
416 Some(SampleFormat::F64) => Some(64),
417 None => None,
418 }
419}